Jump to content

_SearchProcessMemory()


Oldschool
 Share

Recommended Posts

Big thanks to GrandMasterCheater FreeFry for the original calc mem reader example found here:

http://www.autoitscript.com/forum/index.ph...st&p=480298

& GrandMasterCheater Nomad for his VirtualQueryEX and other mem functions from:

http://www.autoitscript.com/forum/index.ph...c=28351&hl=

Startup the Calculator and enter 453

Posted Image

Download the script and execute, you will see this:

Posted Image

Right now a complete process mem scan is taking 20 seconds, which is ridiculously long, as other memory scanners make this same scan in under 1 second. Anyone who helps make it faster will get kudos.

EDIT:

Alright, the function is lightning fast now, thanks to Siao. File has been updated.

ProcMemSearch7.au3

Edited by Oldschool
Link to comment
Share on other sites

Anyone who helps make it faster will get kudos.

If anyone puts some serious attention to it, kudos won't be enough :) For example if I play around with attached file to make it work as I imagine, more than 90% of the code would be changed/added new. Because right now it isn't really polished UDF which you can start optimizing. All these Exits should be changed to Returns, the function should obviously take some parameters, at least: 1) process, 2) value to search, then probably 3) value type: byte/short/int32/int64/float32/float64/string/unicode string (probably only some of those for starters), also maybe decide on 4) search return type: first match only/array of matches.

As for how to make it faster...

instead of iterating through the page byte by byte with "$r = 1 To $mbi[3]", read whole page at once as binary, and do StringInStr() for search value in it (or if you want to read it one-by-one and compare manually like you do now, use BinaryMid), and compute the address with simple math. Point being, right now you're repeating some actions which aren't that cheap to begin with (such as dllcall a ReadProcessMemory and that func of yours) over and over again, which could be avoided or at least minimized.

instead of converting read value with some _UnicodeToStr function (which looks pretty weird - it's not a proper Unicode-to-Ansi conversion - and unoptimized in itself) to compare it with search value for each iteration, make search value as hex string depending on the search type only once, in the beginning. Generating such hex string is easy with two dllstructs (one of chosen type ("str", "wstr", "float", "double", etc) to write, and one of "byte" type to read back, use the pointer of the first one when creating it), or, alternatively you can do most of these conversions without structs, using functions like StringToBinary (check it's params), and Binary(); and Hex() the resulting binary. Or you can keep it binary if you don't want to use StringInStr approach.

Edited by Siao

"be smart, drink your wine"

Link to comment
Share on other sites

Here's what I was talking about as far as "faster" goes, in case my mumbling above isn't clear enuff:

In that attached example, replace

;In this particular case we know what we looking for, so we will narrow down the field to speed up the search
        If $mbi[4] = 4096 And $mbi[5] = 4 And $mbi[6] = 16777216 Then;a.k.a MEM_COMMIT + PAGE_READWRITE + MEM_IMAGE
            For $r = 1 To $mbi[3]
                Local $Address = $mbi[0]+$r
                DllCall("Kernel32.dll", 'int', 'ReadProcessMemory', 'int', $procHwnd, 'int', $Address, 'ptr', DllStructGetPtr($pBuffer), 'int', DllStructGetSize($pBuffer), 'int', '')
                If _UnicodeToStr(DllStructGetData($pBuffer, 1)) = $SearchValue Then MsgBox(0, "Found Match", Hex($Address)&' = '&$SearchValue,1); convert the unicode text(as it turned out to be stored as) to normal text and display it
                $pBuffer = DllStructCreate("byte[8]")
            Next
        EndIf
        $i += $mbi[3]

with

$SearchValue = Hex(StringToBinary("453", 2));;unicode string hex to search for
        If $mbi[4] = 4096 And $mbi[5] = 4 And $mbi[6] = 16777216 Then;a.k.a MEM_COMMIT + PAGE_READWRITE + MEM_IMAGE
            $pBuffer = DllStructCreate("byte[" & $mbi[3] & "]")
            DllCall("Kernel32.dll", 'int', 'ReadProcessMemory', 'int', $procHwnd, 'int', $mbi[0], 'ptr', DllStructGetPtr($pBuffer), 'int', DllStructGetSize($pBuffer), 'int', '')
            $x = StringInStr(DllStructGetData($pBuffer, 1), $SearchValue)
            If Mod($x, 2) Then;;if aligned at byte (and obviously <> 0)
                $Address = ($x - 3) / 2 + $i;;subtract 2 for "0x", 1 to make 0-based; and divide by 2 to get binary offset, then add current page address
                Return Hex($Address)&' = '&"453 (Unicode)"
            EndIf
        EndIf
        $i += $mbi[3]

... as a proof of concept.

The rest is up to you, as I don't intend to steal your show.

Edited by Siao

"be smart, drink your wine"

