Jump to content

DllCall - imagehlp.dll - MapFileAndCheckSum


Recommended Posts

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

Link to comment
Share on other sites

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 by hawky358
Link to comment
Share on other sites

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

Link to comment
Share on other sites

Here is the source of my command line checksum fixer for exe and dll files;

If $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

Link to comment
Share on other sites

$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
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

@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

Link to comment
Share on other sites

I just managed to squeese in some time before the holiday, to modify the srcipt;

If $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

Link to comment
Share on other sites

  • 13 years later...
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 by luger
Link to comment
Share on other sites

$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 by luger
Link to comment
Share on other sites

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.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

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.

When the words fail... music speaks.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...