ReadProcessMemory... :( Please Help!

Just starting out? Need help? Post your questions and find answers here.
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

ReadProcessMemory... :( Please Help!

Post by abystus »

I am having some problems with ReadProcessMemory returning the value at the address specified. Yes the code below is a compilation of many examples that i have tried to get working and for the most part works except getting a correct return value. The example below displays the same buffer content (supposedly return value) for every address given to it even if i manually change the address. Also maybe someone could provide a better in-depth representation of what exactly each parameter of this Api is for? Any help is appreciated since this Api thing is giving me a headache...

-Abystus


OpenWindow(0,300,300,800,500,"Memster",#PB_Window_SystemMenu)
ButtonGadget(0,10,10,200,20, "Read Memory")
EditorGadget(1, 10, 35, 300, 400)
SetForegroundWindow_(WindowID(0))

Procedure ReadMemory()
HWND = FindWindow_(NULL, "Calculator")
si.SYSTEM_INFO
*mbi.MEMORY_BASIC_INFORMATION
ret = 0
pid = 0

GetWindowThreadProcessId_(HWND, @pid)
;Opens Process With full access
hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, #False, pid);

;Grab System Information
GetSystemInfo_(si)

;Find Memory Bounds
lpMin = si\lpMinimumApplicationAddress
lpMax = si\lpMaximumApplicationAddress
lLenMBI = SizeOf(MEMORY_BASIC_INFORMATION )

adr=AllocateMemory(28)
*mbi=adr

;Do the Scan
For x = lpMin To lpMax
WaitWindowEvent()
ret = VirtualQueryEx_(hProcess, lpMin, adr,lLenMBI )
If ret = lLenMBI
If *mbi\RegionSize > 0
sBuffer = AllocateMemory(*mbi\RegionSize)
res = ReadProcessMemory_(hProcess, *mbi\BaseAddress, sBuffer, *mbi\RegionSize, @lWritten)
If lWritten>0 And res>0
SetGadgetText(1, GetGadgetText(1) + Str(sBuffer) + #CRLF$ )
EndIf
FreeMemory(sBuffer)
EndIf
lpMem = *mbi\BaseAddress + *mbi\RegionSize
Else
Break
EndIf
;Delay(1)
Next

EndProcedure

Repeat
;Get Event
EventID = WaitWindowEvent()
If EventID = #PB_Event_Gadget
;If event is a gadget event then do case
Select EventGadget()
Case 0
ReadMemory()
EndSelect
Else
;Else Choose from available window events
Select EventID
Case #PB_Event_CloseWindow
Break
Default
EndSelect
EndIf
Delay(1)
ForEver
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

Code tags & indention please.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

Sorry about the tag, I was in a rush to post it. The below should be in the correct format :).

Code: Select all

;Generate Window/Controls
OpenWindow(0,300,300,800,500,"Memster",#PB_Window_SystemMenu)
ButtonGadget(0,10,10,200,20, "Read Memory")
EditorGadget(1, 10, 35, 300, 400)
SetForegroundWindow_(WindowID(0))

Procedure ReadMemory()
;Find Window
HWND = FindWindow_(NULL, "Calculator")

;Declaration
si.SYSTEM_INFO
*mbi.MEMORY_BASIC_INFORMATION
ret = 0
pid = 0

;Get ProcessID
GetWindowThreadProcessId_(HWND, @pid)

;Opens Process With full access
hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, #False, pid);

;Grab System Information
GetSystemInfo_(si)

;Find Memory Bounds
lpMin = si\lpMinimumApplicationAddress
lpMax = si\lpMaximumApplicationAddress

lLenMBI = SizeOf(MEMORY_BASIC_INFORMATION )

adr=AllocateMemory(28)
*mbi=adr

;Do the Scan
For x = lpMin To lpMax
  WaitWindowEvent()
  ret = VirtualQueryEx_(hProcess, lpMin, adr,lLenMBI )
  If ret = lLenMBI
    If *mbi\RegionSize > 0
      sBuffer = AllocateMemory(*mbi\RegionSize)
      res = ReadProcessMemory_(hProcess, *mbi\BaseAddress, sBuffer, *mbi\RegionSize, @lWritten)
        If lWritten>0 And res>0
          SetGadgetText(1, GetGadgetText(1) + Str(sBuffer) + #CRLF$ )
        EndIf
      FreeMemory(sBuffer)
    EndIf
    lpMem = *mbi\BaseAddress + *mbi\RegionSize
  Else
    Break
  EndIf
Next

EndProcedure

Repeat
;Get Event
EventID = WaitWindowEvent()
  If EventID = #PB_Event_Gadget
   ;If event is a gadget event then do case
    Select EventGadget()
    Case 0
      ReadMemory()
    Default
    ;Do Nothing
    EndSelect
  Else
   ;Else Choose from available window events
    Select EventID
    Case #PB_Event_CloseWindow
    ;Break if Window was closed
     Break
    Default
    ;Do Nothing
    EndSelect
  EndIf
;Delay to avoid Memory Usage
Delay(1)
ForEver
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Answer to part of the question.

http://msdn.microsoft.com/en-us/library/ms680553.aspx

cheers
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

Yea I've seen that page quite a few times and the way I understand it from examples and even from that page is that the buffer is suppose to return the value at said address. However it appears that whatever the buffer is set to is not changed when the API returns, if it is even returning correctly for me that is. I do not get any errors on execution or anything else that I would expect to get. If anyone has a better example to go by to read a range of memory within a process and return values said addresses within that range please do.
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: ReadProcessMemory... :( Please Help!

Post by Demivec »

abystus wrote:I am having some problems with ReadProcessMemory returning the value at the address specified. Yes the code below is a compilation of many examples that i have tried to get working and for the most part works except getting a correct return value. The example below displays the same buffer content (supposedly return value) for every address given to it even if i manually change the address. Also maybe someone could provide a better in-depth representation of what exactly each parameter of this Api is for? Any help is appreciated since this Api thing is giving me a headache...

I've commented the Scanning routine to highlight some problems:

Code: Select all

;Do the Scan
For x = lpMin To lpMax
  WaitWindowEvent()
  ret = VirtualQueryEx_(hProcess, lpMin, adr,lLenMBI )
  If ret = lLenMBI
    If *mbi\RegionSize > 0
      sBuffer = AllocateMemory(*mbi\RegionSize)
      res = ReadProcessMemory_(hProcess, *mbi\BaseAddress, sBuffer, *mbi\RegionSize, @lWritten) ;<== *mbi\BaseAddress is a constant
        If lWritten>0 And res>0
          SetGadgetText(1, GetGadgetText(1) + Str(sBuffer) + #CRLF$ ) ;<== sBuffer contains an address so Str(sBuffer) displays the address
        EndIf
      FreeMemory(sBuffer)
    EndIf
    lpMem = *mbi\BaseAddress + *mbi\RegionSize  ;<== this is a constant and is never used
  Else
    Break
  EndIf
Next

Here is the same routine with a few changes to solve those same problems (note you will want to shut down the Calendar program to abort scanning with the default region because it is 900+ megabytes long):

Code: Select all

 ;Do the Scan
  For x = lpMin To lpMax
    WaitWindowEvent()
    ret = VirtualQueryEx_(hProcess, lpMin, adr,lLenMBI )
    If ret = lLenMBI
      If *mbi\RegionSize > 0 
        
        If lpMem <> 0 
          lpMem + *mbi\RegionSize  ;<=== progressively update memory pointer
        Else
          lpMem = *mbi\BaseAddress + *mbi\RegionSize
        EndIf
        
        sBuffer = AllocateMemory(*mbi\RegionSize)
        res = ReadProcessMemory_(hProcess, lpMem, sBuffer, *mbi\RegionSize, @lWritten) ;<==Change memory address so that all memory regions will be returned as we loop
        If lWritten>0 And res>0
          For i = 0 To lWritten - 1 ; <=== hack, replace Null bytes with a different character so that contents of memory can be read as a string
            If PeekB(sBuffer) = 0 
              PokeB(sBuffer + i,1)
            EndIf 
          Next 
          SetGadgetText(1, GetGadgetText(1) + PeekS(sBuffer) + #CRLF$ ) ;<== convert memory contents to a string with PeekS(sBuffer)
        EndIf
        FreeMemory(sBuffer)
      EndIf
    Else
      Break
    EndIf
  Next
The memory area being scanned is large. You'll want to limit what you return and display in the editor gadget from that region to perhaps text only if it is for visual interest only, depending on your needs. Another alternative would be to keep a buffer containing the non-zero memory portions along with a list specifying the locations for those portions within the original address range. I hope this helps. :wink:
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

Very interesting, I tested it out and I'm getting some weird return data shown below:

Code: Select all


È



{
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

I found some old VB6 Code to read from a specific address. I have used this before in the making of a trainer type application. I may work on trying to convert this logic into purebasic since its a little more already predefined as what and where things need to happen. Im sure if i can reproduce this in PB giving it addresses should be the easy part :).

- Abystus

Code: Select all

Public Function ReadAByte(gamewindowtext As String, address As Long, valbuffer As Byte)
Dim hwnd As Long
Dim pid As Long
Dim phandle As Long
hwnd = FindWindow(vbNullString, gamewindowtext)
If (hwnd = 0) Then
MsgBox "The Game Is Not Working", vbCritical, "Error"
End
Exit Function
End If
GetWindowThreadProcessId hwnd, pid
phandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
If (phandle = 0) Then
MsgBox "Can't get ProcessId", vbCritical, "Error"
Exit Function
End If
ReadProcessMemory phandle, address, valbuffer, 1, 0&
CloseHandle hProcess
End Function
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

[quote="abystus"]Very interesting, I tested it out and I'm getting some weird return data shown below:

Code: Select all


È



{
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

The question is "what format did you need it in?" Your previous code obtained the contents of memory and then just displayed the starting address of the buffer. It was a little hard to guess what you were trying to "see". I guessed and displayed the memory contents in the buffer as characters. If you want to operate/search the contents of the buffer instead of "seeing" it then that is the part you would need to add to the previous code.
Maybe I'm just not understanding what exactly we are returning from the previous source. The vb6 alternative produces a numeric value from what is stored at that address. I am wanting to produce a similar execution in PB.

Basically if you look at pretty much any memory editor you will see memory values are stored with a numeric/hexadecimal value as shown below:

Image

In the image each 2 digit hex value is a representation of the value at each memory address on that line, 6 memory addresses at a time in this instance. However when a program returns a reference to value it should most likely be in the numerical format 0-255 instead of 0-FF. Maybe our example is returning the very right column shown in the memory editor? I'm not really that great at interacting with memory from a programmers stand point, I normally deal with business applications. However I am trying to understand just exactly what we are returning in our example.

I understand you converted it to string using SPeek which in turn might have caused it to possibly convert from ascii to character format. I could possibly be getting to wrapped up in this conversion from vb to pb, I figured it would be much simpler and even comparable to the vb alternative. Am I just missing something here? Thanks again for all your help on this matter, maybe I should go back to hacking memory instead of trying to program for it ;).

-Abystus[/code]
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

Hey just letting you know I got a chance to sit down and see exactly what you were doing, I changed PeekS to PeekB and seem to be getting some acceptable results, however I see the addresses skip around a good bit. Im taking a guess that the below code is working correctly:

Code: Select all

;Generate Window/Controls
OpenWindow(0,300,300,800,500,"Memster",#PB_Window_SystemMenu)
ButtonGadget(0,10,10,200,20, "Read Memory")
EditorGadget(1, 10, 35, 300, 400)
EditorGadget(3, 220, 10, 50, 20)
SetForegroundWindow_(WindowID(0))

Procedure ReadMemory()
;Find Window
HWND = FindWindow_(NULL, "Calculator")

;Declaration
si.SYSTEM_INFO
*mbi.MEMORY_BASIC_INFORMATION
ret = 0
pid = 0

;Get ProcessID
GetWindowThreadProcessId_(HWND, @pid)

;Opens Process With full access
hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, #False, pid);

;Grab System Information
GetSystemInfo_(si)

;Find Memory Bounds
lpMin = si\lpMinimumApplicationAddress
lpMax = si\lpMaximumApplicationAddress

lLenMBI = SizeOf(MEMORY_BASIC_INFORMATION )

adr=AllocateMemory(28)
*mbi=adr

;;Do the Scan
  For x = lpMin To lpMax
    WaitWindowEvent()
    ret = VirtualQueryEx_(hProcess, lpMin, adr,lLenMBI )
    If ret = lLenMBI
      If *mbi\RegionSize > 0
       
        If lpMem <> 0
          lpMem + *mbi\RegionSize  ;<=== progressively update memory pointer
        Else
          lpMem = *mbi\BaseAddress + *mbi\RegionSize
        EndIf
       
        sBuffer = AllocateMemory(*mbi\RegionSize)
        res = ReadProcessMemory_(hProcess, lpMem, sBuffer, *mbi\RegionSize, @lWritten) ;<==Change memory address so that all memory regions will be returned as we loop
        If lWritten>0 And res>0
          For i = 0 To lWritten - 1 ; <=== hack, replace Null bytes with a different character so that contents of memory can be read as a string
        ;WaitWindowEvent()
            ;if PeekB(sBuffer) = 0
             ; PokeB(sBuffer + i,1)
            ;EndIf
          Next
          If Str(PeekB(sBuffer)) = GetGadgetText(3)
          SetGadgetText(1, GetGadgetText(1) + Str(lpmem) + " " + Str(PeekB(sBuffer)) + #CRLF$ ) ;<== convert memory contents to a string with PeekS(sBuffer)
          EndIf
        EndIf
        FreeMemory(sBuffer)
      EndIf
    Else
      Break
    EndIf
  Next

EndProcedure

Repeat
;Get Event
EventID = WaitWindowEvent()
  If EventID = #PB_Event_Gadget
   ;If event is a gadget event then do case
    Select EventGadget()
    Case 0
      ReadMemory()
    Default
    ;Do Nothing
    EndSelect
  Else
   ;Else Choose from available window events
    Select EventID
    Case #PB_Event_CloseWindow
    ;Break if Window was closed
     Break
    Default
    ;Do Nothing
    EndSelect
  EndIf
;Delay to avoid Memory Usage
Delay(1)
ForEver
I didn't quite understand that the buffer was a value holder (almost like a storage address) that you had to peek into to get a value from in a desired format (I thought it just returned). The example now lists the address along with its value in the box. Let me know what you think, and so very sorry for all the confusion :).

-Abystus
abystus
User
User
Posts: 12
Joined: Wed Feb 25, 2009 3:30 am

Post by abystus »

OK just tested it out with a live address and a value I already know and it is returning correctly, however I'm thinking my address range is off because I'm not finding correct addresses within the desired process. Is there anything that could be causing it not to be pulling the memory specific to the calculator application? Like somehow the base address is off? Getting really close :) to having this thing working.

-Abystus
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

@abystus

I know its an old thread, but i was searching around for something else and came across this thread. Maybe this would be useful for getting a module base address:

Code: Select all

; get the module base

#MAX_MODULE_NAME32=255 
#MAX_MODULE_NAME32plus=#MAX_MODULE_NAME32+1 
#TH32CS_SNAPPROCESS=$2 
#TH32CS_SNAPMODULE=$8 

OpenLibrary(0, "kernel32.dll") 

Procedure.s RetrieveModuleBase(ProcName.s, ModuleName.s) 
lReturnID.l 
hSnapProcess.l 
hSnapModule.l 
proc.PROCESSENTRY32 
Module.MODULEENTRY32 

hSnapProcess=CallFunction(0, "CreateToolhelp32Snapshot", #TH32CS_SNAPPROCESS, 0) 
If hSnapProcess <> 0 
  proc\dwSize = SizeOf(proc) 
  lReturnID = CallFunction(0, "Process32First", hSnapProcess, @proc) 
  While lReturnID<>0 
    If FindString(Left(PeekS(@proc\szExeFile), Len(ProcName)), ProcName, 1)=1 
      hSnapModule = CallFunction(0, "CreateToolhelp32Snapshot", #TH32CS_SNAPMODULE, proc\th32ProcessID) 
      If hSnapModule 
        Module\dwSize = SizeOf(Module) 
        lReturnID = CallFunction(0, "Module32First", hSnapModule, @Module) 
        
        While lReturnID<>0 
          If FindString(Left(PeekS(@Module\szModule), Len(ModuleName)), ModuleName, 1)=1 
            CloseLibrary(0) 
            ProcedureReturn "$"+Hex(Module\modBaseAddr) 
          EndIf 
          lReturnID = CallFunction(0, "Module32Next", hSnapModule, @Module) 
        Wend 
      EndIf 
    EndIf 
    lReturnID = CallFunction(0, "Process32Next", hSnapProcess, @proc) 
  Wend 
EndIf 
CloseLibrary(0) 
ProcedureReturn  "0" 
EndProcedure    

Debug RetrieveModuleBase("notepad.exe", "kernel32.dll")
works it seems in Vista and Windows 7, don't know about XP as i haven't used it in XP.

He's probably not around any longer.
Poplar
User
User
Posts: 16
Joined: Sun Apr 30, 2017 12:27 pm

Re: ReadProcessMemory... :( Please Help!

Post by Poplar »

About "kernel32.dll" module's base, we can use "Debug "$"+Hex(GetModuleHandle_("kernel32.dll"))".
Mike Yurgalavage
Enthusiast
Enthusiast
Posts: 118
Joined: Thu May 17, 2007 8:35 pm
Location: USA

Re: ReadProcessMemory... :( Please Help!

Post by Mike Yurgalavage »

Poplar wrote:About "kernel32.dll" module's base, we can use "Debug "$"+Hex(GetModuleHandle_("kernel32.dll"))".
Yes but imagine if you wanted the base of a non-system .dll in another program (i.e. notepad.exe). I don't think your method would work.

best,
Mike
Post Reply