BattleTech (CE Mono vs. JustDecompile)

Post here (make sure thread doesn't exist first) any type of tutorials: text, images, videos or oriented discussions on specific games. No online-related discussions/posts OR warez!
Post Reply
User avatar
SunBeam
Administration
Administration
Posts: 4932
Joined: Sun Feb 04, 2018 7:16 pm
Reputation: 4630

BattleTech (CE Mono vs. JustDecompile)

Post by SunBeam »

[B][U]EPISODE 1[/U][/B]



Hello folks,



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 [I]Unity[/I] decompiled outputs versus the decompiled code Cheat Engine's [I]MonoDataCollector[/I] returns. For this explanation I will use the below:

[LIST]

[*][B]Cheat Engine 6.7[/B] -- ignore my screenshots showing RC3; you don't need RC3

[*][B]Telerik JustDecompile[/B] -- you can download it [URL='https://www.telerik.com/products/decompiler.aspx']here[/URL]

[*][B]BattleTech[/B] game

[*][B]BattleTech table[/B] found in [URL='https://fearlessrevolution.com/threads/battletech.6603/#post-43753']this[/URL] post

[/LIST]

Alright.



Fire up the game, get in a map. Open Cheat Engine, load table, activate [B][ Enable ][/B] script, activate [B]Cheat Handler[/B] script. Go back in-game and hit [B]Numpad 0[/B] to execute [B]DEBUG_PlayerOneGodMode[/B]. Wait, wait.. what did I just do? Rewind, please :)



Let's take them one by one, from [B]JustDecompile[/B] perspective, so you understand how I got to what the script just did ;)



Fire-up Telerik JustDecompile. Hit Ctrl+O and choose [B]Assembly-CSharp.dll[/B] from your [I]BATTLETECHBattleTech_DataManaged[/I] folder. Once opened, you will see a [B]Search[/B] button up-top on the toolbar. Click it and type [I]godmode[/I] in the [B]Find What:[/B] field. You will see this:



[IMG]https://i.imgur.com/iXWPTYU.png[/IMG]



See [B]DEBUG_PlayerOneGodMode[/B] reference on second line? Double-click it. You'll be taken here:



[IMG]https://i.imgur.com/QymRkUj.png[/IMG]



What can you see in that picture? Simple:

[LIST]

[*]Left tree shows the function, as well as the [I]Namespace[/I] and [I]Type Name[/I]. Why are these important? Because we will make use of them to get to this function in Cheat Engine.

[*]Based on the above, we can concatenate these to a string with ":" as separator - - [COLOR=rgb(85, 57, 130)]BattleTech.UI[/COLOR]:[COLOR=rgb(26, 188, 156)]CombatDebugHUD[/COLOR] - - this denotes hierarchy in C++ (a member of).

[*][COLOR=null]Our function is called DEBUG_PlayerOneGodMode, so it will be appended to the above with a ":" as well (member of).[/COLOR]

[*][COLOR=null]Final result is:[/COLOR]

[COLOR=null][code]BattleTech.UI:CombatDebugHUD:DEBUG_PlayerOneGodMode[/code][/COLOR]

[*][COLOR=null]We'll see in a bit what's to be done with this.[/COLOR]





[*]Right side of the window shows you the actual function and its code:

[/LIST]

[code=cpp]

public void DEBUG_PlayerOneGodMode()

{

this.SetGodMode(this.combatHUD.Combat.Teams.Find((Team x) => x.GUID == "bf40fd39-ccf9-47c4-94a6-061809681140"));

}

[/code]



Looking further at the code, we see this function calls-in another: [B]SetGodMode[/B]. Click on it and you are taken here:



[IMG]https://i.imgur.com/GXKsusO.png[/IMG]



Transcript:



[code=cpp]

private void SetGodMode(Team team)

