|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
Dark Byte Site Admin Reputation: 457
Joined: 09 May 2003 Posts: 25262 Location: The netherlands
|
Posted: Mon Dec 02, 2013 9:38 am Post subject: Leisure suit larry: Mono hack method |
|
|
This is a tutorial on how to debug and modify a mono game. In this case, leisure suit larry
Launch LSL. Set it to run in 640x480 in windowed mode
Load a saved game (skip the age restriction)
Go to a slot machine
Find balance (1D601DF4)
Guess or find the base address. (I use the debugger for this, but my guess is that the base is at 1D601DE8)
043F177A : mov [eax+14],ecx accesses ballance (so my guess was wrong: 1D601DE0 )
Let's do a stacktrace while we're here. (Click more info->S rightclick, and choose lock and do manual stacktrace)
stacktrace:
0441de1c
04420db5
04420a5c
0438e781
mono.mono_set_default....
While mono does provide some nice functions that output the description of methods and objects, most of them output to stdout. (AllocConsole won't work)
So for most Object field descriptions we'll have to rely on the single command mono_object_* mono_class_* functions
Luckily, the method description from EIP function (mono_pmip) just returns a string pointer (strdup'ed so might be a little memory leak if you use it constantly without cleaning up)
If that wasn't there, I'd have to rely on mono_jit_info_table_find to get the jit_info and from there get the method, methodname, classname, etc...
So, let's first check the addresses in the stacktrace
Code: |
alloc(bla, 2048)
alloc(r1,4)
registersymbol(r1)
alloc(r2,4)
registersymbol(r2)
alloc(r3,4)
registersymbol(r3)
alloc(r4,4)
registersymbol(r4)
alloc(r5,4)
registersymbol(r5)
bla:
call mono.mono_get_root_domain
push eax
call mono.mono_thread_attach
add esp,4
push 043F177A
call mono.mono_pmip
add esp,4
mov [r1], eax
push 0441de1c
call mono.mono_pmip
add esp,4
mov [r2], eax
push 04420db5
call mono.mono_pmip
add esp,4
mov [r3], eax
push 04420a5c
call mono.mono_pmip
add esp,4
mov [r4], eax
push 0438e781
call mono.mono_pmip
add esp,4
mov [r5], eax
ret
createthread(bla)
|
Let's look at the results
I go to r1, select the 4 bytes, and press space, note down the string, press backspace, go 4 bytes next, and repeat till done.
The results:
GameVariables:UpdateVariableStatus (VariableUpdate) + 0x162 (043F1618 043F17E0) [02EB4E70 - Unity Root Domain] (This is what writes my Balance)
GameVariables:UpdateInt (eGameVariable,int) + 0x3c (0441DDE0 0441DE21) [02EB4E70 - Unity Root Domain]
SlotMachineManager:SetLarrysMoney (int) + 0x35 (04420D80 04420DBA) [02EB4E70 - Unity Root Domain]
SlotMachineManager:Spin () + 0x154 (04420908 04420D2E) [02EB4E70 - Unity Root Domain]
(wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr) + 0x41 (0438E740 0438E7D5) [02EB4E70 - Unity Root Domain]
This means:
The SlotMachineManager has a method called Spin which takes no parameter.
It in turn calls SetLarrysMoney with a integer as value. Perhaps it's the new value, or a relative value. We don't know yet.
SetLarrysMoney calls UpdateInt on a GameVariables object, with a eGameVariable (enum, object, int?) and a int as parameter
UpdateInt on it's turn calls UpdateVariableStatus with a VariableUpdate parameter, which does the actual change
As we can see here, there is already quite a useful method to use as a hacking point: SetLarrysMoney.
If we can modify this parameter we are already set
Let's look at the stacktrace to check out the parameters it received
Just 1 parameter, but since this is an object oriented program, there is usually a _this parameter passed as well, so check the 2 parameters:
08D51578 , FFFFFFF6
Let's check out the parameters
08d51578:
This looks like a class object. Let's verify with some inside knowledge I have about mono (You could use the mono_object_ functions to get this same data)
First entry of a method is a vtable pointer
08d51578=A0 7C 2D 0B (b2d7ca0)
The first entry of a vtable is a Class description pointer
b2d7ca0=90 8C 1A 0B (b1a8c90)
at offset 30 of the class description is a pointer to the classname
b1a8c90+30=69 E1 EA 0A (aeae169)
aeae169="SlotMachineManager"
so that's confirmed. The first parameter passed is the _this parameter
Now let's check out the second parameter: FFFFFFF6
FFFFFFF6 is decimal 4294967286
That's value doesn't make sence at first glance, but if you assume it's a signed integer, it does. Signed, this value represents -10
Which is correct, as I did a gamble for $10
So, we can now assume that SlotMachineManager:SetLarrysMoney takes a signed integer, where a negative value decreases your money, and a positive value increases it
Let's play with this
Go to the entrypoint of SlotMachineManager:SetLarrysMoney ( 04420D80 this time ) and do a code injection there (tip, do a cheat table framework first and then a code injection template so the disable parts get filled in for you)
You could also do it after the stackframe init, but this is the easiest to automate
let's write a simple text script and remember the following:
[esp]=return address
[esp+4]=_this
[esp+8]=valuechange
We want to change valuechange so [esp+8] is the one we wish to change.
so the test script will look like:
Code: |
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
mov [esp+8],#1000 //change the second parameter to +1000
originalcode:
push ebp
mov ebp,esp
sub esp,08
exit:
jmp returnhere
04420D80:
jmp newmem
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
04420D80:
push ebp
mov ebp,esp
sub esp,08
//Alt: db 55 8B EC 83 EC 08
|
Now, each time you click on spin, your money will increase by 1000. Also, each time you win, you gain 1000
the problem we're facing now is how to get the address of SlotMachineManager:SetLarrysMoney automatically, or call it ourselves
First method is the old fashioned way: Just do an aobscan for this function
Second method is to use the profiler , get all jit callbacks, and when SetLarrysMoney is jitted, store the jitted address (check out http://forum.cheatengine.org/viewtopic.php?t=569785)
Third method is to get a SlotMachineManager object and call SetLarrysMoney automatically. We might be able to create it and hope for the best, but most likely it will require aditional parameters to set up. So easiest method is to watch object allocations using the profiler, and look for creations where the classname is SlotMachineManager, and then store that as last known slotmachine.
And then add a hotkey that invokes SetLarrysMoney with a specific value each time the hotkey is pressed.
Second and Third method require you attach soon in the life of the game though
--------------Invoking a method--------------
The general code to invoke a method:
Check out http://www.mono-project.com/Embedding_Mono
MonoObject* mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc);
aa code:
Code: |
push exception
push params
push object
push method
call mono.mono_runtime_invoke
add esp,10
|
exception is an output variable, so just needs an address to store the pointer (if there is an exception) can be 0
params is a pointer to an array of pointers to each individual parameter. In SetLarrysMoney this should be a pointer to an array with a pointer to a integer containing the balance change
object is the object to invoke the method on. In this case the SlotMachineManager object. (08D51578)
method is the method description to invoke. To get that, we must either find that method inside the SlotMachineManager, get the method from the jit profiler, or get it from the jitted code using mono_jit_info_table_find (or any other method that gets you as method, there's a lot)
So, first we need the method. Since we already know the jitted code here (04420D80) we could use mono_jit_info_table_find
Code: |
alloc(bla, 2048)
alloc(method,4)
registersymbol(method)
alloc(jit_info,4)
registersymbol(jit_info)
alloc(domain,4)
registersymbol(domain)
bla:
call mono.mono_get_root_domain
mov [domain],eax
push eax
call mono.mono_thread_attach
add esp,4
push 04420D80 //jitted address
push [domain]
call mono.mono_jit_info_table_find
add esp,8
//eax now contains a jit_info object
mov [jit_info],eax
push eax
call mono.mono_jit_info_get_method
add esp,4
mov [method],eax
ret
createthread(bla)
|
but if you don't go for the jit profiler, but do use the alloc profiler, or do signature scanning to find the class, you can find the method using that as well
so, we know the classobject is at 08D51578
Code: |
alloc(bla, 2048)
alloc(method,4)
registersymbol(method)
alloc(class,4)
registersymbol(class)
alloc(domain,4)
registersymbol(domain)
alloc(methodDesc,4)
registersymbol(methoddesc)
alloc(strSetLarrysMoneySearchString, 64)
strSetLarrysMoneySearchString:
db '*:SetLarrysMoney',0
bla:
call mono.mono_get_root_domain
mov [domain],eax
push eax
call mono.mono_thread_attach
add esp,4
//build a method_desc (search command)
push 0
push strSetLarrysMoneySearchString //pointer to a string with the methodname formatted using classname:methodname. (classname can bea wildcard)
call mono.mono_method_desc_new
add esp,8
mov [methodDesc], eax
//now get the Class from this object
push 08D51578
call mono.mono_object_get_class
add esp,4
mov [class],eax
//now get the method from the class
push [class] //class
push [methodDesc] //method desc
call mono.mono_method_desc_search_in_class
add esp,8
mov [method], eax
ret
createthread(bla)
|
and now we have the method
This means we have everything to invoke the method: Object and Method
so, back to
Code: |
push exception
push params
push object
push method
call mono.mono_runtime_invoke
add esp,10
|
Now, to invoke SetLarrysMoney just execute this:
Code: |
alloc(bla, 2048)
alloc(exception,4)
registersymbol(exception)
alloc(value,4)
value:
dd #1000
alloc(parameterarray,8)
parameterarray:
dd value
dd 0
alloc(result,4)
bla:
call mono.mono_get_root_domain
mov [domain],eax
push eax
call mono.mono_thread_attach
add esp,4
push exception
push parameterarray
push 08D51578
push [method]
call mono.mono_runtime_invoke
add esp,10
mov [result],eax //if result=0 then it's an error
ret
createthread(bla)
|
Which on each call will give you $1000
Next time I'll post a tutorial on how to obtain fields from elements _________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
|
Dark Byte Site Admin Reputation: 457
Joined: 09 May 2003 Posts: 25262 Location: The netherlands
|
Posted: Sat Dec 14, 2013 7:18 pm Post subject: |
|
|
Slight addition to this. Once you hve found the method (using image or class search) you can use mono_compile_method to JIT the function immediately. When it has already been jitted, this will will return a pointer to the already existing method _________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
|
KO48 How do I cheat? Reputation: 0
Joined: 10 Aug 2014 Posts: 1
|
Posted: Sun Aug 10, 2014 2:59 am Post subject: |
|
|
Where do you see those results ?
in mono debugger ?
Quote: | The results:
GameVariables:UpdateVariableStatus (VariableUpdate) + 0x162 (043F1618 043F17E0) [02EB4E70 - Unity Root Domain] (This is what writes my Balance)
GameVariables:UpdateInt (eGameVariable,int) + 0x3c (0441DDE0 0441DE21) [02EB4E70 - Unity Root Domain]
SlotMachineManager:SetLarrysMoney (int) + 0x35 (04420D80 04420DBA) [02EB4E70 - Unity Root Domain]
SlotMachineManager:Spin () + 0x154 (04420908 04420D2E) [02EB4E70 - Unity Root Domain]
(wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr) + 0x41 (0438E740 0438E7D5) [02EB4E70 - Unity Root Domain] |
Because I'm currently trying to achieve what you're doing for a unity webplayer game, and there's a few changes for example, we need to use mono-vc-1_* instead of mono_* (I struggled a long time with that before understanding it).
Also i guess i could see the results with windbg for example,
but how can we attach cheat engine and windbg/mono at the same time.
Sorry for such newbie questions, and thank you so much for your time.
- KO48 _________________
We'll see that later. |
|
Back to top |
|
|
Dark Byte Site Admin Reputation: 457
Joined: 09 May 2003 Posts: 25262 Location: The netherlands
|
Posted: Sun Aug 10, 2014 3:11 am Post subject: |
|
|
The script i posted will allocate 5 pointers and give them a name
r1, r2, r3, r4, r5
After the script has been executed go to the hexview, go to address, and fill in as address r1
That will get you to that pointerlist
From there you can follow the pointers (tip: select the bytes that make up a pointer and press space. You'll then jump to that location. Backspace is back)
Alternatively, add 5 strings to the cheat table with an pointer address of r1 to r5 and offset 0 _________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
|
Redouane Master Cheater Reputation: 3
Joined: 05 Sep 2013 Posts: 363 Location: Algeria
|
Posted: Sun Aug 10, 2014 6:39 am Post subject: |
|
|
Small question:
In the stacktrace,why did you execute:
?
When you push an element,everything in the stack gets shifted four bytes.also,since both of the functions mono.mono_thread_attach and mono.mono_pmip take an argument,why do they return without clearing it?they execute 'ret' and not 'ret 04'. |
|
Back to top |
|
|
Dark Byte Site Admin Reputation: 457
Joined: 09 May 2003 Posts: 25262 Location: The netherlands
|
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|