How do you disable a 'cmp' comparator?

Section's for general approaches on hacking various options in games. No online-related discussions/posts OR warez!
User avatar
SunBeam
Administration
Administration
Posts: 4959
Joined: Sun Feb 04, 2018 7:16 pm
Reputation: 4697

Re: How do you disable a 'cmp' comparator?

Post by SunBeam »

Metanoia wrote:
Thu Jul 25, 2024 2:54 pm
...
Not the case here, but haven't you ever pushed rcx, rdx and after a function CALL, you got something else in them on pop? :D I guess he's using statics to make sure those addresses won't be stack-overwritten by something unforeseen :P Some sort of "best practices" of his. For short operations with no CALLs in-between, sure, push/pop works; but for more extensive ones, you may end-up with wrong data when pop-ing. So always check the registers states before a CALL and after a CALL, on pop, to make sure you got 1:1 matches.

Metanoia
Scammer
Posts: 71
Joined: Thu Mar 07, 2024 7:16 pm
Reputation: 42

Re: How do you disable a 'cmp' comparator?

Post by Metanoia »

SunBeam wrote:
Thu Jul 25, 2024 3:00 pm
Metanoia wrote:
Thu Jul 25, 2024 2:54 pm
...
Not the case here, but haven't you ever pushed rcx, rdx and after a function CALL, you got something else in them on pop? :D I guess he's using statics to make sure those addresses won't be stack-overwritten by something unforeseen :P Some sort of "best practices" of his. For short operations with no CALLs in-between, sure, push/pop works; but for more extensive ones, you may end-up with wrong data when pop-ing. So always check the registers states before a CALL and after a CALL, on pop, to make sure you got 1:1 matches.
I assumed they were referring to your video where there are no calls unless its written with invisible ink.

But yes I know what you mean.

Image

User avatar
Rhark
Expert Cheater
Expert Cheater
Posts: 3749
Joined: Tue Apr 16, 2019 1:27 am
Reputation: 1494

Re: How do you disable a 'cmp' comparator?

Post by Rhark »

Metanoia wrote:
Thu Jul 25, 2024 2:54 pm
...

Or just slap it on the stack why make a symbol for it ...
Care to give an example?

Metanoia
Scammer
Posts: 71
Joined: Thu Mar 07, 2024 7:16 pm
Reputation: 42

Re: How do you disable a 'cmp' comparator?

Post by Metanoia »

Rhark wrote:
Thu Jul 25, 2024 4:03 pm
Metanoia wrote:
Thu Jul 25, 2024 2:54 pm
...

Or just slap it on the stack why make a symbol for it ...
Care to give an example?
Make space on the stack with sub rsp, ???. For a 128-bit register thats 16 bytes, so use sub rsp, 10. Now that you have space you can move shit onto the stack using the appropriate move instruction. I'll use movss to save the first section of the register. It will look like this:

sub rsp, 10 ; <-- 16 in hex
movss [rsp], xmm1

; ... other code here ...

movss xmm1, [rsp]
add rsp, 10

If you want to save more just make more space and do (Whatever move here) [rsp+10],xmm69 and so on.

Here is a useful link: [Link]

Another link : [Link] <-- The people here share a brain cell its not sub rsp,16 its 10.

User avatar
SilverRabbit90
Table Makers
Table Makers
Posts: 245
Joined: Fri Jan 15, 2021 12:01 am
Reputation: 213

Re: How do you disable a 'cmp' comparator?

Post by SilverRabbit90 »

Guys, I know that theoretically the problem is solved, but I'm stubborn so I found another way; basically, I created a timer with Lua (even if there is no longer a need).

Script 1 for Enable:
Spoiler

Code: Select all

[ENABLE]

aobscanmodule(BuidingSpeedTest2,JWE2.exe,F3 0F 10 46 04 F3 0F 5C C1) // should be unique
alloc(newmem,$1000,BuidingSpeedTest2)

alloc(QuickBuild,8)
registersymbol(QuickBuild)

