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... ), 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:
Citar
Offset(10) => &buffer[10]
*Offset(0) = 'M';*Offset(1) ='Z';
*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:
Citar
RVA(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:
Código:
Address(0x431212) = 325
Offset(325) = 0x431212
bueno, ahora si el código:
Código
///////////////////////////////////////////////////////////////////////////// // 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... ...
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ó...