BattleTech (CE Mono vs. JustDecompile)

SunBeam

SunBeam

Administrator
Staff member
Administrator
Joined
Feb 4, 2018
Messages
3,481
EPISODE 1<br />
<br />
Hello folks,<br />
<br />
Thought I'd give this a shot, since there's a ton of people out there not mastering (or at least understanding) the concept of matching whatever they see in Unity decompiled outputs versus the decompiled code Cheat Engine's MonoDataCollector returns. For this explanation I will use the below:<br />
  • <br />
  • Cheat Engine 6.7 -- ignore my screenshots showing RC3; you don't need RC3<br />
  • Telerik JustDecompile -- you can download it here<br />
  • BattleTech game<br />
  • BattleTech table found in this post<br />
<br />
Alright.<br />
<br />
Fire up the game, get in a map. Open Cheat Engine, load table, activate [ Enable ] script, activate Cheat Handler script. Go back in-game and hit Numpad 0 to execute DEBUG_PlayerOneGodMode. Wait, wait.. what did I just do? Rewind, please :)<br />
<br />
Let's take them one by one, from JustDecompile perspective, so you understand how I got to what the script just did ;)<br />
<br />
Fire-up Telerik JustDecompile. Hit Ctrl+O and choose Assembly-CSharp.dll from your <SteamLibFolder>BATTLETECHBattleTech_DataManaged folder. Once opened, you will see a Search button up-top on the toolbar. Click it and type godmode in the Find What: field. You will see this:<br />
<br />
<br />
<br />
See DEBUG_PlayerOneGodMode reference on second line? Double-click it. You'll be taken here:<br />
<br />
<br />
<br />
What can you see in that picture? Simple:<br />
  • <br />
  • Left tree shows the function, as well as the Namespace and Type Name. Why are these important? Because we will make use of them to get to this function in Cheat Engine.<br />
  • Based on the above, we can concatenate these to a string with ":" as separator - - BattleTech.UI:CombatDebugHUD - - this denotes hierarchy in C++ (a member of).<br />
  • Our function is called DEBUG_PlayerOneGodMode, so it will be appended to the above with a ":" as well (member of).<br />
  • Final result is:<br />
    Code:
    BattleTech.UI:CombatDebugHUD:DEBUG_PlayerOneGodMode
    <br />
  • We'll see in a bit what's to be done with this.<br />
    <br />
    <br />
  • Right side of the window shows you the actual function and its code:<br />
