Jump to content
Tuts 4 You

[InlineMe] VMProtect IsValidImageCRC()


SunBeam
Go to solution Solved by MistHill,

Recommended Posts

[ Solutions so far: JohnWho, What ] (read whole thread)

Hello folks.

Decided I would post this here as well, as we're lacking some exercise on these kinds of targets.

Purpose: open up the test target, click OK on the message box.

TFO4QiP.jpg

Use any tool you want to alter a byte in application's active memory (e.g.: "MZ" string at ImageBase) and another message will appear.

jQubpLp.jpg

Goal: make the 'bad' message never pop-up, but not through patching the all-too-clear JUMP. Inline VMProtect's CRC check method so the CALL always returns 1. Again, not through MOV EAX,1|RETN :) (P.S.: imagine you don't know where this function is in a well-obfuscated target).

Link: https://www.mediafire.com/file/sngpjiclmdgu3m2/Lic_MessageBox_2.rar/file

Password: sunbeam

Kudos,
Sun

Lic_MessageBox_2.rar

Edited by Teddy Rogers
Updated download link. Also attached same file.
  • Like 2
Link to comment
Share on other sites

.0041A380: 81FA2F014000                   cmp          edx,00040012F -- 1

.0041A386: 7507                           jnz         .00041A38F -- 2

.0041A388: B840800000                     mov          eax,000008040 

.0041A38D: 81FAB0C64000                   cmp          edx,00040C6B0 -- 3

.0041A393: 7507                           jnz         .00041A39C -- 4

.0041A395: B839187C2D                     mov          eax,02D7C1839 

.0041A39A: E9D71FFFFF                     jmp         .00040C376 -- 5
Link to comment
Share on other sites

Wouldn't it be easier if you tell me whats wrong instead of having me guessing?


 


Does it not work for you or what?


Link to comment
Share on other sites

Hi guys.


 


@JohnWho: He's talking about the jumps :) Unless your intention was to put them like that, the correct code would be:



0041A380    81FA 2F014000           CMP EDX,0040012F
0041A386    75 07                   JNZ SHORT 0041A38D <-- instead of 41A38F
0041A388    B8 40800000             MOV EAX,8040
0041A38D    81FA B0C64000           CMP EDX,0040C6B0
0041A393    75 07                   JNZ SHORT 0041A39A <-- instead of 41A39C
0041A395    B8 39187C2D             MOV EAX,2D7C1839
0041A39A  - E9 D71FFFFF             JMP 0040C376

And from the looks of it, I take it you've changed code in 40012F and 40C6B0 areas.


 


Well, second one is obvious, since you hooked code, so 40C6AA+1 = 40C6B0:



0040C6A9    60                      PUSHAD
0040C6AA  - E9 D1DC0000             JMP 0041A380

While first one is at 40012E (-1 from 2F): 0x40 <- 0x00.


 


Funny thing about it is if left patched like this, loop never exits. The TEST EAX,EAX after the CALL is never hit again.


 


All in all, awesome work ;)


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

^ Indeed :) Would it be possible to get this documented or just pointers on how to approach it?


 


The way I see it:


 


1. If you plan on patching lots of memory, whatever changes you want make, you will have to fix the CRC in your hooked code. In gamehacking we're using Cheat Engine to hook various portions of code, hence if I hook 8-10 locations, I have to fix CRC as you probably did.


 


2. Would it be possible to kill the return value (1 to 0)? That is if you figure out where this is calculated. That way, no matter how much code I'd hook, return is always 1 (all OK), thus not having to worry about correct hashes.


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

Yes would probably have to fix crc several times but if you do:


 


example, patch:


 


461022 -> to something


 


473100 -> to something


 


481203 -> to something


 


you might only need to fix CRC after 481203, depending on where code block ends, so often you can get away with 3-5 fixes


 


 


It has been my plan to reverse the CRC check more deeply to find a clean fix but i haven't had the time for it. Memory crc and file crc is done by same routine btw.


Link to comment
Share on other sites

Correct, same code flow, but separate functions:


 


File CRC:



0040C6A9    60                      PUSHAD
0040C6AA  - E9 D1DC0000             JMP 0041A380
 
00419DF6    3202                  XOR AL,BYTE PTR DS:[EDX]
..
00418617    60                    PUSHAD
00418618    E9 47020000           JMP 00418864
..
00418864    9C                    PUSHFD
00418865    FF4D 00               DEC DWORD PTR SS:[EBP]

Memory CRC (same function, but recompiled for this particular use):



0040CBE3    3202            XOR AL,BYTE PTR DS:[EDX]
0040CBE5    56              PUSH ESI
0040CBE6    42              INC EDX
0040CBE7    E8 BDFAFFFF     CALL 0040C6A9
..
0040C6A9    60              PUSHAD
0040C6AA  ^ E9 C7FCFFFF     JMP 0040C376 <-- your hook
..
0040C376    9C              PUSHFD
0040C377    FF4D 00         DEC DWORD PTR SS:[EBP]

Will try and use same memory range, with both the OK and BAD values for the CRC and see where VMProtect's coming up with that 0/1 BOOL.


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

@JohnWho: This is the code behind the example:



#define WIN32_LEAN_AND_MEAN
 
#include <windows.h>
#include "VMProtectSDK.h"
 
DWORD dwShow = 0;
 
VOID CALLBACK CheckMemory( HWND hWnd, UINT uMsg, UINT timerId, DWORD dwTime )
{
if( VMProtectIsValidImageCRC() )
{
// nothing here
}
else
{
MessageBox( 0, "VMProtectIsValidImageCRC active check failed. You changed code! Exiting. Patch the CRC check (not the JUMP!).", "Info", MB_ICONEXCLAMATION );
ExitProcess( 0 );
}
 
}
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
MSG msg;
 
MessageBox( 0, "VMProtectIsValidImageCRC active check enabled. CRC value is OK now. Try changing code and see what happens.", "Info", MB_OK );
 
SetTimer( NULL, 0, 2000, (TIMERPROC)&CheckMemory );
 
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return TRUE;
}

It's not perfect, but for the purpose of this exercise, works as supposed. There are games (e.g.: Assassin's Creed - Unity) that use a similar approach to check memory integrity, but their timer is set to 200ms :) 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).


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

You are right, not same routine... in every target i checked it was same routine, maybe it was different versions/builds.


 


I didn't even fix the file crc, didn't realize it was enabled :P


Link to comment
Share on other sites

Hehe, it's still there. Same flow: VirtualProtect, GetModuleFileNameW, CreateFileW, GetFileSize(Ex), CreateFileMappingW, MapViewOfFile (might've forgotten something here), UnmapViewOfFile, CloseHandle (on the mapping). I thought the logic was to generate the file CRC (on the sections they indicate they're checking) then compare the computed value(s) with the ones calculated via IsValidImageCRC function :) That would explain why file CRC is there, even though I've not enabled it in the protector options (I've left everything out, using Maximum Speed).


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

Hehe :D


 


Well, I'm not yet satisfied till I find that 1/0 BOOL to patch in VMProtect - OR - the way the function that checks memory is called X times (infinitely). Will try and think of more combos with VMProtect's SDK.


 


BR,


Sun


Edited by SunBeam
Link to comment
Share on other sites

I'm not following all your rules here of what is valid / not valid solution, but changing the interval arg to something very high, then change it back to 2000 before the call (that way it makes 1 check after starting, then waits a 4 days, etc before calling again and u can mod the binary), or block WM_TIMER from arriving, etc work fine for me. Probably many more ways without wasting time reversing the whole crc routine.


 


Edit - Another way it to get the return value of SetTimer() which doesnt involve moding any code then from outside of the application call KillTimer(NULL, ReturnValueOfSetTimer) and it won't get called anymore.


Edited by simple
Link to comment
Share on other sites

@simple: The target here mimics Ubisoft's memory check implemented in Assassin's Creed: Unity. That target is x64 for one, and for two, I don't know (haven't looked into it, but will do so) if they use a timer or a thread (I think it's a thread). I created this to illustrate what happens in some games that have VMProtect used as protection. I was looking into a more-VMProtect-wise approach, rather than patching the timer or similar. As I said, in that game the timer might be obfuscated as well. Last I tried, I set a break on WM_TIMER, nothing came up, so I'm sure they're using something else.


 


BR,


Sun


Edited by SunBeam
  • Like 1
Link to comment
Share on other sites

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