Source Code Cheat Engine 7.1 game: Mouse teleport hack for level 3

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

h4nsbr1x

Full Member
Jul 24, 2020
7
198
0
Game Name
Cheat engine 7.1
Anticheat
N/A
Tutorial Link:
N/A
How long you been coding/hacking?
NaN days
Coding Language
asm
This took me the best part of a day to get right. Things that ground my gears:

  • GetCursorPos does some test to see if the pointer is writeable, but this check means it won't write to executable memory (even if it's also writeable). Fix: make some space on the stack
  • Got properly Micro$hafted by wsprintf. The new "use rcx/rdx/r8/r9" convention is annoying enough as it is, but the worst thing is that you also need to decrement the stack by 0x20 anyway in case the callee decides to mess with that space. Fix: make some space on the stack
You're welcome to cut out the console code (both the AllocConsole hook and the wsprintfs) as they're just there for debugging, but some of you might find it interesting.

Here's the basic idea:

  1. Hook into something that gets called regularly
  2. Check if the right mouse button is down
  3. Get the mouse coordinates
  4. Translate these into relative coordinates
  5. Translate these into the range -1.0 -> 1.0
  6. Update the multi-level pointer
Steps 1->5 are way easier if we just inject a DLL in its own thread, and step 6 is much more stable than my hack to guess if the multi-level pointer is ready to be traversed. In any case, here's a step by step for how to build a teleport hack.

Firstly, finding the place to hook... we already know where the integrity protection code is, so we can hook into that... but we need to have a handle for the window. I ended up using IDA to scan the import table and find calls to RegisterClass to find where the WindowProc is, and then hooked at the start of that. You can do this in a debugger by finding the RegisterClass call (just ctrl+g and search user32.RegisterClassA) and breakpoint. We'll store this hWnd in a variable for later

Code:
  push rax
  push rbx
  push rcx
  push rdx
  mov rcx, [rcx+8]
  mov [hWnd], rcx
Anyway, let's create a console so we can print out stuff as we go:

Code:
  mov rax, [hStdout]
  cmp rax, 0
  jne haveConsole

  call AllocConsole

  mov rcx, -0B
  call GetStdHandle

  mov [hStdout], rax
  mov rcx, szHaveConsole
  call print
  jmp haveConsole

szHaveConsole:
  db 'Console is loaded', 0x0D, 0x0A, 0

hStdout:
  db 00 00 00 00 00 00 00 00

haveConsole:
We start with a QWORD set to 0, and check if it's still 0 - if so then create the console (this won't break if we hit it multiple times) and then call GetStdHandle to get the handle for STDOUT. The print function:

Code:
bytesWritten:
  db 00 00 00 00 00 00 00 00

print:
  push rcx
  call lstrlenA

  pop rdx
  mov rcx, [hStdout]
  mov r9, bytesWritten
  mov r8d, eax
  push 0
  sub rsp, 0x20
  call WriteFile
  add rsp, 0x28

  ret
Windows treats the handle for STDOUT as a normal hFile so we can just write to it with the WriteFile API call.

Now we check if the right mouse button is down:

Code:
  mov ecx, 2 // VK_RBUTTON
  call GetAsyncKeyState
  mov ch, [mouseDown]
  and ah, 0x80 // most significant bit tells us if button is down
  cmp ah, ch
  je mouseButtonUnchanged
  mov [mouseDown], ah

  cmp ah, 0
  je buttonUp
  jmp buttonDown
The GetAsyncKeyState works for mouse buttons as well as normal keys... thanks Bill



Next, we get the mouse coordinates:

Code:
mouseX:
  db 00 00 00 00
mouseY:
  db 00 00 00 00
 xor rcx,rcx
  push rcx
  mov rcx, rsp
  call GetCursorPos
  mov rcx, [rsp]
  mov [mouseX], rcx
  pop rcx
If we were using a real programming language we could declare local variables, and the compiler would create space on the stack. But we're being children here and trying to do it all in asm inside of cheat engine, so we have to make space ourselves. Remember, GetCursorPos will *not* work if you use space you've created in your code cave. The function updates a POINT structure, which is 2x DWORD, and we can just treat this as 1x QWORD. We have to clear the stack to make sure it still lines up correctly, so the pop RCX at the end is just some accounting (yes, you can do add rsp, 8 or lea rsp[rsp+8], but pop rcx should be the shortest command in terms of bytes used).