{

for (int i = 0; i < team.units.Count; i++)

{

AbstractActor item = team.units[i];

item.StatCollection.ModifyStat("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);

item.StatCollection.ModifyStat("debug", -1, "ReceivedInstabilityMultiplier", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);

item.StatCollection.ModifyStat("debug", -1, "IgnorePilotInjuries", StatCollection.StatOperation.Set, true, -1, true);

for (int j = 0; j < item.Weapons.Count; j++)

{

Weapon weapon = item.Weapons[j];

weapon.StatCollection.ModifyStat("debug", -1, "DamagePerShot", StatCollection.StatOperation.Float_Multiply, 10f, -1, true);

weapon.StatCollection.ModifyStat("debug", -1, "AccuracyModifier", StatCollection.StatOperation.Float_Add, -20f, -1, true);

}

item.Combat.MessageCenter.PublishMessage(new FloatieMessage(item.GUID, item.GUID, "GOD MODE", FloatieMessage.MessageNature.Buff));

}

}

[/code]





So, what the [I]developer[/I] God Mode does in reality is:

[LIST]

[*][B]DamageReductionMultiplierAll[/B] is set to its [I]current value * 0.1f[/I]

[*][B]ReceivedInstabilityMultiplier[/B] is set to [I]current value * 0.1f[/I]

[*][B]IgnorePilotInjuries[/B] becomes [I]active[/I]

[*][B]DamagePerShot[/B] is set to [I]current value * 10f[/I]

[*][B]AccuracyModifier[/B] is set to [I]current value + (-20f)[/I]

[/LIST]

This happens for all Mechs in player's team.



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 [I]instance pointer[/I] to be able to call it. Even though you see a [I]void[/I] type of function, this only means it doesn't take-in parameters. However, the instance pointer - also referenced as [I]this[/I] - is required.



How do you know you need this and where to find it?



[B]1)[/B] You know you need this due to this piece of code:



[code=cpp]

public void DEBUG_PlayerOneGodMode()

{

this.SetGodMode(this.combatHUD.Combat.Teams.Find((Team x) => x.GUID == "bf40fd39-ccf9-47c4-94a6-061809681140"));

}

[/code]





See [B]this.[/B]SetGodMode? See [B]this.[/B]combatHUD.Combat.Teams.Find? That's how you know :)



[B]2)[/B] To get it you would need to break on a function you know - from testing - is executed in the [B]Namespace:Type Name[/B] (just so I use the references from JustDecompile). Most Unity functions come - conveniently - with an [B]Update [/B]function :) You'll see in a bit.



Time to move to Cheat Engine; remember you've loaded the table and executed [B]Numpad 0[/B]. 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.



To get to these functions we'll make use of what I mentioned earlier:

[LIST]

[*]Cheat Engine > Memory Viewer

[*]Ctrl+G >

[COLOR=null][code]BattleTech.UI:CombatDebugHUD:DEBUG_PlayerOneGodMode - OR - BattleTech.UI:CombatDebugHUD:SetGodMode[/code][/COLOR]

[*][COLOR=null][COLOR=null][COLOR=null]OK[/COLOR][/COLOR][/COLOR]

[/LIST]

(before [I]executing [/I]the functions)



DEBUG_PlayerOneGodMode:



[IMG]https://i.imgur.com/Tz26mV2.png[/IMG]



SetGodMode:



[IMG]https://i.imgur.com/0dPssTj.png[/IMG]



(after [I]executing [/I]the functions)



DEBUG_PlayerOneGodMode:



[IMG]https://i.imgur.com/4XUpp7h.png[/IMG]



SetGodMode:



[IMG]https://i.imgur.com/8ADF4fk.png[/IMG]



OK, OK.. but what about the [I]instance pointer[/I]? We'll get it from debugging [COLOR=null][COLOR=null]BattleTech.UI:CombatDebugHUD:[B]Update[/B]. So head there in Cheat Engine's Memory Viewer via Ctrl+G and set a breakpoint at the function's prologue:[/COLOR][/COLOR]



[IMG]https://i.imgur.com/bspBxf1.png[/IMG]



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.



Your [I]instance pointer[/I] is in [B]RCX[/B]. In my case, 0x0000000142C77380.



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 :)



[code=CEA]

mono_thAttach:

sub rsp,28

// attach thread to mono domain

call mono.mono_get_root_domain

mov rcx,rax

call mono.mono_thread_attach

mov [self],rax

add rsp,28

ret



mono_thDetach:

sub rsp,28

// detach thread from mono domain

mov rcx,[self]

call mono.mono_thread_detach

