Dealing with the VMProtect-ed Integrity Check in Far Cry 4

Post here (make sure thread doesn't exist first) any type of tutorials: text, images, videos or oriented discussions on specific games. No online-related discussions/posts OR warez!
Post Reply
User avatar
SunBeam
Administration
Administration
Posts: 4932
Joined: Sun Feb 04, 2018 7:16 pm
Reputation: 4629

Dealing with the VMProtect-ed Integrity Check in Far Cry 4

Post by SunBeam »

Dropping these here. Too-da-loo :)

Part #1:



Part #2:



BR,
Sun

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

Re: Dealing with the VMProtect-ed Integrity Check in Far Cry 4

Post by SunBeam »

I've managed last night to trace as far as possible into the VM from the last RET hit here..

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                            | <--
..into VMProtect where the integrity check occurs. Then I managed to find out which is the last address whose bytes are being read:

Code: Select all

00007FFCC789CB4A+8D -> 00007FFCC789CBD6
If you set a hardware breakpoint on access on the byte of 7FFCC789CBD6, then set another breakpoint on execute here..

Code: Select all

FC64.dll+76D636 - 48 8B 0D B3AE6202     - mov rcx,[FC64.dll+2D984F0]
..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:

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
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:

Code: Select all

00007FFCC72C5D71 | 48 C1 F3 21              | SHL RBX,21
Trace the code till the RET and see where it exits. For me it exits here:

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                                                     |
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 :P

Back to this function:

Code: Select all

00007FFCC6EA7BF0 | 48:895C24 08        | MOV QWORD PTR SS:[RSP+8],RBX   |
..
00007FFCC6EA7C48 | C3                  | RET                            | <--
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? ;)

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
}
BR,
Sun

archive password: frf
Attachments
runtrace.rar
trace_from_bp_on_access_to_bp_on_execute
(426.4 KiB) Downloaded 1707 times

IroniaTheMaster
What is cheating?
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

Post by IroniaTheMaster »

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

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

Re: Dealing with the VMProtect-ed Integrity Check in Far Cry 4

Post by SunBeam »

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).

Post Reply

Who is online

Users browsing this forum: No registered users