Code Virtualization en C

Iniciado por Karman, 14 Abril 2011, 19:59 PM

0 Miembros y 1 Visitante están viendo este tema.

Karman

buenas, tras tiempo depurando programas con códigos en parte virtualizados se me planteó la duda como hacer un sistema de virtualización de código, por eso armé "esto" que no está completo ni nada, pero que puede llegar a servir de ejemplo supongo  :P

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.

#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\
 ");
 printf("%d %d %d\n",a,b,c);
 return;
}

int main(void){
 cryptme();
 system("pause");
 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:

 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:

void cryptme(){
 int a,b,c;
 a=15,b=8;c=a-b;
 printf("%d %d %d\n",a,b,c);
 return;
}


Código (asm) [Seleccionar]
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 (asm) [Seleccionar]
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  :P

S2