Risolto Incompresione sul funzionamento del formato PE

DanyDollaro

Utente Electrum
14 Luglio 2018
148
41
58
138
Salve, stavo leggendo la documentazione della microsft sul formato PE, e non mi torna il significato dell'ultimo paragrafo in "Section Data", il link alla sezione è questo, mentre questo è il paragrafo che non capisco:

1677025729360.png



Nel caso l'allineamento (virtuale) delle sezioni specificato nell'intestazione sia minore delle dimensioni di una pagina del sistema in uso, al file immagine viene applicata la seguente restrizione:

the location of section data in the file must match its location in memory when the image is loaded
Per location of section data in the file presumo intenda il membro PointerToRawData contenuto nell'intestazione che indica la posizione della sezione nel file sul disco, e questo valore deve combaciare con la posizione della sezione mappata in memoria, che corrisponde al membro VirtualAddress nella medesima intestazione, però non mi torna il motivo di questa restrizione:

Prendiamo il caso di un sistema le cui pagine sono grandi 0x1000 byte, ed un file immagine con:
Codice:
SectionAlignemnt = 0x500
FileAlignment    = 0x200
Il file immagine avrà 2 sezioni chiamate .sec1 e .sec2 con:
Codice:
.sec1
    PointerToRawData = 0x200
    SizeOfRawData    = 0x400
    VirtualAddress   = 0x1000
    VirtualSize      = 0x500
    
.sec2
    PointerToRawData = 0x600
    SizeOfRawData    = 0x800
    VirtualAddress   = 0x1500
    VirtualSize      = 0x1000
Ogni sezione ha PointerToRawData diverso da VirtualAddress eppure non capisco dove sia il problema nel mappare un file eseguibile del genere:

il loader dovrebbe allocare 2 pagine da 0x1000 byte, scrivere sulla prima pagina il contenuto della prima sezione .sec1 che avendo come dimensioni virtuali 0x500 byte lascerà altri 0x500 byte liberi, poi all'indirizzo virtuale 0x2000 verrà mappata la seconda sezione .sec2, e non a 0x1500 come specificato dal membro VirtualAddress, anche se non so se sia corretto mappare una sezione a un indirizzo virtuale che non sia il suo.

La restrizione di cui ho parlato prima dice che per tutte le sezioni il membro PointerToRawData debba essere uguale a VirtualAddress (se ho capito bene), il che implica anche che le dimensioni delle sezioni sul file ed una volta mappate sia lo stesso siccome una sezione con un VirtualSize > SizeOfRawData sfaserebbe gli indirizzi delle sezioni a venire.

Quindi, quale sarebbe la ragione dietro a questo vincolo?
 
Seguendo l'esempio di .sec1 e .sec2 con un alignment di memoria inferiore alla dimensione delle pagine di sistema, due sezioni che stanno sulla stessa pagina NON possono avere caratteristiche diverse (una magari è RW e l'altra RX per fare un esempio), infatti Windows può impostare flag di protezione solo su pagine intere.

Aggiungo un altra citazione dalla documentazione:
If the SectionAlignment member is less than the system page size, FileAlignment must be the same as SectionAlignment.
Questo aiuta a far combaciare il VirtualAddress con il PointerToRawData di quella sezione e spiega anche perché il loader non può caricare il tuo esempio (oltre a dover correggere di conseguenza sia pointer che size di RawData di entrambe le sezioni).

Le ragioni posso solo ipotizzarle visto che non le hanno scritte, credo sia per semplificare il lavoro del loader per questi PE "speciali" mantenendo la compatibilità, infatti questa cosa permette di avere più sezioni nella stessa pagina e seguendo questa convenzione il loader può sapere se il risultato sarà un mapping valido oppure no a priori.

Se vuoi un esempio di exe funzionante che usa questo sistema da un'occhiata alla ricerca TinyPE.
 
Ultima modifica:
Ho appena finito di leggere Tiny PE, davvero interessante, a quanto pare anche loro non hanno trattato la ragione per cui i file eseguibili con SectionAlignment minore di una pagina di sistema debba per forza avere PointerToRawData uguale a VirtualAddress per ogni sezione, ma sono d'accordo con te che sia per qualche problema di compatibilità.

Interessante il fatto che FileAlignment possa essere impostato ad 1 ed ottenere un file eseguibile funzionante quando nella documentazione ufficiale dice che questo valore debba per forza essere maggiore o uguale a 512 byte :rolleyes:, ma presumo sia un comportamento non intenzionale del loader.

Ieri feci un paio di test per creare un file eseguibile con SectionAlignment uguale a 0x200, e riuscii a creare questo:
raw.png

Ormai ho eliminato quel sample e non posso fare uno screen nel quale si veda il campo delle sezioni per intero, ma in questo caso il membro VirtualSize è stato impostato uguale a SizeOfRawData anche per l'ultima sezione non ostante non sfasi l'indirizzo base di nessun'altra sezione, nel caso questi due valori differiscano per qualsiasi sezione, il loader rifiuterà di caricarlo.

Avviando il sample e vedendo la mappa della memoria da Xdbg, a quanto pare il loader forza il caricamento delle sezioni al loro VirtualAddress, quindi riguardo all'ipotetico scenario che ho trattato prima non sarebbe stato corretto mappare .sec2 al RVA 0x2000 ma bensì a 0x1500 descritto nell'intestazione, mentre per gestire la protezione delle pagine il loader lo risolve in questo modo:

Mapped.png


Se 2 sezioni con attributi diversi finiscono all'interno di una pagina il loader imposterà gli attributi della pagina in modo da soddisfare tutte e due le richieste, quindi per due sezioni con RW e l'altra con RX imposterà la protezione della pagina a RWX, del resto me lo sarei aspettato siccome vidi che questo comportamento del loader fu abusato per nascondere la presenza di driver mappati manulmente.

Quindi sono arrivato alla conclusione che effettivamente la documentazione ufficiale del format PE della Miscodoft non tratti molti aspetti interessanti riguardo al loader, quindi vedrò di continuare le mie richerche altrove, grazie per il vostro supporto :D.