We're logging stuff to the console as we go because it's so easy to mess up here

Code:
szScreenCoordsFormat:
  db 'X: %d, Y: %d', 0x0D, 0x0A, 0
szScreenCoordsString:
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  mov rcx, szScreenCoordsString
  mov rdx, szScreenCoordsFormat
  xor r8, r8
  xor r9, r9
  mov r8d, [mouseX]
  mov r9d, [mouseY]
  lea rsp [rsp-20]
  call wsprintfA
  lea rsp [rsp+20]
  mov rcx, szScreenCoordsString
  call print
For the record, this is really, really stupid stuff to do. There's no bounds checking on wsprintfA, and I'm just too lazy to do another lstrlen and make space on the stack (now I think of it, that's really not that hard to do but whatever). This all works (although wsprinfA destroys the 4 QWORDS on the stack, FYI), but just don't ever do this.

sprintf8x11.png


Stay safe kids!

Where were we? Oh that's right, GetCursorPos gives us screen coordinates, so we need to convert them to window coordinates. There's an API call for that:

Code:
  mov rcx, [hWnd]
  mov rdx, [mouseX]
  push rdx
  mov rdx, rsp
  call ScreenToClient
  mov rcx, [rsp]
  mov [mouseX], rcx
  pop rcx
We use the same stack local variable hackery as above because god knows what old gatesy was smoking when they built this API



We then need the dimensions of the window, so we use GetWindowRect to do this:

Code:
  mov rcx, [hWnd]
  xor rdx, rdx
  push rdx
  push rdx
  mov rdx, rsp
  call GetWindowRect
  pop rdx // top:left
  pop rax // bottom:right
  sub rax, rdx // height:width
  mov [width], eax
  shr rax, 20
  mov [height], eax
The RECT struct is 4x DWORDs, and I wanted to just access these directly, but why not do this the difficult way. If it doesn't make sense to you then start up a MASM project in visual studio, put it in, step through in a debugger and figure out what each of the steps do. Done that? Good. This next bit is going to make you shit your pants.

Code:
  fild DWORD PTR [mouseX]
  fimul DWORD PTR [two]
  fisub DWORD PTR [width]
  fidiv DWORD PTR [width]
  fstp DWORD PTR [mouseXFloat]
  fild DWORD PTR [mouseY]
  fimul DWORD PTR [two]
  fisub DWORD PTR [height]
  fidiv DWORD PTR [height]
  fstp DWORD PTR [mouseYFloat]
This is asm for:

Code:
float mouseXFloat = float(mouseX) * 2.0 / float(width);
float mouseYFloat = float(mouseY) * 2.0 / float(height);
For you comedians out there, there's a FIST opcode for storing a value as an integer without popping it off the FPU stack



I think that's all the important stuff. Here's the multilevel pointer code:

Code:
  // dereference pointer
  mov rax, ["gtutorial-x86_64.exe"+0024C3E0]
  mov rax, [rax+708]
  cmp eax, 0
  jl playerNotFound
  mov edx, [mouseXFloat]
  mov [rax+24], edx
  mov edx, [mouseYFloat]
  mov [rax+28], edx
Hacky as balls. We're fortunate that this space ends up being > 0x80000000 (which is the most negative of numbers) on the load screen, and then gets set to something normal when level 3 starts up (so if you turn on all the cheats and right click on the load screen it won't kill you).

I think that's everything. Remember, use string handling functions safely!

arithmetic8x11.png


Code:
{ Game   : gtutorial-x86_64.exe
  Version: 7.1
  Date   : 2020-07-31
  Author : h4nsbr1x

  Right click to teleport
}

[ENABLE]

aobscanmodule(INJECT,gtutorial-x86_64.exe,90 48 8B 45 F8 48 8D 48 28) // should be unique
alloc(newmem,$1000,"gtutorial-x86_64.exe"+1049EA)

label(code)
label(return)

