win32api and readprocessmemory problem.. sojo, james, tracer? HELP!

Blitz3D Forums/Blitz3D Programming/win32api and readprocessmemory problem.. sojo, james, tracer? HELP!

Mike Yurgalavage(Posted 2004) [#1]
i am currently writing a trainer (or trying to) that will access another program and write to it's memory addresses. at any rate, i have accomplished findind the thread process ID for a program, but i am having trouble reading the memory addresses of the program. for now, i am using the calculator program as a test just to see if i am reading the addresses correctly. i am lost. take a look at the code and see if you can tell what i am trying to accomplish and what i am doing wrong! any help would be appreciated.

best,
mike

[CODE]
; -----------------------------------------------------------------------------
; Create or open existing kernel32.decls file in userlibs folder and place
; these lines in it (uncommented!)...
; -----------------------------------------------------------------------------
;
;.lib "kernel32.dll"
;
;CreateToolhelp32Snapshot% (flags, th32processid)
;Process32First% (snapshot, entry*)
;Process32Next% (snapshot, entry*)
;CloseHandle% (object)
;OpenProcess% (dwDesiredAccess%, Null, dwProcessId%) : "OpenProcess"
;ReadProcessMemory% (hProcess%, lpBaseAddress*, lpBuffer*, nSize%, Null)
;WriteProcessMemory% (hProcess%, lpBaseAddress*, lpBuffer*, nSize%, lpNumberOfBytesWritten%)
;
; -----------------------------------------------------------------------------

; -----------------------------------------------------------------------------
; Create or open existing user32.decls file in userlibs folder and place
; these lines in it (uncommented!)...
; -----------------------------------------------------------------------------
;
;.lib "user32.dll"
;
;FindWindow% (Null, Caption$) : "FindWindowA"
;SetWindowState(hwnd,command):"ShowWindow"
;ShowWindow% (hwnd%, nCmdShow%) : "ShowWindow"
;GetWindowThreadProcessId% (hwnd%, ProcessId%) : "GetWindowThreadProcessId"
;
; -----------------------------------------------------------------------------

; -----------------------------------------------------------------------------
;Make sure that the calculator program included with windows is running so that this will identify it
; -----------------------------------------------------------------------------

Const MAX_PATH = 264
Const TH32CS_SNAPHEAPLIST = $1
Const TH32CS_SNAPPROCESS = $2
Const TH32CS_SNAPTHREAD = $4
Const TH32CS_SNAPMODULE = $8
Const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE
Const TH32CS_INHERIT = $80000000
Const INVALID_HANDLE_VALUE = -1
Const SizeOf_PE32 = 296
Const STANDARD_RIGHTS_REQUIRED = $F0000
Const SYNCHRONIZE = $100000
Const PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or $FFF)

;x = NULL in most instances


Type PROCESSENTRY32
Field dwSize
Field cntUsage
Field th32ProcessID
Field th32DefaultHeapID
Field th32ModuleID
Field cntThreads
Field th32ParentProcessID
Field pcPriClassBase
Field dwFlags
Field szExeFile$ [MAX_PATH]
End Type


snap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0)

If snap
Proc32=CreateBank(SizeOf_PE32)
PokeInt(Proc32, 0, BankSize(Proc32)) ; dwSize

If Process32First (snap, Proc32) ;<--- read first running process
Print "Process ID: " + th32ProcessID
Print

While Process32Next (snap, Proc32) ;<--- read each running process
dwSize=PeekInt(Proc32,0)
cntUsage=PeekInt(Proc32,4)
th32ProcessID=PeekInt(Proc32,8)
th32DefaultHeapID=PeekInt(Proc32,12)
th32ModuleID=PeekInt(Proc32,16)
cntThreads=PeekInt(Proc32,20)
th32ParentProcessID=PeekInt(Proc32,24)
pcPriClassBase=PeekInt(Proc32,28)
dwFlags=PeekInt(Proc32,32)
offset = 36
Repeat
char = PeekByte(Proc32, offset)
offset = offset + 1
szExeFile$ = szExeFile$ + Chr$(char)
Until char = 0
szExeFile$=Left$(szExeFile$,Len(szExeFile$)-1)
Print
Print szExeFile$ ;<--- process name
Print "Process ID: " + th32ProcessID
If szExeFile$="calc.exe" Then program=th32ProcessID;<--- program = processID of calculator
szExeFile$=""