Link to comment
Share on other sites

If anyone puts some serious attention to it, kudos won't be enough :) For example if I play around with attached file to make it work as I imagine, more than 90% of the code would be changed/added new. Because right now it isn't really polished UDF which you can start optimizing. All these Exits should be changed to Returns, the function should obviously take some parameters, at least: 1) process, 2) value to search, then probably 3) value type: byte/short/int32/int64/float32/float64/string/unicode string (probably only some of those for starters), also maybe decide on 4) search return type: first match only/array of matches.

Very true indeed, this is just a quick example...

instead of iterating through the page byte by byte with "$r = 1 To $mbi[3]", read whole page at once as binary, and do StringInStr() for search value in it (or if you want to read it one-by-one and compare manually like you do now, use BinaryMid), and compute the address with simple math. Point being, right now you're repeating some actions which aren't that cheap to begin with (such as dllcall a ReadProcessMemory and that func of yours) over and over again, which could be avoided or at least minimized.

instead of converting read value with some _UnicodeToStr function (which looks pretty weird - it's not a proper Unicode-to-Ansi conversion - and unoptimized in itself) to compare it with search value for each iteration, make search value as hex string depending on the search type only once, in the beginning. Generating such hex string is easy with two dllstructs (one of chosen type ("str", "wstr", "float", "double", etc) to write, and one of "byte" type to read back, use the pointer of the first one when creating it), or, alternatively you can do most of these conversions without structs, using functions like StringToBinary (check it's params), and Binary(); and Hex() the resulting binary. Or you can keep it binary if you don't want to use StringInStr approach.

Cool, thank you for the advice and the examples below, I got to play with it a little to understand how you calculated the location of the address containing the value. I was trying to do something similar originally, but could not get it to work.
Link to comment
Share on other sites

@oldschool

Why do you have a COM object error routine in there ?

There are no objects declared in your script !!

So these can be deleted as far as I can see.

Further when running the script I end up with and error message saying :

"_WinAPI_OpenProcess:standard:access denied"

So you still have some job to do.

regards

ptrex

Link to comment
Share on other sites

@oldschool

Why do you have a COM object error routine in there ?

There are no objects declared in your script !!

So these can be deleted as far as I can see.

Further when running the script I end up with and error message saying :

"_WinAPI_OpenProcess:standard:access denied"

So you still have some job to do.

regards

ptrex

Just leftovers from non related functions I was using while testing my internal script.

I don't know why you get an error from Win_API. You are right about the work to be done, I do need to turn this into a proper UDF like was suggested. This is something I was twiddling with for a couple of days, and got excited and posted when I got it to work. I do have some time on my hands, so I will get it closer to local standards soon.

Link to comment
Share on other sites

@Siao

Kudos as promised to Siao. This puppy is lightning fast now...not finished with the datatypes yet, but the structure is laid out much more UDFish now.

Local $Output
        
        Select
            Case $dType = 1
                $SearchV = Hex(StringToBinary($SearchValue, 2));;unicode string hex to search for
                ;In this particular case we know what we looking for, so we will narrow down the field to speed up the search
                If $mbi[4] = 4096 And $mbi[5] = 4 And $mbi[6] = 16777216 Then ;a.k.a MEM_COMMIT + PAGE_READWRITE + MEM_IMAGE
                    Local $pBuffer = DllStructCreate("byte["&$mbi[3]&"]")
                    DllCall("Kernel32.dll", 'int', 'ReadProcessMemory', 'int', $procHwnd, 'int', $mbi[0], 'ptr', DllStructGetPtr($pBuffer), 'int', DllStructGetSize($pBuffer), 'int', '')
                    $x = StringInStr(DllStructGetData($pBuffer, 1), $SearchV)
                    If @Error Then SetError(@Error + 1)
                    If Mod($x, 2) Then ;if aligned at byte (and obviously <> 0)
                        $Address = ($x - 3) / 2 + $i ;subtract 2 for "0x", 1 to make 0-based; and divide by 2 to get binary offset, then add current page address
                        $Output &= Hex($Address)& @CR &$SearchValue
                    EndIf               
                EndIf
                $pBuffer = ""
                
            Case $dType = 2
                
            Case $dType = 3
                
        EndSelect 
        
        $i += $mbi[3]
        
    WEnd

    $retArray = StringSplit($Output, @CRLF)
    Return $retArray

Returns in a snap!!!

Posted Image

EDIT: Just want to add that the way you get that address location using StringInStr is brilliant! I forgot that StringInStr actually returns a location (or maybe I never paid enough attention to know). :) In any event, you are the man.

I updates the OP with the current version.

Edited by Oldschool
Link to comment
Share on other sites

@oldschool

First of all you need to add : #Include <Constants.au3> on the top,

in order to properly declare the $PROCESS_ALL_ACCESS

