Jump to content
Tuts 4 You

[InlineMe] VMProtect IsValidImageCRC()

Go to solution Solved by MistHill,

Recommended Posts

In Assassin's Creed: Unity, I want to hook this piece of code:




I want to place a JMP there to a code-cave of mine where I acquire value of RCX register as the pointer used for an internal DebugMenu. What I did now was to debug every byte at that location:


140EA52A0: +0: 48 89 5C 24 08 | +5: 48 <-


I am interested mainly in the byte at 140EA52A5. That's the one I will be restoring the CRC for. Using Cheat Engine as the debugger, I found that RAX register (holding the CRC value) is E1A44BE4 when 140EA52A5 is checked. One down, one more to go. Am following JohnWho's methodology here :)


Now, back to VMProtect, code is checked within this function:

1455D741F - 32 02                 - xor al,[rdx] <-- classic XOR operation
1455D7421 - F9                    - stc 
1455D7422 - E9 9C060000           - jmp 1455D7AC3
1455D7AC3 - F8                    - clc 
1455D7AC4 - F6 C4 85              - test ah,-7B
1455D7AC7 - 48 83 C2 01           - add rdx,01 <-- move to next address
1455D7ACB - E9 1E2F19FD           - jmp 14276A9EE
14276A9EE - E9 43BCE602           - jmp 1455D6636
1455D6636 - FF 4D 00              - dec [rbp+00] <-- decrement counter
1455D6639 - E9 0C5AFFFF           - jmp 1455CC04A
1455CC04A - E9 207F0200           - jmp 1455F3F6F
1455F3F6F - 0F85 897EFDFF         - jne 1455CBDFE --> jumps at [1]
1455CBDE2 - 48 8B 55 00           - mov rdx,[rbp+00] <-- RBP contains the checked address
1455CBDE6 - 66 0FAC C9 0D         - shrd cx,cx,0D
1455CBDEB - 66 D3                 -  
1455CBDED - F1                    - db F1
1455CBDEE - 48 83 C5 08           - add rbp,08
1455CBDF2 - 66 0FA5 F1            - shld cx,si,cl
1455CBDF6 - 0FAC D9 16            - shrd ecx,ebx,16
1455CBDFA - FE C1                 - inc cl
1455CBDFC - 29 C0                 - sub eax,eax
1455CBDFE - 66 0FBD CB            - bsr cx,bx <-- [1]

So what I have to do more is hook 1455D7ACB:


1455D7ACB: +0: E9 20 7F 02 00 | +5: 51 (1455CC04F added to list, CRC is )


EDIT: I can now say they're using threads. If I break on the XOR and try to trace code with regular stepping it so happens that debugger lands in another thread. I'm basically executing same code in two threads, starting with the location I put the breakpoint on, asynchronous.


Problem I'm having now is this one:




The VMProtect code is checked from multiple locations (I remember SecuROM) :D So I have to chain-patch these spots to get a working bypass.


So far I found 3 checks for CRC: main game code is checked in one spot (let's call it XOR_1); XOR_1 is checked by itself and XOR_2 (in another place); XOR_2 is checked by XOR_1, itself, and XOR_3 (in another place); XOR_3 is checked by XOR_2 and itself :D


I don't know if there are any other timed checks (will leave game running for a while).


So basically, my check doesn't mimic properly their implementation.


EDIT: Aha! A 4th one poped up! :)




Edited by SunBeam
  • Like 2
Link to comment

Your rules are pretty confusing when u say stuff like this in your challenge...



So, the elegant way to disable the check (I think they need a timer or something, thread, to infinitely check on the code via VMProtect's SDK API) is to disable the timer/thread/etc. - whatever they use (figuring out first what's the used method).


But it doesn't matter whether it's  virtualized x86, x64, arm, a process, a thread, a callback, or on a mcu that communicates to the host machine via a satellite on Jupiter's 3rd moon, there's always going to be a way to block the code and there's no such thing as a "good implementation" of stuff like this. If it can't be done from user space then definately from the kernel. Your method is time consuming very unstable. Mine is fast and stable, but to each their own - good luck ; )

  • Like 2
