Jump to content
Tuts 4 You

[DevirtualizeMe] Oreans Virtualizer


Pancake

Recommended Posts

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 by Pancake
Link to comment

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 by Pancake
Link to comment

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


Link to comment
  • 2 weeks later...

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.


 


  • Like 5
Link to comment

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 by Pancake
Link to comment
  • 3 weeks later...
r00t_H@ck3r

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 by r00t_H@ck3r
Link to comment

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