My reduced code looks like this:
Code: Select all
{
char* p1 = VMProtectDecryptString("IdenticalTest") + 3;
DoStuff(p1, "Foo");
VMProtectFreeString(p1 - 3);
}
{
char* p1 = VMProtectDecryptString("IdenticalTest") + 3;
DoStuff(p1, "Bar");
VMProtectFreeString(p1 - 3);
}
...
Here are some disasm snippets for it:
Code: Select all
00F0DBA0 mov esi,dword ptr [__imp__VMProtectDecryptStringA@4 (1517A84h)]
...
00F0DBC5 mov edi,dword ptr [__imp__VMProtectFreeString@4 (1517A80h)]
...
00F0DBCD mov ebx,offset string "\xb2\xdf\xf6Service" (1578570h) ; Here it goes
00F0DBD2 push ebx
00F0DBD3 call esi ; VMProtectDecryptStringA
... DoStuff()
00F0DC49 mov eax,dword ptr [p1]
00F0DC4C add eax,0FFFFFFFDh
00F0DC4F push eax
00F0DC50 call edi ; VMProtectFreeString
00F0DC52 push ebx
00F0DC53 call esi ; VMProtectDecryptStringA
Once the executable is protected, the second DoStuff() call receives a pointer to garbage as first argument.
In the protected version the disasm looks like this:
ebx: Still some old value from before the function call. Doesn't point to anything interesting at this time.
Code: Select all
00000000`22e11738 b0 08 b4 00 f1 08 b4 00 32 09 b4 00 73 09 b4 00 b4 09 b4 00 33 0a b4 00 ........2...s.......3...
00000000`22e11750 b5 0a b4 00 f1 0a b4 00 2d 0b b4 00 6d 0b b4 00 ad 0b b4 00 e0 0b b4 00 ........-...m...........
00000000`22e11768 12 0c b4 00 44 0c b4 00 76 0c b4 00 9e 0c b4 00 c6 0c b4 00 e1 0c b4 00 ....D...v...............
Code: Select all
005edbcd e9f29ed400 jmp 01337ac4 ; Constant hiding I assume?
005edbcd mov ebx,offset string "\xb2\xdf\xf6Service" ; This *was* the original line, just inserted here as explanation
005edbd2 53 push ebx
Code: Select all
00000000`063c55f8 b2 df f6 53 65 72 76 69 63 65 00 ab ab ab ab ab ab ab ab fe ee fe ee fe ...Service..............
00000000`063c5610 00 00 00 00 00 00 00 00 c8 9a 30 76 32 cd 00 00 c0 68 3c 06 60 51 3c 06 ..........0v2....h<.`Q<.
00000000`063c5628 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ........................
Code: Select all
005edbd3 ffd6 call esi ; Also DecryptString?!
....
005edc09 8b45fc mov eax,dword ptr [ebp-4] ss:002b:0018dba4=063c55fb
005edc0c 83c0fd add eax,0FFFFFFFDh
005edc0f 50 push eax
005edc10 ffd7 call edi <-- FreeString
Code: Select all
00000000`063c55f8 c0 68 3c 06 60 51 3c 06 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe .h<.`Q<.................
00000000`063c5610 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ........................
00000000`063c5628 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe ........................
Code: Select all
005edc12 53 push ebx ; Broken EBX?
005edc13 ffd6 call esi ; Also DecryptString?
This results in garbage being passed on as string to further DoStuff() calls
For some reason it doesn't occur in a minimal test-application.
But only in the full one. Maybe because different heuristics or optimizations are triggered.
Is this information sufficient for fixing the issue, or should I try harder to generate a minimal application for reproducing the issue?