Link to comment

The function which is used for vmp's hash checking is written as something similar to

ecx = eax = 0;
for (i = 0; i < Size; i++) {
ecx = eax
ecx = ecx >> 0x19
eax = (eax << 0x07) | ecx
eax = (eax & 0xFFFFFF00) | (eax & 0xFF) ^ memaddress[i]

I dont see the point of patching the handlers mainly because it will require one to modifying the nature of the handlers and make a backup of original file . If handler integrity check is enabled, it will be harder. The simplest method is to patch the VMP crc function call at all .


Your vmp hash check handler starts at 


Mem Address and range to checked can be seen as pushed values via EBP

Edited by Conquest
Link to comment

@simple: I haven't properly checked the target when I thought of the implementation in this test app. It looks like there's checks to the checks that check the checks :D Something like that. The idea here was to actually follow VMProtect flow and find the return of IsValidImageCRC API. Or as Conquest has put it, patching the CALL itself. Again, in this test application you know where it is. In others you don't. How you plan to find it, that's my goal.


Simply put, the timer was my implementation of "calling a check function at a given interval". The timer was not the target here. What I said about it is related to my main target, ACU. Thought it would apply to it, but I see things are different.


Back to the real target (not this test one of mine), how do you plan to find each and every piece of code that accesses the rest of them? The way I see it CRC is done at certain intervals, just to be sure at any given point in time one checker (any of the checkers) isn't patched.




Edited by SunBeam
Link to comment

Again, in this test application you know where it is. In others you don't. How you plan to find it, that's my goal.

I wont spoil the fun of yours, but lets say it happens this way 


Lets say function is something likenormalfunc{//do operationif(VMProtectIsValidImageCRC()){//You go gurl!};else {//naughty boy};}Equivalent asm is //original asmcall VMProtectIsValidImageCRC()test eax,eax//do whatever requestedWhen vmped it will be differentEach function will be compiled as different vm I wont reveal all the details but the vmp when switching function has to initiate a complete cleanup phase to keep the stack exact replica of original execution. it simply means the VMProtectIsValidImageCRC() is assumed as different function call unknown to vmp and full call jump is requested. It will be something similar to ; GetI32 (VMProtectIsValidImageCRC function address hardcoded and encrypted inside pcode-store); CleanUp (restores Stack and registry as emulated inside vm); Cleanup handler Ret to new function; VMProtectIsValidImageCRC() is evaluated // This phase can be replaced.; Vm Execution of original function restores

Exact details is too large to explain here and i am not willing to share it atm since i dont have complete grasp of it yet. Basic is you have t find the return handler to know the call jumps. Its same for themida as well.

Edited by Conquest
Link to comment

Hi, my solution.

0040B53F 8B4424 18 mov eax,dword ptr ss:[esp+0x18]

change to 

0040B53F 33C0 xor eax,eax
0040B541 B0 01 mov al,0x1
Edited by White、、
  • Like 1
Link to comment

Good work, White  ;)


@simple & Conquest: This is basically where I kinda stopped in terms of reversing - virtualization. I consider it takes an awful amount of time to get the hang of it, like learning a whole programming language (but more intricate), time I don't really have nowadays (work, fiancé, house in the plans etc.). I am trying to proceed as I can (and know, mostly) making use of what I've grasped so far - but damn willing to understand anything anyone throws at me. The initiative is there.


With regards to my target(s), I noticed both Assassin's Creed: Unity and Far Cry 4 have the same mechanism implemented. One checks verifies the integrity of the other. My understanding of what happens from target launch till it being actually in memory here is the following:


- VMProtect performs its usual checks;

- unpacks the target in memory;

- creates checksums of certain memory sections (or chunks of code);

- once done, game will use those active checks to verify (at given intervals) if game/application code has been altered;

- some of these checks check both themselves and other checks that check them (lol);


Since patching the checks to return the correct checksums is a bit tedious:


a. Either intercept VMProtect's checksum generation routine and make it checksum the already-altered memory (that way it won't bitch when you have the hook already there);

b. Swap base pointer in the XOR loop: xor al,[rdx] <- rdx is base + offset;


Plan would be to load-up the unpacked module (.exe or .dll - or at least some sections I know it checks) in memory and when the XOR is hit, redirect it to check the dummy memory instead of the actual memory (base + offset -> dummy_base + same offset). It would be always intact (the dummy).





P.S.: Got no knowledge of kernel-mode stuff, so.. yeah..

Edited by SunBeam
  • Like 1
Link to comment

You can check This link to have some rough idea about how to bypass the protection (ignore the mushroom game specific parts and check the vmp related part only). One thing i didnt mention is that ,there is not specific way to bypass these checks unless modifying the whole source, reason is there can be as great as 10 separate vm machines running making 10 separate hash check handlers executing(excluding handler integrity checks). Patching all of them is cumbersome. 

Link to comment

Hi Conquest.


MapleStory was one of the games I used to work on at some point. But at the time, it was all about unpacking (they used ASProtect back then, moved to Themida later on) and doing what I described above: finding out how the memory is checked and changing pointer to use the dummy loaded in memory. But yeah, it was just 1 check. Mostly simple stuff to an intermediate reverser.


Very interesting article, summarizing my thoughts exactly :)


In the meantime, I'll try and update my test target with some extra checks and an approach on how to swap pointer to checked memory.





P.S.: MapleStory is still with Nexon? I remember they sold out the title :)

Edited by SunBeam
Link to comment

@Conquest: Now I understand why I've seen two types of checks:


a. First set of checks in Unity or Far Cry 4 is VMProtect's - let's say - IsValidImageCRC; these are the XOR r8,[r32/r64] instructions;

b. Second set of checks is comprised of instructions like MOV AX,[RDX];


Now, for a., we talked about the generics: start, size, RAX/EAX holds the hash value. Well, for b., the CRC value is someplace else. What I'm curious about is to debug the app and see if for this portion it checks I get same result with a regular CRC32 compute.




Edited by SunBeam
Link to comment

P.S.: MapleStory is still with Nexon? I remember they sold out the title :)

It belonged to wizet and nexon bought out wizet or something like that. Now its all about pay to win

Link to comment

@Conquest: Now I understand why I've seen two types of checks:


a. First set of checks in Unity or Far Cry 4 is VMProtect's - let's say - IsValidImageCRC; these are the XOR r8,[r32/r64] instructions;

b. Second set of checks is comprised of instructions like MOV AX,[RDX];


Now, for a., we talked about the generics: start, size, RAX/EAX holds the hash value. Well, for b., the CRC value is someplace else. What I'm curious about is to debug the app and see if for this portion it checks I get same result with a regular CRC32 compute.




I will add a few more information to aid you in debugging , there are separate handlers for operations , Read operations has 3 types of Heap read(byte,word and dword), stack read and hash check( total 7 atleast ), same with write operations. If the program has something like

Mov R32,[R32]

It will be Dword read handler


mov eax,[ebp]add ebp,4mov eax,[eax]mov [ebp],eax

And so on for other data sizes . It is possible that you may encounter 30~40 handlers reading memory as different type of original instructions being changed into different type of handlers (ie. byte,word, dword or even tricking memory operations by doing them on stack) etc. So it is really irritating fixing all of them.

Edited by Conquest
Link to comment

I tested something today. I've dumped the target at OEP and fixed PE header (contained lots of crap). Then did the following:

alloc( hook, 256 )
alloc( dummy, 120000 )
label( originalcode )
loadbinary( dummy, C:\Documents and Settings\Administrator\Desktop\dumped.exe ) // our dumped dummy
push eax
push ecx
mov eax,[ebp] // get address from [ebp]
mov ecx,target.exe // get ImageBase
sub eax,ecx // get offset
add eax,dummy // find location in dumped dummy
mov [ebp],eax // swap checked address
pop ecx
pop eax
neg dl
sar dl,cl
mov edx,[ebp]
jmp 40CCA4 // return back
jmp hook // hooked handler
neg dl
sar dl,cl
mov edx,[ebp]
dealloc( hook )
dealloc( dummy )