newmem:
  push rax
  push rbx
  push rcx
  push rdx
  mov rcx, [rcx+8]
  mov [hWnd], rcx

  mov rax, [hStdout]
  cmp rax, 0
  jne haveConsole

  call AllocConsole

  mov rcx, -0B
  call GetStdHandle
  mov [hStdout], rax

  mov rcx, szHaveConsole
  call print

  jmp haveConsole

szHaveConsole:
  db 'Console is loaded', 0x0D, 0x0A, 0

haveConsole:
  // let's see if the right mouse button is being pressed

  mov ecx, 2 // VK_RBUTTON
  call GetAsyncKeyState
  mov ch, [mouseDown]
  and ah, 0x80 // most significant bit tells us if button is down
  cmp ah, ch
  je mouseButtonUnchanged
  mov [mouseDown], ah

  cmp ah, 0
  je buttonUp
  jmp buttonDown
mouseX:
  db 00 00 00 00
mouseY:
  db 00 00 00 00
hWnd:
  db 00 00 00 00 00 00 00 00
width:
  db 00 00 00 00
height:
  db 00 00 00 00
two:
  db 02 00 00 00
mouseXFloat:
  db 00 00 00 00
mouseYFloat:
  db 00 00 00 00

buttonDown:
  //mov rcx, mouseX
  xor rcx,rcx
  push rcx
  mov rcx, rsp
  call GetCursorPos
  mov rcx, [rsp]
  mov [mouseX], rcx
  pop rcx
  mov rcx, szScreenCoordsString
  mov rdx, szScreenCoordsFormat
  xor r8, r8
  xor r9, r9
  mov r8d, [mouseX]
  mov r9d, [mouseY]
  lea rsp [rsp-20]
  call wsprintfA
  lea rsp [rsp+20]
  mov rcx, szScreenCoordsString
  call print

  // convert coords relative to screen
  mov rcx, [hWnd]
  mov rdx, [mouseX]
  push rdx
  mov rdx, rsp
  call ScreenToClient
  mov rcx, [rsp]
  mov [mouseX], rcx
  pop rcx
  mov rcx, szScreenCoordsString
  mov rdx, szScreenCoordsFormat
  xor r8, r8
  xor r9, r9
  mov r8d, [mouseX]
  mov r9d, [mouseY]
  lea rsp [rsp-20]
  call wsprintfA
  lea rsp [rsp+20]
  mov rcx, szScreenCoordsString
  call print

  // get window rectangle to find width
  mov rcx, [hWnd]
  xor rdx, rdx
  push rdx
  push rdx
  mov rdx, rsp
  call GetWindowRect
  pop rdx // top:left
  pop rax // bottom:right
  sub rax, rdx // height:width
  mov [width], eax
  shr rax, 20
  mov [height], eax

  mov rcx, szScreenCoordsString
  mov rdx, szScreenCoordsFormat
  xor r8, r8
  xor r9, r9
  mov r8d, [width]
  mov r9d, [height]
  lea rsp [rsp-20]
  call wsprintfA
  lea rsp [rsp+20]
  mov rcx, szScreenCoordsString
  call print

  // convert to floats relative to screen
  fild DWORD PTR [mouseX]
  fimul DWORD PTR [two]
  fisub DWORD PTR [width]
  fidiv DWORD PTR [width]
  fstp DWORD PTR [mouseXFloat]
  fild DWORD PTR [mouseY]
  fimul DWORD PTR [two]
  fisub DWORD PTR [height]
  fidiv DWORD PTR [height]
  fstp DWORD PTR [mouseYFloat]

  mov rcx, szScreenCoordsString
  mov rdx, szScreenCoordsHexFormat
  xor r8, r8
  xor r9, r9
  mov r8d, [mouseXFloat]
  mov r9d, [mouseYFloat]
  lea rsp [rsp-20]
  call wsprintfA
  lea rsp [rsp+20]
  mov rcx, szScreenCoordsString
  call print

  // dereference pointer
  mov rax, ["gtutorial-x86_64.exe"+0024C3E0]
  mov rax, [rax+708]
  cmp eax, 0
  jl playerNotFound
  mov edx, [mouseXFloat]
  mov [rax+24], edx
  mov edx, [mouseYFloat]
  mov [rax+28], edx