QuickBuild:
dd 1

label(code)
label(return)

newmem:

cmp [QuickBuild],1
jne code

// Set the construction time to 5 seconds
mov [rsi+04],(float)5
movss xmm0,[rsi+04]
mov [QuickBuild],0

code:
movss xmm0,[rsi+04]
jmp return

BuidingSpeedTest2:
  jmp newmem
return:
registersymbol(BuidingSpeedTest2)

// Lua script to re-enable QuickBuild after 6 seconds
{$lua}
if syntaxcheck then return end

function reenableQuickBuild()
  while true do
    local QuickBuildAddress = getAddressSafe("QuickBuild")
    if QuickBuildAddress then
      if readInteger(QuickBuildAddress) == 0 then
        -- Wait for 6 seconds
        sleep(6000)
        -- Re-enable QuickBuild
        writeInteger(QuickBuildAddress,1)
      end
    end
    sleep(500)  -- Check every second
  end
end

-- Start the re-enable thread
createThread(reenableQuickBuild)
{$asm}

[DISABLE]

BuidingSpeedTest2:
  db F3 0F 10 46 04

unregistersymbol(QuickBuild)
unregistersymbol(BuidingSpeedTest2)
dealloc(newmem)
dealloc(QuickBuild)
Script 2 To Active functions:
Spoiler

Code: Select all

[ENABLE]
QuickBuild:
dd 1
 
 
[DISABLE]
QuickBuild:
dd 0

Video:
Spoiler


Here is my Cheat Engine Tutorial:
Spoiler
[Link]
Last edited by SilverRabbit90 on Mon Mar 24, 2025 10:46 pm, edited 1 time in total.

User avatar
SilverRabbit90
Table Makers
Table Makers
Posts: 245
Joined: Fri Jan 15, 2021 12:01 am
Reputation: 213

Re: How do you disable a 'cmp' comparator?

Post by SilverRabbit90 »

For anyone interested, I have created some scripts in Python 3 that allow you to extract or compare AoB from a script (the AoB written in [DISABLE] between {}).
Spoiler


Here is my Cheat Engine Tutorial:
Spoiler
[Link]

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

Re: How do you disable a 'cmp' comparator?

Post by SunBeam »

SilverRabbit90 wrote:
Thu Jul 25, 2024 11:18 am
...
If you're set out to work on gamehacking like you do, without extra documentation or read-ups, then you'll keep wondering why this and that. I'm not here to apply morals on you or shove information into your head; but please, learn the difference between volatile and non-volatile registers (see here: [Link] --> "Microsoft x64 calling convention"). Aside from that, you should check the way the function is constructed and what happens at your hook/injection point. If some register (or multiple) is overwritten IMMEDIATELY after your hook, then you can use that safely:

Code: Select all

addr0: mov rax,[rcx+20] <-- hook here and notice how RBX is overwritten below
addr1: mov rbx,[rax+40]
Given the above you can use RBX directly without doing push rbx/pop rbx. So this would happen:

Code: Select all

addr0: jmp CodeCave

CodeCave:
mov rbx,pPlayer
mov rbx,[rbx+20]
..
.. // etc. using rbx without pushing or poping it
original:
mov rax,[rcx+20]
jmp back/addr1

back/addr1:
mov rbx,[rax+40]
People only use push/pop when not needed because they're lazy. In x86 assembly you would always see pushad/pushfd + popfd/popad. Everyone and their dog would do that.. cuz "it's safer" T_T..