Otherwise this will not run in AU3 - 3.2.11.0 and higher.

Secondly.

Even if it is declared properly, it does not return a proper result.

At least not over here.

regards

ptrex

Link to comment
Share on other sites

Secondly.

Even if it is declared properly, it does not return a proper result.

At least not over here.

It works fine over here on 3.2.10.0, I took that structure right out of FreeFry's post (check the OP for link), so I assume it must have worked for him also...

Must be the version you are running. You are running the latest?

Just in case, here is an alternative:

$iv_Pid = ProcessExists("calc.exe")
    $iv_DesiredAccess = 0x1F0FFF
    $av_OpenProcess = DllCall('Kernel32.dll', 'int', 'OpenProcess', 'int', $iv_DesiredAccess, 'int', 1, 'int', $iv_Pid)
    $procHwnd = $av_OpenProcess[0]
    If Not $procHwnd Then MsgBox(0, "","Error while getting process handle!")
Link to comment
Share on other sites

That has to do with the system ptrex is running on, not AutoIt version.

For me (XPsp2 with no security bs) the example with PROCESS_ALL_ACCESS works just fine.

As far as memory search goes, you don't need all access rights, just PROCESS_QUERY_INFORMATION for VirtualQueryEx and PROCESS_VM_READ for ReadProcessMemory. That would be $iv_DesiredAccess = 0x0410

Although that probably isn't guaranteed to work for any process on any system too.

Set the $fDebugPriv param to True in _WinAPI_OpenProcess(), this should grant you any access rights you want.

"be smart, drink your wine"

Link to comment
Share on other sites

@Siao

I've been giving you structure some more thought, and there is a slight problem with StringInStr...->occurrence.

If you got more than 1 identical value in the block they will be overlooked. I think there might be a string function by weaponX somewhere that solves that, but I'm not sure. Godda dig around.

Link to comment
Share on other sites

Solved

Select
            Case $dType = 1
                $SearchV = Hex(StringToBinary($SearchValue, 2));;unicode string hex to search for
                ;In this particular case we know what we looking for, so we will narrow down the field to speed up the search
                If $mbi[4] = 4096 And $mbi[5] = 4 And $mbi[6] = 16777216 Then ;a.k.a MEM_COMMIT + PAGE_READWRITE + MEM_IMAGE
                    Local $pBuffer = DllStructCreate("byte["&$mbi[3]&"]")
                    DllCall("Kernel32.dll", 'int', 'ReadProcessMemory', 'int', $procHwnd, 'int', $mbi[0], 'ptr', DllStructGetPtr($pBuffer), 'int', DllStructGetSize($pBuffer), 'int', '')
                    $oc = 1
                    While 1
                        $x = StringInStr(DllStructGetData($pBuffer, 1), $SearchV, 0, $oc)
                        If @Error Then SetError(@Error + 1)
                        If Mod($x, 2) Then ;if aligned at byte (and obviously <> 0)
                            $Address = ($x - 3) / 2 + $i 
                            $Output &= Hex($Address) & @CR & $SearchValue & @CR
                            $oc +=1
                         Else
                            ExitLoop
                        EndIf
                    WEnd
                EndIf
                $pBuffer = ""

Posted Image

Posted Image

Link to comment
Share on other sites

@ptrex

I moved the script into 1 of my VMs with an older stripped XP OS, and I'm getting the same problem you were with _WinAPI_OpenProcess there. The interesting thing is it does not even light up blue in SciTe, as it does on on other machines...all with 3.2.10.0 and separately downloaded SciTe.

Did you figure out what the problem was?

Link to comment
Share on other sites

  • 2 weeks later...

@oldschool

This one runs fine on my machine, using au3 - 3.2.11.4.

#Include <WinAPI.au3>
#Include <Constants.au3>
#Include <Array.au3>

HotKeySet("{ESC}", "_Exit")

;If Not ProcessExists("calc.exe") Then Exit ; exit if calculator is not running;
    ;$procHwnd = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, False, ProcessExists("calc.exe"))
;If Not $procHwnd Then _Exit("Error while getting process handle!") ; if we didn't get a valid 'access' handle then exit

    $iv_Pid = ProcessExists("calc.exe")
    $iv_DesiredAccess = 0x1F0FFF
    $av_OpenProcess = DllCall('Kernel32.dll', 'int', 'OpenProcess', 'int', $iv_DesiredAccess, 'int', 1, 'int', $iv_Pid)
    $procHwnd = $av_OpenProcess[0]
    If Not $procHwnd Then MsgBox(0, "","Error while getting process handle!")

$SearchValue = 453  
$dType = 1

$input = _SearchProcessMemory($procHwnd, $SearchValue, $dType)
_ArrayDisplay($input)


