Source Code Shellcode in cheat engine

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

h4nsbr1x

Dank Tier Donator
Full Member
Jul 24, 2020
14
468
0
Game Name
Cheat engine 64 bit tutorial first example
Anticheat
N/A
Tutorial Link
N/A
How long you been coding/hacking?
3 days
Coding Language
asm
The tutorial for the cheat engine sample games was a lot of fun, and one of the comments was "we could make the player to follow the mouse as an anti-gravity hack", and that got me thinking about ways to make API calls from inside cheat engine.

The short answer: Cheat engine is smart enough to know where APIs are and you can just call them directly:

Code:
{ Game   : Tutorial-x86_64.exe
  Version: 0.01
  Date   : 2020-07-30
  Author : h4nsbr1x

  This makes a message box pop up when you hit the "hit me button"
}

[ENABLE]

aobscanmodule(INJECT,Tutorial-x86_64.exe,29 83 F0 07 00 00) // should be unique
alloc(newmem,$1000,"Tutorial-x86_64.exe"+2B08C)

label(code)
label(return)

newmem:
  push rcx
  push rdx
  push r8
  push r9
  xor rcx, rcx
  mov rdx, message
  mov r8, caption
  xor r9, r9
  call MessageBoxA
  pop r9
  pop r8
  pop rdx
  pop rcx

code:
  sub [rbx+000007F0],eax
  jmp return

message:
  db 'Message', 0
caption:
  db 'Caption', 0

INJECT:
  jmp newmem
  nop
return:
registersymbol(INJECT)

[DISABLE]

INJECT:
  db 29 83 F0 07 00 00

unregistersymbol(INJECT)
dealloc(newmem)

