Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Leisure suit larry: Mono hack method

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Tutorials
View previous topic :: View next topic  
Author Message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Mon Dec 02, 2013 9:38 am    Post subject: Leisure suit larry: Mono hack method This post has 2 review(s) Reply with quote

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
View user's profile Send private message MSN Messenger
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Sat Dec 14, 2013 7:18 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
KO48
How do I cheat?
Reputation: 0

Joined: 10 Aug 2014
Posts: 1

PostPosted: Sun Aug 10, 2014 2:59 am    Post subject: Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Sun Aug 10, 2014 3:11 am    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Redouane
Master Cheater
Reputation: 3

Joined: 05 Sep 2013
Posts: 363
Location: Algeria

PostPosted: Sun Aug 10, 2014 6:39 am    Post subject: Reply with quote

Small question:
In the stacktrace,why did you execute:
Code:
add esp,4
?
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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Sun Aug 10, 2014 8:22 am    Post subject: Reply with quote

mono uses the cdecl calling convention
that means that the caller needs to clean up the stack instead of the function that was called

also, if you're interested in this check out https://code.google.com/p/cheat-engine/source/browse/#svn%2Ftrunk%2FCheat%20Engine%2FMonoDataCollector%2FMonoDataCollector and https://code.google.com/p/cheat-engine/source/browse/trunk/Cheat%20Engine/bin/autorun/monoscript.lua (this comes with ce and detects when mono is running. The functions I mentioned in the first post are also implemented)

for ko48, it assumes the normal mono_* functions, but it shouldn't be too difficult to adapt to the new function names (basically just change the GetProcAddress lines in CPipeServer::InitMono() )

_________________
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
View user's profile Send private message MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Tutorials All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites