joakim Posted May 12, 2010 Posted May 12, 2010 Hi Autoiters. I want to calculate the checksum of a PE file with the function MapFileAndCheckSum found in imagehlp.dll. But I am stuck and always get returned a "1" which indicates a file open failure. This is what I've got so far; Dim $outHeaderSum Dim $outCheckSum Dim $input = '"' & @ScriptDir & "\1.exe" & '"' ;Dim $input = @ScriptDir & "\1.exe" ;Dim $input = "1.exe" _MapFileAndCheckSum($input) MsgBox( 0, "HeaderSum:", $outHeaderSum) MsgBox( 0, "CheckSum:", $outCheckSum) Func _MapFileAndCheckSum($input) Local $aResult $aResult = DllCall("imagehlp.dll", "long" , "MapFileAndCheckSum" , "str" ,$input , "dword" ,$outHeaderSum, "dword" ,$outCheckSum) If @error Then Return SetError(@error, 0, 0) MsgBox( 0, "$aResult[0] returned:", $aResult[0]) Return $aResult[0] EndFunc What am I doing wrong? Do I need to use the function CheckSumMappedFile instead? Joakim
hawky358 Posted May 12, 2010 Posted May 12, 2010 (edited) Since $outHeaderSum and $outCheckSum are pointers (outputs) you need to define them as such for Autoit to use them. $outHeaderSum = DLLStructCreate("dword") ;Here you create a structure for a pointer to write into $outCheckSum = DLLStructCreate("dword") Dim $input = '' & @ScriptDir & "\1.xls" & '' ;Dim $input = @ScriptDir & "\1.exe" ;Dim $input = "1.exe" _MapFileAndCheckSum($input) MsgBox( 0, "HeaderSum:", $outHeaderSum) MsgBox( 0, "CheckSum:", $outCheckSum) Func _MapFileAndCheckSum($input) Local $aResult $aResult = DllCall("imagehlp.dll", "long" , "MapFileAndCheckSum" , "str" ,$input , "ptr" ,DllStructGetPtr($outHeaderSum), "ptr" ,DllStructGetPtr($outCheckSum)) ; NOTE THAT THE TYPES CHANGED TO ptr If @error Then Return SetError(@error, 0, 0) MsgBox( 0, "$aResult[0] returned:", $aResult[0]) $outHeaderSum = DllStructGetData($outHeaderSum, 1) ;<Here you read out of your structure again $outCheckSum = DllStructGetData($outCheckSum,1) Return $aResult EndFunc Check out the help for DLLStructCreate, DllStructGetPtr and DllStructGetData. I didn't check the .dll functions so I'm not sure what the checksum returns, maybe it needs to be processed further. Edit: Oh I forgot to mention that you need to take out the starting and trailing " from the file path Edited May 12, 2010 by hawky358
joakim Posted May 12, 2010 Author Posted May 12, 2010 Thank you very much. It now works. $outHeaderSum is found at hex offset 150 in any pe file, whereas $outCheckSum is the actual checksum of a pe file. It is returned in hex in reverse order. Btw, do you happen to know how to write arbitrary values (like the checksum) to a specific offset inside a binary file (instead of rewriting the whole file)? Joakim
joakim Posted May 14, 2010 Author Posted May 14, 2010 Here is the source of my command line checksum fixer for exe and dll files; expandcollapse popupIf $cmdline[0] = 0 Then MsgBox( 0, "Hey you!!!", "This is command line tool - get me a file name as input.") Exit EndIf If $cmdline[0] <> 1 Then MsgBox( 0, "Hey you!!!", "I only take 1 parameter (a file name).") Exit EndIf $input = $cmdline[1] $struct_HeaderSum = "dword" $struct_CheckSum = "dword" $outHeaderSum = DLLStructCreate($struct_HeaderSum) $outCheckSum = DLLStructCreate($struct_CheckSum) _MapFileAndCheckSum($input) If Hex($outHeaderSum) = Hex($outCheckSum) Then MsgBox( 0, "Checksum Correct", "Nothing to do - exiting..", 2) Exit EndIf ;Setting the correct checksum in the right format $1 = StringMid(Hex($outCheckSum), 7, 2) $2 = StringMid(Hex($outCheckSum), 5, 2) $3 = StringMid(Hex($outCheckSum), 3, 2) $4 = StringMid(Hex($outCheckSum), 1, 2) $rev_checksum = $1 & $2 & $3 & $4 ;Get signature at offset 150 and prepare replacement $openinput = FileOpen($input, 16) FileSetPos($openinput, Dec(150), 0) $readinput = FileRead($openinput) $search = StringMid($readinput, 3, 32) $replace = $rev_checksum & StringMid($search, 9, 24) FileFlush($openinput) FileClose($openinput) $openinput = FileOpen($input, 16) $readinput = FileRead($openinput) $content = StringReplace($readinput,$search,$replace) ;Write file with correct checksum $inputlen = StringLen($input) $inputcut = StringMid($input, 1, $inputlen - 4) $newfile = $inputcut & ".bin" $open_new = FileOpen($newfile, 18) FileWrite($open_new,$content) FileClose($open_new) FileClose($openinput) ;Backup old file $inputlen = StringLen($input) $inputcut = StringMid($input, 1, $inputlen - 4) FileMove($input, $inputcut & ".bak", 1) $outputlen = StringLen($newfile) $outputcut = StringMid($newfile, 1, $outputlen - 4) FileMove($newfile, $outputcut & ".exe", 1) Func _MapFileAndCheckSum($input) Local $aResult $aResult = DllCall("imagehlp.dll", "long" , "MapFileAndCheckSum" , "str" ,$input , "ptr" ,DllStructGetPtr($outHeaderSum), "ptr" ,DllStructGetPtr($outCheckSum)) If @error Then Return SetError(@error, 0, 0) If $aResult[0] <> 0 Then MsgBox( 0, "Error Occured", "$aResult[0] returned: " & $aResult[0]) Exit EndIf $outHeaderSum = DllStructGetData($outHeaderSum, 1) $outCheckSum = DllStructGetData($outCheckSum,1) Return $aResult EndFunc Nothing fancy and there probably should have been more error checking. Joakim
JFX Posted May 16, 2010 Posted May 16, 2010 $outHeaderSum is found at hex offset 150 in any pe file Hi Joakim, It's not on the same adress in any PE file. This should helps reading the right adress. Func _PECheckSumAdress($PEfile) If FileExists($PEfile) Then $File = FileOpen($PEfile, 16) $var = FileRead($File, 0x3f + 1) FileClose($File) $Adress = Dec(_ReverseHex(StringRight($var, 4 * 2))) + Dec(58); Checksum Adress Is a 4byte value at adress 0x3C Return $Adress EndIf EndFunc Func _ReverseHex($Input) Local $Output = '' IF Round(stringlen($Input) / 2,0) <> stringlen($Input) / 2 Then ; String must be a multiple of 2, if not add a Zero to beginning $Input = '0' & $Input EndIf For $i = 2 to StringLen($Input) Step 2 $Output = $Output & StringRight($Input,2) $Input = StringTrimRight($Input,2) Next Return $Output EndFunc
joakim Posted May 16, 2010 Author Posted May 16, 2010 I thought the offsets of the components of IMAGE_OPTIONAL_HEADER was somewhat fixed. Do you have an example of when the offset of checksum would be different? What do you mean by address of 0x3c? It's too late for me to dig into it now. Looking more to it tomorrow. Btw, the code I posted works on my 32-bit Intel (could that be the difference?). Joakim
JFX Posted May 17, 2010 Posted May 17, 2010 Sorry for my bad description, as example take imagex.exe for win 7 waik is checksum is located on offset 138 hexadecimal On address 0x3C you have the offset of the PE Header in little-endian format (_ReverseHex) Going 88 bytes (+ Dec(58)) further from that point you have the right address of the pe checksum
joakim Posted May 17, 2010 Author Posted May 17, 2010 @JFX You are right. Thanks. Maybe I assumed that both the size and the offset were fixed, although just one of them are fixed. I will now assume that the pointer to the pe header are fixed and always at 0x3c. When I am back from vacation I will update the code here as well as the patcher for winload.exe (load alternative SYSTEM hive). Joakim
joakim Posted May 18, 2010 Author Posted May 18, 2010 I just managed to squeese in some time before the holiday, to modify the srcipt; expandcollapse popupIf $cmdline[0] = 0 Then MsgBox( 0, "Hey you!!!", "This is command line tool - get me a file name as input.") Exit EndIf If $cmdline[0] <> 1 Then MsgBox( 0, "Hey you!!!", "I only take 1 parameter (a file name).") Exit EndIf $input = $cmdline[1] $struct_HeaderSum = "dword" $struct_CheckSum = "dword" $outHeaderSum = DLLStructCreate($struct_HeaderSum) $outCheckSum = DLLStructCreate($struct_CheckSum) _MapFileAndCheckSum($input) If Hex($outHeaderSum) = Hex($outCheckSum) Then MsgBox( 0, "Checksum Correct", "Nothing to do - exiting..", 2) Exit EndIf ;Setting the correct checksum in the right format $1 = StringMid(Hex($outCheckSum), 7, 2) $2 = StringMid(Hex($outCheckSum), 5, 2) $3 = StringMid(Hex($outCheckSum), 3, 2) $4 = StringMid(Hex($outCheckSum), 1, 2) $rev_checksum = Binary('0x' & $1 & $2 & $3 & $4) MsgBox( 0, "", $rev_checksum) ;Detect PE header and correct checksum Dim $PEfile, $AdressDec _PECheckSumAdress($input) $openinput = FileOpen($input, 16) $Adress = $AdressDec FileFlush($openinput) FileClose($openinput) $openinput = FileOpen($input, 16) $inputlen = StringLen($input) $inputcut = StringMid($input, 1, $inputlen - 4) $newfile = $inputcut & ".bin" ;1 $open_new = FileOpen($newfile, 18) $write1 = FileRead($openinput, $Adress) FileWrite($open_new,$write1) ;2 $write2 = $rev_checksum FileWrite($open_new,$write2) FileClose($open_new) ;3 $open_new = FileOpen($newfile, 17) $part2 = FileGetSize($input) - ($Adress + 4) FileSetPos($openinput, $Adress + 4, 0) $write3 = FileRead($openinput, $part2) FileWrite($open_new,$write3) FileClose($open_new) FileFlush($openinput) FileClose($openinput) Dim $renamedout, $renamedin Dim $extin = "bak" Dim $extout = "exe" _inprename($input, $extin) _outprename($newfile, $extout) Func _outprename($newfile, $extout) Local $outputlen = StringLen($newfile) Local $outputcut = StringMid($newfile, 1, $outputlen - 3) Local $renamedout = $outputcut & $extout FileMove($newfile, $renamedout, 1) EndFunc Func _inprename($input, $extin) Local $inputlen = StringLen($input) Local $inputcut = StringMid($input, 1, $inputlen - 3) Local $renamedin = $inputcut & $extin FileMove($input, $renamedin, 1) EndFunc Func _MapFileAndCheckSum($input) Local $aResult $aResult = DllCall("imagehlp.dll", "long" , "MapFileAndCheckSum" , "str" ,$input , "ptr" ,DllStructGetPtr($outHeaderSum), "ptr" ,DllStructGetPtr($outCheckSum)) If @error Then Return SetError(@error, 0, 0) If $aResult[0] <> 0 Then MsgBox( 0, "Error Occured", "$aResult[0] returned: " & $aResult[0]) Exit EndIf $outHeaderSum = DllStructGetData($outHeaderSum, 1) $outCheckSum = DllStructGetData($outCheckSum,1) Return $aResult EndFunc Func _PECheckSumAdress($PEfile) If FileExists($PEfile) Then $File = FileOpen($PEfile, 16) $var = FileRead($File, 0x3f + 1) FileClose($File) $AdressDec = Dec(_ReverseHex(StringRight($var, 4 * 2))) + Dec(58); Checksum Adress Is a 4byte value at adress 0x3C Return $AdressDec EndIf EndFunc Func _ReverseHex($Input) Local $Output = '' IF Round(stringlen($Input) / 2,0) <> stringlen($Input) / 2 Then ; String must be a multiple of 2, if not add a Zero to beginning $Input = '0' & $Input EndIf For $i = 2 to StringLen($Input) Step 2 $Output = $Output & StringRight($Input,2) $Input = StringTrimRight($Input,2) Next Return $Output EndFunc I changed it a bit and did the reassembly of the final binary in 3 stages. First part is up the offset of the checksum. Then in the second part the new checksum is added after the first part. Then finally the third part, starting after the checksum and to the end of file, is added after the second part. I suppose a simpler search/replace routine would be better, but could not figure out a working way. Joakim
luger Posted June 4, 2023 Posted June 4, 2023 (edited) Global $input = "" ConsoleWrite(_PeCheckSumAdress($input)) Func _PeCheckSumAdress($input) Local $PeHeaderOffset = Hex(0x3C, 2) Local $CheckSumOffset = Dec(58) Local $reverse, $hex_reversed $File = FileOpen($input, 16) FileSetPos($File, Dec($PeHeaderOffset), 1) $reverse = Hex(FileRead($File, 4)) For $i=7 To 0 Step -2 $hex_reversed &= StringMid($reverse, $i, 2) Next $Adress = Dec($hex_reversed) + $CheckSumOffset FileClose($File) Return $Adress EndFunc Shortened and remastered version "_PeCheckSumAdress($input)" Edited June 4, 2023 by luger
luger Posted June 4, 2023 Posted June 4, 2023 (edited) $input = "" ConsoleWrite(_MapFileAndCheckSum($input)[0] & @CRLF) ConsoleWrite(_MapFileAndCheckSum($input)[1] & @CRLF) ConsoleWrite(_MapFileAndCheckSum($input)[2] & @CRLF) Func _MapFileAndCheckSum($input) Local $sonuc[3], $HeaderSum, $CheckSum $dll = DllOpen("imagehlp.dll") $HeaderSum = DLLStructCreate("dword") $CheckSum = DLLStructCreate("dword") $sonuc[0] = DllCall($dll, "long", "MapFileAndCheckSum", "str", $input, "ptr", DllStructGetPtr($HeaderSum), "ptr", DllStructGetPtr($CheckSum))[0] DllClose($dll) $sonuc[1] = Hex(DllStructGetData($HeaderSum, 1), 8) $sonuc[2] = Hex(DllStructGetData($CheckSum,1), 8) Return $sonuc EndFunc Shortened and remastered version "Func _MapFileAndCheckSum($input)" Edited June 5, 2023 by luger
argumentum Posted June 4, 2023 Posted June 4, 2023 6 hours ago, Andreik said: grave digging ... the way I see it, if the context is relevant, opening a new thread would have to bring context to it. This way the context is historically in the same place, so is not unwelcomed. Now if it was a question then yes, open a new thread. My 2 cents, I know that your position is the preferred one Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Andreik Posted June 4, 2023 Posted June 4, 2023 3 minutes ago, argumentum said: ... the way I see it, if the context is relevant, opening a new thread would have to bring context to it. This way the context is historically in the same place, so is not unwelcomed. Now if it was a question then yes, open a new thread. My 2 cents, I know that your position is the preferred one This is how I see: it's not relevant for OP anymore after 13 years. Probably it would be better to open a new thread providing the (better) code and eventually link the threads to explain what kind of issues might solve the code. But as you said, our opinions are not necessarily the best, there are forum guidelines.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now