Wend

EndIf

CloseHandle (snap)

EndIf

Print
Print "Calculator program is thread process " + program + "."

; <--- from here down is where i don't know what i am doing right

app=openprocess(PROCESS_ALL_ACCESS,x,program)
Print "Value for app is " + app + "." ;<--- just to see what it contains
pip=CreateBank(3) ; <-- is bank the way to go here? how big?
zap=readprocessmemory(app,$77d43999,pip,4,x) ;<--- Not sure what To put here at all...
tip=PeekByte (pip,0) ;<--- just want to see what one address contains.
Print "the address $77d43999 contains the number " + tip + "." ; always zero or not what really is at that address

;obviously i am doing something wrong because i can look at address $77d43999 with tsearch and it is not what tip says
;i am not even sure where the addresses for the calculator program begins?? i thought that
;app would point at that or something. anyways, HELP!

Input ()
End

;code below is what we are trying to accomplish, but it is in purebasic format (!?$)

;app = OpenProcess_(#PROCESS_ALL_ACCESS,Null,pid)

; Buffer For storing stuff, in bytes
;pip = AllocateMemory(1,3)

; To read from process
;zap = ReadProcessMemory_(app,4987153,pip,3,Null)
;tip = PeekL(pip)

; To write to process
;zing = PokeL(pip,9474192)
;zip = WriteProcessMemory_(app,4987153,pip,3,Null)
[/CODE]

you must have the calculator program (calc.exe) running in the background when you run this. i am not sure how to accesses the addresses that the calc program is residing in memory. that's what i am trying to accomplish. this code requires two .decls (one for user32.dll and one for kernel32.dll) the .decls are included at the top of the code after the ";"'s. just uncomment them and save them in the userlibs directory. I hope someone can help!


soja(Posted 2004) [#2]
Her eare some issues I've noticed after looking at it for a minute:

1) Your bank (pip) should be at least 4 bytes, though it wouldn't hurt to be more.
2) You can't rely on the calc process' base address the be the same all the time. You have to get that information somehow, and use that instead of 77d43999. (I'm not sure how ATM.)
3) If you want to pass in NULL, just declare the parameter as % and then pass in 0. (You were doing that, but a little bit strangely.)

If you haven't made any headway, I'll take a look after I get a good night's sleep.

PS: It's trivial, but you may be interested to know that you can put any number of declarations for any number of libraries in a single .decls file, as long as they're seperated by the appropriate .lib directive.


Tracer(Posted 2004) [#3]
This is the thing with games these days.

Moving addresses...

The CODE of the game is always in the same spot, but the addresses that the code use to store lifes, etc move around in memory..

What you need to do is find the CODE that decreases lifes or something in a game .. this code will point to a register + <a number> .. the address in the register + the number = the location in memore of the lifes.

I tend to go a bit further with this when i make a trainer, i find the code (usually an asm SUB command) and remove it :) the lifes will no longer decrease.

There's a tool out there, if it still is out there that can help with this. It's called TSearch.. it basically does the same thing as my own trainer creation tool but a little more 'difficult'.



In Tsearch, select the game process (run game first of course), use it's normal value location system to find the value you want to find, say, lifes:

Look in the game, say there's 3 lifes left.
-Go to TSearch, search for 3, with 2 bytes (doesn't always work but it will not find millions of hits this way)
-Go back to the game, loose a life
-Go to TSearch, search for 2
-Repeat until you know which address hold the amount of lifes. Add the address to the list on the right.
-Now, go to one of the menu's and "enable debugger"
-Right click on the address in the list on the right and click on "Autohack"
-Go to the game, loose a life.
-Go to TSearch, and open the autohack window (same menu as enable debugger i think)
-There should be a little machine code command in the list in that window .. click on the little box in front of the command to turn it on.. and voila..lifes will no longer decrease when you die.
-Select the machine code command and click on "TMK", and then "Button Script", a small window will appear with a little text in it .. there's the address that you need to NOP (those are the list of 90's you see in the text, 90 = NOP)..

WriteProcessMemory to this address (in decimal, not hex, like the text shows) with the 90's and your trainer will kill the CODE to decrease the lifes :)

Guess i'll code something.

Tracer


Tracer(Posted 2004) [#4]
Ok then,

Download the below "trainme.exe" in zip form, run it, and rack up some number on there.

Run the BB program below and then try decreasing using the dwn button.

Const MAX_PATH = 264 
Const TH32CS_SNAPHEAPLIST = $1 
Const TH32CS_SNAPPROCESS = $2 
Const TH32CS_SNAPTHREAD = $4 
Const TH32CS_SNAPMODULE = $8 
Const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE 
Const TH32CS_INHERIT = $80000000 
Const INVALID_HANDLE_VALUE = -1 
Const SizeOf_PE32 = 296 
Const STANDARD_RIGHTS_REQUIRED = $F0000 
Const SYNCHRONIZE = $100000 
Const PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or $FFF) 

snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 

If snap 
	Proc32 = CreateBank(SizeOf_PE32) 
	PokeInt(Proc32,0,BankSize(Proc32))

	If Process32First(snap,Proc32)
		While Process32Next(snap,Proc32)
			dwSize              = PeekInt(Proc32,0) 
			cntUsage            = PeekInt(Proc32,4) 
			th32ProcessID       = PeekInt(Proc32,8) 
			th32DefaultHeapID   = PeekInt(Proc32,12) 
			th32ModuleID        = PeekInt(Proc32,16) 
			cntThreads          = PeekInt(Proc32,20) 
			th32ParentProcessID = PeekInt(Proc32,24) 
			pcPriClassBase      = PeekInt(Proc32,28) 
			dwFlags             = PeekInt(Proc32,32) 

			offset = 36 
			
			Repeat 
				char = PeekByte(Proc32,offset) 
				offset = offset + 1 
				szExeFile$ = szExeFile$ + Chr$(char) 
			Until char = 0 
			
			szExeFile$ = Left$(szExeFile$,Len(szExeFile$) - 1) 
			
			If szExeFile$ = "trainme.exe"
				program = th32ProcessID
			EndIf
			
			szExeFile$="" 
		Wend 
	EndIf 
	CloseHandle (snap) 
EndIf 

; Open Process with full access
app = OpenProcess(PROCESS_ALL_ACCESS,x,program)

; Create a little bank
buffer = CreateBank(10)

; To READ from memory.. not used in this case, but the below works.
;result = ReadProcessMemory(app,4207112,buffer,2,x)


; Train Me has the command for decreasing the number in the window
; at address 401429 in hex, to make it easy, we'll use dec to
; do our process writing, which is 4199465.

; *NOTE*
; The Hex down below is what it showed when _I_ ran Train Me
; It doesn't mean that it will be that on your computer!
; Keep in mind, CODE is static in memory, DATA is _NOT_