This one actually works. Fun fact is active memory is not checked anymore. If there would be other checkers, re-routing would be done in the same way, I guess.




Edited by SunBeam
Link to comment

Last time i hacked games was when Battlefield 2 was in, and back then the easiest way to hook code and avoid punkbuster was to hook dma pointers... no work anymore?

  • Like 1
Link to comment
  • 1 month later...
  • Solution

Well, this topic is very interesting indeed.AFAIK, VMProtect usually includes four types of code integrity checks in an application protected with all advanced options enabled.1. File Check
As SunBeam said, APIs called: CreateFile, CreateFileMapping, and MapViewOfFile.
This happens during the shell code execution, i.e. between the target's entry point and the OEP.
For this InlineMe, the followings are checked.

          Address  Length   Hash          -------  ------   ----#00000001 003D0170 00010E78 D2953B20#00000002 003E1018 000061E8 632E395E#00000003 003D012C 0000003C 1EBECA14#00000004 003D0000 00000128 10C6C1DDFile Mapping Address: 003D0000

Normally only 4 items in the table, and table itself not Hash checked.

2. Memory Check #1
This is for verifying the file image in memory when the target loaded.
If you examined the table records, you'll figure out that it's also a file check!
Large part of the records are for .vmp1 section validation, only few for PE Header.
Table size varies from different version or target.
There is a hash check for table itself, and the valid Hash stored at some place, which can be easily patched.


3. Memory Check #2
This is the memory check in a real sense, and applies mainly for the application.
If the application was compressed, it happens after the user code, data and resource got unpacked.
Table size is more bigger than the previous one, and the worse thing is that the table's Hash is hardcode in PCODE!
In contrast with the Memory Check #1, records in the table not all meaningful. There is a End Flag, after that all the data is garbage.

These three checks above are performed at the shell code execution stage.
The record in table can be defined as:

typedef struct {   long EncryptOffsetRVA;   long EncryptLength;   long EncryptHash;} VMP_HashCheck_Record;

The check procedure:

   a) decrypting OffsetRVA, +Modulebase, +Reloc. Factor;    decrypting Length;   c) call getHash handler to calculate Hash;   d) encrypting the Hash;   e) comparing it with the one in record.

4. Random Memory Check
No this kind of check in SunBeam's unsophisticated InlineMe.
If the "Check the integrity of VM objects" option enabled, and a piece of the application code was virtualized, it will have an opportunity to show up.
A check table does exist also, in which only the offset RVA are encrypted, and each length is one byte long for better execution performance.
This silently check could be done many times in a virtualized code portion, but only one record is picked up randomly by the vRdtsc handler.
According to VMProtect's help document, "A silent check means that the protected program will not show any messages in case the integrity of VM objects is broken, but it will cause the virtualized code to function incorrectly, which will lead to critical errors and, therefore, to the complete crash of the protected program."

Okay, what the hell is going on within the SDK API VMProtectIsValidImageCRC?
It's just the Memory Check #1 & #2!As Conquest mentioned previously, the getHash handler here is very very important!
It's the critical clue for inline patching, even repacking a VMP target IMHO.If you logged the getHash handler(entry: 0041847A for vmp1) from the target's entry(00414699) to OEP(00401224), you'll find out that all the three checks are performed even this target not packed!
Which means the VMProtectIsValidImageCRC function has been called once by the shell code in .vmp1 section.
After read the log of the second getHash handler(entry: 0040CC9D for vmp0) between two Timer function CheckMemory(00401000) calls, it can be proved that only Memory Check #1 & #2 been done!Before continuing, let's discuss a little about foregoing approaches.White's solution is to patch the VMProtectIsValidImageCRC's result, it's simple and excellent only for this target!
But, it's not the good approach because the patch point is within vRet handler, which is not generally applicable and dangerous in other case!
First, the return value gets crap when went back to normal code from some virtualized user function or other SDK API in an application protected.
Second, if the "Encrypt registers at VM output" option enabled, the return values at this point must be decrypted by another routines in different places.The ultimate aim is to prevent these checks or altering the content of tables.
The more professional way is to patch the PCODE, that is difficult and needs advanced skill!
The easy way is to hook the getHash handler, and patch the returns, somewhat as JohnWho did.The attached script is my method which just for demonstrating how to patch getHash handler and Memory Check #2 table, it's proper and enough for this scenario, I think.Followings are some references for this InlineMe.

