THMag 04/Packeur et unpackeur : une autre vision
Un article de HackaWiki.
Packeur
Le programme principal se résume à :
BOOL CPackeurApp::InitInstance()
{
CWinApp::InitInstance();
C_Chapeau *pt_chapeau = new C_Chapeau;
pt_chapeau->Full_Encodage();
delete pt_chapeau;
return FALSE;
}
Si l’on regarde l’enchaînement de la fonction « Full_Encodage » on retrouve la description faite plus avant :
int C_Chapeau::Full_Encodage()
{
// Assignation et vérification des fichiers en entrée
int status = Assigned_File();
if (status == TRUE)
{
char *pt_out;
DWORD count_out;
pt_G3D = new G3D;
Ouvre_fichier_lecture(output_file_ended);
count_out = Compresse_Buffer((char *) m_pFileData, &pt_out);
// ".g3d"
Transforme(0,&pt_G3D->buffer_local[0]);
// "Inject_"
Transforme(1,&pt_G3D->buffer_local_1[0]);
// "%s%s%s"
Transforme(2,&pt_G3D->buffer_local_2[0]);
// "rb"
Transforme(3,&pt_G3D->buffer_local_3[0]);
// "wb+"
Transforme(4,&pt_G3D->buffer_local_4[0]);
pt_G3D->Work(input_file_to_encoded, pt_out, count_out);
delete pt_G3D;
}
}
Les chaînes de caractères n’apparaissent pas en clair dans l’exécutable, elles sont encodées. La fonction « Transforme » réalise le décodage juste avant leur utilisation. L’encodage est très simple et ne met en œuvre aucun algorithme particulier juste un masque (définie par un define) et un complément à FFh. L’initialisation d’une chaîne caractère se résume donc à :
[…]
// "wb+"
table_constante[4][0] = (char)~('w' + MY_KEY);
table_constante[4][1] = (char)~('b' + MY_KEY);
table_constante[4][2] = (char)~('+' + MY_KEY);
table_constante[4][3] = (char)~(MY_KEY);
[…]
void C_Chapeau::Transforme(int ligne, char *pt_out)
{
int local = 0;
if ( ligne > MAX_LIGNE) return;
while (table_constante[ligne][local] != ~(MY_KEY))
{
*pt_out = ~(table_constante[ligne][local]) - MY_KEY;
local++;
if ( local > MAX_COLONNE)
{
*pt_out=0x00;
return;
}
pt_out++;
}
*pt_out = 0x00;
}
fonction « Compresse_Buffer» réalise successivement la compression d’un buffer puis l’encodage de ce même buffer :
[…]
// Calcul de la future taille compressée
wts = pt_huff->Dictionary((BYTE*)pt_in,m_nDocLength,&dwbt[0],&dwc[0]);
DWORD dwLength = pt_huff->CountCompress((BYTE*)pt_in,m_nDocLength,wts,&dwc[0]);
// Allocation du buffer temporaire pour Huufamn
pt_inter = (char *)GlobalAlloc(GMEM_FIXED,dwLength);
// au boulot maintenant que tout est prèt
pt_huff->Compress((BYTE*)pt_in,m_nDocLength,wts,&dwbt[0],&dwc[0],(BYTE*)pt_inter);
// Allocation du buffer temporaire pour Base 64
DWORD dwRunLength = pt_base->EncodedRunLength((BYTE*)pt_inter,dwLength);
// Allocation du buffer temporaire
pt_out = (char *)GlobalAlloc(GMEM_FIXED,dwRunLength);
pt_base->RunLengthEncode((BYTE*)pt_inter,dwLength,(BYTE*)pt_out);
GlobalFree(pt_inter);
return(dwRunLength);
[…]
La classe principale de la classe d’insertion d’un buffer dans une section est réalisée par la fonction « WORK ». Le tout est toujours aussi simple quand on connait le structure des fichiers exécutables (format Pe pour Windows)
[…]
void GInsert::Work(CString exe_in, char *pt_in_crypted, unsigned int lg_ressource_crytree)
{
….
// on change le nom du fichier de sortie à partir du nom du fichier d'entré
exe_out = Transform(exe_in) ;
exe=fopen( exe_in ,&buffer_local_3[0]);
if(exe) // si pas d'erreur
{
//on lit les entetes
fread(&Dos_header,sizeof(IMAGE_DOS_HEADER),1,exe);
fseek(exe,Dos_header.e_lfanew,0);
fread(&Nt_header,sizeof(IMAGE_NT_HEADERS),1,exe);
……
//et les entêtes des sections
for(a = 0 ;a < ( nb_sections + 1) ; a++)
{ fwrite(&tab_sections[a],sizeof(IMAGE_SECTION_HEADER),1,result); }
// on doit faire du padding car la taille du
// headers est par exmple 1000h et on doit
// écrire de l'index courant à cette limite
long longueur = Nt_header.OptionalHeader.SizeOfHeaders - sizeof(IMAGE_SECTION_HEADER);
for(a = ftell(exe) ; a < longueur ; a++) { fwrite("\x00",1,1,result); }
…..
//on met ensuite les datas de notre section
fwrite((char *)pt_in_crypted,lg_ressource_crytree,1,result);
//padding sur notre section
longueur = tab_sections[nb_sections].SizeOfRawData;
for(a = lg_ressource_crytree ; a < longueur ; a++) { fwrite("\x00",1,1,result); }
fclose(result);
[…]
Unpackeur
Le programme principal se résume à :
BOOL CUnPackeurApp::InitInstance()
{
CWinApp:InitInstance();
//Une seule instance ?
if ( Seul() == TRUE)
{
// I'm alone
C_Chapeau *pt_chapeau = new C_Chapeau;
pt_chapeau->Full_Decodage("K:\\Application(s)\\wwwwww\\Inject_packeur_vide"); // Phase de test
//pt_chapeau->Full_Decodage(m_pszAppName); // Appel Réel
delete pt_chapeau;
}
return FALSE;
}
Tout comme dans la phase précédente on retrouve :
[…]
// Assignation et vérification des fichiers en entrée
//".exe"
Transforme(7,&buffer_local[0]);
File_in = File_in + &buffer_local[0];
if (Recherche(File_in) == TRUE)
{
// "".g3d""
input_file_to_encoded = File_in;
Transforme(0,&pt_load->buffer_local[0]);
status = pt_load->Open_File(input_file_to_encoded);
if ( status == TRUE)
{
count_out = Decompresse_Buffer((char *)pt_load->buffer_data, &pt_out_local);
GetTempFileName(0x00,0x00,0,&buffer_file[0]);
buffer_file[(strlen(buffer_file)-1) ]= 'e';
buffer_file[(strlen(buffer_file)-2) ]= 'x';
buffer_file[(strlen(buffer_file)-3) ]= 'e';
GetCurrentDirectory(MAX_PATH,&Dir[0]);
Transforme(8,&buffer_texte[0]);
sprintf(&buffer_final[0],&buffer_texte[0],&Dir[0],&buffer_file[1]);
Ouvre_fichier_ecriture(&buffer_final[0],pt_out_local, count_out);
Lancement_avec_wait(&buffer_file[1]);
DeleteFile(&buffer_final[1]);
free(pt_load->buffer_data);
}
delete pt_load;
m_pFileData = m_hFile = m_hFileMapping = m_hFileMapping = NULL;
[…]
Le fichier extrait de la section est ensuite écris sur le disque puis lancé. Après son exécution, on le détruit sur le disque du, pour ne laisser aucune trace. La fonction de décompression est symétrique par rapport à celle de compression.
[…]
DWORD C_Chapeau::Decode()
{
// Calcul de la taille décompressée
DWORD dwCount = pt_base->DecodedRunLength((BYTE*)pt_in);
// Allocation du buffer temporaire pour Base 64
pt_inter = (char *)GlobalAlloc(GMEM_FIXED,dwCount);
// Au boulot maintenant que tout est prêt
pt_base->RunLengthDecode((BYTE*)pt_in,(BYTE*)pt_inter);
// Calcul de la taille décompréssé pour Base 64
DWORD dwUnCount = pt_huff->GetSize((BYTE*)pt_inter);
// Allocation du buffer temporaire pour Huffman
pt_out = (char *)GlobalAlloc(GMEM_FIXED,dwUnCount);
// au boulot maintenant que tout est prêt
DWORD dwLength = pt_huff->UnCompress((BYTE*)pt_inter,(BYTE*)pt_out);
// Libération du buffer temporaire
GlobalFree(pt_inter);
return (dwLength);
}
[…]
Le lancement d’un sous process est des plus courant avec une fonction prévu à cet effet. Le process appelant est en attente de fin d’exécution du process lancé.
[…]
Lancement_avec_wait(CString Fichier)
{
// Mise forme des différentes variables nécessaires
sprintf(nom_fichier_commande,"%s",Fichier);
GetCurrentDirectory(MAX_PATH,&dir[0]);
memset(&suInfo, 0, sizeof(suInfo));
suInfo.cb = sizeof(suInfo);
// Lancement du sous process
bWorked = ::CreateProcess(NULL, nom_fichier_commande, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS, NULL, dir, &suInfo, &procInfo);
if (bWorked == FALSE)
{
dwResult = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, LANG_NEUTRAL, (LPTSTR) &pstrError, 0,
NULL);
Transforme(9,&buffer_texte[0]);
if (dwResult == 0) pstrError = &buffer_texte[0];
Transforme(10,&buffer_texte_1[0]);
str.Format(&buffer_texte_1[0], pstrError, dwError);
if (dwResult != 0) ::LocalFree(pstrError);
AfxMessageBox(str);
// Erreur lors du lancement
return (FALSE);
}
else
{ // on attends
DWORD dwReturn = ::WaitForSingleObject(procInfo.hProcess, INFINITE);
}
// tout c'est bien passé
return (TRUE);
}
[…]
Commentaire
Je tiens à ajouter un commentaire à cet article. En tant que reverser amateur, je peux affirmer que ce type de packeur (ou protector) est totalement inefficace. En effet, il se contente d'unpacker l'exécutable sur disque, il suffit alors de modifier le loader pour qu'il n'efface pas l'exécutable créé, et ensuite de récupérer cet exécutable. Généralement les packers font toutes leurs manipulations en mémoire, ce qui leur permet d'une part d'aller plus vite (car temps d'accès bcp plus court), et d'autre part de compliquer la tâche du reverser, en utilisant des espaces mémoire hors sections par exemple, ou encore des détournements divers.
Au fait, cet article a mystérieusement disparu du wiki, après publication du mag 04... --195.221.243.132 8 jun 2006 à 23:48 (CEST)
