_  _  _  _  _  _  _  _  _  _

                       / \/ \/ \/ \/ \/ \/ \/ \/ \/ \

                       \ AGGIUNGERE UNA DLL A UN PE /

                       /                            \

                       \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/



                             - IL PREAMBOLO -

Autore:    NaGA

Tools:     Hiew, il vostro cervello (sono quasi obbligatori)

Optionals: Un compilatore C (se volete automatizzare la procedura)



                     

                                - L'INTRO -

In principio era il caos, poi arrivarono i cracker, e le cose peggiorarono...





                              - IL PROBLEMA -

Se fate cracking o reversing (perche' i programmi, cosi' come sono, proprio

non vi piacciono) o se siete dei virus coders (perche' siete bastardi dentro)

vi sarete trovati nella situazione di dover chiamare delle API che non erano

state previste al momento dell'assemblaggio del codice che state modificando.

A questo punto i casi sono due:

1)Avete culo e la funzione che vi serve fa cmq parte di un modulo gia' linkato

  a run time (es. kernel32).

2)Siete un po' sfortunati e la funzione che vi serve si trova in una DLL che

  il programma da modificare non usa affatto.



Se ricadete nella prima casistica e' tutto molto semplice: basta dare uno

sguardo al segmento di import o usare l'API GetProcAddress (se non sbaglio

viene mappata sempre, con kernel32) per ottenere l'indirizzo che vi serve.

N.B. Se non avete capito niente di quello che c'e' scritto, fatevi un giro fra

gli altri tutorial sui PE, per chiarire ogni vostro dubbio.



Se invece ricadete nella seconda casistica (quella degli sfigati) le cose si

complicano un po'. L'header del segmento di Import non si puo', infatti,

modificare.

O almeno e' quello che ci vuole far credere il caro zio Bill...





                 

                               - LA SOLUZIONE -

La soluzione e' quanto mai banale, e il fatto di non aver trovato nessun

tutorial in proposito, probabilmente e' dovuto alla mia incapacita' nell'usare

i motori di ricerca.



Il suddetto header e' indirizzato da una dword che si trova all'offset 0x80

dall'inizio dell'header PE (che dovrebbe corrispondere allo 0x100 dall'inizio

del file). Quello che dovremo fare e' spostare l'header da un altra parte,

aggiungere una entry relativa alla DLL che ci serve, e modificare la dword

a 0x100 per farla puntare al nostro nuovo header. A questo punto potremo

inserire delle nuove entry per le funzioni che ci servono (magari proprio al

posto del vecchio header) o usare GetProcAddress per avere tutti gli indirizzi

utili.



E' probabile che a questo punto sarete molto confusi, quindi rivediamo tutto

passo passo.



-In Teoria.

L'header del segmento di import e' formato da una serie di entries e termina

con una entry vuota.

Ogni Entry ha la seguente struttura:

   ________________________________

  |                                |

  | DD - Name LookUpTable    (+0)  |

  | DD - UNUSED              (+4)  |

  | DD - UNUSED              (+8)  |

  | DD - Import Name         (+12) |

  | DD - Address LookUpTable (+16) |

  |________________________________|



La dword a +12 punta ad una stringa (NULL terminated) che rappresenta il nome

               della DLL da importare.

La dword a +0  punta alla tabella dei nomi.

La dword a +16 punta alla tabella degli indirizzi.



La tabella dei nomi altro non e' che una serie di indirizzi a stringhe che

rappresentano le funzioni della DLL che ci interessa usare.

Ogni stringa comincia con una word (usata per distinguere fra selezione

per nome o per numero, ma questo e' un'altra storia) e termina con un NULL.

La tabella finisce con un indirizzo nullo.



La tabella degli indirizzi verra' riempita al momento dell'esecuzione con gli

offset delle corrispondenti funzioni richiamate.