Apart from that, as explained in the my next reply, right underneath yours (viewtopic.php?p=363640#p363640 -- hope you read that), in x64 programming only the first 4 or 5 SSE2 registers are really important (xmm0, ..., xmm3). The rest (unless the function context implies it) are not used, so basically "volatile". Unless the function context is not using any of them (like my xmm15), you're free to use it. You can load ANYTHING in it (you can use it as stack space). You have room for 4 quads in each of them, 16 bytes in total, so go crazy! :P I recommend using the unaligned version of the instructions (e.g.: movups) instead of the 16-bytes aligned ones (movaps). Why? To avoid crashes if your RSP is not 16-bytes aligned.

Let me know if you have any other questions; if remotely interested in ASM (I saw you push on to show us what you made for this game rather than confirm you learned anything out of this discussion).

Best regards,
Sun

User avatar
SilverRabbit90
Table Makers
Table Makers
Posts: 245
Joined: Fri Jan 15, 2021 12:01 am
Reputation: 213

Re: How do you disable a 'cmp' comparator?

Post by SilverRabbit90 »

SunBeam wrote:
Tue Mar 25, 2025 2:32 pm
SilverRabbit90 wrote:
Thu Jul 25, 2024 11:18 am
...
If you're set out to work on gamehacking like you do, without extra documentation or read-ups, then you'll keep wondering why this and that. I'm not here to apply morals on you or shove information into your head; but please, learn the difference between volatile and non-volatile registers (see here: [Link] --> "Microsoft x64 calling convention"). Aside from that, you should check the way the function is constructed and what happens at your hook/injection point. If some register (or multiple) is overwritten IMMEDIATELY after your hook, then you can use that safely:

Code: Select all

addr0: mov rax,[rcx+20] <-- hook here and notice how RBX is overwritten below
addr1: mov rbx,[rax+40]
Given the above you can use RBX directly without doing push rbx/pop rbx. So this would happen:

Code: Select all

addr0: jmp CodeCave

CodeCave:
mov rbx,pPlayer
mov rbx,[rbx+20]
..
.. // etc. using rbx without pushing or poping it
original:
mov rax,[rcx+20]
jmp back/addr1

back/addr1:
mov rbx,[rax+40]
People only use push/pop when not needed because they're lazy. In x86 assembly you would always see pushad/pushfd + popfd/popad. Everyone and their dog would do that.. cuz "it's safer" T_T..

Apart from that, as explained in the my next reply, right underneath yours (viewtopic.php?p=363640#p363640 -- hope you read that), in x64 programming only the first 4 or 5 SSE2 registers are really important (xmm0, ..., xmm3). The rest (unless the function context implies it) are not used, so basically "volatile". Unless the function context is not using any of them (like my xmm15), you're free to use it. You can load ANYTHING in it (you can use it as stack space). You have room for 4 quads in each of them, 16 bytes in total, so go crazy! :P I recommend using the unaligned version of the instructions (e.g.: movups) instead of the 16-bytes aligned ones (movaps). Why? To avoid crashes if your RSP is not 16-bytes aligned.

Let me know if you have any other questions; if remotely interested in ASM (I saw you push on to show us what you made for this game rather than confirm you learned anything out of this discussion).

Best regards,
Sun

I understand the register rbx can actually be very useful when it comes to game updates, where sometimes the offset where the value is stored (in code) changes.

Here's an example:

Code 1 (New - Game Version):
Spoiler

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

label(code)
label(return)

newmem:

code:
movsxd rax,dword ptr [rsi+30]
add eax,edi
jmp return

TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
db 48 63 46 28 03 C7

unregistersymbol(*)
dealloc(*)
Code 2 (Old - Game Version)
Spoiler

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

label(code)
label(return)

newmem:

code:
movsxd rax,dword ptr [rsi+45]
add eax,edi
jmp return

TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
db 48 63 46 28 03 C7

unregistersymbol(*)
dealloc(*)
As we can see in the new version of the game, the offset where our value was located "45", became "30" after the update. So if we wrote something like this:
Spoiler

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

label(code)
label(return)

newmem:

mov [rsi+30],(int)100000000

code:
movsxd rax,dword ptr [rsi+45]
add eax,edi
jmp return

TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
readmem(originalbytesA,9)
//db 48 63 46 28 03 C7

unregistersymbol(*)
dealloc(*)
The game would crash.


If we wanted to make the code work for both the old and new versions of the game, in this case, we would need to write the code like this:

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

alloc(originalbytesA,9)
registersymbol(originalbytesA)

originalbytesA:
readmem(TreeGatheringSpeedOnGhateringZa,9)

label(code)
label(return)

label(offset_changed)

newmem:
mov rbx,[rsi+45]
cmp rbx,0x12345678
jne offset_changed
jmp return

offset_changed:
mov [rsi+45],(int)100000000
movsxd rax,dword ptr [rsi+45]
add eax,edi
jmp return

code:
movsxd rax,dword ptr [rsi+45]
add eax,edi
jmp return

TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
readmem(originalbytesA,9)
//db 48 63 46 28 03 C7

unregistersymbol(*)
dealloc(*)
Or like this:

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

alloc(originalbytesA,9)
registersymbol(originalbytesA)

originalbytesA:
readmem(TreeGatheringSpeedOnGhateringZa,9)

label(code)
label(return)

newmem:
push rbx // Save rbx (non-volatile register)
mov rbx,[rsi+45] // Load the original value
cmp rbx,0x12345678 // Compare with the "magic" value
je code // If it matches, jump to original code

// MODIFY THE VALUE IF NECESSARY
mov [rsi+45],(int)100000000 // Set the new value

code:
movsxd rax,dword ptr [rsi+45] // ORIGINAL CODE (always executed)
add eax,edi

pop rbx // Restore rbx
jmp return // Return to the game

TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
readmem(originalbytesA,9)
//db 48 63 46 28 03 C7

unregistersymbol(*)
dealloc(*)
But as you mentioned, rbx doesn't need push and pop, so the first type of code works perfectly fine.


If something doesn't work, you could simply add a backup to "code:", so the full code would become something like this (although, from experience, I can say that I only had to use this method once because in that case, not only the offset changed after the game update, but also the register where our value was stored):
Spoiler

Code: Select all

[ENABLE]

aobscan(TreeGatheringSpeedOnGhateringZa,48 63 46 ?? 03 C7 89 46 ?? 48 8B 46) // should be unique
alloc(newmem,$1000,TreeGatheringSpeedOnGhateringZa)

alloc(originalbytesA,9)
registersymbol(originalbytesA)

originalbytesA:
readmem(TreeGatheringSpeedOnGhateringZa,9)

label(return)

label(offset_changed)

newmem:
mov rbx,[rsi+45]
cmp rbx,0x12345678
jne offset_changed
jmp return

offset_changed:
mov [rsi+45],(int)100000000
movsxd rax,dword ptr [rsi+45]
add eax,edi
jmp return


bkpGatheringSpeeds:
  readmem(TreeGatheringSpeedOnGhateringZa,6)


TreeGatheringSpeedOnGhateringZa:
jmp newmem
nop
return:
registersymbol(TreeGatheringSpeedOnGhateringZa)
registersymbol(bkpGatheringSpeeds)

[DISABLE]

TreeGatheringSpeedOnGhateringZa:
readmem(originalbytesA,9)

unregistersymbol(*)
dealloc(*)

Of course, readmem(TreeGatheringSpeedOnGhateringZa,6) just needs to be replaced with the correct byte number instead of the 6.

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

Re: How do you disable a 'cmp' comparator?

Post by SunBeam »

There is no such thing as "sometimes" :) You have to check the function flow. If you intend to use a specific register, then I recommend loading the exe in x64dbg, Ctrl+G to go to the function prologue and then hit H key and click on RBX register. x64dbg will highlight the register wherever used in the code, so you can track if it's saved at prologue to be used throughout. The only rule is about volatile/non-volatile, nothing else. Any other interpretation is solely tied to a specific game or one's personal views.

If some register or offset changes, that's due to: a) entire function having been recoded; b) compiler optimizations enabled/disabled; c) Denuvo or some other tool used throughout, which relocates/reassembles the code; d) structure changes (e.g.: adding 1 member to a structure would shift all known offsets by its data type - 4, 8, etc.)

That's about it, in general.

Post Reply

Who is online

Users browsing this forum: No registered users