; The address contains the following:
; 89 1D 08 32 40 00
; The address where the value is held is: 403208
; 08 32 40, seem em above? reverse em.. 403208  HEY! whaddaya know :)
; So, ReadProcessMemory CAN be used to find the amount in memory
; even if it changes address! (mind you, in my little Train Me, it's very
; straightforward, it make be VERY different in games.

; So, what to do to stop it from decreasing (but, we want to make sure
; it CAN increase!)

; 4199465 is the address of the command that decreases the number.. so,
; all we have to do is NOP (No OPeration) it out. The command is six
; bytes in length, so we need to put six NOP's in it's place.

PokeByte buffer,0,144 ; <- 144 dec = 90 hex = NOP
PokeByte buffer,1,144 ; <- 144 dec = 90 hex = NOP
PokeByte buffer,2,144 ; <- 144 dec = 90 hex = NOP
PokeByte buffer,3,144 ; <- 144 dec = 90 hex = NOP
PokeByte buffer,4,144 ; <- 144 dec = 90 hex = NOP
PokeByte buffer,5,144 ; <- 144 dec = 90 hex = NOP

; We poked 6 bytes into the bank, each a 144, which is 90 (NOP) in hex.

; Now we write the six bytes to Train Me's memory and remove the
; decreasing command.
result = WriteProcessMemory(app,4199465,buffer,6,x)

; Now try to use the "dwn" button! :)
; Note how the up button still works!, this is because we only removed
; the code that decreases the number!


The little prog i made to demonstrate (PureBasic):

http://www.bannerpacking.com/trainme.zip

And it's source code:
win = OpenWindow(1,10,10,132,50,#PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered,"Train Me")
If CreateGadgetList(WindowID(1))
  ButtonGadget(1,10,10,24,24,"Up")
  TextGadget(2,60,15,40,24,"0")
  ButtonGadget(3,100,10,24,24,"Dn")
EndIf

num = 0
SetGadgetText(2,Str(num))
Repeat
  EventID.l = WaitWindowEvent()

  If EventID = #PB_EventGadget

      Select EventGadgetID()
        Case 1
          num = num + 1
          
          If num > 200
            num = 200
          EndIf
          
          SetGadgetText(2,Str(num))

        Case 3
          num = num - 1
          
          If num < 0
            num = 0
          EndIf
          
          SetGadgetText(2,Str(num))
          
      EndSelect
  EndIf
  
  If EventID = #PB_Event_CloseWindow  ; If the user has pressed on the close button
    Quit = 1
  EndIf

Until Quit = 1


There ya go :)

Tracer


Tracer(Posted 2004) [#5]
Oh.. i forgot :)

.lib "kernel32.dll"

CreateToolhelp32Snapshot% (flags, th32processid)
Process32First% (snapshot, entry*)
Process32Next% (snapshot, entry*)
CloseHandle% (object)
OpenProcess% (dwDesiredAccess%, bInheritHandle%, dwProcessId%) : "OpenProcess"
ReadProcessMemory% (hProcess%, lpBaseAddress%, lpBuffer*, nSize%, lpNumberOfBytesRead%)
WriteProcessMemory% (hProcess%, lpBaseAddress%, lpBuffer*, nSize%, lpNumberOfBytesWritten%)


Mike, the above is WHY your code didn't work! .. you had "lpBaseAddress" set as a pointer (with the *) .. it's NOT a pointer! Replace the ReadProcessMemory and WriteProcessMemory in your userlib with the ones above!

Tracer


BlitzSupport(Posted 2004) [#6]

the above is WHY your code didn't work! .. you had "lpBaseAddress" set as a pointer (with the *) ..


In fairness, that may have been my .decls!

[EDIT] Wait, no it wasn't... woo-hoo!


Mike Yurgalavage(Posted 2004) [#7]
man that was about the most conclusive explanation i have seen! i can't wait until lunch so i can try it out while i am not working. thanks to everyone (soja, tracer, and others) for helping me with this. i am still trying to work through the api calls. this has been a great learning experience and i appreciate the help. by the way i have been using tsearch and tmk (check my comment lines in the above code where i mention tsearch) for trainers i made in the past, but the tmk only let's you do certain things. i could make more graphical and customized trainers with blitz3d package. thanks again!

best,
mike


Mike Yurgalavage(Posted 2004) [#8]
just tried it out. works like a charm. thanks! i am going to test it on several programs that i made trainers with tmk for in the past.

thanks again!

best,
mike


Tracer(Posted 2004) [#9]
Make sure you use the autohack feature in TSearch! Otherwise your trainer may not work for long :)

Tracer