add rsp,28

ret

[/code]





Then a function that would use the two above:



[code=CEA]

AddFunds_do:

sub rsp,28

call mono_thAttach

mov rcx,[pSimGameState]

test rcx,rcx

je short @f

mov rdx,[dwFundsAmount]

xor r8,r8

mov r9d,1

call _AddFunds

@@:

call mono_thDetach

add rsp,28

ret

[/code]





So up to this point you have the tools you need to understand a [I]void[/I] 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.



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).



Let's see; I'll take the first parameter for a spin, the rest should be easy once you get this one:



[code=cpp]

item.StatCollection.ModifyStat("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);

[/code]





In Cheat Engine's ASM:



[code=CEA]

B8192400 - 55 - push rbp

B8192401 - 48 8B EC - mov rbp,rsp

B8192404 - 56 - push rsi

B8192405 - 57 - push rdi

B8192406 - 41 55 - push r13

B8192408 - 41 56 - push r14

B819240A - 41 57 - push r15

B819240C - 48 83 EC 28 - sub rsp,28 { 40 }

B8192410 - 48 8B F2 - mov rsi,rdx

B8192413 - 45 33 ED - xor r13d,r13d

B8192416 - E9 ED020000 - jmp BattleTech.UI:CombatDebugHUD:SetGodMode+308

B819241B - 48 8D 64 24 00 - lea rsp,[rsp+00]

B8192420 - 48 8B 46 48 - mov rax,[rsi+48]

B8192424 - 48 8B C8 - mov rcx,rax

B8192427 - 49 8B D5 - mov rdx,r13

B819242A - 48 83 EC 20 - sub rsp,20 { 32 }

B819242E - 83 38 00 - cmp dword ptr [rax],00 { 0 }

B8192431 - 49 BB C032EC0800000000 - mov r11,System.Collections.Generic:List`1:get_Item { [EC8B4855] }

B819243B - 41 FF D3 - call r11

B819243E - 48 83 C4 20 - add rsp,20 { 32 }

B8192442 - 48 8B F8 - mov rdi,rax

B8192445 - 48 8B C7 - mov rax,rdi

B8192448 - 48 8B C8 - mov rcx,rax

B819244B - 83 39 00 - cmp dword ptr [rcx],00 { 0 }

B819244E - 48 8B 40 58 - mov rax,[rax+58]

B8192452 - F3 0F10 05 06030000 - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] }

B819245A - F3 0F5A C0 - cvtss2sd xmm0,xmm0

B819245E - 48 8B C8 - mov rcx,rax

B8192461 - BA 50AF3A67 - mov edx,673AAF50 { [0862A2E0] }

B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }

B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] }

B819247A - 6A 01 - push 01 { 1 }

B819247C - 6A FF - push -01 { 255 }

B819247E - 48 83 EC 08 - sub rsp,08 { 8 }

B8192482 - F2 0F5A E8 - cvtsd2ss xmm5,xmm0

B8192486 - F3 0F11 2C 24 - movss [rsp],xmm5

B819248B - 6A 0C - push 0C { 12 }

B819248D - 48 83 EC 20 - sub rsp,20 { 32 }

B8192491 - 83 38 00 - cmp dword ptr [rax],00 { 0 }

B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }

B819249E - 41 FF D3 - call r11

B81924A1 - 48 83 C4 40 - add rsp,40 { 64 }

B81924A5 - 83 3F 00 - cmp dword ptr [rdi],00 { 0 }

B81924A8 - 48 8B 47 58 - mov rax,[rdi+58]

B81924AC - F3 0F10 05 9C020000 - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+350] { [0.10] }

B81924B4 - F3 0F5A C0 - cvtss2sd xmm0,xmm0

B81924B8 - 48 8B C8 - mov rcx,rax

B81924BB - BA 50AF3A67 - mov edx,673AAF50 { [0862A2E0] }

B81924C0 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }

B81924CA - 41 B9 4015447C - mov r9d,7C441540 { [0862A2E0] }

B81924D0 - 6A 01 - push 01 { 1 }

B81924D2 - 6A FF - push -01 { 255 }

B81924D4 - 48 83 EC 08 - sub rsp,08 { 8 }

B81924D8 - F2 0F5A E8 - cvtsd2ss xmm5,xmm0

B81924DC - F3 0F11 2C 24 - movss [rsp],xmm5

B81924E1 - 6A 0C - push 0C { 12 }

B81924E3 - 48 83 EC 20 - sub rsp,20 { 32 }

B81924E7 - 83 38 00 - cmp dword ptr [rax],00 { 0 }

B81924EA - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }

B81924F4 - 41 FF D3 - call r11

[/code]





[IMG]https://i.imgur.com/bxCHSDU.png[/IMG]



But, but.. how do I know which is which? Well.. it's called [B]debugging + sense of observation[/B]. It's something you'll have to learn in your own free time.



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 [I]rcx[/I] with always be the [I]this[/I] pointer - - the instance pointer - - so start from [I]rdx[/I] onward:



[code=cpp]

item.StatCollection.ModifyStat("debug", -1, "DamageReductionMultiplierAll", StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true);

[/code]





[I]item.StatCollection.ModifyStat[/I] is a function; its JustDecompile ASM reference is here:



[code=CEA]

B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] }

B819249E - 41 FF D3 - call r11

[/code]





First parameter of this function is a string - [I]"debug"[/I]; in ASM it should be a buffer referenced somewhere (see [B]rdx[/B]):



[code=CEA]

B8192461 - BA 50AF3A67 - mov edx,673AAF50 { [0862A2E0] }

[/code]





[IMG]https://i.imgur.com/1dT8V6h.png[/IMG]



Second parameter is -1. This is dead obvious marked here (see [B]r8[/B]):



[code=CEA]

B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 }

[/code]





Third parameter is another string - [I]"DamageReductionMultiplierAll"[/I]; in ASM it should be a buffer referenced somewhere (see [B]r9[/B]):



[code=CEA]

B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] }

[/code]





[IMG]https://i.imgur.com/6CDo1Ss.png[/IMG]



Next-up, we have these:



[code=cpp]

StatCollection.StatOperation.Float_Multiply, 0.1f, -1, true

[/code]





In JustDecompile, if you click on [B]Float_Multiply[/B] you'll be taken to this location; care to count with me? :D



[IMG]https://i.imgur.com/R03eLEu.png[/IMG]



[code]

Set, //0

Int_Add, //1

Int_Subtract, //2

Int_Multiply, //3

Int_Divide, //4

Int_Divide_Denom, //5

Int_Mod, //6

Int_Multiply_Float, //7

Int_Divide_Float, //8

Int_Divide_Denom_Float, //9

Float_Add, //A

Float_Subtract, //B

Float_Multiply, //C

Float_Divide, //D

Float_Divide_Denom, //E

Float_Multiply_Int, //F

Float_Divide_Int, //10

Float_Divide_Denom_Int, //11

String_Append, //12

String_Prepend, //13

Bitflag_SetBit, //14

Bitflag_FlipBit, //15

Bitflag_Combine //16

[/code]





So [I]Float_Multiply[/I] is 0xC in that list, right? :) Good. Let's continue; next-up is a float value - 0.1 - followed by a -1 and a [I]true[/I]. 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:



[code=CEA]

B8192452 - F3 0F10 05 06030000 - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] }

B819245A - F3 0F5A C0 - cvtss2sd xmm0,xmm0

..

..

B819247A - 6A 01 - push 01 { 1 }

B819247C - 6A FF - push -01 { 255 }

B819247E - 48 83 EC 08 - sub rsp,08 { 8 }

B8192482 - F2 0F5A E8 - cvtsd2ss xmm5,xmm0

B8192486 - F3 0F11 2C 24 - movss [rsp],xmm5

B819248B - 6A 0C - push 0C { 12 }

[/code]





Cheat Engine actually shows us where the 0.1 float reference is. It is first converted into a double-precision float (see CVTSS2SD instruction [URL='https://www.felixcloutier.com/x86/CVTSS2SD.html']here[/URL]), then to a single-precision float (see CVTSD2SS instruction [URL='https://www.felixcloutier.com/x86/CVTSD2SS.html']here[/URL]). Then comes the push 1, push -1, storing the MMX conversion to stack and the push C (remember Float_Multiply?).



Let's read it now, from top to bottom:



[code=CEA]

B8192452 - F3 0F10 05 06030000 - movss xmm0,[BattleTech.UI:CombatDebugHUD:SetGodMode+360] { [0.10] } // 0.1f

B819245A - F3 0F5A C0 - cvtss2sd xmm0,xmm0

B819245E - 48 8B C8 - mov rcx,rax

B8192461 - BA 50AF3A67 - mov edx,673AAF50 { [0862A2E0] } // "debug" string

B8192466 - 49 B8 FFFFFFFFFFFFFFFF - mov r8,FFFFFFFFFFFFFFFF { -1 } // -1

B8192470 - 49 B9 601F4B7501000000 - mov r9,00000001754B1F60 { [0862A2E0] } // "DamageReductionMultiplierAll" string

B819247A - 6A 01 - push 01 { 1 } // true

B819247C - 6A FF - push -01 { 255 } // -1

B819247E - 48 83 EC 08 - sub rsp,08 { 8 }

B8192482 - F2 0F5A E8 - cvtsd2ss xmm5,xmm0

B8192486 - F3 0F11 2C 24 - movss [rsp],xmm5 // 0.1f written here

B819248B - 6A 0C - push 0C { 12 } // StatCollection.StatOperation.Float_Multiply

B819248D - 48 83 EC 20 - sub rsp,20 { 32 }

B8192491 - 83 38 00 - cmp dword ptr [rax],00 { 0 }

B8192494 - 49 BB D05719B800000000 - mov r11,BattleTech:StatCollection:ModifyStat { [EC8B4855] } // item.StatCollection.ModifyStat function

B819249E - 41 FF D3 - call r11 // called here

[/code]





And for the grand finale, let's check the stack before [I]call r11 [/I]happens:



[IMG]https://i.imgur.com/cPFtjrS.png[/IMG]



Am hoping this was clear enough to help you modify the God Mode function to your liking :p



BR,

Sun
Last edited by SunBeam on Thu Jan 01, 1970 12:00 am, edited 9 times in total.

HK-Demoneye
What is cheating?
What is cheating?
Posts: 3
Joined: Sun Apr 29, 2018 11:02 pm
Reputation: 0

BattleTech (CE Mono vs. JustDecompile)

Post by HK-Demoneye »

That may be the single most difficult thing I've ever read in my life.

diabloamezon
Cheater
Cheater
Posts: 45
Joined: Fri Mar 03, 2017 3:55 am
Reputation: 1

BattleTech (CE Mono vs. JustDecompile)

Post by diabloamezon »

It's time to learn. :p

User avatar
SunBeam
Administration
Administration
Posts: 4932
Joined: Sun Feb 04, 2018 7:16 pm
Reputation: 4630

BattleTech (CE Mono vs. JustDecompile)

Post by SunBeam »

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
Noobzor
Noobzor
Posts: 6
Joined: Wed May 02, 2018 7:54 pm
Reputation: 0

BattleTech (CE Mono vs. JustDecompile)

Post by Tetnacious »

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.

User avatar
SunBeam
Administration
Administration
Posts: 4932
Joined: Sun Feb 04, 2018 7:16 pm
Reputation: 4630

BattleTech (CE Mono vs. JustDecompile)

Post by SunBeam »

[B][U]EPISODE 2[/U][/B]



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.



If you check my table [URL='https://fearlessrevolution.com/threads/battletech.6603/#post-43753']here[/URL], you will notice I use [I]hooks[/I] to get the instance pointers needed for some of the functions in the [B]Cheat Handler[/B]. Considering the code runs in a thread, therefore requires no per-se interaction from the user - just using the designed hotkey to [I]execute[/I] 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 [URL='https://fearlessrevolution.com/attachments/battletech-ct.9982/']table[/URL], which, as you reported, crashes on latest version. Let's see why:



[code]

[ENABLE]



define( CombatDebugHUD_Update, "BattleTech.UI:CombatDebugHUD:Update" )

define( SimGameState_Update, "BattleTech:SimGameState:Update" )



..

..



//***************************

//* Hooks *

//***************************



//get .this Instance pointer ;)



CombatDebugHUD_Update:

jmp CombatDebugHUD_Update_h

CombatDebugHUD_Update_b:



SimGameState_Update:

jmp SimGameState_Update_h

db 90 90 90 90 90

SimGameState_Update_b:



..

..



CombatDebugHUD_Update_h:

mov [pCombatDebugHUD],rcx

CombatDebugHUD_Update_o:

readmem( CombatDebugHUD_Update, 15 )

jmp CombatDebugHUD_Update_b



SimGameState_Update_h:

mov [pSimGameState],rcx

SimGameState_Update_o:

readmem( SimGameState_Update, 19 )

jmp SimGameState_Update_b



..

..



[DISABLE]



CombatDebugHUD_Update:

readmem( CombatDebugHUD_Update_o, 15 )



SimGameState_Update:

readmem( SimGameState_Update_o, 19 )



..

..

[/code]





If we go to [I]CombatDebugHUD_Update[/I], which in reality is [B]BattleTech.UI:CombatDebugHUD:Update[/B], we see this:



[code]

543BA8D0 - 55 - push rbp

543BA8D1 - 48 8B EC - mov rbp,rsp

543BA8D4 - 53 - push rbx

543BA8D5 - 56 - push rsi

543BA8D6 - 57 - push rdi

543BA8D7 - 41 54 - push r12

543BA8D9 - 41 55 - push r13

543BA8DB - 41 56 - push r14

543BA8DD - 41 57 - push r15

543BA8DF - 48 83 EC 28 - sub rsp,28

543BA8E3 - 48 89 4D A8 - mov [rbp-58],rcx

[/code]





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:



[code]

E8 xx xx xx xx - JMP Hook

[/code]



or this:

[code]

FF 25 xx xx xx xx xx xx xx xx xx xx xx xx - jmp Hook

[/code]





We rule out the possibility of 5-bytes JMPs; let's focus on 14-bytes.



Back to my explanation. We're at BattleTech.UI:CombatDebugHUD:Update with displayed ASM:



[code]

543BA8D0 - 55 - push rbp

543BA8D1 - 48 8B EC - mov rbp,rsp

543BA8D4 - 53 - push rbx

543BA8D5 - 56 - push rsi

543BA8D6 - 57 - push rdi

543BA8D7 - 41 54 - push r12

543BA8D9 - 41 55 - push r13

543BA8DB - 41 56 - push r14

543BA8DD - 41 57 - push r15

543BA8DF - 48 83 EC 28 - sub rsp,28

543BA8E3 - 48 89 4D A8 - mov [rbp-58],rcx

[/code]





In order to produce a JMP, CE will overwrite 14 bytes at the prologue of our function. So this:



[code]

CombatDebugHUD_Update:

jmp CombatDebugHUD_Update_h

[/code]



produces this:

[code]

543BA8D0 - FF25 00000000 99037B5FF77F0000 - jmp 7FF75F7B0399

543BA8DE - 57 - push rdi

543BA8DF - 48 83 EC 28 - sub rsp,28

[/code]





So code from 543BA8D0 (prologue) down to 543BA8DE was replaced with a [B]long[/B] 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:



[code]

55 48 8B EC 53 56 57 41 54 41 55 41 56 41

[/code]





As you can see the 0x41 byte is part of a 2-bytes instruction. So CE will break it with the JMP:



[code]543BA8DD - 41 57 - push r15[/code]





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:



[code]

CombatDebugHUD_Update:

jmp CombatDebugHUD_Update_h

CombatDebugHUD_Update_b:



..

..



CombatDebugHUD_Update_h:

..

..

jmp CombatDebugHUD_Update_b

[/code]





By assigning the [I]CombatDebugHUD_Update_b[/I] label right under [I]jmp CombatDebugHUD_Update_h[/I], I am telling the JMP in the cave to land right below it to the invalid instruction: [I]jmp CombatDebugHUD_Update_b[/I]. 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.



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 [I]hooks[/I]. Which is what I'm going to show you in the next episode ;)



BR,

Sun
Last edited by SunBeam on Thu Jan 01, 1970 12:00 am, edited 2 times in total.

Post Reply

Who is online

Users browsing this forum: No registered users