Func _SearchProcessMemory($procHwnd, $SearchValue, $dType)
    
    ;GetSystemInfo
    $systemInfo = DllStructCreate ("short;short;dword;int;int;dword;dword;dword;dword;short;short")
    DllCall ("Kernel32.dll", "int", "GetSystemInfo", "ptr", DllStructGetPtr($systemInfo))
    $lpMinimumApplicationAddress = DllStructGetData ($systemInfo, 4)
    $lpMaximumApplicationAddress = DllStructGetData ($systemInfo, 5)
    $systemInfo=""

    $i = $lpMinimumApplicationAddress
    While $i < $lpMaximumApplicationAddress
        
        Local $mbi[7] ; MEMORY_BASIC_INFORMATION Structure
        Local $v_Buffer = DllStructCreate('dword;dword;dword;dword;dword;dword;dword')      
        If @Error Then SetError(@Error + 1)
        
        DllCall('Kernel32.dll', 'int', 'VirtualQueryEx', 'int', $procHwnd, 'int', $i, 'ptr', DllStructGetPtr($v_Buffer), 'int', DllStructGetSize($v_Buffer))
        
        If Not @Error Then          
            For $j = 0 to 6             
                $mbi[$j] = StringStripWS(DllStructGetData($v_Buffer, ($j + 1)),3)               
            Next            
        Else
            SetError(6)
        EndIf
        ;_ArrayDisplay($mbi)
        
        Local $Output
        
        Select
            Case $dType = 1
                $SearchV = Hex(StringToBinary($SearchValue, 2));;unicode string hex to search for
                ;In this particular case we know what we looking for, so we will narrow down the field to speed up the search
                If $mbi[4] = 4096 And $mbi[5] = 4 And $mbi[6] = 16777216 Then ;a.k.a MEM_COMMIT + PAGE_READWRITE + MEM_IMAGE
                    Local $pBuffer = DllStructCreate("byte["&$mbi[3]&"]")
                    DllCall("Kernel32.dll", 'int', 'ReadProcessMemory', 'int', $procHwnd, 'int', $mbi[0], 'ptr', DllStructGetPtr($pBuffer), 'int', DllStructGetSize($pBuffer), 'int', '')
                    $oc = 1
                    While 1
                        $x = StringInStr(DllStructGetData($pBuffer, 1), $SearchV, 0, $oc)
                        If @Error Then SetError(@Error + 1)
                        If Mod($x, 2) Then ;if aligned at byte (and obviously <> 0)
                            $Address = ($x - 3) / 2 + $i 
                            $Output &= Hex($Address) & @CR & $SearchValue & @CR
                            $oc +=1
                         Else
                            ExitLoop
                        EndIf
                    WEnd
                EndIf
                $pBuffer = ""
                
            Case $dType = 2
                
            Case $dType = 3
                
        EndSelect 
        
        $i += $mbi[3]
        
    WEnd

    $retArray = StringSplit($Output, @CRLF)
    Return $retArray
EndFunc


Func _Exit($s_Msg="")
   
    MsgBox(0, "Error", $s_Msg)
    Exit
   
EndFunc

regards,

ptrex

Link to comment
Share on other sites

  • 2 years later...

TheOnlyOne,

Don't bother with this function. It's terribly flawed (not x64 safe, allocates memory without concern to how huge allocations could be, uses StringinString whereas 'memchr' (in NTDLL.DLL) will ALWAYS get the job done a zillion times faster, etc), plus the above function is limited to only one thing - searching for bytes.

The reason you'll get more results in CheatEngine is because it doesn't screen memory as specifically as this does. You'll also be able to scan for other data types - 16-bit, 32-bit, 64-bit data, strings and so forth.

Now, as for CheatEngine - its very neat and all, but it also has its shortcomings in scanning memory - it returns >8 bit data as unaligned addresses more often than not, and many times misses the correct aligned memory addresses in the process. Its one thing to scan for values in code segments (here things won't necessarily be aligned), but that's not where you should be looking for stored values. Variables in memory will most ALWAYS be aligned, which is why I think CheatEngine is retarded (and why it misses the mark half the time). Plus it will give locations located in DLL modules that have nothing to do with a program's variables. Because of this, I've written my own Memory Scanner, and made sure it looks for aligned addresses. It gives a much better list of 'possible locations' then what CheatEngine gives.

If however you are scanning for strings or series of data bytes, it works well (though it doesn't consider the possibility that there can be another match starting again within the match it found, and instead just skips over the whole length of the match). You'll also get lucky many times with repeated scans of memory after values change because *eventually* it'll find something that matches (on an aligned boundary).

Sorry if I'm going off on a rant haha. Don't expect this function to replace CheatEngine, it can't. Try writing your own scanner. Its hella fun (not) :mellow:

*edit: I just realized today that CheatEngine does allow aligned-scans (woops) if you check the 'FastScan' checkbox. Oops - I take back what I said.

Edited by Ascend4nt
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...