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

SunBeam

SunBeam

Administrator
Staff member
Administrator
Joined
Feb 4, 2018
Messages
3,481
Dropping these here. Too-da-loo :)

Part #1:

https://www.youtube.com/watch?v=VxigMMwBX4M

Part #2:

https://www.youtube.com/watch?v=OK8U-ZSNXvA

BR,
Sun
 
SunBeam

SunBeam

Administrator
Staff member
Administrator
Joined
Feb 4, 2018
Messages
3,481
I've managed last night to trace as far as possible into the VM from the last RET hit here..
Code:
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:
00007FFCC789CB4A+8D -> 00007FFCC789CBD6
If you set a hardware breakpoint on access on the byte of 7FFCC789CBD6, then set another breakpoint on execute here..
Code:
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:
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:
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:
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:
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:
00007FFCC6EA7C43 | 48:83C4 20                             | ADD RSP,20                                                        | <-- hook
00007FFCC6EA7C47 | 5F                                     | POP RDI                                                           |
00007FFCC6EA7C48 | C3                                     | RET                                                               |
Code:
{ 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 KB Views: 189
Top