Modulebase: 00400000Imagebase:  00400000Memory Check #1 Table=====================Address: 00412180, Size: 00000210(0000002C Items), Table Hash: 84268688Item            eOffset  eLength  eHash     Decrypting  dOffset  dLength  cHash    ceHash-----           -------  -------  -----                 -------  -------  -----    ------#00000001:      99F316C6 43B06508 5BDB89EE      =>      00411000 0000111F 43C5CF7B 5BDB89EE#00000002:      99F6FCF6 43B075DF 056F5F20      =>      00419CF9 000000CA ED58A5AD 056F5F20...#0000002B:      99F5875A 43B07219 B182023B      =>      0041359C 0000000C 996C48C8 B182023B#0000002C:      99F5375A 43B07219 B05176BB      =>      00413588 0000000C 983BBC48 B05176BBAlgorithm---------   OffsetRVA decrypting: +6A08E97A, Shld E, ++, bswap   Length decrypting:    Shrd 18, ++, Shld 18, ~, ^E347219D, +301E1D57, ~, ^77D93621   Hash encrypting:      --, --, ~, bswap, +8B45E9E9, --, ~, bswapMemory Check #2 Table=====================Address: 0040D164, Size: 000021CC(000002D1 Items), Table Hash: C8A37D66Item            eOffset  eLength  eHash     Decrypting  dOffset  dLength  cHash    ceHash-----           -------  -------  -----                 -------  -------  -----    ------#00000001:      BA3B965B 99C2CE3A AB72BDF6      =>      00403768 00000007 EEA5E87A AB72BDF6#00000002:      BA3B62F3 9CC2CE3A 55DA3F26      =>      00406AD0 00000004 01200000 55DA3F26...#00000231:      BA3B9899 8DC2CE3A 0552AC2B      =>      0040352A 00000013 3A5489D0 0552AC2B#00000232:      BA3BB96E 9AC2CE3A 9502AE76      =>      00401455 00000006 71567840 9502AE76#00000233:      BA3BCDC4 61346F34 11058009      =>      FFFFFFFF        <= *** Table End Flag found!Algorithm---------   OffsetRVA decrypting: +45C4323A, ~, --, --   Length decrypting:    -A1C2CE3B, bswap, ++, ~   Hash encrypting:      ^6153D6ED, -86B3B143, bswap, ~Note: cHash  - Hash calculated by getHash handler;      ceHash - Encrypted cHash; should == eHash if everythig is fine, else patches or software breakpoints detected.


/*    Note: run this script at the target entry: 00414699 !!!    MistHill at tuts4you.com, 2015/05/25*/    history 0    lclr    bphwc    bpmc    bc    var pWinMain    var vGetHash    var dwModuleBase    var pMemoryCheckTable    var bufTemp    mov pWinMain,            0000102F    ; all RVA    mov vGetHash,            0000CC9D    mov pMemoryCheckTable,   0000D164    gmi eip, MODULEBASE    mov dwModuleBase, $RESULT    add pWinMain, dwModuleBase    add vGetHash, dwModuleBase    add pMemoryCheckTable, dwModuleBase    bphws pWinMain    erun    bphwc pWinMain    cmp eip, pWinMain    jne L_Exit    mov [vGetHash-914], #49E8ED0D0000894500#    mov [pMemoryCheckTable+18], #59518BD18D89D50D00003B4DFC7507B8667DA3C8EB108D92D973FFFF3B55FC7505B87AE8A5EEC3#    mov bufTemp, [pMemoryCheckTable+1A58], 0C    mov [pMemoryCheckTable+0C], bufTemp, 0CL_Exit:    ret
  • Like 6
Link to comment
  • 1 year later...
  • 2 years later...
  • 1 year later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...