[Source] Crypter Simple Vr 2(?) FIX 1

Iniciado por Karman, 18 Julio 2009, 04:09 AM

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

Karman

Bueno, los que me hayan leído mis últimos post verán que arme una clase para desarmar la clásica estructura PE... el tema es que hacía un tiempo había visto el código de un crypter simple en este foro (para ser más exacto http://foro.elhacker.net/programacion_cc/source_crypter_simple-t204227.0.html;msg969905#msg969905) el cual no funcionaba muy bien (con algunos ejecutables el resultado era desastroso) porque no tenía en cuenta un par de cosas que he agregado a este nuevo crypter...

Antes que nada paso a explicar un par de cosas para que a los que le interese el código puedan seguirlo...

1º y fundamental... los tipos de direccionamiento (la parte más complicada...  :P), bueno... en esta clase que cree hay 3 tipos básicos de direccionamiento:
A:Por Offset: la clase lo que hace es subir a memoria el archivo, entonces este tipo de direccionamiento nos lleva directamente a una posición de memoria del archivo, osea suponiendo que nuestra variable que contiene el contenido del archivo se llame buffer:

CitarOffset(10) => &buffer[10]
*Offset(0) = 'M';*Offset(1) ='Z';

B: Por RVA: la clase es capaz de resolver direcciones virtuales, en este caso al setear un valor, se calculará el offset de acuerdo a este valor y obtendremos la posición de memoria del archivo... por ejemplo:

si una sección empieza físicamente en 400, pero tiene una dirección virtual de 1000, al hacer:

CitarRVA(1024) = &buffer[424]

sin importar en cual sección se encuentre... (el algoritmo se encarga de eso)
C: por dirección referenciada: esta última opción (que agregué en esta versión) permite obtener el offset real de una dirección (la inversa de Offset), por ejemplo si tenemos una dirección 0x431212 y queremos saber en que punto del archivo se encuentra:

Address(0x431212) = 325
Offset(325) = 0x431212


bueno, ahora si el código:

Código (cpp) [Seleccionar]
/////////////////////////////////////////////////////////////////////////////
// Name:        PE simple crypter (FIX 1)
// Purpose:     Encriptador simple de Ficheros PE
// Author:      Karman
// Created:     2009-10-7
// Copyright:   (c) Exinferis Inc
// Web:         http://www.inexinferis.com.ar
// Vr. : 0.1.1
/////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include "ExecAnalizer.h"

CCHAR DataDirectories[][24]={
 "Export Table",
 "Import Table",
 "Resource Table",
 "Exception Table",
 "Certificate Table",
 "Relocation Table",
 "Debug Table",
 "Architecture Table",
 "Machine Table",
 "Thread Local Storage",
 "Load Config Table",
 "Bound Import Table",
 "Import Address Table",
 "Delay Import Table",
 "COM+ Runtime Header"
};

BYTE ourcode[]={
 //push old entrypoint...
 0x68,0x00,0x00,0x00,0x00,
 //push size of code
 0x68,0x00,0x00,0x00,0x00,
 //push address of code
 0x68,0x00,0x00,0x00,0x00,
 //call decrypt
 0xE8,0x00,0x00,0x00,0x00,
 //Ret
 0xc3
};

DWORD WINAPI GetFunctionSize(PBYTE dwStart);
VOID  WINAPI descifrar(PBYTE address,INT size, DWORD oep);
VOID  WINAPI cifrar(PBYTE address,INT size);

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPInst,LPSTR lpCmd,int nShow){
 DWORD nImageBase,nEntryPoint;
 PIMAGE_NT_HEADERS pINH;
 PIMAGE_SECTION_HEADER pISHCode;
 PCHAR forig="C:\\WINDOWS\\regedit.exe";
 PCHAR fcrytp="C:\\WINDOWS\\cifrado.exe";
 printf("Abriendo -> %s\n",forig);
 ExecAnalizer crypt(forig);
 //nos aseguramos que sea PE...
 if(crypt.GetDOSHeader()&&crypt.HasNewHeader()&&crypt.IsPE()){
   printf("\tFile Is -> PE\n");
   pINH = crypt.GetNTHeader();
   //datos básicos del ejecutable
   nEntryPoint=pINH->OptionalHeader.AddressOfEntryPoint;
   nImageBase=pINH->OptionalHeader.ImageBase;
   printf("\tEntryPoint -> %X\n",nEntryPoint);
   printf("\tImageBase -> %X\n",nImageBase);
   puts("Buscando Code Section:");
   //sección del código
   pISHCode=crypt.RVA2Section(nEntryPoint);
   if(pISHCode){
     printf("\tCode Section Found At -> %X\n",crypt.Address2Offset((PBYTE)pISHCode));
     DWORD codeStart=pISHCode->VirtualAddress;
     DWORD codeEnd=pISHCode->VirtualAddress+pISHCode->SizeOfRawData;
     printf("\tCode Section Start At -> %X\n",codeStart);
     printf("\tCode Section End At -> %X\n",codeEnd);
     //buscamos si alguna otra cosa se encuentra tb en el area de código
     puts("Buscando Tablas en Code Section (Calculando tamaño real del código)");
     for(int i=0;i<15;i++){
       if(pISHCode==crypt.RVA2Section(pINH->OptionalHeader.DataDirectory[i].VirtualAddress)){
         printf("\t%s is in Code Section At %X - %X\n",DataDirectories[i],
           pINH->OptionalHeader.DataDirectory[i].VirtualAddress,
           pINH->OptionalHeader.DataDirectory[i].Size
         );
         DWORD tAdress=pINH->OptionalHeader.DataDirectory[i].VirtualAddress;
         DWORD tSize=(pINH->OptionalHeader.DataDirectory[i].VirtualAddress+pINH->OptionalHeader.DataDirectory[i].Size);
         if(nEntryPoint<tAdress){
           if(codeEnd>tAdress)
             codeEnd=tAdress;
         }else{
           if(codeStart<tSize)
             codeStart=tSize;
         }
       }
     }
     puts("Tamaño real del código:");
     printf("\tCode Section Start At -> %X\n",codeStart);
     printf("\tCode Section End At -> %X\n",codeEnd);
     // Valores reales (en el archivo)
     DWORD rCodeStart=(DWORD)crypt.RVA2Offset(codeStart);
     DWORD rCodeEnd=(DWORD)crypt.RVA2Offset(codeEnd);
     //buscamos espacio libre dentro de la sección code para copiarnos...
     puts("Buscando Espacio para nuestro Código:");
     //tamaño de nuestro código...
     DWORD decrypcodefuncsize=GetFunctionSize((PBYTE)descifrar);
     DWORD decrypcodesize=decrypcodefuncsize+0x20;
     //Espacios libres???
     DWORD dwBlanks=0,dwAddress=(pISHCode->PointerToRawData+pISHCode->SizeOfRawData);
     for(;((dwAddress>rCodeEnd)&&(dwBlanks<decrypcodesize));dwAddress--)
       if(!*crypt.OffsetValue(dwAddress))dwBlanks++;else dwBlanks=0;
     //tiene espacio???
     printf("\tEspacio Necesario: %X - Espacio encontrado: %X\n",decrypcodesize,dwBlanks);
     if(decrypcodesize>=dwBlanks){
       printf("\tFree Space At -> %X\n",dwAddress);
       //ciframos...
       cifrar((PBYTE)crypt.RVAValue(codeStart),codeEnd-codeStart);
       //copiamos la función que descifra
       memcpy((PVOID)crypt.OffsetValue(dwAddress),(PVOID)descifrar,decrypcodefuncsize);
       //armamos cabecera...
       *(DWORD *)&ourcode[0x01]=nEntryPoint+nImageBase;
       *(DWORD *)&ourcode[0x06]=codeEnd-codeStart;
       *(DWORD *)&ourcode[0x0B]=codeStart+nImageBase;
       *(DWORD *)&ourcode[0x10]=-(decrypcodefuncsize+20);
       dwAddress+=decrypcodefuncsize;
       memcpy((PVOID)crypt.OffsetValue(dwAddress),(PVOID)ourcode,24);
       //calculamos nuevo entrypoint
       pINH->OptionalHeader.AddressOfEntryPoint=crypt.Offset2RVA(dwAddress);
       // Damos permiso de escritura a la sección. Si no hacemos esto nos dara error.
       pISHCode->Characteristics |= IMAGE_SCN_MEM_WRITE;
       // Corregimos tamaño de la sección virtual si insuficiente
       if(pISHCode->Misc.VirtualSize<(crypt.Offset2RVA(dwAddress)-pISHCode->VirtualAddress+24))
         pISHCode->Misc.VirtualSize=(crypt.Offset2RVA(dwAddress)-pISHCode->VirtualAddress+24);
       crypt.Save(fcrytp);
     }else
       puts("\tSin espacio Suficiente... :(");
   }
   
   system("pause");
 }
}

