Minima Anonyma Tabularia Ex Reti
nihil vacuum neque sine signo apud MATER
Questa pubblicazione contiene
informazioni e codice sorgente relativi alla sicurezza informatica. L’obbiettivo
di queste informazioni è di aiutare gli utenti ad accrescere la propria capacità
di programmazione. Questo materiale è a puro scopo didattico e non include
codice distruttivo né documenti attinenti ad alcuna attività illegale. Si
declina ogni responsabilità nel caso chiunque utilizzi le suddette informazioni
per creare, compilare e diffondere intenzionalmente programmi diretti a
danneggiare o interrompere un sistema informatico o telematico, ovvero a
provocarne l'interruzione, totale o parziale, o l'alterazione del suo
funzionamento Art. 615-quinquies (Legge n. 547/93 pubblicata in Gazzetta
Ufficiale n. 305 del 30.12.1993)
§ MATER
Liber Tertius §
Editoriale # 1
Innanzitutto grazie a tutti.
MATER ha ricevuto un'accoglienza calorosa e di questo ve ne
siamo grati. Abbiamo ricevuto auguri da amici, incoraggiamenti da conoscenti e
fuggevoli saluti da surfnetisti solitari. Molte sono state le domande,e per
evitare in futuro imbarazzanti incomprensioni, proviamo a rispondere ad alcune
di esse.Vari ad esempio ci hanno chiesto chi siamo cosa facciamo.
Vediamo un po' cosa è questa nebulosa Familia. Familia NON è un
gruppo di virusvriters o di pirati informatici o, come qualcuno, utilizzando
erroneamente il termine, definisce Hackers. Familia è un gruppo composto da
studiosi del software, il cui interesse comune è accrescere la propria
conoscenza relativa alle tecniche virali. I virus ci interessano per il loro contenuto tecnico e non per i
loro effetti. La nostra attività è lo studio. Non abbiamo alcun interesse a
creare o diffondere nuovi virus. Anzi, in questo senso la nostra attività
potrebbe essere assimilata, guarda un
po', più ad una casa di AV.
Di conseguenza qui non troverete codice virale inedito, né
metodi per crakkare programmi o penetrare in siti senza autorizzazione. MATER,
e qui qualcuno storcerà il naso, non contiene materiale nuovo: questa zine si
limita a raccogliere, vagliare, analizzare. Niente nuovi sorgenti, quindi, ma
nuovi commenti e relazioni. Non la distruzione di dati, ma la costruzione del
sapere.
Ciao a tutti.
Milla/Familia
Editoriale # 2
nullum malum gaudium est. Come quelli che se la prendono con il
mio ablativo bastardo (che tuttavia rimarrà: non tutto è deus ex machina,
nessuna perversione è gioiosa), bisogna chinare la testa ed accettare la
realtà. La realtà si chiama Windows, ormai presente nel Tela Totius Terrae (o
preferite WEB ?). Purtroppo, quisquis
se multum Windowsae dedit ingente sibi materiam perturbationis et
inexplicabilem fecit. Chi si è affidato alle flatulenze gatiane non avrà vita
facile. Gli anelli della libertà si stringono (quoque tu, Ring0), le API ci
sovrastano con i loro sciami, il nostro meraviglioso limbo degli Interrupt si
allontana…dovremmo quindi abbandonare ogni speranza ?
La risposta la conosciamo tutti. Quindi, figlioli, al lavoro.
Solo lo studio ci salverà. Ricordiamoci che per questa via… itur ad astra !
Valete.
S3n3ca~/Familia
indice
Questo è un mondo
nuovo. Scordatevi il DOS, scordatevi di quelle zone buie e fredde dello schermo
dove i comandi di sistema brillavano solitari, magari allietati dai fotoni di
fosfori monocromatici. Scordatevi dei cari, buoni 64K nel cui caldo abbraccio
amavamo (ma di certo maledicendo) rigirarci. Scordatevi di quelle miniature a
16 bit che erano i “vecchi” registri, esili zattere sulle quali scivolavamo
tranquilli. Scordatevi (ma sul serio ?) di quelle chiamate repentine a INT21, con
le quali eravamo abituati a strapazzare RAM e HD, periferiche e device (ma
qualcuno dice che siano la stessa cosa). Scordatevi del passato. Ora c’è
Windows.
Ora c’è il
faccione rubicondo del suo padroncino che ce lo rifila come il sistema più
stabile (sic) dell’universo, che ci dirà cosa, dove, come codificare, se e
quando farlo, qui-si-può-là-già-non-si-può-più, quello è “reserved” e
quell’altro no ma lascialo stare… Insomma la goduria è finita.
Ma è davvero
finita ?
Non staremo qui a
sproloquiare sul perché e sul percome, o a riportare una storia che ormai
conoscono anche i sassi. Che Windows ammazzi le bisce morte non è più un
segreto. Tuttavia Windows a 32 bit è qui tra noi, e sebbene mandi a f****** una
quindicina di volte al giorno il suo ideatore, il 99,999% del pianeta continua
ad usarlo. Ed anche i virus lo fanno. E siccome MATER studia i virus, MATER
seguirà questa strada.
Ordunque.
Individuiamo a questo punto tre importanti novità:
1.
da 16 a 32
Dall’ambiente a
16 bit a quello a 32 bit cambiano alcune cosine. Fermo restando che vi
consigliamo di procurarvi un manuale che tratta tale argomento (ce ne sono di
ottimi in circolazione), vi spiegheremo in poche righe e per quello che ci
riguarda, i cambiamenti più eclatanti.
Nella
programmazione vengono utilizzate double words
invece che words, e ciò ha aperto parecchie possibilità ai vwriters.
Abbiamo due segmenti da aggiungere ai già conosciuti CS, DS, ES e SS: FS e GS.
Abbiamo anche nuovi registri a 32 bit:
EAX, EBX, ECX, EDX, ESI, EDI, EBP e ESP. Vediamo come funzionano.
Immaginiamo di dover accedere alla parola meno significativa di EAX. Cosa
dobbiamo fare ? A questa porzione di registro
si può accedere usando il registro AX che contiene la parola LSW. Immaginiamo
di avere EAX = 00000000. Se vogliamo inserire 1234h nella LSW di EAX dobbiamo
eseguire un "mov ax,1234h" e tutto è a posto. Ma se volessimo
accedere alla MSW (Most Significant Word) di EAX ?. In questo caso non possiamo
usare un registro. Dovremo invece utilizzare l’istruzione ROL (o SHL).
2.
API
Il secondo
cambiamento importante sono le API. Esse saranno l’argomento del numero
successivo.
3.
PE
La terza novità
riguarda il formato dei files eseguibili. Andiamo oltre il formato COM, oltre
l’EXE del DOS (trattati negli scorsi numeri) e il NE di Win16.
Approderemo ora
al formato Portable Executable (PE). E di questo parleremo ora.
Con l’avvento dei
sistemi multipiattaforma dell’ambiente Win32, quei cari ragazzi della Microsoft
hanno pensato di studiare un formato di file eseguibile che andasse bene un po’
per tutti. Dopo un periodo di pensoso travaglio, è nato così il formato PE,
assai diverso dai precedenti (COM, EXE DOS, NE, etc.) e un pochino più
complicato. E’ molto importante capire questa struttura per studiare i virus di
Win32.
Ecco, tanto per
iniziare, un piccolo schema:
offset 0__________________________________MZ_________ MS_DOS Header
________________________________PE\0\0_______ Signature ___________
IMAGE_FILE_HEADER |
_____________________________________________ _ |
| |
_____________________________________________ | IMAGE_OPTIONAL_HEADER|
| |
Data Directory | |
_____________________________________________ _| ____________________|
+-_____________________________________________ |
+|-_____________________________________________ |
+||-_____________________________________________ | Section Table (array di
+|||-_____________________________________________ | IMAGE_SECTION_HEADERs)
+||||-_____________________________________________ |
||||| |
||||>______________________________________________ _|
||||
|||| .text
|||+> _____________________________________________
|||
||| .data
||+-> _____________________________________________
||
|| .edata
|+-> _____________________________________________
| .idata
+-à _____________________________________________
.reloc
_____________________________________________
_____________________________________________
numeri di linea COFF
_____________________________________________
Simboli COFF
_____________________________________________
informazioni del Debug CodeView
_____________________________________________
Ora, qualche spiegazione:
DOS-stub e Signature (vecchio header EXE) – DOS HEADER
off dim. nome descrizione
+0 WORD e_magic; Magic (sigla MZ)
2 WORD e_cblp; Bytes dell’ultima pagina del file
4 WORD e_cp; Pagine nel file
6 WORD e_crlc; Rilocazioni
8 WORD e_cparhdr; Misura dell’header nei paragrafi
A WORD e_minalloc; Memoria extra minima per i paragrafi
C WORD e_maxalloc; Memoria extra massima per i paragrafi
E WORD e_ss; Valore iniziale di SS (relativo)
10 WORD e_sp; Valore iniziale di SP
12 WORD e_csum; Checksum
14 WORD e_ip; Valore iniziale di IP
16 WORD e_cs; Valore iniziale di CS (relativo)
18 WORD e_lfarlc; Indirizzo nel file della tabella di rilocazione
1A WORD e_ovno; numero di overlay
1C WORD e_res[4]; Riservato (words)
24 WORD e_oemid; Identificatore OEM
26 WORD e_oeminfo; Informazione OEM
28 WORD e_res2[10]; Riservato (words)
3C DWORD e_lfanew; Indirizzo nel file del nuovo header exe
qui è inserito lo STUB (mozzicone MS-DOS, che è opzionale, ed è un programmino per il messaggio di incompatibilità del PE file con l’ambiente DOS (avete mai provato a lanciare un eseguibile windows da DOS ?).
FILE HEADER (IMAGE FILE HEADER)
+0 PE Signature: PE\0\0
4 WORD Machine:
6 WORD NumberOfSections:
8 DWORD TimeDateStamp:
C DWORD PointerToSymbolTable:
10 DWORD NumberOfSymbols:
14 WORD SizeOfOptionalHeader:
16 WORD Characteristics:
Totale: 18h BYTES
Segue una breve descrizione dei campi:
PE\0\0:
La marcatura che contraddistingue ogni file PE.
Machine:
Indica il tipo di processore. esempio:
IMAGE_FILE_MACHINE_I386 equ
14Ch ; Intel 386.
IMAGE_FILE_MACHINE_R3000
equ 162h ; MIPS little-endian,160h big-endian
IMAGE_FILE_MACHINE_R4000
equ 166h ; MIPS little-endian
IMAGE_FILE_MACHINE_R10000
equ 168h ; MIPS little-endian
IMAGE_FILE_MACHINE_ALPHA
equ 184h ; Alpha_AXP
IMAGE_FILE_MACHINE_POWERPC equ
1F0h ; IBM PowerPC
Little-Endian
Number Of Sections:
numero delle. sezioni presenti e numero dielementi della tabella section headers.
Time Date Stamp:
time stamp usata per verificare la versione del modulo (dal 31.12.69) alla data del linkaggio.
Pointer To Symbol Table:
offset della tabella simboli COFF (OBJ).
Number Of Symbols:
numero dei simboli della tabella COFF (OBJ).
Size Of Optional header:
misura degli optional headers (di solito 0xE0). Somma dei bytes che IMAGE_OPTIONAL_HEADER occupa (vedi la descrizione di IMAGE_OPTIONAL_HEADER)
Characteristics:
informazioni sul file (se è un exe e non una lib, etc)
OPTIONAL HEADER (IMAGE OPTIONAL HEADER)
18 WORD Magic:
1a UCHAR MajorLinkerVersion:
1b UCHAR MinorLinkerVersion:
1c DWORD SizeOfCode:
20 DWORD SizeOfInitializedData:
24 DWORD SizeOfUninitializedData;
28 DWORD AddressOfEntryPoint:
2c DWORD BaseOfCode:
30 DWORD BaseOfData:
34 DWORD ImageBase:
38 DWORD SectionAlignment:
3c DWORD FileAlignment:
40 WORD MajorOperatingSystemVersion:
42 WORD MinorOperatingSystemVersion:
44 WORD MajorImageVersion:
46 WORD MinorImageVersion:
48 WORD MajorSubsystemVersion:
4a WORD MinorSubsystemVersion:
4c DWORD Reserved1:
50 DWORD SizeOfImage:
54 DWORD SizeOfHeaders:
58 DWORD CheckSum:
5c WORD Subsystem:
5e WORD DllCharacteristics;
60 DWORD SizeOfStackReserve:
64 DWORD SizeOfStackCommit:
68 DWORD SizeOfHeapReserve:
6c DWORD SizeOfHeapCommit:
70 DWORD LoaderFlags:
74 DWORD NumberOfRvaAndSizes:
Totale : 78h BYTES (assieme
a IMAGE_FILE_HEADER)
Magic:
Magic: sempre 0x010B
Major Linker Version and
Minor Linker Version:
versione del linker che ha prodotto il file
Size Of Code:
misura di tutte le sezioni codice (somma in bytes)
Size Of Initialized Data:
somma delle sezioni dati inizializzati
Size Of Uninitialized Data:
somma delle sezioni dati non inizializzati
Address of EntryPoint:
RVA entry point del modulo (da cui il loader darà esecuzione del PE).
30 DWORD BaseOfData: RVA della prima sezione di dati
Base Of Code:
RVA della prima sezione di
codice (.text, CODE). In memoria le sezioni codice vengono prima delle sezioni
dati e dopo l’header PE. E’ usualmente 0x1000 nei
Linker Microsoft.
Base Of Data:
RVA della prima sezione di
dati. Di solito per ultima in memoria, dopo l’header e le sezioni codice.
Image Base:
indirizzo lineare
indirizzamento linker (base di riferimento di tutti gli RVA). Quando il linker
crea un eseguibile, assume che il file sarà mappato in memoria in una specifica
locazione. Tale indirizzo è salvato in questo campo. Se il file è realmente
mappato dal loader a quell’indirizzo, il codice non ha bisogno di alcun
aggiustamento prima di essere eseguito. Negli eseguibili
prodotti per Windows NT, L’image base
di default è 0x10000. Per le DLL, il default è 0x400000. In Win9X,
l’indirizzo 0x10000 non può essere usato per caricare eseguibili a 32-bit
perché tale indirizzo è situato in una regione condivisa da altri processi. Per
questo motivo Microsoft ha cambiato
l’indirizzo di base di default per gli eseguibili Win32 a 0x400000. I vecchi
programmi che erano stati linkati assumono come base l’inidirzzo 0x10000
saranno più lenti nel caricamento sotto Win9X perché il loader deve applicare
la rilocazione della base.
Questo campo e' di vitale importanza in quanto riporta la
cosidetta
"preferred imagebase" ovvero l'indirizzo
lineare nello spazio di indirizzamento privato utilizzato dal linker per
risolvere gran parte dei fixup nonche' la base a cui si riferiscono tutti gli
RVA: questo significa che se il loader di Windows deve mappare l'immagine ad un
indirizzo diverso sara' necessario applicare le base relocations.
Section Alignment:
modulo per allineamento delle
sezioni per la mappatura in memoria. Quando ogni sezione è mappata in meoria, il sistema garantisce che la sezione comincerà a un indirizzo
virtuale che è un multiplo di questo valore.Il default è 0x1000. Quando il
loader di window mappa in memoria il file immagine fa in modo che occupi uno
blocco consecutivo di memoria nello spazio di indirizzamento.Tuttavia per
questioni di ottimizzazione nella gestione della memoria virtuale (ad exp.
nello share di porzioni di codice, nel caricamento di pagine non presenti,ecc.)
in w9x ogni sezione deve essere allineata ad un multiplo della unita' minima
gestita dal VMM : 1 pagina x86 = 4096 = 1000h (attenzione a non confonderla con
la granularita' di allocazione che e' di 64k). Questa limitazione non si
applica a NT (il minimo e' 32byte) ma non credo che vogliate degli eseguibili
"incompatibili".
File Alignment:
modulo per allineamento
sezioni su disco. In un PE, il sistema garantisce che dati su disco che
identificano ogni sezioni partano da un multiplo di questo valore. Il default è 0x200 bytes,probabilmente per assicurare che le
sezioni partano all’inizio di un settore del disco (che è 0x200 bytes). Questo campo è equivalente
all’allineamento segment/resource nei files NE
files. A differenza di un NE, il
PE non ha centinaia di sezioni, così lo spazio non viene sprecato. Questo campo
e' un antico retaggio di quando Windows95 utilizzava il filesystem FAT, e per
ottimizzare i caricamenti si era pensato di allineare i dati delle sezioni su
disco ad un multiplo della grandezza di un settore (200h = 512 b).
Major Operating System Version and Minor Operating System Version:
versione del SO minima richiesta per l’eseguibile (1.0)
Major Image Version and Minor
Image Version:
campo definibile dall’utente (switch /version del linker)
Major Subsystem Version and
Minor Subsystem Version:
massimo e minimo sub sistema (es. Win NT 3.1)
Reserved1:
sempre = 0 (molto usato dai code writers per un infection mark)
Size Of Image:
grandezza immagine in memoria (header+VirtualSize sezioni). Questo campo riporta la grandezza dell'immagine una volta in memoria e quindi lo spazio totale che il loader deve riservare per il suo caricamento. E' costituita dalla somma dell'header + le VirtualSize delle sezioni presenti ed arrotondata al multiplo piu' vicino della SectionAlignment. Quest'ultimo fatto e' stato fonte di problemi per molti coders che avevano testato il loro virus solo con win95 dato che questo ignora l'allineamento continuando pacificamente mentre per NT questo non va bene.
Size Of Headers:
somma delle dimensioni headers che precedono i dati delle sezioni. Il valore qui riportato altro non e' che la somma delle dimensioni dei vari headers che precedono i dati delle sections (DosHeader+ Stub+ NtHeaders, SectionHeaders): in sostanza e' una sorta di puntatore ai rawdata dato che ImageBase+ SizeOfHeaders vi porta direttamente all'inizio della prima sezione, sia che stiate lavorando con l'immagine di un processo in memoria,o su disco.
Checksum:
CheckSum: cheksum immagine file PE
SubSystem:
1=nativo; 2=Win GUI; 3=Win CUI; 5=OS2; 7=POSIX
Dll Characteristics:
Un set di flags indicante in quale circostanza può essere
chiamata una funzione di inizializzazione DLL (come DllMain). Questo valore
sembra essere settato a 0. Altri valori:
1 Call when DLL is
first loaded into a process's address space
2 Call when a thread
terminates
4 Call when a thread
starts up
8 Call when DLL exits
Size Of Stack Reserve:
memoria virtuale per lo stack (0x100000 – 1Mb)
Size Of Stack Commit:
memoria allocata inizialmente per lo stack (0x1000 – 1 pagina)
Size Of Heap Reserve:
memoria per heap (1 pagina)
Size Of Heap Commit:
vedi sopra. 1 pagina.
Loader Flags:
per debugging
Number Of Rva And Sizes:
Questo campo indica la dimensione dell'array di strutture
IMAGE_DATA_DIRECTORY che inizia dal campo DataDirectory. Attualmente e' fissato
a 16 elementi ma non è detto che rimanga così per sempre.
SECTION DIRECTORIES (SECTION TABLE)
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
* IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 *
Ogni elemento della struttura riporta l’RVA e la VirtualSIze di specifiche informazioni /strutture.
78 DWORD ExportDirectory VA funzioni esportate dal modulo
7c DWORD ExportDirectory Size
80 DWORD ImportDirectory VA funzioni importate dal modulo
84 DWORD ImportDirectory Size
88 DWORD ResourceDirectory VA inizio della resource directory (resROOS)
8c DWORD ResourceDirectory Size
90 DWORD ExceptionDirectory VA
94 DWORD ExceptionDirectory Size
98 DWORD SecurityDirectory VA
9c DWORD SecurityDirectory Size
a0 DWORD BaseRelocationTable VA
a4 DWORD BaseRelocationTable Size
a8 DWORD DebugDirectory VA
ac DWORD DebugDirectory Size
b0 DWORD ArchitectureSpecificData VA
b4 DWORD ArchitectureSpecificData Size
b8 DWORD RVAofGP VA
bc DWORD RVAofGP Size
c0 DWORD TLSDirectory VA
c4 DWORD TLSDirectory Size
c8 DWORD LoadConfigurationDirectory VA
cc DWORD LoadConfigurationDirectory Size
d0 DWORD BoundImportDirectoryinheaders VA
d4 DWORD BoundImportDirectoryinheaders Size
d8 DWORD ImportAddressTable VA
dc DWORD ImportAddressTable Size
e0 DWORD DelayLoadImportDescriptors VA
e4 DWORD DelayLoadImportDescriptors Size
e8 DWORD COMRuntimedescriptor VA
ec DWORD COMRuntimedescriptor Size
f0 DWORD 0
f4 DWORD 0
Come si può vedere ogni DataDirectory e' una struttura che
rappresenta per il loader una sorta di shortcut per accedere velocemente alle
informazioni piu' sensibili per la creazione/inizializzazione del processo.
Ogni elemento (indici da 0 a 15) riporta l'RVA e la VirtualSize di specifiche
informazioni/strutture. Le piu' importati sono:
0 : funzioni
esportate dal modulo (ET)
1 : funzioni
importate ma non bounded (IT)
2 : inizio della resource directory
(resROOT)
5 : base relocations
9 : blocco thread local storage (TLS)
11: funzioni importate bound
(BIT)
12: import
addresse table (IAT)
Il loader fa sempre riferimento a questa tabella per accedere ai dati del processo e non alla tabella dei section headers.
SECTION HEADERS (IMAGE SECTION HEADER)
di seguito all’ optional header (a +f8h) inizia l’array di strutture IMAGE_SECTION_HEADER noto come section table o tabella delle sezioni; ogni elemento dell’array descrive i dati essenziali di una sezione presente nel file.
+0 8byte ANSI name:
+8 dword misc (actual size)
+C dword virtual address:
10 dword sizeofrawdata:
14 dword pointerToRawData:
18 dword pointerToRelocations:
1C dword PointerToLinenumbers
20 word NumberOfRelocations
22 word NumberOfLineNumbers
24 dword Characteristics: alcuni dei flags possibili:
- 0x00000020 contiene codice
- 0x00000040 contiene dati initializati
- 0x00000080 contiene dati non inizial.
- 0x00000200 contiene commenti
- 0x02000000 puo’ essere discarded
- 0x10000000 la section è shareable
- 0x20000000 la section è executable
- 0x40000000 la section è readable
- 0x80000000 la section è writeable
Totale : 28h BYTES
Section Name:
Nome della sezione
ANSI e non UNICODE.
Virtual Size:
convenzionalmente contiene la dimensione fisica (vedi
SizeOfRawData) dei dati arrotondata ad un multiplo del section aligment. Questo
campo in pratica dovrebbe dire al loader quanto spazio riservare in memoria per
questa sezione. (dovrebbe perche' il loader sembra perfettamente ignorare
questo campo in presenza di una rawsize "valida" ed effettuare da se
i calcoli per una VSize corretta. Questo probabilmente spiega anche il fatto
che la ImageSize venga ignorata da w9x. Cmq e' anche perfettamente lecito avere
una rawsize = 0 e una VSize=0x1000,tant'e' che i packer sfruttano proprio
questa caratteristica cambiando la rawsize ma lasciando inalterata la VSize (a
dir il vero la VSize puo' anche sovrapporsi alla sezione successiva dato che e'
cmq uno spazio solo "riservato" e non necessariamente utilizzato)
purche' ovviamente non ci sia vera sovrascrizione.
Virtual Address:
RVA che permette di calcolare la posizione che avra' la sezione
una volta caricata in memoria dal loader. Deve essere maggiore, o un multiplo,
del section alignment (che lo ricordiano non puo' essere minore di 0x1000 per
compatibilita' con 9x).
Size Of Raw Data:
la dimensione fisicamente occupata dai dati su disco solitamente
allineata al file alignment. Questo campo puo' essere totalmente indipendente
dalla Vsize. Per esempio, assumiamo un
file alignment di 0x200. Se il campo VirtualSize ci dice che la sezione è lunga
0x35A bytes questo campo riporterà che la sezione è lunga 0x400 bytes.
Pointer To Raw Data:
l'offset "fisico" a cui trovare i dati della sezione.
Pointer To Relocations:
in un EXE questo campo
(e il seguente) non è importante, ed è settato a 0.
Pointer To Line Numbers:
vedi sopra.
Number Of Relocations:
Questo campo sembra rilevante solo per gli OBJ.
Number Of Line Numbers:
numero di linee
Characteristics:
i flag che identificano le caratteristiche (codice,dati,ecc.) e
quindi le i flags e le protezioni di pagina che verranno applicate
(writable,readable,ecc.). Alcuni dei flags possibili:
- 0x00000020 contiene codice
- 0x00000040 contiene dati initializati
- 0x00000080 contiene dati non inizial.
- 0x00000200 contiene commenti
- 0x02000000 puo’ essere discarded
- 0x10000000 la section è shareable
- 0x20000000 la section è executable
- 0x40000000 la section è readable
- 0x80000000 la section è writeable
Export Directory
+0 DWORD Characteristics;
4 DWORD TimeDateStamp;
8 WORD MajorVersion;
a WORD MinorVersion;
c DWORD Name;
10 DWORD Base;
14 DWORD NumberOfFunctions;
18 DWORD NumberOfNames;
1c DWORD *AddressOfFunctions;
20 DWORD *AddressOfNames;
24 DWORD *AddressOfNameOrdinals;
Import Directory
+0 DWORD OriginalFirstThunk;
4 DWORD TimeDateStamp;
8 DWORD ForwarderChain;
c DWORD Name;
10 DWORD FirstThunk;
SEGUONO LE SEZIONI
.text section: codice general-purpose
.data section: dati inizializzati
.bss section: variabili non inizializzate
.rsrc section: risorse del modulo
.idata section: informazioni sulle funzioni e dati importati da DLL esterne
.edata section: lista delle funzioni e dati esportati ad altri moduli
.reloc section tabella di rilocazioni (delta offset)
ULTERIORI LETTURE
Vi consigliamo di scovare
la documentazione di Matt Pietrek: “Peering Inside the PE: A Tour
of the Win32 Portable Executable File Format”, 1994
e di
Luevelsmeyer 1998, che si trovano un po’ ovunque.
Ora, la pratica. Vediamo un
po’ più da vicino la struttura di un file PE.
A tale scopo scriviamo il seguente
programma:
.386
.model flat
locals
extrn ExitProcess:PROC
.DATA
var
db ?
.CODE
CORTO:
push
0
call ExitProcess
Ends
End CORTO
Come possiamo vedere è un programma
molto semplice, che si limita a inserire un valore nello stack e chiamare una
sola funzione API (ExitProcess). Vedremo nel capitolo successivo cosa sono
queste API.
Assembliamo e linkiamo il
nostro programma, chiamato corto.asm, e produciamo il file PE corto.exe. Come
viene memorizzato tale file eseguibile sul disco ? Sbirciando un pochino su
disco dove è stato memorizzato corto.exe vedremmo qualcosa di simile:
0000: 4d 5a 50 00 02 00 00 00 04
00 0f 00 ff ff 00 00
"MZP............."
0010: b8 00 00 00 00 00 00 00 40 00 1a 00 00 00 00
00 "........@......."
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
00 "................"
…….
e così via. Ad una prima
occhiata ci appare poco comprensibile; aggiungiamoci qualche definizione:
NOTA: ricordiamo che quel
birichino dell’Intel memorizza i dati al contrario, dal byte meno significativo
(o basso) al byte più significativo (o alto). Così dove vediamo:
78 56
34 12 dobbiamo leggere: 12 34 56 78.
Dumping File:
CORTO.EXE
vecchio header EXE – DOS HEADER
0
1 2 3 4 5
6 7 8 9 A
B C D E F
0000: 4d 5a 50 00 02 00 00 00 04
00 0f 00 ff ff 00 00
"MZP............."
0010: b8 00 00 00 00 00 00 00 40 00 1a 00 00 00 00
00 "........@......."
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
00 "................"
+0 WORD e_magic; Magic (sigla MZ)
2 WORD e_cblp; Bytes dell’ultima pagina del file (qui 50)
4 WORD e_cp; Pagine nel file (qui 2)
6 WORD e_crlc; Rilocazioni (0)
8 WORD e_cparhdr; Misura dell’header nei paragrafi (4)
A WORD e_minalloc; Memoria extra minima per i paragrafi (f)
C WORD e_maxalloc; Memoria extra massima per i paragrafi (ffff)
E WORD e_ss; Valore iniziale di SS (relativo) (0)
10 WORD e_sp; Valore iniziale di SP (b8)
12 WORD e_csum; Checksum (0)
14 WORD e_ip; Valore iniziale di IP (0)
16 WORD e_cs; Valore iniziale di CS (0)
18 WORD e_lfarlc; Indirizzo nel file della tab. di rilocazione (40)
1A WORD e_ovno; numero di overlay (1a)
1C WORD e_res[4]; Riservato (words) (0)
24 WORD e_oemid; Identificatore OEM
26 WORD e_oeminfo; Informazione OEM
28 WORD e_res2[10]; Riservato (words)
3C DWORD e_lfanew; Indirizzo nel file del nuovo header exe (100)
STUB DOS
0
1 2 3 4 5
6 7 8 9 A
B C D E F
0040: ba 10 00 0e 1f b4 09 cd 21 b8 01 4c cd 21 90
90 "........!..L.!.."
0050: 54 68 69 73 20 70 72 6f 67
72 61 6d 20 6d 75 73 "This
program mus"
0060: 74 20 62 65 20 72 75 6e 20
75 6e 64 65 72 20 57 "t be run
under W"
0070: 69 6e 33 32 0d 0a 24 37 00 00 00 00 00 00 00
00 "in32..$7........"
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 "................"
Questo sopra è lo STUB (mozzicone MS-DOS. E’ opzionale ed è un programmino per il messaggio di incompatibilità del PE file con l’ambiente DOS. Segue:
FILE HEADER (IMAGE FILE HEADER)
0 1
2 3 4 5 6
7 8 9 A B C D
E F
0100: 50 45 00 00 4c 01 04 00 77
29 f1 86 00 00 00 00
"PE..L...w)......"
0110: 00 00 00 00 e0 00 8e 81
E cos’ via.
Ma vediamo l’output dello
stesso eseguibile corto.exe fornito dal programma dumper di Matt Pietrek, Pedump:
Dump of file CORTO.EXE
File Header
Machine: 014C (i386)
Number of Sections: 0004
TimeDateStamp: 86F12977
PointerToSymbolTable: 00000000
NumberOfSymbols: 00000000
SizeOfOptionalHeader: 00E0
Characteristics: 818E
EXECUTABLE_IMAGE
LINE_NUMS_STRIPPED
LOCAL_SYMS_STRIPPED
BYTES_REVERSED_LO
32BIT_MACHINE
BYTES_REVERSED_HI
Optional Header
Magic 010B
linker version 2.25
size of code 200
size of initialized data 400
size of uninitialized data 0
entrypoint RVA 1000
base of code 1000
base of data 2000
image base 400000
section align 1000
file align 200
required OS version 1.00
image version 0.00
subsystem version 3.10
Reserved1 0
size of image
5000
size of headers 400
checksum 0
Subsystem 0002 (Windows GUI)
stack reserve size 100000
stack commit size 2000
heap reserve size 100000
heap commit size 1000
RVAs & sizes 10
Data Directory
EXPORT rva: 00000000 size: 00000000
IMPORT rva: 00003000 size: 00000054
RESOURCE rva: 00000000 size: 00000000
EXCEPTION rva: 00000000 size: 00000000
SECURITY rva: 00000000 size: 00000000
BASERELOC rva: 00004000 size: 0000000C
DEBUG rva: 00000000 size:
00000000
COPYRIGHT rva: 00000000 size: 00000000
GLOBALPTR rva: 00000000 size: 00000000
TLS rva: 00000000
size: 00000000
LOAD_CONFIG rva: 00000000 size: 00000000
BOUND_IMPORT rva: 00000000 size: 00000000
IAT rva: 00000000
size: 00000000
unused rva: 00000000 size: 00000000
unused rva: 00000000 size: 00000000
unused rva: 00000000 size: 00000000
Section Table
01 CODE VirtSize: 00001000 VirtAddr:
00001000
raw data offs: 00000600
raw data size: 00000200
relocation offs:
00000000 relocations: 00000000
line # offs: 00000000 line #'s: 00000000
characteristics: 60000020
CODE MEM_EXECUTE
MEM_READ
02 DATA VirtSize: 00001000 VirtAddr:
00002000
raw data offs: 00000800
raw data size: 00000000
relocation offs:
00000000 relocations: 00000000
line # offs: 00000000
line #'s: 00000000
characteristics: C0000040
INITIALIZED_DATA MEM_READ
MEM_WRITE
03 .idata VirtSize: 00001000 VirtAddr:
00003000
raw data offs: 00000800
raw data size: 00000200
relocation offs:
00000000 relocations: 00000000
line # offs: 00000000
line #'s: 00000000
characteristics: C0000040
INITIALIZED_DATA MEM_READ
MEM_WRITE
04 .reloc VirtSize: 00001000 VirtAddr:
00004000
raw data offs: 00000A00 raw data size:
00000200
relocation offs:
00000000 relocations: 00000000
line # offs: 00000000
line #'s: 00000000
characteristics: 50000040
INITIALIZED_DATA MEM_SHARED
MEM_READ
Imports Table:
KERNEL32.dll
Hint/Name Table: 00003028
TimeDateStamp: 00000000
ForwarderChain: 00000000
First thunk RVA: 00003030
Ordn Name
0 ExitProcess (IAT: 00003046)
base relocations:
Virtual Address: 00001000 size:
0000000C
00001009 HIGHLOW
00001000 ABSOLUTE
Section Hex Dumps
section 01 (CODE) size:
00000200 file offs: 00000600
00000000: 6a 00 e8 00 00 00 00
ff 25 30 30 40 00 00 00 00
j.......%00@....
00000010: 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
................
------ ------ ------ cut ------ ------ ------
000001F0: 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
................
section 02 (DATA) size:
00000000 file offs: 00000800
section 03 (.idata) size:
00000200 file offs: 00000800
00000000: 28 30 00 00 00 00 00
00 00 00 00 00 38 30 00 00
(0..........80..
00000010: 30 30 00 00 00 00 00
00 00 00 00 00 00 00 00 00
00..............
00000020: 00 00 00 00 00 00 00
00 46 30 00 00 00 00 00 00
........F0......
00000030: 46 30 00 00 00 00 00
00 4b 45 52 4e 45 4c 33 32 F0......KERNEL32
00000040: 2e 64 6c 6c 00 00 00 00 45 78 69 74 50 72 6f 63 .dll....ExitProc
00000050: 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 00 ess.............
------ ------ ------ cut ------ ------ ------
000001F0: 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
................
section 04 (.reloc) size:
00000200 file offs: 00000A00
00000000: 00 10 00 00 0c 00 00
00 09 30 00 00 00 00 00 00
.........0......
------ ------ ------ cut ------ ------ ------
000001F0: 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
................
Come si può vedere, è
abbastanza eloquente ed autoesplicativo e non necessita di ulteriori
delucidazioni.