Added:
- Super Accuracy
- Fast Fire
- No Recoil / Screen Shake
Download:
BR,
Sun
[ 16.01.2019 - Update #1 ]
Adjusted some stuff, as well as adding infinite clip ammo and instant/infinite amulet charge.
BR,
Sun
[ 30.12.2018 - First Release ]
«[ INTRO ]»
Hello folks.
I've been on to Sniper Elite's engine since the third title came out. Haven't studied a lot of it, but now that I had some spare time (instead of addressing other countless table/trainer updates and user requests), let me brief you in a bit on this engine. Recently learned its name is Asura. The version.. I don't know yet. But will determine some internals at some point
Alright; so, in Sniper Elite 4 I had this piece of code that I hooked to get player structure and player id:
Code: Select all
00000001415E82D0 | 48:83EC 28 | SUB RSP,28 |
00000001415E82D4 | 48:8B05 75AF62FF | MOV RAX,QWORD PTR DS:[140C13250] |
00000001415E82DB | 48:85C0 | TEST RAX,RAX |
00000001415E82DE | 74 4A | JE sniperelite4_dx11_dumped.1415E832A |
00000001415E82E0 | 8B50 14 | MOV EDX,DWORD PTR DS:[RAX+14] |
00000001415E82E3 | 81FA E7030000 | CMP EDX,3E7 |
00000001415E82E9 | 75 09 | JNE sniperelite4_dx11_dumped.1415E82F4 |
00000001415E82EB | 803D 66F857FF 00 | CMP BYTE PTR DS:[140B67B58],0 |
00000001415E82F2 | 75 2C | JNE sniperelite4_dx11_dumped.1415E8320 |
00000001415E82F4 | 48:8D0D 35F857FF | LEA RCX,QWORD PTR DS:[140B67B30] |
00000001415E82FB | E8 F09CA1FF | CALL sniperelite4_dx11_dumped.141001FF0 |
00000001415E8300 | 48:89C2 | MOV RDX,RAX |
00000001415E8303 | 48:85C0 | TEST RAX,RAX |
00000001415E8306 | 74 1A | JE sniperelite4_dx11_dumped.1415E8322 |
00000001415E8308 | 48:8B40 08 | MOV RAX,QWORD PTR DS:[RAX+8] |
00000001415E830C | 48:6348 04 | MOVSXD RCX,DWORD PTR DS:[RAX+4] |
00000001415E8310 | F64411 10 01 | TEST BYTE PTR DS:[RCX+RDX+10],1 |
00000001415E8315 | 75 09 | JNE sniperelite4_dx11_dumped.1415E8320 |
00000001415E8317 | 803D B4955FFF 00 | CMP BYTE PTR DS:[140BE18D2],0 |
00000001415E831E | 74 02 | JE sniperelite4_dx11_dumped.1415E8322 |
00000001415E8320 | 31D2 | XOR EDX,EDX |
00000001415E8322 | 48:89D0 | MOV RAX,RDX | <-- hook
00000001415E8325 | 48:83C4 28 | ADD RSP,28 |
00000001415E8329 | C3 | RET |
00000001415E832A | 48:83C4 28 | ADD RSP,28 |
00000001415E832E | C3 | RET |
Code: Select all
Hook:
mov rax,rdx // original code
test rax,rax // check if NULL
je short @f // if 0, exit; else..
push rdi // store rdi
mov [pPlayer],rdx // store rdx as Player pointer
mov edi,[rax+rcx+18] // get id into rdi/edi
mov [dwPlayerId],edi // store id into PlayerId as DWORD
pop rdi // restore rdi
@@:
add rsp,28 // out
jmp GetPlayerAndId+7 // jump back
Had some struggle finding it mostly because the developers decided to optimize the compiled code and throw in some inlining in their project. As such, the CALL you see in my function above at 1415E82FB address is spread across whole function That still didn't stop me finding it So here's the function in Strange Brigade:
Code: Select all
0000000144B44090 | 53 | PUSH RBX |
0000000144B44091 | 55 | PUSH RBP |
0000000144B44092 | 41:56 | PUSH R14 |
0000000144B44094 | 48:83EC 20 | SUB RSP,20 |
0000000144B44098 | 45:0FB6F0 | MOVZX R14D,R8B |
0000000144B4409C | 89D5 | MOV EBP,EDX |
0000000144B4409E | 48:89CB | MOV RBX,RCX |
0000000144B440A1 | 81FA E7030000 | CMP EDX,3E7 |
0000000144B440A7 | 75 17 | JNE strangebrigade_dx12.144B440C0 |
0000000144B440A9 | 8079 28 00 | CMP BYTE PTR DS:[RCX+28],0 |
0000000144B440AD | 74 11 | JE strangebrigade_dx12.144B440C0 |
0000000144B440AF | 31C0 | XOR EAX,EAX |
0000000144B440B1 | 48:83C4 20 | ADD RSP,20 |
0000000144B440B5 | 41:5E | POP R14 |
0000000144B440B7 | 5D | POP RBP |
0000000144B440B8 | 5B | POP RBX |
0000000144B440B9 | C3 | RET |
0000000144B440BA | 66:0F1F4400 00 | NOP WORD PTR DS:[RAX+RAX],AX |
0000000144B440C0 | B9 88130000 | MOV ECX,1388 | <-- this would be where the inlined CALL starts
..
..
0000000144B44153 | 48:85D2 | TEST RDX,RDX | <-- CALL ends here
0000000144B44156 | 74 20 | JE strangebrigade_dx12.144B44178 |
0000000144B44158 | 48:8B42 08 | MOV RAX,QWORD PTR DS:[RDX+8] |
0000000144B4415C | 48:6348 04 | MOVSXD RCX,DWORD PTR DS:[RAX+4] |
0000000144B44160 | F64411 10 01 | TEST BYTE PTR DS:[RCX+RDX+10],1 |
0000000144B44165 | 0F85 44FFFFFF | JNE strangebrigade_dx12.144B440AF |
0000000144B4416B | 803D 552C20FC 00 | CMP BYTE PTR DS:[140D46DC7],0 |
0000000144B44172 | 0F85 37FFFFFF | JNE strangebrigade_dx12.144B440AF |
0000000144B44178 | 48:89D0 | MOV RAX,RDX |
0000000144B4417B | 48:83C4 20 | ADD RSP,20 |
0000000144B4417F | 41:5E | POP R14 |
0000000144B44181 | 5D | POP RBP |
0000000144B44182 | 5B | POP RBX |
0000000144B44183 | C3 | RET |
Upon checking the TEST at 144B44160, I noticed there's a bunch of other addresses using the instruction. As such, I looked for a spot checking that BYTE at [rcx+rdx+10] that'd work for only our Player structure pointer Then I discovered that I am actually looking at a pointer, player-related, that isn't the REAL Player pointer we need Found a stable location where only one hit appeared in the "Find out what addresses this instruction accesses" list:
Code: Select all
StrangeBrigade_DX12.exe+C7E3F2F - 48 8B 93 B0080000 - mov rdx,[rbx+000008B0]
StrangeBrigade_DX12.exe+C7E3F36 - 48 85 D2 - test rdx,rdx
StrangeBrigade_DX12.exe+C7E3F39 - 0F84 AF000000 - je StrangeBrigade_DX12.exe+C7E3FEE
StrangeBrigade_DX12.exe+C7E3F3F - 48 8B 42 08 - mov rax,[rdx+08]
StrangeBrigade_DX12.exe+C7E3F43 - 48 63 48 04 - movsxd rcx,dword ptr [rax+04]
StrangeBrigade_DX12.exe+C7E3F47 - F6 44 11 10 01 - test byte ptr [rcx+rdx+10],01 <--
Code: Select all
StrangeBrigade_DX12.exe+C238C48 - 48 63 41 04 - movsxd rax,dword ptr [rcx+04]
StrangeBrigade_DX12.exe+C238C4C - 44 8B 74 30 18 - mov r14d,[rax+rsi+18] <-- rax is 0x8C0 here; rsi == 0xC0728BD0
StrangeBrigade_DX12.exe+C238C51 - 41 81 FE E7030000 - cmp r14d,000003E7 { 999 }
..
..
StrangeBrigade_DX12.exe+C238CC0 - 49 8B 14 CC - mov rdx,[r12+rcx*8]
StrangeBrigade_DX12.exe+C238CC4 - 45 8B 0C 8B - mov r9d,[r11+rcx*4]
StrangeBrigade_DX12.exe+C238CC8 - 48 85 D2 - test rdx,rdx
StrangeBrigade_DX12.exe+C238CCB - 0F85 F5000000 - jne StrangeBrigade_DX12.exe+C238DC6
StrangeBrigade_DX12.exe+C238CD1 - 45 85 C9 - test r9d,r9d
StrangeBrigade_DX12.exe+C238CD4 - 0F85 F5000000 - jne StrangeBrigade_DX12.exe+C238DCF
StrangeBrigade_DX12.exe+C238CDA - 48 89 FA - mov rdx,rdi
StrangeBrigade_DX12.exe+C238CDD - F0 FF 0D 3CAFAAF4 - lock dec [StrangeBrigade_DX12.exe+CE3C20] { [00000000] }
StrangeBrigade_DX12.exe+C238CE4 - 48 85 D2 - test rdx,rdx
StrangeBrigade_DX12.exe+C238CE7 - 0F84 75050000 - je StrangeBrigade_DX12.exe+C239262
StrangeBrigade_DX12.exe+C238CED - 48 8B 42 08 - mov rax,[rdx+08]
StrangeBrigade_DX12.exe+C238CF1 - 48 63 48 04 - movsxd rcx,dword ptr [rax+04]
StrangeBrigade_DX12.exe+C238CF5 - 84 5C 11 10 - test [rcx+rdx+10],bl <-- rcx is 0xDE0 here; rdx == 0xC1AEAF90 (can also be retrieved from [C0728BD0+8B0])
Code: Select all
StrangeBrigade_DX12.exe+DA911B0 - 48 83 EC 28 - sub rsp,28 { 40 }
StrangeBrigade_DX12.exe+DA911B4 - 84 C9 - test cl,cl
StrangeBrigade_DX12.exe+DA911B6 - 0F84 17010000 - je StrangeBrigade_DX12.exe+DA912D3
StrangeBrigade_DX12.exe+DA911BC - 48 8B 05 65C635F3 - mov rax,[StrangeBrigade_DX12.exe+DED828] { [1401FE180] }
StrangeBrigade_DX12.exe+DA911C3 - 48 89 5C 24 30 - mov [rsp+30],rbx
StrangeBrigade_DX12.exe+DA911C8 - 48 8D 1D A1197BF2 - lea rbx,[StrangeBrigade_DX12.exe+242B70] { [83485640] }
StrangeBrigade_DX12.exe+DA911CF - 48 89 7C 24 20 - mov [rsp+20],rdi
StrangeBrigade_DX12.exe+DA911D4 - 40 30 FF - xor dil,dil
StrangeBrigade_DX12.exe+DA911D7 - 48 85 C0 - test rax,rax
StrangeBrigade_DX12.exe+DA911DA - 74 11 - je StrangeBrigade_DX12.exe+DA911ED
StrangeBrigade_DX12.exe+DA911DC - 48 89 C3 - mov rbx,rax
StrangeBrigade_DX12.exe+DA911DF - 48 C7 05 3EC635F3 00000000 - mov qword ptr [StrangeBrigade_DX12.exe+DED828],00000000 { 0 }
StrangeBrigade_DX12.exe+DA911EA - 40 B7 01 - mov dil,01 { 1 }
StrangeBrigade_DX12.exe+DA911ED - 48 8D 0D 4C12E9F2 - lea rcx,[StrangeBrigade_DX12.exe+922440] { ["Does this player have extra health above the base health."] }
StrangeBrigade_DX12.exe+DA911F4 - FF D3 - call rbx
StrangeBrigade_DX12.exe+DA911F6 - 40 84 FF - test dil,dil
StrangeBrigade_DX12.exe+DA911F9 - 48 8B 7C 24 20 - mov rdi,[rsp+20]
StrangeBrigade_DX12.exe+DA911FE - 74 13 - je StrangeBrigade_DX12.exe+DA91213
StrangeBrigade_DX12.exe+DA91200 - 48 89 1D 21C635F3 - mov [StrangeBrigade_DX12.exe+DED828],rbx { [1401FE180] }
StrangeBrigade_DX12.exe+DA91207 - B0 01 - mov al,01 { 1 }
StrangeBrigade_DX12.exe+DA91209 - 48 8B 5C 24 30 - mov rbx,[rsp+30]
StrangeBrigade_DX12.exe+DA9120E - 48 83 C4 28 - add rsp,28 { 40 }
StrangeBrigade_DX12.exe+DA91212 - C3 - ret
StrangeBrigade_DX12.exe+DA91213 - 48 8B 0D 669E2BF3 - mov rcx,[StrangeBrigade_DX12.exe+D4B080] { [03F5F740] }
StrangeBrigade_DX12.exe+DA9121A - 41 B0 01 - mov r8l,01 { 1 }
StrangeBrigade_DX12.exe+DA9121D - 41 0FB6 D0 - movzx edx,r8l
StrangeBrigade_DX12.exe+DA91221 - 48 8B 01 - mov rax,[rcx]
StrangeBrigade_DX12.exe+DA91224 - FF 50 60 - call qword ptr [rax+60]
StrangeBrigade_DX12.exe+DA91227 - 48 8B 5C 24 30 - mov rbx,[rsp+30]
StrangeBrigade_DX12.exe+DA9122C - B0 01 - mov al,01 { 1 }
StrangeBrigade_DX12.exe+DA9122E - 48 83 C4 28 - add rsp,28 { 40 }
StrangeBrigade_DX12.exe+DA91232 - C3 - ret
..
..
StrangeBrigade_DX12.exe+DAEC300 - 48 83 EC 28 - sub rsp,28 { 40 }
StrangeBrigade_DX12.exe+DAEC304 - 48 8B 05 356628F3 - mov rax,[StrangeBrigade_DX12.exe+D72940] { [A6FF5780] }
StrangeBrigade_DX12.exe+DAEC30B - 48 85 C0 - test rax,rax
StrangeBrigade_DX12.exe+DAEC30E - 74 4B - je StrangeBrigade_DX12.exe+DAEC35B
StrangeBrigade_DX12.exe+DAEC310 - 48 8B 0D 915728F3 - mov rcx,[StrangeBrigade_DX12.exe+D71AA8] { [43CCBFF0] }
StrangeBrigade_DX12.exe+DAEC317 - 48 85 C9 - test rcx,rcx
StrangeBrigade_DX12.exe+DAEC31A - 74 33 - je StrangeBrigade_DX12.exe+DAEC34F
StrangeBrigade_DX12.exe+DAEC31C - 80 3D 676528F3 00 - cmp byte ptr [StrangeBrigade_DX12.exe+D7288A],00 { 0 }
StrangeBrigade_DX12.exe+DAEC323 - 74 2A - je StrangeBrigade_DX12.exe+DAEC34F
StrangeBrigade_DX12.exe+DAEC325 - 83 B9 8C020000 00 - cmp dword ptr [rcx+0000028C],00 { 0 }
StrangeBrigade_DX12.exe+DAEC32C - 75 21 - jne StrangeBrigade_DX12.exe+DAEC34F
StrangeBrigade_DX12.exe+DAEC32E - 8B 91 78020000 - mov edx,[rcx+00000278]
StrangeBrigade_DX12.exe+DAEC334 - 41 B0 01 - mov r8l,01 { 1 }
StrangeBrigade_DX12.exe+DAEC337 - 48 8D 0D B27A1FF3 - lea rcx,[StrangeBrigade_DX12.exe+CE3DF0] { [C18E5810] }
StrangeBrigade_DX12.exe+DAEC33E - E8 8D3464F2 - call StrangeBrigade_DX12.exe+12F7D0
StrangeBrigade_DX12.exe+DAEC343 - 48 85 C0 - test rax,rax
StrangeBrigade_DX12.exe+DAEC346 - 75 13 - jne StrangeBrigade_DX12.exe+DAEC35B
StrangeBrigade_DX12.exe+DAEC348 - 48 8B 05 F16528F3 - mov rax,[StrangeBrigade_DX12.exe+D72940] { [A6FF5780] }
StrangeBrigade_DX12.exe+DAEC34F - 48 8B 80 E8000000 - mov rax,[rax+000000E8] <-- gets 0xC1AEAF90 in rax
StrangeBrigade_DX12.exe+DAEC356 - 48 83 C4 28 - add rsp,28 { 40 }
StrangeBrigade_DX12.exe+DAEC35A - C3 - ret
StrangeBrigade_DX12.exe+DAEC35B - 48 83 C4 28 - add rsp,28 { 40 }
StrangeBrigade_DX12.exe+DAEC35F - C3 - ret
In the end I chose this place for the hook:
Code: Select all
StrangeBrigade_DX12.exe+AF05B34 - 49 8B 40 08 - mov rax,[r8+08]
StrangeBrigade_DX12.exe+AF05B38 - 48 63 48 04 - movsxd rcx,dword ptr [rax+04]
StrangeBrigade_DX12.exe+AF05B3C - 42 F6 44 01 10 01 - test byte ptr [rcx+r8+10],01 <-- hook
Now that the script is in place, we can make use of pPlayer and dwPlayerId in other scripts. At the time I'm writing this, I've already figured out infinite magazine (managed to make use of some internal engine functions which led to switching magazine to infinite; will detail it later). Pretty much like what you see for the revolver, but for any weapon See below:
«[ GOD MODE ]»
You can find health by scanning memory for floats, ranged 10-100 and do sequential has increased/decreased/unchanged depending on what happens to your health in-game. At the point where I am in-game (have only played with Frank), the max value is 40.0. Just so you know what you'd have to start from. Once you debug the found address/value on write as some mob hits you, you'll get an instruction part of a function where health is subtracted and written via MMX instructions:
Code: Select all
StrangeBrigade_DX12.exe+5FDF7BC - 44 0F28 C8 - movaps xmm9,xmm0
StrangeBrigade_DX12.exe+5FDF7C0 - F3 0F5C CE - subss xmm1,xmm6
StrangeBrigade_DX12.exe+5FDF7C4 - 0F2F CF - comiss xmm1,xmm7
StrangeBrigade_DX12.exe+5FDF7C7 - F3 0F11 4B 50 - movss [rbx+50],xmm1 <-- health on being hit
- start by breaking the below instruction in the health subtraction function
StrangeBrigade_DX12.exe+5FDF787 - F6 44 0A 10 01 - test byte ptr [rdx+rcx+10],01
- get an enemy to hit you
- once CE freezes scroll to the function's epilogue
StrangeBrigade_DX12.exe+5FDF750 - 53 - push rbx
- set breakpoint on it
- F9 to resume
- note down [rsp] on next break
StrangeBrigade_DX12.exe+BB32550 - E8 1B4165F4 - call StrangeBrigade_DX12.exe+186670
- ret epilogue
StrangeBrigade_DX12.exe+5FDF750 - 53 - push rbx
to
StrangeBrigade_DX12.exe+5FDF750 - C3 - ret
- resume game
- see if you still take damage on next hit
- we do take visual damage
- HP doesn't decrease
- enemy HP doesn't decrease if I fire at them
- everyone has "god mode" basically
- leave that ret on so we don't die
- head to StrangeBrigade_DX12.exe+BB32550
- scroll up to prologue
StrangeBrigade_DX12.exe+BB324C0 - 48 89 5C 24 10 - mov [rsp+10],rbx
- set breakpoint on it
- note down [rsp] on break
StrangeBrigade_DX12.exe+BB0BC4D - E8 9EECA4F4 - call StrangeBrigade_DX12.exe+55A8F0
- now ret it
StrangeBrigade_DX12.exe+BB324C0 - 48 89 5C 24 10 - mov [rsp+10],rbx
to
StrangeBrigade_DX12.exe+BB324C0 - C3 - ret
- we still take damage and get pushed if hit
- aim is a no stagger god mode and no visual effects on-screen
- let's continue
- head to StrangeBrigade_DX12.exe+BB0BC4D
- scroll to prologue
StrangeBrigade_DX12.exe+BB0BB40 - 53 - push rbx
- set breakpoint
- note down [rsp] on break
StrangeBrigade_DX12.exe+BAFBEA6 - FF 50 38 - call qword ptr [rax+38]
- now ret the epilogue
StrangeBrigade_DX12.exe+BB0BB40 - 53 - push rbx
to
StrangeBrigade_DX12.exe+BB0BB40 - C3 - ret
- still getting damaged
- head to StrangeBrigade_DX12.exe+BAFBEA6
- scroll to prologue
StrangeBrigade_DX12.exe+BAFBE50 - 53 - push rbx
- break it and get [rsp] on hit
StrangeBrigade_DX12.exe+C73DB2C - FF 50 18 - call qword ptr [rax+18]
- time to ret it
StrangeBrigade_DX12.exe+BAFBE50 - 53 - push rbx
to
StrangeBrigade_DX12.exe+BAFBE50 - C3 - ret
- once you do that, you will notice 2 things:
a) no more blood on your screen and Frank whining
b) any mob trying to hit you hits thin air; you're not being pushed on mob hits
So now it's just a matter of applying a filter at that location so that we tell the game engine to exit the function (that's what "ret" means) only when our player is hit. And for that we can use pPlayer and dwPlayerId If you break at StrangeBrigade_DX12.exe+BAFBE50 and check the registers, you will see that when you get hit, RBX=RDX=pPlayer and RSI=dwPlayerId Another thing you might've noticed is all the spawned AIs have their ids generated starting with +1 from yours (when the engine generates a wave of mobs, it will check first entity's id; well, that's us; from that id onward it will increment by 1 each spawned AI's id; assuming new ones are spawned, if the id already exists, it will loop +1 till it finds a "free" one).
So the God Mode script will look like this:
Code: Select all
[ENABLE]
aobscanmodule( h_GodMode, StrangeBrigade_DX12.exe, 534883EC408079??004889CB75??48 )
registersymbol( h_GodMode )
label( h_GodMode_o )
registersymbol( h_GodMode_o )
alloc( GodMode_h, 0x1000, StrangeBrigade_DX12.exe )
GodMode_h:
push rax // store register
mov rax,[pPlayer] // load our Player pointer in rax
cmp rdx,rax // check it against rdx
pop rax // restore rax
jne short @f // ZF is preserved, even if we did "pop rax"
ret // if us, ret
h_GodMode_o: // else, run original code for any other AI
readmem( h_GodMode, 5 )
jmp h_GodMode+5
h_GodMode:
jmp GodMode_h
[DISABLE]
h_GodMode:
readmem( GodMode_o, 5 )
dealloc( GodMode_h )
unregistersymbol( h_GodMode_o )
unregistersymbol( h_GodMode )
Will post more as I progress, alongside a table Hold tight.
BR,
Sun
L.E.#1: Looks like you can get damaged by your own grenades. Oh wellz, not that big of a fuzz
How to use this cheat table?
- Install Cheat Engine
- Double-click the .CT file in order to open it.
- Click the PC icon in Cheat Engine in order to select the game process.
- Keep the list.
- Activate the trainer options by checking boxes or setting values from 0 to 1