DWORD WINAPI GetFunctionSize(PBYTE dwStart){
 PBYTE dwEnd=dwStart;
while(*dwEnd!=0xC3&&*dwEnd!=0xC2)dwEnd++;
if(*dwEnd==0xC2)return (dwEnd-dwStart+3);
return (dwEnd-dwStart+1);
}

VOID  WINAPI descifrar(PBYTE address,INT size, DWORD oep){
 while(size>0){
   *(address++)^=0x65;
   size--;
 }
 //set ret to old entry point
 asm("mov %0 , 0x04(%%esp)"::"r"(oep));
}

VOID  WINAPI cifrar(PBYTE address,INT size){
 while(size>0){
   *(address++)^=0x65;
   size--;
 }
}


Detalles a destacar a diferencia de la primera versión del cripter:
1º Checkeo "de qué se cifra": el código no cifra toda la sección CODE, ya que muchos compiladores tienen la opción de combinar dentro de la sección CODE otras estructuras (IMPORT por ejemplo), y al cifrar esta estructura el programa ya no es válido (el cifrado de la IAT va más allá de este simple código, además de que desconozco como realmente se hace)...

2º Corrección de secciones: nuevamente el código anterior agregaba su código dentro de la sección CODE, pero no verificaba que esta modificación esté dentro de los valores definidos en la sección...

3º El código para descifrar mantiene cierta "lógica" secuencial, (aunque no logré hacer funcionar el código original) cuando lo intentaba debuggear los debuggers no lo entendían...

bue... eso es todo...  :P ...

S2

PD: me olvidaba de poner la dir del code:

Crypter Simple

PD2: Desconozco si el código tenga algún problema... con los ejecutables que probé funcionó...  :P

Maurice_Lupin

el link de descarga no funciona si puedes ponerlo en mediafire, gracias.
Me parece un buen tema para no tener ni un comentario.

Saludos
Un error se comete al equivocarse.