<br />
C++:
<br />
public void DEBUG_PlayerOneGodMode()<br />
{<br />
    this.SetGodMode(this.combatHUD.Combat.Teams.Find((Team x) => x.GUID == "bf40fd39-ccf9-47c4-94a6-061809681140"));<br />
}<br />
<br />
<br />
Looking further at the code, we see this function calls-in another: SetGodMode. Click on it and you are taken here:<br />
<br />
<br />
<br />
Transcript:<br />
<br />
C++:
<br />
private void SetGodMode(Team team)<br />
{<br />
    for (int i = 0; i < team.units.Count; i++)<br />
    {<br />
        AbstractActor item = team.units[i];<br />
        item.StatCollection.ModifyStat<float>("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);<br />
        item.StatCollection.ModifyStat<float>("debug", -1, "ReceivedInstabilityMultiplier", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);<br />
        item.StatCollection.ModifyStat<bool>("debug", -1, "IgnorePilotInjuries", StatCollection.StatOperation.Set, true, -1, true);<br />
        for (int j = 0; j < item.Weapons.Count; j++)<br />
        {<br />
            Weapon weapon = item.Weapons[j];<br />
            weapon.StatCollection.ModifyStat<float>("debug", -1, "DamagePerShot", StatCollection.StatOperation.Float_Multiply, 10f, -1, true);<br />
            weapon.StatCollection.ModifyStat<float>("debug", -1, "AccuracyModifier", StatCollection.StatOperation.Float_Add, -20f, -1, true);<br />
        }<br />
        item.Combat.MessageCenter.PublishMessage(new FloatieMessage(item.GUID, item.GUID, "GOD MODE", FloatieMessage.MessageNature.Buff));<br />
    }<br />
}<br />
<br />
<br />
<br />
So, what the developer God Mode does in reality is:<br />
  • <br />
  • DamageReductionMultiplierAll is set to its current value * 0.1f<br />
  • ReceivedInstabilityMultiplier is set to current value * 0.1f<br />
  • IgnorePilotInjuries becomes active<br />
  • DamagePerShot is set to current value * 10f<br />
  • AccuracyModifier is set to current value + (-20f)<br />
<br />
This happens for all Mechs in player's team.<br />
<br />
Now that you've seen what's going on, next logical question would be: "OK, but how do I run this in Cheat Engine? I want to activate the function!". The logical answer would be create a thread to call in this function - DEBUG_PlayerOneGodMode - and execute it. All would be nice and dandy, but once you manage doing that and are able to call it, you'll notice game crashes. Why? Because you also need an instance pointer to be able to call it. Even though you see a void type of function, this only means it doesn't take-in parameters. However, the instance pointer - also referenced as this - is required.<br />
<br />
How do you know you need this and where to find it?<br />
<br />
1) You know you need this due to this piece of code:<br />
<br />
C++:
<br />
public void DEBUG_PlayerOneGodMode()<br />
{<br />
    this.SetGodMode(this.combatHUD.Combat.Teams.Find((Team x) => x.GUID == "bf40fd39-ccf9-47c4-94a6-061809681140"));<br />
}<br />
<br />
<br />
<br />
See this.SetGodMode? See this.combatHUD.Combat.Teams.Find? That's how you know :)<br />
<br />
2) To get it you would need to break on a function you know - from testing - is executed in the Namespace:Type Name (just so I use the references from JustDecompile). Most Unity functions come - conveniently - with an Update function :) You'll see in a bit.<br />
<br />
Time to move to Cheat Engine; remember you've loaded the table and executed Numpad 0. If you've not done it, do it now; I'll explain why it is important for this tutorial: although Cheat Engine is able to decompile Unity code, it will not also execute functions to retrieve symbols. So, all code in the two functions - DEBUG_PlayerOneGodMode, SetGodMode - will show no actual symbolicals.<br />
<br />
To get to these functions we'll make use of what I mentioned earlier:<br />
  • <br />
  • Cheat Engine > Memory Viewer<br />
  • Ctrl+G ><br />
    Code:
    BattleTech.UI:CombatDebugHUD:DEBUG_PlayerOneGodMode - OR - BattleTech.UI:CombatDebugHUD:SetGodMode
    <br />
  • OK<br />
<br />
(before executing the functions)<br />
<br />
DEBUG_PlayerOneGodMode:<br />
<br />
<br />
<br />
SetGodMode:<br />
<br />
<br />
<br />
(after executing the functions)<br />
<br />
DEBUG_PlayerOneGodMode:<br />
<br />
<br />
<br />
SetGodMode:<br />
<br />
<br />
<br />
OK, OK.. but what about the instance pointer? We'll get it from debugging BattleTech.UI:CombatDebugHUD:Update. So head there in Cheat Engine's Memory Viewer via Ctrl+G and set a breakpoint at the function's prologue:<br />
<br />
<br />
<br />
Note that once you set breakpoint there, Mono gets un-linked. You'll know this has happened when you don't see symbols anymore :) Do your stuff, then activate it again in main CE window (Mono > Activate mono features). Reason why this happens is the mono thread makes use of your hardware breakpoints.<br />
<br />
Your instance pointer is in RCX. In my case, 0x0000000142C77380.<br />
<br />
What you can do at this point - - as I did - - is to hook the Update function and store this pointer to a static location. Then make use of it in your thread code. Another thing to note is your CreateThread needs to be attached/detached to/from the mono thread. How can this be done? See the code in the table :)<br />
<br />
CEA:
<br />
mono_thAttach:<br />
sub rsp,28<br />
// attach thread to mono domain<br />
call mono.mono_get_root_domain<br />
mov rcx,rax<br />
call mono.mono_thread_attach<br />
mov [self],rax<br />
add rsp,28<br />
ret<br />
<br />
mono_thDetach:<br />
sub rsp,28<br />
// detach thread from mono domain<br />
mov rcx,[self]<br />
call mono.mono_thread_detach<br />
add rsp,28<br />
ret<br />
<br />
<br />
<br />
Then a function that would use the two above:<br />
<br />
CEA:
<br />
AddFunds_do:<br />
sub rsp,28<br />
call mono_thAttach<br />
mov rcx,[pSimGameState]<br />
test rcx,rcx<br />
je short @f<br />
  mov rdx,[dwFundsAmount]<br />
  xor r8,r8<br />
  mov r9d,1<br />
  call _AddFunds<br />