Es: Prendiamo HyperTerminal (dovreste avercelo tutti). Apritelo con Hiew. 

    Premete F8 (per avere l'header) e poi F7 (per avere le informazioni di

    import/export). Selezionate Import. Dovreste vedere quanto segue...



    HYPERTRM.EXE  .R     PE.00025000     --------     6144 ª Hiew 6.04 (c)SEN

.00025000:  7C 50 00 00-80 4F 50 2A-FF FF FF FF-66 51 00 00  |P  ╟OP*    fQ

.00025010:  C0 50 00 00-64 50 00 00-9E 4F 50 2A-FF FF FF FF  +P  dP  ╫OP*    

.00025020:  A0 51 00 00-A8 50 00 00-70 50 00 00-A0 1C 0C 32  ßQ  ┐P  pP  ß..2

.00025030:  FF FF FF FF-D4 51 00 00-B4 50 00 00-00 00 00 00      ╚Q  ªP

.00025040:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00



    Questo e' l'header. La prima dword puntera' alla Tabella dei 

    nomi. Riordinando i byte e aggiungendo ImageBase (in questo caso 

    0x00020000) avremo il suo indirizzo: 0x2507C

    Se andiamo a questo indirizzo potremo vedere le seguenti righe...



.00025070:  C2 51 00 00-AE 51 00 00-00 00 00 00-52 51 00 00  -Q  ½Q      RQ

.00025080:  44 51 00 00-24 51 00 00-34 51 00 00-0A 51 00 00  DQ  $Q  4Q  
Q

.00025090:  02 51 00 00-F4 50 00 00-18 51 00 00-74 51 00 00  .Q  ╢P  .Q  tQ



    Questo e' l'inizio della tabella dei nomi. Il primo elemento ad esempio 

    (a 0x2507C) punta a 0x25152. Andando a questo indirizzo troveremo...



.00025150:  00 00 25 02-5F 65 78 63-65 70 74 5F-68 61 6E 64    %._except_hand

.00025160:  6C 65 72 33-00 00 4D 53-56 43 52 54-32 30 2E 64  ler3  MSVCRT20.d



    Il nome della funzione!



    Se riguardate l'header dovreste anche vedere dove inizia la tabella degli 

    indirizzi (0x250C0). A RunTime l'indirizzo di _except_handler3 vi verra'

    caricato proprio a 0x250C0. Cioe' l'indirizzo della prima funzione nel 

    primo elemento, quello della seconda nel secondo elemento e cosi' via.



N.B. Tutti gli indirizzi sono RVA e ad essi va sommato ImageBase.

Questo porta a qualche calcolo in piu', ma ci garantisce una perfetta 

rilocabilita' dell'header.



-In Pratica

Facciamo finta di voler usare ShellExecuteA in un prog che non linka shell32

(es. HyperTerminal)



1) Leggete la dword a 0x100. Questo e' l'inizio dell'header di import.

Portatevi a tale indirizzo: 0x25000 (ricordatevi di sommare ImageBase). 

Cominciate a copiare tutto quello che c'e', a blocchi di 20 byte, 

in una zona vuota del file (o, se vi piace di piu', in un nuovo segmento

che vi sarete creati). Quando incontrate una entry che ha l'ImportName 

nullo fermatevi, perche' l'header e' finito (in questo caso ci sono solo 3 

entries).



In C sarebbe:

---------------------------------------------------------------------------



//Trasforma l'RVA in indirizzo fisico

DWORD Where=ImportOffS-ImRVA+ImAdd; 

fseek (ffileS,Where, SEEK_SET);

fseek (ffileD,BlankSpace, SEEK_SET); 

for(;;)

{

	fread(buff,1,20,ffileS);

	// Se trova una entry nulla finisce

	if ( *((DWORD *)&buff[12])==0) break;

	fwrite(buff,1,20,ffileD);		

}



dove

ImportOffS e' l'indirizzo a 0x100.



