Pancake Posted June 28, 2015 Posted June 28, 2015 (edited) Updated 30/6 Inspired by HellSpider's topic i upload a simple unvirtualizeme protected with latest licensed Oreans Code Virtualizer, for research purposes. It supports 4 types of VMs, each can be "white" , "red" or "black" (small, bigger, biggest). Pressing buttons 1-4 will pop up 2 messageboxes. Source: int Func1(int a, int b, int c){ VIRTUALIZER_FISH_RED_START MessageBoxA(0, "Fish Red", "UnvirtualizeMe", 0); Sleep((a + b + c) % 1000); MessageBoxA(0, "Complexity 2/10 Speed 9/10 Size 260", "Fish Red", 0); return GetTickCount(); VIRTUALIZER_FISH_RED_END}void* Func2(int* a, int* b, int* c){ VIRTUALIZER_PUMA_BLACK_START MessageBoxA(0, "Puma Black", "UnvirtualizeMe", 0); Sleep((*a + *b + *c) % 1000); MessageBoxA(0, "Complexity 9/10 Speed 1/10 Size 2000", "Puma Black", 0); return GetTickCount; VIRTUALIZER_PUMA_BLACK_END}void Func3(char* text, char* title, char* description){ VIRTUALIZER_SHARK_WHITE_START MessageBoxA(0, text, title, 0); MessageBoxA(0, description, text, 0); VIRTUALIZER_SHARK_WHITE_END}char* src = "abcdef";char* Func4(){ VIRTUALIZER_TIGER_RED_START MessageBoxA(0, "Tiger Red", "UnvirtualizeMe", 0); char* temp = new char[10]; strcpy(temp, src); MessageBoxA(0, "Complexity 1/10 Speed 9/10 Size 1150", "Tiger Red", 0); delete[] temp; return src; VIRTUALIZER_TIGER_RED_END}int main(){ int* x = new int(7); int* y = new int(8); int* z = new int(9); while (1){ Sleep(100); if (GetAsyncKeyState('1') & 1){ Func1(7, 8, 9); } if (GetAsyncKeyState('2') & 1){ Func2(x, y, z); } if (GetAsyncKeyState('3') & 1){ Func3("Shark White", "UnvirtualizeMe", "Complexity 8/10 Speed 3/10 Size 650"); } if (GetAsyncKeyState('4') & 1){ Func4(); } }} The zip contains 3 exes, "first.exe" with new section and no string virtualization, "second.exe" with "insert in last section" and string virtualization option, "projekt.exe" is clean file. I also attach pdb and map For some reason im unable to delete the old "projekt.zip" from attachments. Updated version is "Release" projekt.zip Release.rar Edited June 30, 2015 by Pancake
Teddy Rogers Posted June 28, 2015 Posted June 28, 2015 You can, just not files with .exe extension. You will have to archive them first... Ted.
GIV Posted June 30, 2015 Posted June 30, 2015 Was protected with a non legitimate copy of OV?My AV delete the file.....
Pancake Posted June 30, 2015 Author Posted June 30, 2015 Original licensed. I got the AV flags often too, im getin mad with that virtualizer
Pancake Posted June 30, 2015 Author Posted June 30, 2015 (edited) Virustotal sees it as clean, if you like i can upload something else. My general idea was to let people see how it works inside. Updated first post Edited June 30, 2015 by Pancake
Pancake Posted July 2, 2015 Author Posted July 2, 2015 I updated first post few days ago, i think it did not bump the topic so, the exes are updated if u want to research
koolk Posted July 15, 2015 Posted July 15, 2015 This is the first time I see compressed VMs (This option isn't available in the demo, so I didn't try it). It is just decompress them all when running the first vm, and then jumping to the vm with a table. I added to my script a basic support for them, mainly for parsing the table, but only when they are already uncompressed. So I just dumped it after running one vm. They made few changes in the last 2-3 version, especially in FISH. So I had to spend few hours to catch up with it (most of that time spent understanding what exactly my script do... haha). So anyway, after I made those stuffs, and fixed some bugs, this is the output of my script of the first.exe (dumped after decompressing vms). This is the usual usage of my script is something like that pe = pefile.PE(...); VMS["FISH"].fix_vms(pe); pe.write(...) but for this case I had to do it more manually, because my script excepts only one vm type, and doesn't support detecting compressed vms. So I just printed all the vms. (You can ignore the question marks, this is just internal syntax of my modified assembler) print VMS["FISH"].get_compressed_vm(exe, 0x100016C3).get_code() print VMS["PUMA"].get_compressed_vm(exe, 0x10001743).get_code() print VMS["SHARK"].get_compressed_vm(exe, 0x100017C3).get_code() print VMS["TIGER"].get_compressed_vm(exe, 0x10001816).get_code() => Parsing FISH32 VM at 0x1005ee3d [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x1001483c push 0x1001484c push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] add eax, dword [ebp+0xc] add eax, dword [ebp+0x10] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 push 0x10014858 push 0x10014864 push 0x0 call dword [0x1000f100] call dword [0x1000f000] jmp ?0x1000172f Parsing PUMA32 VM at 0x102ad763 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x1001488c push 0x1001489c push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] mov eax, dword [eax] mov ecx, dword [ebp+0xc] add eax, dword [ecx] mov edx, dword [ebp+0x10] add eax, dword [edx] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 push 0x100148a8 push 0x100148b4 push 0x0 call dword [0x1000f100] mov eax, dword [0x1000f000] jmp ?0x100017b4 Parsing SHARK32 VM at 0x1035d372 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 mov eax, dword [ebp+0xc] push eax mov ecx, dword [ebp+0x8] push ecx push 0x0 call dword [0x1000f100] push 0x0 mov edx, dword [ebp+0x8] push edx mov eax, dword [ebp+0x10] push eax push 0x0 call dword [0x1000f100] jmp ?0x100017f9 Parsing TIGER32 VM at 0x104a0de9 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x100148e4 push 0x100148f4 push 0x0 call dword [0x1000f100] push 0xa call ?0x1000258e add esp, 0x4 mov dword [ebp+0xffffffe8], eax mov eax, dword [ebp+0xffffffe8] mov dword [ebp+0xfffffff4], eax mov ecx, dword [0x100172e8] mov dword [ebp+0xfffffff0], ecx mov edx, dword [ebp+0xfffffff4] mov dword [ebp+0xfffffff8], edx mov eax, dword [ebp+0xfffffff8] mov dword [ebp+0xffffffe0], eax label_104a2164: mov ecx, dword [ebp+0xfffffff0] mov dl, byte [ecx] mov byte [ebp+0xffffffff], dl mov eax, dword [ebp+0xfffffff8] mov cl, byte [ebp+0xffffffff] mov byte [eax], cl mov edx, dword [ebp+0xfffffff0] add edx, 0x1 mov dword [ebp+0xfffffff0], edx mov eax, dword [ebp+0xfffffff8] add eax, 0x1 mov dword [ebp+0xfffffff8], eax cmp byte [ebp+0xffffffff], 0x0 jnz label_104a2164 push 0x0 push 0x10014900 push 0x1001490c push 0x0 call dword [0x1000f100] mov ecx, dword [ebp+0xfffffff4] mov dword [ebp+0xffffffec], ecx mov edx, dword [ebp+0xffffffec] push edx call ?0x10003033 add esp, 0x4 cmp dword [ebp+0xffffffec], 0x0 jnz label_104a261e mov dword [ebp+0xffffffe4], 0x0 jmp label_104a26a0 label_104a261e: mov dword [ebp+0xfffffff4], 0x8123 mov eax, dword [ebp+0xfffffff4] mov dword [ebp+0xffffffe4], eax label_104a26a0: mov eax, dword [0x100172e8] jmp ?0x100018e7 And for second.exe, the only special part is virtualized strings. So this is for example decompiled Func1 and Func2: push 0x0 call ?0x104c5102 call ?0x104c585e push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] add eax, dword [ebp+0xc] add eax, dword [ebp+0x10] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 call ?0x104c5fa3 call ?0x104c66c4 push 0x0 call dword [0x1000f100] call dword [0x1000f000] jmp ?0x1000172f push 0x0 call ?0x104c876b call ?0x104c8db8 push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] mov eax, dword [eax] mov ecx, dword [ebp+0xc] add eax, dword [ecx] mov edx, dword [ebp+0x10] add eax, dword [edx] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 call ?0x104c9444 call ?0x104c9a9f push 0x0 call dword [0x1000f100] mov eax, dword [0x1000f000] jmp ?0x100017b4 As you can see, there are call instead of push of strings. What this call does is pointing esi to some point, and than calling some virtualized function. Here is an example of such decompiled function: push ecx cmp dword [esi], 0x0 jnz label_104c52c8 mov ecx, 0x0 mov dword [esi+0x8], ecx mov ecx, 0xe push esi lea esi, [esi+0xc] label_104c524f: test ecx, ecx jz label_104c52ac xor byte [esi], 0x3a add byte [esi], 0xba inc esi dec ecx jmp label_104c524f label_104c52ac: pop esi mov dword [esi], 0x1 label_104c52c8: lea ecx, [esi+0xc] mov dword [esp+0xc], ecx mov dword [esi+0x4], ecx mov ecx, 0x0 test ecx, ecx jz label_104c53e7 lea ecx, [esi+0x4] mov dword [esp+0xc], ecx label_104c53e7: mov ecx, 0x0 test ecx, ecx jz label_104c5544 pop ecx pop esi popf pop eax ret label_104c5544: mov ecx, dword [esp+0x10] mov esi, dword [esp+0xc] mov dword [esp+0x10], esi mov dword [esp+0xc], ecx pop ecx pop esi popf ret This function is decoding ("decrypting") the string after the first run at esi. It is possible to detect and fix such things automatically, but probably pointless because this protection (together with compressed vm feature) won't be in any real product, since almost no one use codevirtualizer by itself (but themida/winlicense) Anyway, I saw that there are two new VMs (DOLPHIN and EAGLE), so I have more research to do Hopefully I will be able to decompile them soon. 5
Pancake Posted July 15, 2015 Author Posted July 15, 2015 (edited) Wow, can i somehow contact you? Im spending a lot of time recently to understand VM software, would you find some time for me? Edited July 15, 2015 by Pancake
r00t_H@ck3r Posted August 1, 2015 Posted August 1, 2015 (edited) This is the first time I see compressed VMs (This option isn't available in the demo, so I didn't try it). It is just decompress them all when running the first vm, and then jumping to the vm with a table. I added to my script a basic support for them, mainly for parsing the table, but only when they are already uncompressed. So I just dumped it after running one vm. They made few changes in the last 2-3 version, especially in FISH. So I had to spend few hours to catch up with it (most of that time spent understanding what exactly my script do... haha). So anyway, after I made those stuffs, and fixed some bugs, this is the output of my script of the first.exe (dumped after decompressing vms). This is the usual usage of my script is something like that pe = pefile.PE(...); VMS["FISH"].fix_vms(pe); pe.write(...) but for this case I had to do it more manually, because my script excepts only one vm type, and doesn't support detecting compressed vms. So I just printed all the vms. (You can ignore the question marks, this is just internal syntax of my modified assembler) print VMS["FISH"].get_compressed_vm(exe, 0x100016C3).get_code() print VMS["PUMA"].get_compressed_vm(exe, 0x10001743).get_code() print VMS["SHARK"].get_compressed_vm(exe, 0x100017C3).get_code() print VMS["TIGER"].get_compressed_vm(exe, 0x10001816).get_code() => Parsing FISH32 VM at 0x1005ee3d [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x1001483c push 0x1001484c push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] add eax, dword [ebp+0xc] add eax, dword [ebp+0x10] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 push 0x10014858 push 0x10014864 push 0x0 call dword [0x1000f100] call dword [0x1000f000] jmp ?0x1000172f Parsing PUMA32 VM at 0x102ad763 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x1001488c push 0x1001489c push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] mov eax, dword [eax] mov ecx, dword [ebp+0xc] add eax, dword [ecx] mov edx, dword [ebp+0x10] add eax, dword [edx] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 push 0x100148a8 push 0x100148b4 push 0x0 call dword [0x1000f100] mov eax, dword [0x1000f000] jmp ?0x100017b4 Parsing SHARK32 VM at 0x1035d372 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 mov eax, dword [ebp+0xc] push eax mov ecx, dword [ebp+0x8] push ecx push 0x0 call dword [0x1000f100] push 0x0 mov edx, dword [ebp+0x8] push edx mov eax, dword [ebp+0x10] push eax push 0x0 call dword [0x1000f100] jmp ?0x100017f9 Parsing TIGER32 VM at 0x104a0de9 [lines removed] Converting VMCode to Assembly... SUCCESS push 0x0 push 0x100148e4 push 0x100148f4 push 0x0 call dword [0x1000f100] push 0xa call ?0x1000258e add esp, 0x4 mov dword [ebp+0xffffffe8], eax mov eax, dword [ebp+0xffffffe8] mov dword [ebp+0xfffffff4], eax mov ecx, dword [0x100172e8] mov dword [ebp+0xfffffff0], ecx mov edx, dword [ebp+0xfffffff4] mov dword [ebp+0xfffffff8], edx mov eax, dword [ebp+0xfffffff8] mov dword [ebp+0xffffffe0], eax label_104a2164: mov ecx, dword [ebp+0xfffffff0] mov dl, byte [ecx] mov byte [ebp+0xffffffff], dl mov eax, dword [ebp+0xfffffff8] mov cl, byte [ebp+0xffffffff] mov byte [eax], cl mov edx, dword [ebp+0xfffffff0] add edx, 0x1 mov dword [ebp+0xfffffff0], edx mov eax, dword [ebp+0xfffffff8] add eax, 0x1 mov dword [ebp+0xfffffff8], eax cmp byte [ebp+0xffffffff], 0x0 jnz label_104a2164 push 0x0 push 0x10014900 push 0x1001490c push 0x0 call dword [0x1000f100] mov ecx, dword [ebp+0xfffffff4] mov dword [ebp+0xffffffec], ecx mov edx, dword [ebp+0xffffffec] push edx call ?0x10003033 add esp, 0x4 cmp dword [ebp+0xffffffec], 0x0 jnz label_104a261e mov dword [ebp+0xffffffe4], 0x0 jmp label_104a26a0 label_104a261e: mov dword [ebp+0xfffffff4], 0x8123 mov eax, dword [ebp+0xfffffff4] mov dword [ebp+0xffffffe4], eax label_104a26a0: mov eax, dword [0x100172e8] jmp ?0x100018e7 And for second.exe, the only special part is virtualized strings. So this is for example decompiled Func1 and Func2: push 0x0 call ?0x104c5102 call ?0x104c585e push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] add eax, dword [ebp+0xc] add eax, dword [ebp+0x10] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 call ?0x104c5fa3 call ?0x104c66c4 push 0x0 call dword [0x1000f100] call dword [0x1000f000] jmp ?0x1000172f push 0x0 call ?0x104c876b call ?0x104c8db8 push 0x0 call dword [0x1000f100] mov eax, dword [ebp+0x8] mov eax, dword [eax] mov ecx, dword [ebp+0xc] add eax, dword [ecx] mov edx, dword [ebp+0x10] add eax, dword [edx] cdq mov ecx, 0x3e8 idiv ecx push edx call dword [0x1000f004] push 0x0 call ?0x104c9444 call ?0x104c9a9f push 0x0 call dword [0x1000f100] mov eax, dword [0x1000f000] jmp ?0x100017b4 As you can see, there are call instead of push of strings. What this call does is pointing esi to some point, and than calling some virtualized function. Here is an example of such decompiled function: push ecx cmp dword [esi], 0x0 jnz label_104c52c8 mov ecx, 0x0 mov dword [esi+0x8], ecx mov ecx, 0xe push esi lea esi, [esi+0xc] label_104c524f: test ecx, ecx jz label_104c52ac xor byte [esi], 0x3a add byte [esi], 0xba inc esi dec ecx jmp label_104c524f label_104c52ac: pop esi mov dword [esi], 0x1 label_104c52c8: lea ecx, [esi+0xc] mov dword [esp+0xc], ecx mov dword [esi+0x4], ecx mov ecx, 0x0 test ecx, ecx jz label_104c53e7 lea ecx, [esi+0x4] mov dword [esp+0xc], ecx label_104c53e7: mov ecx, 0x0 test ecx, ecx jz label_104c5544 pop ecx pop esi popf pop eax ret label_104c5544: mov ecx, dword [esp+0x10] mov esi, dword [esp+0xc] mov dword [esp+0x10], esi mov dword [esp+0xc], ecx pop ecx pop esi popf ret This function is decoding ("decrypting") the string after the first run at esi. It is possible to detect and fix such things automatically, but probably pointless because this protection (together with compressed vm feature) won't be in any real product, since almost no one use codevirtualizer by itself (but themida/winlicense) Anyway, I saw that there are two new VMs (DOLPHIN and EAGLE), so I have more research to do Hopefully I will be able to decompile them soon. if you are who I think you are. then the mushroom still uses CISC unless they changed recently in 16X and not using vmprotect vm.. also quite nice you are able to decrypt them but are they able to parse them properly? vm are quite shitty and it really mess your system up. Edited August 1, 2015 by r00t_H@ck3r
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now