{
// ORIGINAL CODE - INJECTION POINT: "Tutorial-x86_64.exe"+2B08C

"Tutorial-x86_64.exe"+2B060: 55                             -  push rbp
"Tutorial-x86_64.exe"+2B061: 48 89 E5                       -  mov rbp,rsp
"Tutorial-x86_64.exe"+2B064: 48 8D A4 24 D0 FE FF FF        -  lea rsp,[rsp-00000130]
"Tutorial-x86_64.exe"+2B06C: 48 89 9D F0 FE FF FF           -  mov [rbp-00000110],rbx
"Tutorial-x86_64.exe"+2B073: 48 89 CB                       -  mov rbx,rcx
"Tutorial-x86_64.exe"+2B076: 48 C7 45 F8 00 00 00 00        -  mov qword ptr [rbp-08],00000000
"Tutorial-x86_64.exe"+2B07E: 90                             -  nop
"Tutorial-x86_64.exe"+2B07F: B9 05 00 00 00                 -  mov ecx,00000005
"Tutorial-x86_64.exe"+2B084: E8 D7 49 FE FF                 -  call Tutorial-x86_64.exe+FA60
"Tutorial-x86_64.exe"+2B089: 83 C0 01                       -  add eax,01
// ---------- INJECTING HERE ----------
"Tutorial-x86_64.exe"+2B08C: 29 83 F0 07 00 00              -  sub [rbx+000007F0],eax
// ---------- DONE INJECTING  ----------
"Tutorial-x86_64.exe"+2B092: 48 8D 4D F8                    -  lea rcx,[rbp-08]
"Tutorial-x86_64.exe"+2B096: E8 85 DD FD FF                 -  call Tutorial-x86_64.exe+8E20
"Tutorial-x86_64.exe"+2B09B: 8B 8B F0 07 00 00              -  mov ecx,[rbx+000007F0]
"Tutorial-x86_64.exe"+2B0A1: 41 B9 FF 00 00 00              -  mov r9d,000000FF
"Tutorial-x86_64.exe"+2B0A7: 4C 8D 85 F8 FE FF FF           -  lea r8,[rbp-00000108]
"Tutorial-x86_64.exe"+2B0AE: 48 C7 C2 FF FF FF FF           -  mov rdx,FFFFFFFFFFFFFFFF
"Tutorial-x86_64.exe"+2B0B5: 48 63 C9                       -  movsxd  rcx,ecx
"Tutorial-x86_64.exe"+2B0B8: E8 E3 AE FD FF                 -  call Tutorial-x86_64.exe+5FA0
"Tutorial-x86_64.exe"+2B0BD: 45 31 C0                       -  xor r8d,r8d
"Tutorial-x86_64.exe"+2B0C0: 48 8D 95 F8 FE FF FF           -  lea rdx,[rbp-00000108]
}
On its own there's a few things to point out:

  1. Save registers that you mess with!
  2. Save registers that you don't mess with but might get trashed! There's rules about which registers are safe (ebx/esp/ebp/esi/edi/r11 upwards), but the rest are up to the caller to protect. Have a look at the surrounding code, but if in doubt, just push everything (unfortunately the PUSHA opcode doesn't work in 64 bit systems)

One more thing: x64 has a new way of doing stdcall. Back when Rake was still shitting his pants (well at least 2 weeks ago) you used to just push all the arguments onto the stack, but nowadays microsoft has gotten all clever and has this system where you use random different registers for arguments just like the good old days for dos (google ralf brown if you want to lose a month of your life) (would you believe there's also a guide to using escort services on the same website... anyway...)

marc1.jpg


If you've done any C/C++ you'll recognise the MessageBox function, although normally you'd call it like this:

C++:
MessageBox(NULL, "Message", "Caption", 0);
In assembly you need to load all the arguments into the right registers, microsoft has a lovely chart for this here x64 calling convention

But I didn't spend my afternoon coming up with this, no sir, I decided to do things the hard way and load all these libraries from scratch to show you all how it's done. Here's the final code for those who want to jump straight to the climax

Code:
{ Game   : Tutorial-x86_64.exe
  Version: 0.1
  Date   : 2020-07-30
  Author : h4nsbr1x

  This script does blah blah blah
}

[ENABLE]

aobscanmodule(INJECT,Tutorial-x86_64.exe,29 83 F0 07 00 00) // should be unique
alloc(newmem,$1000,"Tutorial-x86_64.exe"+2B08C)

label(code)
label(return)

newmem:
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi

    mov rcx, 0x60
    mov rax, gs:[rcx] // PEB
    mov rax, [rax+0x18] // PEB LDR
    mov rsi, [rax+0x20] // LDR InMemoryOrderLinks (first module, start of linked list)
    lodsq              // load from rsi to rax (second module)
    xchg rax, rsi      // traverse linked list
    lodsq              // load from rsi to rax (third module)
    mov rbx, [rax+0x20] // base address (the linked list pointer starts at +10h)
    mov rdi, [rax+0x50] // name of module
    mov r12, rbx

    xor rax, rax
    mov eax, [r12+0x3C] // jump to PE header
    xor r13, r13
    mov r13d, [r12+rax+0x88] // r13 is the relative offset of the export table
    add r13, r12                // r13 is now an absolute offset to the export table

    mov rcx, szGetProcAddress
    call getKernel32ProcAddress
    mov esi, rax

    mov rcx, szLoadLibrary
    call getKernel32ProcAddress
    mov edi, rax


    // load user32.dll

    mov rcx, szUser32Dll
    call edi
    mov r10, rax

    mov rdx, szMessageBoxA
    mov rcx, r10
    call esi
    mov r11, rax

    // test a call to messagebox
    xor rcx, rcx
    mov rdx, messageBoxText
    mov r8, messageBoxCaption
    xor r9, r9
    call r11

    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8

code:
  sub [rbx+000007F0],eax
  jmp return

getKernel32ProcAddress:
    push rbx
    push rsi
    push rdi

    mov rdx, rcx
    xor rcx, rcx
    xor rbx, rbx
    mov ecx, [r13+0x18] // number of function names
    mov ebx, [r13+0x20] // address of function names
    add rbx, r12        // rbx is now an absolute offset

findAddress:
    dec rcx
    xor rsi, rsi
    mov esi, [rbx+rcx*4]
    add rsi, r12
    mov rdi, rdx
    call strcmp
    cmp al, 0
    je foundAddress
    jecxz giveup
    jmp findAddress

foundAddress:
    xor rbx, rbx
    mov ebx, [r13+0x24]
    add rbx, r12 // ebx points to the ordinal table
    mov cx, [rbx+rcx*2] // rcx has the ordinal of the function
    xor rsi, rsi
    mov esi, [r13+0x1C]
    add rsi, r12 // rsi is the address table
    xor rax, rax
    mov eax, [rsi+rcx*4]
    add rax, r12 // ebx is the address of GetProcAddress
    jmp finishGetKernel32ProcAddress

giveup:
    xor rax, rax

finishGetKernel32ProcAddress:

    pop rdi
    pop rsi
    pop rbx
    ret

strcmp:
    push rsi
    push rdi

    xor rax, rax

testChars:
    lodsb
    mov ah,al
    xchg rsi,rdi
    lodsb
    cmp ah,al
    jne fail
    cmp ah, 0
    je pass
    jmp testChars

fail:
    xor rax, rax
    inc rax

    pass:

    pop rdi
    pop rsi
    ret

szMessageBoxA:
  db 'MessageBoxA', 0
szGetProcAddress:
  db 'GetProcAddress', 0
szLoadLibrary:
  db 'LoadLibraryA', 0
szUser32Dll:
  db 'user32.dll', 0
messageBoxText:
  db 'in the message box', 0
messageBoxCaption:
  db 'what what', 0

INJECT:
  jmp newmem
  nop
return:
registersymbol(INJECT)

[DISABLE]

INJECT:
  db 29 83 F0 07 00 00

unregistersymbol(INJECT)
dealloc(newmem)

{
// ORIGINAL CODE - INJECTION POINT: "Tutorial-x86_64.exe"+2B08C

"Tutorial-x86_64.exe"+2B060: 55                             -  push rbp
"Tutorial-x86_64.exe"+2B061: 48 89 E5                       -  mov rbp,rsp
"Tutorial-x86_64.exe"+2B064: 48 8D A4 24 D0 FE FF FF        -  lea rsp,[rsp-00000130]
"Tutorial-x86_64.exe"+2B06C: 48 89 9D F0 FE FF FF           -  mov [rbp-00000110],rbx
"Tutorial-x86_64.exe"+2B073: 48 89 CB                       -  mov rbx,rcx
"Tutorial-x86_64.exe"+2B076: 48 C7 45 F8 00 00 00 00        -  mov qword ptr [rbp-08],00000000
"Tutorial-x86_64.exe"+2B07E: 90                             -  nop
"Tutorial-x86_64.exe"+2B07F: B9 05 00 00 00                 -  mov ecx,00000005
"Tutorial-x86_64.exe"+2B084: E8 D7 49 FE FF                 -  call Tutorial-x86_64.exe+FA60
"Tutorial-x86_64.exe"+2B089: 83 C0 01                       -  add eax,01
// ---------- INJECTING HERE ----------
"Tutorial-x86_64.exe"+2B08C: 29 83 F0 07 00 00              -  sub [rbx+000007F0],eax
// ---------- DONE INJECTING  ----------
"Tutorial-x86_64.exe"+2B092: 48 8D 4D F8                    -  lea rcx,[rbp-08]
"Tutorial-x86_64.exe"+2B096: E8 85 DD FD FF                 -  call Tutorial-x86_64.exe+8E20
"Tutorial-x86_64.exe"+2B09B: 8B 8B F0 07 00 00              -  mov ecx,[rbx+000007F0]
"Tutorial-x86_64.exe"+2B0A1: 41 B9 FF 00 00 00              -  mov r9d,000000FF
"Tutorial-x86_64.exe"+2B0A7: 4C 8D 85 F8 FE FF FF           -  lea r8,[rbp-00000108]
"Tutorial-x86_64.exe"+2B0AE: 48 C7 C2 FF FF FF FF           -  mov rdx,FFFFFFFFFFFFFFFF
"Tutorial-x86_64.exe"+2B0B5: 48 63 C9                       -  movsxd  rcx,ecx
"Tutorial-x86_64.exe"+2B0B8: E8 E3 AE FD FF                 -  call Tutorial-x86_64.exe+5FA0
"Tutorial-x86_64.exe"+2B0BD: 45 31 C0                       -  xor r8d,r8d
"Tutorial-x86_64.exe"+2B0C0: 48 8D 95 F8 FE FF FF           -  lea rdx,[rbp-00000108]
}
Let's go through piece by piece

Code:
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
As discussed, it's easier to just push everything instead of trying to be clever and feeling sorry for yourself later

Code:
    mov rcx, 0x60
    mov rax, gs:[rcx] // PEB
    mov rax, [rax+0x18] // PEB LDR
    mov rsi, [rax+0x20] // LDR InMemoryOrderLinks (first module, start of linked list)
    lodsq              // load from rsi to rax (second module)
    xchg rax, rsi      // traverse linked list
    lodsq              // load from rsi to rax (third module)
    mov rbx, [rax+0x20] // base address (the linked list pointer starts at +10h)
    mov rdi, [rax+0x50] // name of module
    mov r12, rbx
This is the part where we probe around inside. This is 64-bit specific (we can write code that handles both and just have a switch between the two), and what this does is jumps to the Thread Environment Block (TEB), to the Process Environment Block (PEB), to the loader (PEB_LDR), and then start scanning through the DLLs that are loaded. Windows ALWAYS loads ntdll first and kernel32 third (or maybe it doesn't, YMMV), so this code gets to the base of kernel32.dll. It's effectively a multilevel pointer if that helps you visualise it.

Why are we getting to kernel32.dll? Because it has two very important functions: GetProcAddress, and LoadLibrary. When you compile an app yourself the linker sets up everything so the right DLLs get loaded at runtime and you have a bunch of handles to call them by. When you're injecting code, you're on your own, so you need LoadLibrary to load DLLs (or get the handle of them if they're already loaded), and GetProcAddress to get the address of functions you want to call in a given DLL.

Code:
    xor rax, rax
    mov eax, [r12+0x3C] // jump to PE header
    xor r13, r13
    mov r13d, [r12+rax+0x88] // r13 is the relative offset of the export table
    add r13, r12                // r13 is now an absolute offset to the export table
This is all just layers of multi-level pointers. It's worth noting that these values we get out are *relative* pointers (relative to the start of the DLL), so we always need to add the address of the base of the DLL, which we stored in ebx in the previous code block.

Code:
    mov rcx, szGetProcAddress
    call getKernel32ProcAddress
    mov esi, rax

    mov rcx, szLoadLibrary
    call getKernel32ProcAddress
    mov edi, rax
This feels like normal code. We put the string we're looking for in ecx, call our getKernel32ProcAddress function, and store the output in a spare register. Let's take a closer look at what this getKernel32ProcAddress function does:

Code:
getKernel32ProcAddress:
    push rbx
    push rsi
    push rdi

    mov rdx, rcx
    xor rcx, rcx
    xor rbx, rbx
    mov ecx, [r13+0x18] // number of function names
    mov ebx, [r13+0x20] // address of function names
    add rbx, r12        // rbx is now an absolute offset
r13 is the base of the import table, so we load the number of functions and the list of names, and then we can just loop through them. Remember these are 16 or 32-bit relative pointers, so we need to set our register to 0 by xoring it with itself, and then we can load data into the smaller version (insert link to how x86/x64 registers are broken down by size). Note that we push rbx, rsi and rdi because that's the callee's responsibility.

Code:
findAddress:
    dec rcx
    xor rsi, rsi
    mov esi, [rbx+rcx*4]
    add rsi, r12
    mov rdi, rdx
    call strcmp
    cmp al, 0
    je foundAddress
    jecxz giveup
    jmp findAddress
We put the number of functions in rcx, so we can just count backwards, load up the function name, and compare it with the string that was passed to us at the start. If it matches then we're home free, if not then continue looping. The wonderfully-named jecxz command will jump if ecx is 0, which is what will happen if we loop through and can't find the function we were looking for.



Code:
foundAddress:
    xor rbx, rbx
    mov ebx, [r13+0x24]
    add rbx, r12 // ebx points to the ordinal table
    mov cx, [rbx+rcx*2] // rcx has the ordinal of the function
    xor rsi, rsi
    mov esi, [r13+0x1C]
    add rsi, r12 // rsi is the address table
    xor rax, rax
    mov eax, [rsi+rcx*4]
    add rax, r12 // ebx is the address of our function
    jmp finishGetKernel32ProcAddress
If we do find the address, we need to get the ordinal table, which as far as I can tell is as useful as the man who puts indicator lights on BMWs. In theory the functions can get out of order, but normally they don't, so this table is just an ordered list of numbers. We then use this ordinal to look up the address of the function we're looking for, and then we jump to the end and return.

You might have noticed the strcmp there, we haven't cheated and used an external library, this is all home rolled too:

Code:
strcmp:
    push rsi
    push rdi

    xor rax, rax

testChars:
    lodsb
    mov ah,al
    xchg rsi,rdi
    lodsb
    cmp ah,al
    jne fail
    cmp ah, 0
    je pass
    jmp testChars

fail:
    xor rax, rax
    inc rax

    pass:

    pop rdi
    pop rsi
    ret
This one is an exercise for the reader. There are uglier ways of doing this, but this is up there.

Anyway, back to the fun stuff:

Code:
// load user32.dll

    mov rcx, szUser32Dll
    call edi
    mov r10, rax

    mov rdx, szMessageBoxA
    mov rcx, r10
    call esi
    mov r11, rax
/CODE]

Remember how we got the address of LoadLibrary and GetProcAddress? We can use these to load user32.dll and then find the address of MessageBoxA. We then store this in r11, and we can just call it to make a message box pop up:

[CODE]    // test a call to messagebox
    xor rcx, rcx
    mov rdx, messageBoxText
    mov r8, messageBoxCaption
    xor r9, r9
    call r11
And there we have it, loaded our own DLLs and found our own function calls like true grownups. Speaking of which:

Code:
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
Tidy your damn room!

 
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