ImRVA e' l'RVA del segmento di import (lo trovate nell'header dei segmenti,

        o con lo HIEW premendo F8 e poi F6); in questo caso vale 0x5000



ImAdd e' l'indirizzo fisico del segmento di import; in questo caso vale 0xA00.



BlankSpace e' una zona vuota del file.



ffileS e ffileD rappresentano il file originale e il file modificato.

----------------------------------------------------------------------------









2) Rimpiazzate l'header con le informazioni che vi servono.

Es:

----------------------------------------------------------------------------

   DWORD Where=ImportOffS-ImRVA+ImAdd;                 // Sovrascriviamo 

   fseek (ffileD,Where, SEEK_SET); 		       // il vecchio header



   fprintf(ffileD,"SHELL32.DLL"); fputc(0x00,ffileD);  // Nome del modulo 

                                                       // da caricare

   fputc(0x72,ffileD);                                 // Id per la funzione

   fputc(0x00,ffileD);                                 //  "  "   "  "

   fprintf(ffileD,"ShellExecuteA");fputc(0x00,ffileD); // Nome della funzione



   DWORD Stuff=ImportOffS+12;   // Puntatore alla stringa

   fwrite(&Stuff,4,1,ffileD);   // "ShellExecuteA" con l'Id



   fputc(0x00,ffileD); // Per marcare la fine della NameTable

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);



   fputc(0x00,ffileD); // AddressTable

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);



   fputc(0x00,ffileD);

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);

   fputc(0x00,ffileD);

-----------------------------------------------------------------------------



A questo punto, al posto del vecchio header, avremo il nome della DLL 

(a ImportOffs+0), il nome della funzione che ci serve (a ImportOffs+12) 

e il puntatore (a ImportOffs+28) alla stringa a ImportOffs+12. Quest'ultimo, 

in realta', rappresenta la tabella dei nomi con un unico elemento 

(cioe' il puntatore alla stringa ShellExecute) e che termina con

una entry nulla:



.00025000:  53 48 45 4C-4C 33 32 2E-44 4C 4C 00-72 00 53 68  SHELL32.DLL r Sh

.00025010:  65 6C 6C 45-78 65 63 75-74 65 41 00-0C 50 00 00  ellExecuteA .P

.00025020:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00



N.B. Per sapere qual'e' l'Id della funzione vi conviene cercarlo in un altro 

prog che la usi (di ShellExecuteA e' 0x7200)!







3) Aggiungete una entry all'header spostato. 

Riposizionatevi alla fine dell'header copiato e partite!



Es:

--------------------------------------------------------------

        Stuff=ImportOffS+28;

        fwrite(&Stuff,4,1,ffileD);      // Puntatore alla NameTable



        Stuff=0x00000000;               

        fwrite(&Stuff,4,1,ffileD);      // Vuoti

        fwrite(&Stuff,4,1,ffileD);



        fwrite(&ImportOffS,4,1,ffileD); // Puntatore al nome della libreria



        Stuff=ImportOffS+36;            // Puntatore alla AddressTable

        fwrite(&Stuff,4,1,ffileD);

---------------------------------------------------------------



Quindi, il nostro header adesso contiene una nuova entry composta cosi'



DD - Puntatore alla NameTable (che ha un unico elemento che punta alla stringa

                               Id+"ShellExecuteA")

DD - Vuoto

DD - Vuoto



DD - Puntatore alla stringa "shell32.dll" (cioe' il nome della libreria)



DD - Puntatore alla AddressTable (cioe' l'offset che a RunTime conterra'

                                  l'indirizzo di ShellExecuteA)



N.B. Ricordatevi di aggiungere una entry (20 byte) nulla per chiudere 

l'header.



Avrete (supponendo che il nosto BlankSpace inizi a 0x257A0) :



-vecchio header spostato

.000257A0:  7C 50 00 00-80 4F 50 2A-FF FF FF FF-66 51 00 00  |P  ╟OP*    fQ

.000257B0:  C0 50 00 00-64 50 00 00-9E 4F 50 2A-FF FF FF FF  +P  dP  ╫OP*    

.000257C0:  A0 51 00 00-A8 50 00 00-70 50 00 00-A0 1C 0C 32  ßQ  ┐P  pP  ß..2

.000257D0:  FF FF FF FF-D4 51 00 00-B4 50 00 00-00 00 00 00      ╚Q  ªP

.000257E0:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00



-nuova entry

.000270F0:  1C 50 00 00-00 00 00 00-00 00 00 00-00 50 00 00  .P           P

.00027100:  24 50 00 00-00 00 00 00-00 00 00 00-00 00 00 00  $P

.00027110:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00

.00027120:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00





4) Modificate il puntatore all'header e la sua dimensione.



Andate all'offset 0x100 e fatelo puntare al vostro nuovo header 

L'indirizzo deve essere RVA (senza pero' ImageBase). Per ottenere l'RVA

prendete l'offset fisico del vostro header (BlankSpace), sottraeteci l'offset

fisico del segmento dove si trova, e sommateci l'RVA (sempre del segmento

dove si trova).



