Dropping these here. Too-da-loo
Part #1:
Part #2:
BR,
Sun
Dealing with the VMProtect-ed Integrity Check in Far Cry 4
Re: Dealing with the VMProtect-ed Integrity Check in Far Cry 4
I've managed last night to trace as far as possible into the VM from the last RET hit here..
..into VMProtect where the integrity check occurs. Then I managed to find out which is the last address whose bytes are being read:
If you set a hardware breakpoint on access on the byte of 7FFCC789CBD6, then set another breakpoint on execute here..
..thus out of VMProtect and back to normal FC64.dll ASM, then hit "Continue game [Offline]", x64dbg will break at 7FFCC789CBD6. From here, start a run trace and "trace into" and wait for a bit - make sure you've freshly re-opened the game, as hardware breakpoints may fail if constantly removing/setting them - and you'll see x64dbg stops at our second breakpoint (at FC64.dll+76D636).
Now check the run trace and you'll see pretty much the same thing as the attached .zip file (I've also attached the .trace file so you can load it in x64dbg):
The VM exit point starts here:
What I tried next was this. Get the game to break on access on that byte when you go into game world or exit to main menu. Once there, set a breakpoint with F2 at:
Trace the code till the RET and see where it exits. For me it exits here:
It's basically the entry point of another VM If you press F9 one more time (keeping the BP set at 7FFCC72C5D71) you'll see this time around the RET exits to where we want to; to FC64.dll+76D636. So one can easily assume this VM running at the RET BEFORE the RET to our spot is actually computing the return address You'll see why this is important to us
Back to this function:
Remember how I said we should break at that RET (hardware breakpoint on execution) and keep F9-ing till the last pointer in our 0x3B*8-sized table? Well, what we want now to do is instead of letting the engine exit to the VM branch that does the integrity check, force it to run the VM that computes the exit point and get out without the check ever running (as in 7FFCC9D22693)
So then I thought "why not simplify it?" - if you remember, the loop executes as long as rbx != rdi (as in current list address is not the last address in the list). Therefore inside our call, close to the RET, why not hijack the exit point when rbx == rdi-8?
BR,
Sun
archive password: frf
Code: Select all
00007FFCC6EA7BF0 | 48:895C24 08 | MOV QWORD PTR SS:[RSP+8],RBX |
00007FFCC6EA7BF5 | 57 | PUSH RDI |
00007FFCC6EA7BF6 | 48:83EC 20 | SUB RSP,20 |
00007FFCC6EA7BFA | 48:8B01 | MOV RAX,QWORD PTR DS:[RCX] |
00007FFCC6EA7BFD | 48:8BD9 | MOV RBX,RCX |
00007FFCC6EA7C00 | FF90 D0000000 | CALL QWORD PTR DS:[RAX+D0] | x
00007FFCC6EA7C06 | 48:8B7B 58 | MOV RDI,QWORD PTR DS:[RBX+58] |
00007FFCC6EA7C0A | 0FB643 60 | MOVZX EAX,BYTE PTR DS:[RBX+60] |
00007FFCC6EA7C0E | 48:C743 58 00000000 | MOV QWORD PTR DS:[RBX+58],0 |
00007FFCC6EA7C16 | C643 60 00 | MOV BYTE PTR DS:[RBX+60],0 |
00007FFCC6EA7C1A | 48:85FF | TEST RDI,RDI |
00007FFCC6EA7C1D | 74 1F | JE fc64.7FFCC6EA7C3E |
00007FFCC6EA7C1F | 84C0 | TEST AL,AL |
00007FFCC6EA7C21 | 74 09 | JE fc64.7FFCC6EA7C2C |
00007FFCC6EA7C23 | 48:8B07 | MOV RAX,QWORD PTR DS:[RDI] |
00007FFCC6EA7C26 | 48:8BCF | MOV RCX,RDI |
00007FFCC6EA7C29 | FF50 18 | CALL QWORD PTR DS:[RAX+18] | x
00007FFCC6EA7C2C | FF4F 08 | DEC DWORD PTR DS:[RDI+8] |
00007FFCC6EA7C2F | 75 0D | JNE fc64.7FFCC6EA7C3E |
00007FFCC6EA7C31 | 48:8B07 | MOV RAX,QWORD PTR DS:[RDI] |
00007FFCC6EA7C34 | BA 01000000 | MOV EDX,1 |
00007FFCC6EA7C39 | 48:8BCF | MOV RCX,RDI |
00007FFCC6EA7C3C | FF10 | CALL QWORD PTR DS:[RAX] | x
00007FFCC6EA7C3E | 48:8B5C24 30 | MOV RBX,QWORD PTR SS:[RSP+30] |
00007FFCC6EA7C43 | 48:83C4 20 | ADD RSP,20 |
00007FFCC6EA7C47 | 5F | POP RDI |
00007FFCC6EA7C48 | C3 | RET | <--
Code: Select all
00007FFCC789CB4A+8D -> 00007FFCC789CBD6
Code: Select all
FC64.dll+76D636 - 48 8B 0D B3AE6202 - mov rcx,[FC64.dll+2D984F0]
Now check the run trace and you'll see pretty much the same thing as the attached .zip file (I've also attached the .trace file so you can load it in x64dbg):
The VM exit point starts here:
Code: Select all
00007FFCC72C5D71 | 48 C1 F3 21 | SHL RBX,21 |
00007FFCC72C5D75 | 48 8D 9E 62 27 09 9B | LEA RBX,QWORD PTR DS:[RSI-64F6D89E] |
00007FFCC72C5D7C | 48 89 EC | MOV RSP,RBP |
00007FFCC72C5D7F | 66 C1 D7 08 | RCL DI,8 |
00007FFCC72C5D83 | 5B | POP RBX |
00007FFCC72C5D84 | 0F BD C8 | BSR ECX,EAX |
00007FFCC72C5D87 | F7 D8 | NEG EAX |
00007FFCC72C5D89 | 58 | POP RAX |
00007FFCC72C5D8A | 66 0F BE C8 | MOVSX CX,AL |
00007FFCC72C5D8E | F6 C7 F2 | TEST BH,F2 |
00007FFCC72C5D91 | 44 87 F7 | XCHG EDI,R14D |
00007FFCC72C5D94 | 66 F7 D3 | NOT BX |
00007FFCC72C5D97 | 59 | POP RCX |
00007FFCC72C5D98 | 10 CB | ADC BL,CL |
00007FFCC72C5D9A | D2 C4 | ROL AH,CL |
00007FFCC72C5D9C | 41 0F CE | BSWAP R14D |
00007FFCC72C5D9F | 5F | POP RDI |
00007FFCC72C5DA0 | 41 C1 FE 17 | SAR R14D,17 |
00007FFCC72C5DA4 | 48 0F B6 D9 | MOVZX RBX,CL |
00007FFCC72C5DA8 | 66 19 EB | SBB BX,BP |
00007FFCC72C5DAB | 66 41 0F BA FE 01 | BTC R14W,1 |
00007FFCC72C5DB1 | 5B | POP RBX |
00007FFCC72C5DB2 | 66 41 31 F6 | XOR R14W,SI |
00007FFCC72C5DB6 | 66 41 BF EC 5E | MOV R15W,5EEC |
00007FFCC72C5DBB | 41 5E | POP R14 |
00007FFCC72C5DBD | FD | STD |
00007FFCC72C5DBE | 0F BD C2 | BSR EAX,EDX |
00007FFCC72C5DC1 | 89 C0 | MOV EAX,EAX |
00007FFCC72C5DC3 | 66 41 0F A5 D5 | SHLD R13W,DX,CL |
00007FFCC72C5DC8 | 58 | POP RAX |
00007FFCC72C5DC9 | 66 41 81 C4 B5 20 | ADD R12W,20B5 |
00007FFCC72C5DCF | 41 F7 D9 | NEG R9D |
00007FFCC72C5DD2 | 66 41 F7 DC | NEG R12W |
00007FFCC72C5DD6 | 41 5A | POP R10 |
00007FFCC72C5DD8 | 66 41 0F AD FC | SHRD R12W,DI,CL |
00007FFCC72C5DDD | 41 5F | POP R15 |
00007FFCC72C5DDF | 66 41 0F AB E4 | BTS R12W,SP |
00007FFCC72C5DE4 | 66 41 C1 E8 02 | SHR R8W,2 |
00007FFCC72C5DE9 | 66 41 81 ED 4A E4 | SUB R13W,E44A |
00007FFCC72C5DEF | 41 5C | POP R12 |
00007FFCC72C5DF1 | 66 44 0F BE CB | MOVSX R9W,BL |
00007FFCC72C5DF6 | 41 58 | POP R8 |
00007FFCC72C5DF8 | 49 81 E5 83 25 9A 18 | AND R13,189A2583 |
00007FFCC72C5DFF | 66 41 19 E5 | SBB R13W,SP |
00007FFCC72C5E03 | 66 41 D3 C9 | ROR R9W,CL |
00007FFCC72C5E07 | 41 59 | POP R9 |
00007FFCC72C5E09 | 66 0F BE E9 | MOVSX BP,CL |
00007FFCC72C5E0D | 66 41 F7 D9 | NEG R9W |
00007FFCC72C5E11 | 66 44 0F BE C9 | MOVSX R9W,CL |
00007FFCC72C5E16 | 41 59 | POP R9 |
00007FFCC72C5E18 | 49 D3 FD | SAR R13,CL |
00007FFCC72C5E1B | 41 BD 4B DD 88 3B | MOV R13D,3B88DD4B |
00007FFCC72C5E21 | 66 41 11 CD | ADC R13W,CX |
00007FFCC72C5E25 | 41 5D | POP R13 |
00007FFCC72C5E27 | 66 39 D6 | CMP SI,DX |
00007FFCC72C5E2A | F6 DE | NEG DH |
00007FFCC72C5E2C | 66 0F AB E2 | BTS DX,SP |
00007FFCC72C5E30 | 5A | POP RDX |
00007FFCC72C5E31 | 66 C1 C5 06 | ROL BP,6 |
00007FFCC72C5E35 | FC | CLD |
00007FFCC72C5E36 | 66 44 0F B6 DB | MOVZX R11W,BL |
00007FFCC72C5E3B | D3 FD | SAR EBP,CL |
00007FFCC72C5E3D | 9D | POPFQ |
00007FFCC72C5E3E | 4C 87 DD | XCHG RBP,R11 |
00007FFCC72C5E41 | E9 85 F6 78 00 | JMP fc64.7FFCC7A554CB |
00007FFCC7A554CB | 5D | POP RBP |
00007FFCC7A554CC | E9 B0 87 25 FF | JMP fc64.7FFCC6CADC81 |
00007FFCC6CADC81 | 41 87 F3 | XCHG R11D,ESI |
00007FFCC6CADC84 | 5E | POP RSI |
00007FFCC6CADC85 | 4C 8D 1C 85 11 99 49 21 | LEA R11,QWORD PTR DS:[RAX*4+21499911] |
00007FFCC6CADC8D | E9 EF D4 B3 00 | JMP fc64.7FFCC77EB181 |
00007FFCC77EB181 | 66 41 F7 D3 | NOT R11W |
00007FFCC77EB185 | 41 5B | POP R11 |
00007FFCC77EB187 | C3 | RET | <-- this is where the integrity check function exits back to FC64.dll
Code: Select all
00007FFCC72C5D71 | 48 C1 F3 21 | SHL RBX,21
Code: Select all
00007FFCC9D22693 | 0F81 657AA600 | JNO fc64.7FFCCA78A0FE |
00007FFCC9D22699 | 68 7C2E058F | PUSH FFFFFFFF8F052E7C |
00007FFCC9D2269E | E9 5693A000 | JMP fc64.7FFCCA72B9F9 |
00007FFCC9D226A3 | 68 86B65533 | PUSH 3355B686 |
Back to this function:
Code: Select all
00007FFCC6EA7BF0 | 48:895C24 08 | MOV QWORD PTR SS:[RSP+8],RBX |
..
00007FFCC6EA7C48 | C3 | RET | <--
So then I thought "why not simplify it?" - if you remember, the loop executes as long as rbx != rdi (as in current list address is not the last address in the list). Therefore inside our call, close to the RET, why not hijack the exit point when rbx == rdi-8?
Code: Select all
00007FFCC6EA7C43 | 48:83C4 20 | ADD RSP,20 | <-- hook
00007FFCC6EA7C47 | 5F | POP RDI |
00007FFCC6EA7C48 | C3 | RET |
Code: Select all
{ Game : FarCry4.exe
Version:
Date : 2019-07-05
Author : SunBeam
This script does blah blah blah
}
define(address,"FC64.dll"+787C43)
define(bytes,48 83 C4 20 5F C3)
[ENABLE]
assert(address,bytes)
alloc(newmem,$1000,"FC64.dll"+787C43)
label(code)
label(return)
newmem:
code:
add rsp,20
pop rdi
push rax
lea rax,[rdi-8]
cmp rax,rbx
pop rax
jne short @f
add rsp,30 // adjust stack to exit to where we want
ret
@@:
ret // exit
address:
jmp newmem
return:
[DISABLE]
address:
db bytes
// add rsp,20
// pop rdi
dealloc(newmem)
{
// ORIGINAL CODE - INJECTION POINT: "FC64.dll"+787C43
"FC64.dll"+787C23: 48 8B 07 - mov rax,[rdi]
"FC64.dll"+787C26: 48 8B CF - mov rcx,rdi
"FC64.dll"+787C29: FF 50 18 - call qword ptr [rax+18]
"FC64.dll"+787C2C: FF 4F 08 - dec [rdi+08]
"FC64.dll"+787C2F: 75 0D - jne FC64.dll+787C3E
"FC64.dll"+787C31: 48 8B 07 - mov rax,[rdi]
"FC64.dll"+787C34: BA 01 00 00 00 - mov edx,00000001
"FC64.dll"+787C39: 48 8B CF - mov rcx,rdi
"FC64.dll"+787C3C: FF 10 - call qword ptr [rax]
"FC64.dll"+787C3E: 48 8B 5C 24 30 - mov rbx,[rsp+30]
// ---------- INJECTING HERE ----------
"FC64.dll"+787C43: 48 83 C4 20 - add rsp,20
"FC64.dll"+787C47: 5F - pop rdi
// ---------- DONE INJECTING ----------
"FC64.dll"+787C48: C3 - ret
"FC64.dll"+787C49: CC - int 3
"FC64.dll"+787C4A: CC - int 3
"FC64.dll"+787C4B: CC - int 3
"FC64.dll"+787C4C: CC - int 3
"FC64.dll"+787C4D: CC - int 3
"FC64.dll"+787C4E: CC - int 3
"FC64.dll"+787C4F: CC - int 3
"FC64.dll"+787C50: 48 89 5C 24 10 - mov [rsp+10],rbx
"FC64.dll"+787C55: 57 - push rdi
}
Sun
archive password: frf
- Attachments
-
- runtrace.rar
- trace_from_bp_on_access_to_bp_on_execute
- (426.4 KiB) Downloaded 1707 times
-
- What is cheating?
- Posts: 1
- Joined: Thu Jul 13, 2023 5:11 pm
- Reputation: 0
Re: Dealing with the VMProtect-ed Integrity Check in Far Cry 4
Is it possible to put it into practice on Watch Dogs 1? i was trying the same method using DBVM but no opcode appears when i click continue in game the list is empty
Re: Dealing with the VMProtect-ed Integrity Check in Far Cry 4
As far as I recall, WD1 has no VMProtect over the binary o_O... That's what the video is about. In general, activate and use the VEH debugger (Debugger Options > Debugger method > Use VEH Debugger).
Who is online
Users browsing this forum: No registered users