I have said this NUMEROUS times:
1) not all of the Unity functions are available when the game is up
2) when they're needed by the game, they will be JIT-ed (assembled) by Unity
3) if they're not needed by the game and only CHEAT ENGINE needs them as part of enabling some script, it will JIT them, meaning it will
assemble them
In either of the scenarios above (2 or 3), the
assembled opcodes won't always be the same. It may happen that on a 3rd run of the game, for the same function, the ASM will be compiled differently. The outcome will be the same, but the instructions (hence the bytes you scan for) will not.
Example:
mov eax,ptr
vs.
lea eax,ptr
mov eax,[eax]
Will have the same effect, yet the opcodes are different. For obvious reasons: 1 line vs 2 lines., more bytes.
Another thing I did in BattleTech was to find the function by name, then SMALL SCAN inside it to find the location I want to hook. That's how I learned the assembled code is not always the same, that my AOBs failed very often.
I've seen Unity and CE do that shit a lot of time, that I gave up using AOBs. Just use Mono like any self-sufficient gamehacker, work your way through the chains, find the function you need by Class/Name and hook its PROLOGUE. Not randomly in the middle of it, not being 100% sure the ASM you look for will be there for granted.
So this:
Code: Select all
{
// ORIGINAL CODE - INJECTION POINT: DragController:Dragging+248
DragController:Dragging+218: 41 FF D3 - call r11
DragController:Dragging+21b: 85 C0 - test eax,eax
DragController:Dragging+21d: 0F 84 78 00 00 00 - je DragController:Dragging+29b
DragController:Dragging+223: 48 8B CE - mov rcx,rsi
DragController:Dragging+226: 48 BA 40 02 02 8B C1 01 00 00 - mov rdx,000001C18B020240
DragController:Dragging+230: 48 8D 64 24 00 - lea rsp,[rsp+00]
DragController:Dragging+235: 90 - nop
DragController:Dragging+236: 49 BB D0 CA 18 83 C1 01 00 00 - mov r11,UnityEngine:MonoBehaviour:StartCoroutine
DragController:Dragging+240: 41 FF D3 - call r11
DragController:Dragging+243: E9 53 00 00 00 - jmp DragController:Dragging+29b
// ---------- INJECTING HERE ----------
DragController:Dragging+248: 48 8B 46 18 - mov rax,[rsi+18]
// ---------- DONE INJECTING ----------
DragController:Dragging+24c: 48 8B C8 - mov rcx,rax
DragController:Dragging+24f: 83 39 00 - cmp dword ptr [rcx],00
DragController:Dragging+252: 48 8B 40 40 - mov rax,[rax+40]
DragController:Dragging+256: 48 8B 80 88 00 00 00 - mov rax,[rax+00000088]
DragController:Dragging+25d: C6 40 61 01 - mov byte ptr [rax+61],01
DragController:Dragging+261: 48 8B 4E 30 - mov rcx,[rsi+30]
DragController:Dragging+265: 33 D2 - xor edx,edx
DragController:Dragging+267: 48 8D AD 00 00 00 00 - lea rbp,[rbp+00000000]
DragController:Dragging+26e: 49 BB 50 92 6C 98 C1 01 00 00 - mov r11,UnityEngine:Object:op_Inequality
DragController:Dragging+278: 41 FF D3 - call r11
}
May very well be compiled like so:
Code: Select all
DragController:Dragging+248: 48 8B 46 18 - mov rax,[rsi+18]
vs.
Code: Select all
DragController:Dragging+xxx: xx xx xx xx - lea rax,[rsi+18]
DragController:Dragging+xxx: xx xx xx - mov rax,[rax]
In which case, if you scan for 48 8B 46 18 - let's assume that it should be 1 unique result you find - then it won't be found for those instances when the code got compiled like in the snippet above. Cuz.. "freedom of speech". What.. a compiler can't choose on its own which form (optimized or not) to apply for a mnemonic?
"It never happened to me". Well, if you take 20 users having the same Intel CPU you have, it won't happen. If you take an AMD user, then CE/Unity might pick a different mnemonic/form for some static instruction and the result will be different. So bitching "it works for me 100%" while others tell you it doesn't for them and solving this with an "I don't know why it happens" isn't a solution in my book. Now you know why it happens. Adjust your model if you care to make everyone happy. Otherwise, please add a disclaimer stating that the JIT-ed code will NOT always be compiled in the same form and some scripts/aobs may fail. That easy.
PeaceBeUponYou wrote: ↑Wed Mar 17, 2021 2:23 am
First of all, this is because of version difference, so its always a problem (for any table of any engine). If you have the same that i have it shouldnt be a problem.
Having the same identical Assembly-CSharp.dll doesn't guarantee the code-assembly to be identical when JIT kicks in. Yes, it will be the same in 80% of the times, but when the compiler will pick, based on where the cursor's at, optimizations and other internal crap not worth mentioning in this post, which form the compiled instructions takes.. and it won't be one that's part of your AOB.. that AOB will fail.
aSwedishMagyar wrote: ↑Tue Mar 16, 2021 6:25 pm
Certain functions may be overloaded in which case you need to use a signature to find them reliably.
I expect more from you. Please read my explanation above to PeaceBeUponYou's "oh, it must be the version then" response.
Point of this post: THE ASM BEHIND THE INSTRUCTIONS YOU SCAN FOR IN AOBS IS NOT ALWAYS THE SAME (1/3 chances it will change, depending on whatever choice the assembler "picks" for a mnemonic). So those of you who are at that incipient knowledge level and think an AOB is all you need to hack a game, you're mad wrong. And that's cuz you sit idly by, self-sufficient, in that bubble of yours, without ever attempting new things in your gamehacking lives. Not to mention you almost never visit some Engine's source-code to learn how it works; that's too much for you, too complicated. Yet have the audacity to spew retarded shit and teach the world your unfounded and not-confirmed beliefs.
If you think Unity is the only Engine causing these issues, you're wrong
Here's some standard ASM, from C++ compiled by MSVS:
Then enter Denuvo. What Denuvo does is it identifies all functions that have size > 5 bytes, then it relocates them to its own allocated memory. When that happens, the original instructions are re-assembled in their new location. Then the original code is simply over-written with a JMP to new location + 0xCC bytes. Now.. when the original instructions are reassembled in their new location, this shit happens:
Look at the bytes
I bet you never knew "MOV RCX,RAX" can have 2 mnemonics, eh?
So if you initially had an AOB that scanned for 488BC8 in the non-protected game and you found 1 result (let's assume that), now that devs use Denuvo which relocates/reassembles code and you scan for 488BC8, you won't find it. Cuz now it's 4889C1.
Now replace Denuvo with Intel or AMD CPUs. These choose the mnemonic for an instruction based on how they are designed. That's what happens with Unity and CE (CE doesn't use its own mechanism to JIT instructions; no, it uses the same logic Unity does).
How much of your AOBs do you really evaluate and think upon to say "well, I'm sure this CAN'T CHANGE so I will use the aob like this"? I bet you almost never consider a LOT of "what IFs"..
If I see anymore bickering in this topic, I will close it down.
BR,
Sun