Poi spostatevi all'offset 0x104 e sommate 0x14 al valore che trovate.

In realta' state ingrandeno l'header di 20 byte (cioe' la dimensione

dell'entry che avete aggiunto).

Questa operazione sembra non servire a niente, ma non si sa mai.





5) Richiamate ShellExecuteA.

A RunTime la dword a ImportOffS+36 (cioe' a 0x25024) verra' riempita con 

l'indirizzo di ShellExecuteA, quindi vi bastera' fare una call reindirizzata 

a ImportOffS+36. Infatti ora shell32 sara' mappata nel vostro spazio di 

indirizzamento!!!!



Per callare basta inserire, dove vi serve, il codice



  FF 15     24 50 02 00

^^call^^   ^^indirizzo^^

 

per richiamare ShellExecuteA =)



Visto che non era tanto difficile?



N.B. FF15 fa una call all'indirizzo contenuto nell'offset che riceve come 

parametro.





                            - I RINGRAZIAMENTI -

Ho fatto praticamente tutto da solo, quindi l'unico che mi sento di 

ringraziare veramente e' il mio vecchio compagno di coding-hacking-cracking 

R@Nc0r.





                               - I SALUTI -

Saluto tutti i miei amici ed in particolar modo |LnZ| e ALoR (RingZer0), 

10t80r (NeuroZone2) e PeSy (Casa mia). Inoltre saluto tutti i membri di 

RingZer0, che spero di conoscere al prossimo HackIt.

Non saluto i bastardi che fanno i tutorials sbagliati, i miei professori

dell'uni, le tipe che se la tirano.





                              - LA MASSIMA -

Non so se, quando cambieranno, le cose saranno migliori....ma so che, se

voglio che siano migliori, devono cambiare.





                               - LA MINIMA -

Durante la notte 12 gradi centigradi nella zona di MilanoLambrate.





(la signature la dovete guardare con l'Edit di MS-DOS, altrimenti si vede

 la seguente schifezza...)



                     ░ ▒ ▓ ▒ ░              ░ ▒ ▓ ▒ ░

                     ░▒▒▒▓▒▒▒░              ░▒▒▒▓▒▒▒░

                     \░░░▒░░░/              \░░░▒░░░/ 

                      ░▒▒▓▒▒░                ░▒▒▓▒▒░

                      ░▒▒▓▒▒░ |   |     /    ░▒▒▓▒▒░

                      ░▒▒▓▒▒░ | \ |    / _\  ░▒▒▓▒▒░

                      ░▒▒▓▒▒░ |_ \_ aG/_  /_ ░▒▒▓▒▒░

                     _░▒▒▓▒▒░_             __░▒▒▓▒▒░_