el conjunto de instrucciones no está totalmente definido, solo definí un par de opcodes para hacer pruebas (faltan muchas instrucciones), además no está implementado la parte de modificación de flags del proceso.
Código
#define _WIN32_WINNT 0x0500 #include <windows.h> #include <stdio.h> #include <stdlib.h> /* REGISTERS ========= 0 eax 2 ebx E ecx 4 edx C ebp 6 esp A edi 8 esi OPCODES ======= 00 YYYYYYYY -> jmp long 01 YYYY -> jmp short 02 YY -> jmp word 03 YYYY -> je short 04 YY -> je word 05 YYYY -> jne short 06 YY -> jne word //MOV REG, 00000 14 YYYYYYYY -> mov eax, long 15 YYYYYYYY -> mov ecx, long 16 YYYYYYYY -> mov edx, long 17 YYYYYYYY -> mov ebx, long 18 YYYYYYYY -> mov esp, long 19 YYYYYYYY -> mov ebp, long 1A YYYYYYYY -> mov esi, long 1B YYYYYYYY -> mov edi, long //MOV [REG-X], XXXXXXXX 1C XX YYYYYYYY -> mov (R) , long -> X reg X offset //MOV REG, [REG-X] 1E XX -> mov eax, (R) -> X reg X reg 1F XX -> mov ebx, (R) 20 XX -> mov ecx, (R) 21 XX -> mov edx, (R) 22 XX -> mov ebp, (R) 23 XX -> mov esp, (R) 24 XX -> mov edi, (R) 25 XX -> mov esi, (R) //MOV REG, REG 26 XX -> mov R, R //MOV [REG-X], REG 28 XX -> mov (R), eax -> X reg X offset 29 XX -> mov (R), ebx 2A XX -> mov (R), ecx 2B XX -> mov (R), edx 2C XX -> mov (R), ebp 2D XX -> mov (R), esp 2E XX -> mov (R), edi 2F XX -> mov (R), esi //SUB EDX, EAX 32 XX -> sub R, R -> X reg X reg //LEA EAX, [EBP-8] 3C XX -> lea eax, (R) -> X reg X reg 3D XX -> lea ebx, (R) 3E XX -> lea ecx, (R) 3F XX -> lea edx, (R) 40 XX -> lea ebp, (R) 41 XX -> lea esp, (R) 42 XX -> lea edi, (R) 43 XX -> lea esi, (R) */ typedef struct _REGISTERS{ ULONG Edi;//00 ULONG Esi;//04 ULONG Ebp;//08 ULONG Esp;//0C ULONG Ebx;//10 ULONG Edx;//14 ULONG Ecx;//18 ULONG Eax;//1C ULONG Flg;//20 ULONG Eip;//24 }REGISTERS, *PREGISTERS; PULONG GetReg(PREGISTERS p,WORD reg){ PULONG ret=NULL; switch(reg){ case 0x0:// eax ret=&p->Eax; break; case 0x2:// ebx ret=&p->Ebx; break; case 0xE:// ecx ret=&p->Ecx; break; case 0x4:// edx ret=&p->Edx; break; case 0xC:// ebp ret=&p->Ebp; break; case 0x6:// esp ret=&p->Esp; break; case 0xA:// edi ret=&p->Edi; break; case 0x8:// esi ret=&p->Esi; break; } return ret; } void decrypt(REGISTERS p){ PBYTE b=(PBYTE)p.Eip; BYTE Reg=0,RegEx=0; PULONG pReg=0,pRegEx=0; CHAR Offset=0; //FIX ME! b+=2;//lo ideal sería un long jmp, pero gcc no deja setearlo (o no se como) //Code Start! while(*b!=0xF0){ switch(*b){ case 0x1C://MOV [REG-X], XXXXXXXX Reg=*(++b); Offset=Reg&0xF; Reg>>=4; if(Reg&0x1) Offset=-Offset; Reg&=0xE; pReg=GetReg(&p,Reg); *((PDWORD)(*pReg+Offset))=*(DWORD*)(++b); b+=4; break; case 0x21://MOV EDX, [REG-X] Reg=*(++b); Offset=Reg&0xF; Reg>>=4; if(Reg&0x1) Offset=-Offset; Reg&=0xE; pReg=GetReg(&p,Reg); p.Edx=*((PDWORD)(*pReg+Offset)); ++b; break; case 0x1E://MOV EAX, [REG-X] Reg=*(++b); Offset=Reg&0xF; Reg>>=4; if(Reg&0x1) Offset=-Offset; Reg&=0xE; pReg=GetReg(&p,Reg); p.Eax=*((PDWORD)(*pReg+Offset)); ++b; break; case 0x32://SUB REG,REG Reg=*(++b); RegEx=Reg&0xF; Reg>>=4; pReg=GetReg(&p,Reg); pRegEx=GetReg(&p,RegEx); p.Eax=*pReg-*pRegEx; ++b; break; case 0x28://MOV [REG-X], EAX Reg=*(++b); Offset=Reg&0xF; Reg>>=4; if(Reg&0x1) Offset=-Offset; Reg&=0xE; pReg=GetReg(&p,Reg); *((PDWORD)(*pReg+Offset))=p.Eax; ++b; break; default: //FIX ME! b++;//Opcode no válido? break; } } p.Eip=(ULONG)++b; } void __stdcall vmachine(void); asm( ".globl _vmachine\r\n" "_vmachine:\r\n"//function header... " SUB $0x24,%esp;\r\n" " PUSH %edi;\r\n" " POP (%esp);\r\n" " PUSH %esi;\r\n" " POP 0x4(%esp);\r\n" " PUSH %ebp;\r\n" " POP 0x8(%esp);\r\n" " PUSH %ebx;\r\n" " POP 0x10(%esp);\r\n" " PUSH %edx;\r\n" " POP 0x14(%esp);\r\n" " PUSH %ecx;\r\n" " POP 0x18(%esp);\r\n" " PUSH %eax;\r\n" " POP 0x1C(%esp);\r\n" " PUSHF ;\r\n" " POP 0x20(%esp);\r\n" " LEA 0x28(%esp),%eax;\r\n" " MOV %eax,0x0C(%esp);\r\n" " CALL _decrypt;\r\n" " POPA ;\r\n" " POPF ;\r\n" " RET" ); void cryptme(){ int a,b,c; asm( " call jmp1;\n\t" "jmp1:jmp _vmachine;\n\t" ); /*a=15,b=8;c=a-b;*/ asm(".byte\t\ 0x1C,0xD4,0x0F,0x00,0x00,0x00,\ 0x1C,0xD8,0x08,0x00,0x00,0x00,\ 0x21,0xD4,\ 0x1E,0xD8,\ 0x32,0x40,\ 0x28,0xDC,\ 0xF0\ "); return; } int main(void){ cryptme(); return 0; }
el código fue compilado sobre GCC sin ningún tipo de optimización (si se optimiza puede no funcionar correctamente porque el compilador elimina las variables sin referencias directas u otros tipos de optimizaciones)
el código:
Código
asm( " call jmp1;\n\t" "jmp1:jmp _vmachine;\n\t" );
se lo podría meter en una macro pero como era solo para probar lo posteo así (de paso queda a la vista para el que le interese portarlo a otro compilador).
el resultado es el siguiente, el código sin virtualizar en un debugger como olly sale:
Código
void cryptme(){ int a,b,c; a=15,b=8;c=a-b; return; }
Código
004015F2 /$ 55 PUSH EBP 004015F3 |. 89E5 MOV EBP, ESP 004015F5 |. 83EC 28 SUB ESP, 28 004015F8 |. C745 FC 0F000>MOV DWORD PTR SS:[EBP-4], 0F ; | 004015FF |. C745 F8 08000>MOV DWORD PTR SS:[EBP-8], 8 ; | 00401606 |. 8B55 F8 MOV EDX, DWORD PTR SS:[EBP-8] ; | 00401609 |. 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4] ; | 0040160C |. 29D0 SUB EAX, EDX ; | 0040160E |. 8945 F4 MOV DWORD PTR SS:[EBP-C], EAX ; | 00401611 |. 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C] ; | 00401614 |. 894424 0C MOV DWORD PTR SS:[ESP+C], EAX ; | 00401618 |. 8B45 F8 MOV EAX, DWORD PTR SS:[EBP-8] ; | 0040161B |. 894424 08 MOV DWORD PTR SS:[ESP+8], EAX ; | 0040161F |. 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4] ; | 00401622 |. 894424 04 MOV DWORD PTR SS:[ESP+4], EAX ; | 00401626 |. C70424 983040>MOV DWORD PTR SS:[ESP], CodeVirt.004>; |ASCII "%d %d %d\n" 0040162D |. E8 56080000 CALL <JMP.&msvcrt.printf> ; \printf 00401632 |. C9 LEAVE 00401633 \. C3 RETN
sin embargo, el código virtualizado se ve:
Código
004015F2 |$ 55 PUSH EBP 004015F3 |. 89E5 MOV EBP, ESP 004015F5 |. 83EC 28 SUB ESP, 28 004015F8 |. E8 00000000 CALL CodeVirt.004015FD 004015FD \$^ EB B9 JMP SHORT CodeVirt.004015B8 004015FF 1C DB 1C 00401600 D4 DB D4 00401601 0F DB 0F 00401602 00 DB 00 00401603 00 DB 00 00401604 00 DB 00 00401605 1C DB 1C 00401606 D8 DB D8 00401607 08 DB 08 00401608 00 DB 00 00401609 00 DB 00 0040160A 00 DB 00 0040160B 21 DB 21 ; CHAR '!' 0040160C D4 DB D4 0040160D 1E DB 1E 0040160E D8 DB D8 0040160F 32 DB 32 ; CHAR '2' 00401610 40 DB 40 ; CHAR '@' 00401611 28 DB 28 ; CHAR '(' 00401612 . DCF0 FDIVR ST, ST ; | 00401614 . 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C] ; | 00401617 . 894424 0C MOV DWORD PTR SS:[ESP+C], EAX ; | 0040161B . 8B45 F8 MOV EAX, DWORD PTR SS:[EBP-8] ; | 0040161E . 894424 08 MOV DWORD PTR SS:[ESP+8], EAX ; | 00401622 . 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4] ; | 00401625 . 894424 04 MOV DWORD PTR SS:[ESP+4], EAX ; | 00401629 . C70424 983040>MOV DWORD PTR SS:[ESP], CodeVirt.004>; |ASCII "%d %d %d\n" 00401630 . E8 53080000 CALL <JMP.&msvcrt.printf> ; \printf 00401635 . C9 LEAVE 00401636 . C3 RETN
con lo cual se ve claramente la diferencia...
lo que se podría agregar al código es (como ya dije) un conjunto más variado de instrucciones y luego un "virtualizador" que convierta las instrucciones normales a las virtualizadas, pero, ese ya es un arduo trabajo y si a alguien le interesa tomarlo como proyecto podría ayudar
S2