@@:<br />
call mono_thDetach<br />
add rsp,28<br />
ret<br />
<br />
<br />
<br />
So up to this point you have the tools you need to understand a void type (no parameters) Unity function and what it needs to be run from a thread in Cheat Engine. Now that you've also executed the DEBUG_PlayerOneGodMode function, thus implicitly the SetGodMode function, let's take a look at how to interpret it from JustDecompile to the ASM code you see in Memory Viewer.<br />
<br />
Another reason for creating this tutorial was the various people nagging me to alter some parameters SetGodMode changed that they didn't like, making the game too easy or dull (e.g.: DamagePerShot too high).<br />
<br />
Let's see; I'll take the first parameter for a spin, the rest should be easy once you get this one:<br />
<br />
C++:
<br />
item.StatCollection.ModifyStat<float>("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);<br />
<br />
<br />
<br />
In Cheat Engine's ASM:<br />
<br />
CEA:
<br />
B8192400 - 55                    - push rbp<br />
B8192401 - 48 8B EC              - mov rbp,rsp<br />
B8192404 - 56                    - push rsi<br />
B8192405 - 57                    - push rdi<br />
B8192406 - 41 55                 - push r13<br />
B8192408 - 41 56                 - push r14<br />
B819240A - 41 57                 - push r15<br />
B819240C - 48 83 EC 28           - sub rsp,28 { 40 }<br />
B8192410 - 48 8B F2              - mov rsi,rdx<br />
B8192413 - 45 33 ED              - xor r13d,r13d<br />
B8192416 - E9 ED020000           - jmp BattleTech.UI:CombatDebugHUD:SetGodMode+308<br />
B819241B - 48 8D 64 24 00        - lea rsp,[rsp+00]<br />
B8192420 - 48 8B 46 48           - mov rax,[rsi+48]<br />
B8192424 - 48 8B C8              - mov rcx,rax<br />
B8192427 - 49 8B D5              - mov rdx,r13<br />
B819242A - 48 83 EC 20           - sub rsp,20 { 32 }<br />
B819242E - 83 38 00              - cmp dword ptr [rax],00 { 0 }<br />
B8192431 - 49 BB C032EC0800000000 - mov r11,System.Collections.Generic:List`1:get_Item { [EC8B4855] }<br />
B819243B - 41 FF D3              - call r11<br />
B819243E - 48 83 C4 20           - add rsp,20 { 32 }<br />
B8192442 - 48 8B F8              - mov rdi,rax<br />
B8192445 - 48 8B C7              - mov rax,rdi<br />
B8192448 - 48 8B C8              - mov rcx,rax<br />
B819244B - 83 39 00              - cmp dword ptr [rcx],00 { 0 }<br />
B819244E - 48 8B 40 58           - mov rax,[rax+58]<br />
B8192452 - F3 0F10 05 06030000   - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] }<br />
B819245A - F3 0F5A C0            - cvtss2sd xmm0,xmm0<br />
B819245E - 48 8B C8              - mov rcx,rax<br />
B8192461 - BA 50AF3A67           - mov edx,673AAF50 { [0862A2E0] }<br />
B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }<br />
B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] }<br />
B819247A - 6A 01                 - push 01 { 1 }<br />
B819247C - 6A FF                 - push -01 { 255 }<br />
B819247E - 48 83 EC 08           - sub rsp,08 { 8 }<br />
B8192482 - F2 0F5A E8            - cvtsd2ss xmm5,xmm0<br />
B8192486 - F3 0F11 2C 24         - movss [rsp],xmm5<br />
B819248B - 6A 0C                 - push 0C { 12 }<br />
B819248D - 48 83 EC 20           - sub rsp,20 { 32 }<br />
B8192491 - 83 38 00              - cmp dword ptr [rax],00 { 0 }<br />
B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }<br />
B819249E - 41 FF D3              - call r11<br />
B81924A1 - 48 83 C4 40           - add rsp,40 { 64 }<br />
B81924A5 - 83 3F 00              - cmp dword ptr [rdi],00 { 0 }<br />
B81924A8 - 48 8B 47 58           - mov rax,[rdi+58]<br />
B81924AC - F3 0F10 05 9C020000   - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+350] { [0.10] }<br />
B81924B4 - F3 0F5A C0            - cvtss2sd xmm0,xmm0<br />
B81924B8 - 48 8B C8              - mov rcx,rax<br />
B81924BB - BA 50AF3A67           - mov edx,673AAF50 { [0862A2E0] }<br />
B81924C0 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }<br />
B81924CA - 41 B9 4015447C        - mov r9d,7C441540 { [0862A2E0] }<br />
B81924D0 - 6A 01                 - push 01 { 1 }<br />
B81924D2 - 6A FF                 - push -01 { 255 }<br />
B81924D4 - 48 83 EC 08           - sub rsp,08 { 8 }<br />
B81924D8 - F2 0F5A E8            - cvtsd2ss xmm5,xmm0<br />
B81924DC - F3 0F11 2C 24         - movss [rsp],xmm5<br />
B81924E1 - 6A 0C                 - push 0C { 12 }<br />
B81924E3 - 48 83 EC 20           - sub rsp,20 { 32 }<br />
B81924E7 - 83 38 00              - cmp dword ptr [rax],00 { 0 }<br />
B81924EA - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }<br />
B81924F4 - 41 FF D3              - call r11<br />
<br />
<br />
<br />
<br />
<br />
But, but.. how do I know which is which? Well.. it's called debugging + sense of observation. It's something you'll have to learn in your own free time.<br />
<br />
Let's take them one by one, for the first piece of code (as I was saying), in the order of how x64 registers are used for the parameters of a function (rcx, rdx, r8, r9, etc.). Just keep in mind rcx with always be the this pointer - - the instance pointer - - so start from rdx onward:<br />
<br />
C++:
<br />
item.StatCollection.ModifyStat<float>("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);<br />
<br />
<br />
<br />
item.StatCollection.ModifyStat<float> is a function; its JustDecompile ASM reference is here:<br />
<br />
CEA:
<br />
B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }<br />
B819249E - 41 FF D3              - call r11<br />
<br />
<br />
<br />
First parameter of this function is a string - "debug"; in ASM it should be a buffer referenced somewhere (see rdx):<br />
<br />
CEA:
<br />
B8192461 - BA 50AF3A67           - mov edx,673AAF50 { [0862A2E0] }<br />
<br />
<br />
<br />
<br />
<br />
Second parameter is -1. This is dead obvious marked here (see r8):<br />
<br />
CEA:
<br />
B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }<br />
<br />
<br />
<br />
Third parameter is another string - "DamageReductionMultiplierAll"; in ASM it should be a buffer referenced somewhere (see r9):<br />
<br />
CEA:
<br />
B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] }<br />
<br />
<br />
<br />
<br />
<br />
Next-up, we have these:<br />
<br />
C++:
<br />
StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true<br />
<br />
<br />
<br />
In JustDecompile, if you click on Float_Multiply you'll be taken to this location; care to count with me? :D<br />
<br />
<br />
<br />
Code:
<br />
Set, //0<br />
Int_Add, //1<br />
Int_Subtract, //2<br />
Int_Multiply, //3<br />
Int_Divide, //4<br />
Int_Divide_Denom, //5<br />
Int_Mod, //6<br />
Int_Multiply_Float, //7<br />
Int_Divide_Float, //8<br />
Int_Divide_Denom_Float, //9<br />
Float_Add, //A<br />
Float_Subtract, //B<br />
Float_Multiply, //C<br />
Float_Divide, //D<br />
Float_Divide_Denom, //E<br />
Float_Multiply_Int, //F<br />
Float_Divide_Int, //10<br />
Float_Divide_Denom_Int, //11<br />
String_Append, //12<br />
String_Prepend, //13<br />
Bitflag_SetBit, //14<br />
Bitflag_FlipBit, //15<br />
Bitflag_Combine //16<br />
<br />
<br />
<br />
So Float_Multiply is 0xC in that list, right? :) Good. Let's continue; next-up is a float value - 0.1 - followed by a -1 and a true. The true is translated as 1. Just so you know. So what we need to find in the ASM code is the right sequence for these 4:<br />
<br />
CEA:
<br />
B8192452 - F3 0F10 05 06030000   - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] }<br />
B819245A - F3 0F5A C0            - cvtss2sd xmm0,xmm0<br />
..<br />
..<br />
B819247A - 6A 01                 - push 01 { 1 }<br />
B819247C - 6A FF                 - push -01 { 255 }<br />
B819247E - 48 83 EC 08           - sub rsp,08 { 8 }<br />
B8192482 - F2 0F5A E8            - cvtsd2ss xmm5,xmm0<br />
B8192486 - F3 0F11 2C 24         - movss [rsp],xmm5<br />
B819248B - 6A 0C                 - push 0C { 12 }<br />
<br />
<br />
<br />
Cheat Engine actually shows us where the 0.1 float reference is. It is first converted into a double-precision float (see CVTSS2SD instruction here), then to a single-precision float (see CVTSD2SS instruction here). Then comes the push 1, push -1, storing the MMX conversion to stack and the push C (remember Float_Multiply?).<br />
<br />
Let's read it now, from top to bottom:<br />
<br />
CEA:
<br />
B8192452 - F3 0F10 05 06030000   - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] } // 0.1f<br />
B819245A - F3 0F5A C0            - cvtss2sd xmm0,xmm0<br />
B819245E - 48 8B C8              - mov rcx,rax<br />
B8192461 - BA 50AF3A67           - mov edx,673AAF50 { [0862A2E0] } // "debug" string<br />
B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 } // -1<br />
B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] } // "DamageReductionMultiplierAll" string<br />
B819247A - 6A 01                 - push 01 { 1 } // true<br />
B819247C - 6A FF                 - push -01 { 255 } // -1<br />
B819247E - 48 83 EC 08           - sub rsp,08 { 8 }<br />
B8192482 - F2 0F5A E8            - cvtsd2ss xmm5,xmm0<br />
B8192486 - F3 0F11 2C 24         - movss [rsp],xmm5 // 0.1f written here<br />
B819248B - 6A 0C                 - push 0C { 12 } // StatCollection.StatOperation.Float_Multiply<br />
B819248D - 48 83 EC 20           - sub rsp,20 { 32 }<br />
B8192491 - 83 38 00              - cmp dword ptr [rax],00 { 0 }<br />
B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] } // item.StatCollection.ModifyStat<float> function<br />
B819249E - 41 FF D3              - call r11 // called here<br />
<br />
<br />
<br />
And for the grand finale, let's check the stack before call r11 happens:<br />
<br />
<br />
<br />
Am hoping this was clear enough to help you modify the God Mode function to your liking :p<br />
<br />
BR,<br />
Sun
 
H

HK-Demoneye

What is cheating?
Joined
Apr 30, 2018
Messages
3
That may be the single most difficult thing I've ever read in my life.
 
D

diabloamezon

Cheater
Joined
Mar 3, 2017
Messages
43
It's time to learn. :p
 
SunBeam

SunBeam

Administrator
Staff member
Administrator
Joined
Feb 4, 2018
Messages
3,481
Well, people complained there aren't that many tutorials and CE is dying. There you go :p To hell with video tutorials I say! :D
 
Tetnacious

Tetnacious

Noobzor
Joined
May 2, 2018
Messages
6
You can't tutorial years of dev experience. There are more moving pieces in these things then you can think of at one time. And this is stuff even most devs will find challenging. If you really want to learn CE you need to learn software development.
 
SunBeam

SunBeam

Administrator
Staff member
Administrator
Joined
Feb 4, 2018
Messages
3,481
EPISODE 2<br />
<br />
There are times in life when you take a step back and see the bigger picture. I was going to update the table last night (which I'm gonna do anyway), then I thought to myself "why not extend the tutorial?" So here goes.<br />
<br />
If you check my table here, you will notice I use hooks to get the instance pointers needed for some of the functions in the Cheat Handler. Considering the code runs in a thread, therefore requires no per-se interaction from the user - just using the designed hotkey to execute it - what if we don't rely on hooks at all? ;) The reason I am mentioning this is game updates have the tendency to break these hooks, as the prologue of the functions often changes (or are JIT-ed differently). To explain it even better, see this practical example in my 1.2 table, which, as you reported, crashes on latest version. Let's see why:<br />
<br />
Code:
<br />
[ENABLE]<br />
<br />
define( CombatDebugHUD_Update, "BattleTech.UI:CombatDebugHUD:Update" )<br />
define( SimGameState_Update, "BattleTech:SimGameState:Update" )<br />
<br />
..<br />
..<br />
<br />
//***************************<br />
//*          Hooks          *<br />
//***************************<br />
<br />
//get .this Instance pointer ;)<br />
<br />
CombatDebugHUD_Update:<br />
jmp CombatDebugHUD_Update_h<br />
CombatDebugHUD_Update_b:<br />
<br />
SimGameState_Update:<br />
jmp SimGameState_Update_h<br />
db 90 90 90 90 90<br />
SimGameState_Update_b:<br />
<br />
..<br />
..<br />
<br />
CombatDebugHUD_Update_h:<br />
mov [pCombatDebugHUD],rcx<br />
CombatDebugHUD_Update_o:<br />
readmem( CombatDebugHUD_Update, 15 )<br />
jmp CombatDebugHUD_Update_b<br />
<br />
SimGameState_Update_h:<br />
mov [pSimGameState],rcx<br />
SimGameState_Update_o:<br />
readmem( SimGameState_Update, 19 )<br />
jmp SimGameState_Update_b<br />
<br />
..<br />
..<br />
<br />
[DISABLE]<br />
<br />
CombatDebugHUD_Update:<br />
readmem( CombatDebugHUD_Update_o, 15 )<br />
<br />
SimGameState_Update:<br />
readmem( SimGameState_Update_o, 19 )<br />
<br />
..<br />
..<br />
<br />
<br />
<br />
If we go to CombatDebugHUD_Update, which in reality is BattleTech.UI:CombatDebugHUD:Update, we see this:<br />
<br />
Code:
<br />
543BA8D0 - 55                    - push rbp<br />
543BA8D1 - 48 8B EC              - mov rbp,rsp<br />
543BA8D4 - 53                    - push rbx<br />
543BA8D5 - 56                    - push rsi<br />
543BA8D6 - 57                    - push rdi<br />
543BA8D7 - 41 54                 - push r12<br />
543BA8D9 - 41 55                 - push r13<br />
543BA8DB - 41 56                 - push r14<br />
543BA8DD - 41 57                 - push r15<br />
543BA8DF - 48 83 EC 28           - sub rsp,28<br />
543BA8E3 - 48 89 4D A8           - mov [rbp-58],rcx<br />
<br />
<br />
<br />
Keep in mind CE will most likely create a 14-bytes jump when allocation is not done close to game module. In this case, the Assembly-CSharp.dll is dynamically allocated, so there are 99% chances you won't be able to create 5-bytes jumps. What I mean with this is when you write "jmp Hook" to your allocated memory space, CE can do this:<br />
<br />
Code:
<br />
E8 xx xx xx xx - JMP Hook<br />
<br />
<br />
or this:<br />
Code:
<br />
FF 25 xx xx xx xx xx xx xx xx xx xx xx xx - jmp Hook<br />
<br />
<br />
<br />
We rule out the possibility of 5-bytes JMPs; let's focus on 14-bytes.<br />
<br />
Back to my explanation. We're at BattleTech.UI:CombatDebugHUD:Update with displayed ASM:<br />
<br />
Code:
<br />
543BA8D0 - 55                    - push rbp<br />
543BA8D1 - 48 8B EC              - mov rbp,rsp<br />
543BA8D4 - 53                    - push rbx<br />
543BA8D5 - 56                    - push rsi<br />
543BA8D6 - 57                    - push rdi<br />
543BA8D7 - 41 54                 - push r12<br />
543BA8D9 - 41 55                 - push r13<br />
543BA8DB - 41 56                 - push r14<br />
543BA8DD - 41 57                 - push r15<br />
543BA8DF - 48 83 EC 28           - sub rsp,28<br />
543BA8E3 - 48 89 4D A8           - mov [rbp-58],rcx<br />
<br />
<br />
<br />
In order to produce a JMP, CE will overwrite 14 bytes at the prologue of our function. So this:<br />
<br />
Code:
<br />
CombatDebugHUD_Update:<br />
jmp CombatDebugHUD_Update_h<br />
<br />
<br />
produces this:<br />
Code:
<br />
543BA8D0 - FF25 00000000 99037B5FF77F0000 - jmp 7FF75F7B0399<br />
543BA8DE - 57                    - push rdi<br />
543BA8DF - 48 83 EC 28           - sub rsp,28<br />
<br />
<br />
<br />
So code from 543BA8D0 (prologue) down to 543BA8DE was replaced with a long JMP. All nice and dandy, but.. from our cave, we want to return to "push rdi" instruction, so game resumes execution after having passed through our cave. If you count 14 bytes from top going down, you got:<br />
<br />
Code:
<br />
55 48 8B EC 53 56 57 41 54 41 55 41 56 41<br />
<br />
<br />
<br />
As you can see the 0x41 byte is part of a 2-bytes instruction. So CE will break it with the JMP:<br />
<br />
Code:
543BA8DD - 41 57                 - push r15
<br />
<br />
<br />
So it wouldn't be correct the return JMP to point to 543BA8DE, because it would execute an invalid instruction. If you check my code, I am indicating exactly that:<br />
<br />
Code:
<br />
CombatDebugHUD_Update:<br />
jmp CombatDebugHUD_Update_h<br />
CombatDebugHUD_Update_b:<br />
<br />
..<br />
..<br />
<br />
CombatDebugHUD_Update_h:<br />
..<br />
..<br />
jmp CombatDebugHUD_Update_b<br />
<br />
<br />
<br />
By assigning the CombatDebugHUD_Update_b label right under jmp CombatDebugHUD_Update_h, I am telling the JMP in the cave to land right below it to the invalid instruction: jmp CombatDebugHUD_Update_b. That's why 1.2 table crashes with latest version of the game. And that's why you should avoid methods like these unless you intend to update the table with each freakin' game update (because the Mono code is JIT-ed differently, having different sizes or containing a different order of the instructions). In short, your AOBs (if you use them) will certainly break as well.<br />
<br />
So.. to avoid this, I thought of not actually using a hook. In some occasions, you can fetch a pointer without the need to use hooks. Which is what I'm going to show you in the next episode ;)<br />
<br />
BR,<br />
Sun
 
Top