playerNotFound:
  jmp buttonUp

szScreenCoordsFormat:
  db 'X: %d, Y: %d', 0x0D, 0x0A, 0
szScreenCoordsHexFormat:
  db 'X: %X, Y: %X', 0x0D, 0x0A, 0
szScreenCoordsString:
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

buttonUp:

  mov ecx,szButtonChanged
  call print

  jmp mouseButtonUnchanged

szButtonChanged:
  db 'Button changed', 0x0D, 0x0A, 0


mouseButtonUnchanged:
cleanUp:

  pop rdx
  pop rcx
  pop rbx
  pop rax

  jmp code

hStdout:
  db 00 00 00 00 00 00 00 00
mouseDown:
  db 00
bytesWritten:
  db 00 00 00 00 00 00 00 00

print:
  push rcx
  call lstrlenA

  pop rdx
  mov rcx, [hStdout]
  mov r9, bytesWritten
  mov r8d, eax
  push 0
  sub rsp, 0x20
  call WriteFile
  add rsp, 0x28

  ret

code:
  nop
  mov rax,[rbp-08]
  jmp return

INJECT:
  jmp newmem
return:
registersymbol(INJECT)

[DISABLE]

INJECT:
  db 90 48 8B 45 F8

unregistersymbol(INJECT)
dealloc(newmem)

{
// ORIGINAL CODE - INJECTION POINT: "gtutorial-x86_64.exe"+1049EA

"gtutorial-x86_64.exe"+1049BA: 00 00                             -  add [rax],al
"gtutorial-x86_64.exe"+1049BC: 00 00                             -  add [rax],al
"gtutorial-x86_64.exe"+1049BE: 00 00                             -  add [rax],al
"gtutorial-x86_64.exe"+1049C0: 55                                -  push rbp
"gtutorial-x86_64.exe"+1049C1: 48 89 E5                          -  mov rbp,rsp
"gtutorial-x86_64.exe"+1049C4: 48 8D A4 24 20 FF FF FF           -  lea rsp,[rsp-000000E0]
"gtutorial-x86_64.exe"+1049CC: 48 89 9D 60 FF FF FF              -  mov [rbp-000000A0],rbx
"gtutorial-x86_64.exe"+1049D3: 48 89 4D F8                       -  mov [rbp-08],rcx
"gtutorial-x86_64.exe"+1049D7: 48 C7 85 68 FF FF FF 00 00 00 00  -  mov qword ptr [rbp-00000098],00000000
"gtutorial-x86_64.exe"+1049E2: 48 C7 45 90 00 00 00 00           -  mov qword ptr [rbp-70],00000000
// ---------- INJECTING HERE ----------
"gtutorial-x86_64.exe"+1049EA: 90                                -  nop
"gtutorial-x86_64.exe"+1049EB: 48 8B 45 F8                       -  mov rax,[rbp-08]
// ---------- DONE INJECTING  ----------
"gtutorial-x86_64.exe"+1049EF: 48 8D 48 28                       -  lea rcx,[rax+28]
"gtutorial-x86_64.exe"+1049F3: 45 31 C0                          -  xor r8d,r8d
"gtutorial-x86_64.exe"+1049F6: BA 20 00 00 00                    -  mov edx,00000020
"gtutorial-x86_64.exe"+1049FB: E8 C0 E7 EF FF                    -  call gtutorial-x86_64.exe+31C0
"gtutorial-x86_64.exe"+104A00: 48 8B 45 F8                       -  mov rax,[rbp-08]
"gtutorial-x86_64.exe"+104A04: 48 83 C0 28                       -  add rax,28
"gtutorial-x86_64.exe"+104A08: 48 8B 55 F8                       -  mov rdx,[rbp-08]
"gtutorial-x86_64.exe"+104A0C: 48 89 42 48                       -  mov [rdx+48],rax
"gtutorial-x86_64.exe"+104A10: 48 8B 45 F8                       -  mov rax,[rbp-08]
"gtutorial-x86_64.exe"+104A14: C6 40 58 01                       -  mov byte ptr [rax+58],01
}
 
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods League of Legends Accounts