Ce tip de extensie au fișierele executabile? Cel mai comun. Structura componentelor software

Formate de fișiere executabile

Memoria virtuală a unui proces este formată din mai multe segmente sau regiuni memorie. Mărimea, conținutul și locația segmentelor în memorie sunt determinate atât de programul însuși, de exemplu, utilizarea bibliotecilor, dimensiunea codului și a datelor, cât și formatul fișierului executabil al acestui program. În majoritatea sălilor de operație moderne sisteme UNIX sunt folosite două format standard fișiere executabile - COFF (Common Object File Format) și ELF (Executable and Linking Format).

O descriere a formatelor de fișiere executabile poate părea redundantă, dar o înțelegere a acestora este necesară pentru a le descrie funcționalitate de bază nuclee ale sistemului de operare. În special, informațiile stocate în fișierele executabile ale formatelor COFF și ELF vă permit să răspundeți la o serie de întrebări care sunt foarte importante pentru funcționarea aplicației și a sistemului în ansamblu:

Ce părți ale programului trebuie încărcate în memorie?

Cum se creează o zonă pentru date neinițializate?

Ce părți ale unui proces ar trebui să fie stocate într-o zonă de schimb de disc (o zonă specială de spațiu pe disc concepută pentru a stoca temporar porțiuni din spațiul de adresă al unui proces), de exemplu atunci când schimbați pagini și ce părți pot fi citite dintr-un fișier dacă necesare și, prin urmare, nu trebuie să fie salvate?

Unde se află în memorie instrucțiunile și datele programului?

Ce biblioteci sunt necesare pentru a rula programul?

Cum sunt legate fișierul executabil de pe disc, imaginea programului din memorie și zona de schimb de disc?

În fig. Figura 2.3 prezintă structura de bază a memoriei pentru procesele încărcate din fișierele executabile COFF și, respectiv, ELF. Deși aspectul segmentelor diferă între cele două formate, componentele de bază sunt aceleași. Ambele procese au cod (text), date și segmente de stivă. După cum puteți vedea din figură, dimensiunea segmentelor de date și a stivei se poate modifica, iar direcția acestei modificări este determinată de formatul fișierului executabil. Dimensiunea stivei este modificată automat de sistemul de operare, în timp ce dimensiunea segmentului de date este controlată de aplicația însăși. Vom discuta aceste probleme în detaliu în secțiunea „Alocarea memoriei” mai târziu în acest capitol.

Orez. 2.3. Imagini de program executabile în formatele COFF și ELF

Segmentul de date include date inițializate, care sunt copiate în memorie din secțiunile corespunzătoare ale fișierului executabil, și date neinițializate, care sunt umplute cu zerouri înainte ca procesul să înceapă execuția. Datele neinițializate sunt adesea numite segment BSS.

Din cartea Photoshop CS2 și fotografie digitală (Tutorial). Capitolele 1-9 autorul Solonitsyn Yuri

Din cartea Linux pentru utilizator autor Kostromin Viktor Alekseevici

11.4.2. Formate de fișiere de font În ultima vreme, literalmente fiecare editor grafic sau programul de publicare a folosit propriul format de fișier cu fonturi și, de regulă, unele programe nu au acceptat formatele altora. De-a lungul timpului, numărul de formate efectiv utilizate

Din cartea Adobe Photoshop CS3 autor Zavgorodniy Vladimir

Capitolul 4 Formate de fișiere grafice Există un număr mare de formate pentru stocarea graficelor raster. diverse formate fișiere. Printre ei sunt amândoi formate universale, care nu este legat de niciun program specific și formate raster „personale” specifice

Din cartea Adobe InDesign CS3 autor Zavgorodniy Vladimir

Formate de fișiere grafice Adobe InDesign poate importa fișiere grafice de diferite formate - atât cele mai comune AI, BMP, EPS, GIF, JPEG, PDF, PSD, TIFF, cât și mai rare DCS, EMF, PCX, PICT, PNG, SCT (ScitexCT) ), WMF.Toate formatele grafice și fișierele sunt împărțite în funcție de tipul de informații pe care le au

Din cartea Internet Solutions de la Dr. Bob de Swart Bob

1. Formate de codificare a fișierelor de Internet Formatele de fișiere de Internet pot fi împărțite în mai multe grupuri. În primul rând, formatele de transfer de fișiere prin FTP, pentru care schema uuencode/decode a fost dezvoltată cu mult timp în urmă, înlocuită ulterior cu xxencode/decode. Mai târziu a existat un refuz în favoarea Base64 și MIME,

autor Raymond Eric Stephen

3.1.6. Formate de fișiere binare Dacă un sistem de operare folosește formate binare pentru date sensibile (cum ar fi conturile de utilizator), este probabil să nu existe tradiție de utilizare a formatelor de text care pot fi citite pentru aplicații. In detalii

Din cartea Photoshop CS3: Training Course autor Timofeev Serghei Mihailovici

Formate de fișiere grafice Orice imagine grafică, indiferent dacă este vectorială sau raster, poate fi stocată pe un computer numai prin înregistrarea acesteia într-un fișier separat. Fiecare fișier are întotdeauna un format specific Formatul indică acest lucru

Din cartea Arta programarii pentru Unix autor Raymond Eric Stephen

3.1.6. Formate de fișiere binare Dacă un sistem de operare folosește formate binare pentru date sensibile (cum ar fi conturile de utilizator), este probabil să nu existe tradiție de utilizare a formatelor de text care pot fi citite pentru aplicații. Mai multe detalii despre

Din carte Instrumente de rețea Linux de Smith Roderick W.

Formate de fișiere de font Există două tipuri de fonturi: fonturi bitmap și contur (fonturile contur sunt adesea numite fonturi scalabile). Aceste tipuri de fonturi au proprietăți diferiteși prelucrate căi diferite. Majoritatea serverelor de fonturi concepute pentru a rula

Din cartea HTML 5, CSS 3 și Web 2.0. Dezvoltarea de site-uri web moderne. autor Dronov Vladimir

Din cartea HTML 5, CSS 3 și Web 2.0. Dezvoltarea de site-uri web moderne autor Dronov Vladimir

Formate de fișiere și formate de codare Formate fisiere multimedia Există nu mai puțin de formate de fișiere grafice. Ca și în cazul graficelor de pe Internet, nu toate browserele Web acceptă formate multimedia, dar doar câteva. (aș dori autorul

Din cartea Computer Sound Processing autor Zagumennov Alexandru Petrovici

Formate de fișiere audio Ad Lib Sample SMPFormatul este folosit de placa de sunet Ad Lib Gold pentru a încărca mostre de instrument în ea. Suportă compresie audio pe 8/16 biți, mono/stereo, Yamaha ADPCM pe 4 biți. Fișierele în acest format au extensia . smp.Amiga SVXAcest tip de fișier este utilizat pe

Din cartea Crearea unui virus și antivirus autorul Guliev Igor A.

Anexa A Formatele antetului fișierului EXE Formatul antetului unui fișier EXE obișnuit La începutul fișierului EXE este porțiunea formatată a antetului fișierului EXE (Tabelul A-1) Urmează Tabelul de relocare, format din indicatori lungi (decalaj: segment). ) pe acelea

Din cartea Photoshop CS4 autor Jvalevski Andrei Valentinovici

Formate de fișiere grafice Un format este o modalitate de înregistrare a unei imagini ca fișier. Există destul de multe formate de fișiere grafice, dar în cele mai multe cazuri sunt folosite doar câteva. Fiecare dintre ei are caracteristici, așa că vă recomandăm

Din cartea Fotografie digitală. Trucuri și efecte autor Gursky Yuri Anatolievici

Formate de fișiere Există multe moduri de a stoca informații despre imagine și, prin urmare, multe formate de fișiere. Atenţie! Pentru a evita pierderea datelor, atunci când lucrați cu imagini, salvați-le în format TIFF sau în formatul „nativ” al programului de editare. JPEGВ

Din cartea Windows 10. Secrete și dispozitiv autor Almametov Vladimir memorie de către încărcătorul sistemului de operare și apoi executat. În sistemul de operare Windows, fișierele executabile au de obicei extensiile „.exe” și „.dll”. Extensia „.exe” se referă la programe care pot fi lansate direct de utilizator. Extensia „.dll” are așa-numitele biblioteci de link-uri dinamice. Aceste biblioteci exportă funcții utilizate de alte programe.

Pentru ca bootloader-ul sistemului de operare să se încarce corect fisier executabilîn memorie, conținutul acestui fișier trebuie să corespundă formatului de fișier executabil acceptat în acest sistem de operare. Au existat și încă există multe formate diferite pe sisteme de operare diferite în momente diferite. În acest capitol, ne vom uita la formatul Portable Executable (PE). Formatul PE este formatul principal pentru stocarea fișierelor executabile în sistemul de operare Windows. Adunări. Fișierele NET sunt, de asemenea, stocate în acest format.

În plus, formatul PE poate fi folosit pentru a reprezenta fișiere obiect. Fișierele obiect sunt utilizate pentru a organiza compilarea separată a unui program. Scopul compilării separate este că părțile programului (modulele) sunt compilate independent în fișiere obiect, care sunt apoi legate de linker într-un singur fisier executabil.

Și acum - puțină istorie. Formatul PE a fost creat de dezvoltatorii Windows NT. Anterior, sistemul de operare Windows folosea formatele New Executable (NE) și Linear Executable (LE) pentru a reprezenta fișiere executabile și pentru a stoca fișiere obiect A fost folosit Object Module Format (OMF). Formatul NE era destinat aplicațiilor Windows pe 16 biți, în timp ce formatul LE, dezvoltat inițial pentru OS/2, era deja pe 32 de biți. Se pune întrebarea: de ce au decis dezvoltatorii Windows NT să renunțe la formatele existente? Răspunsul devine evident când te gândești că majoritatea echipei care a lucrat la crearea Windows NT a lucrat anterior la Digital Equipment Corporation. Ei dezvoltau instrumente pentru sistemul de operare VAX/VMS la DEC și aveau deja abilitățile și codul gata făcut pentru a lucra cu fișiere executabile reprezentate în Common Object File Format (COFF). În consecință, formatul COFF, într-o formă ușor modificată, a fost transferat în Windows NT și a primit numele PE.

„Glosarul .NET Framework” spune că PE este o implementare format Microsoft COFF. În același timp, se afirmă că PE este un format de fișier executabil, iar COFF este un format fișiere obiect. În general, putem observa confuzii în documentația Microsoft cu privire la numele formatului. În unele locuri îl numesc COFF, iar în altele îl numesc PE. Adevărat, se poate observa că în textele noi denumirea COFF este folosită din ce în ce mai rar. Mai mult, formatul PE este în continuă evoluție. De exemplu, în urmă cu câțiva ani, Microsoft a încetat să stocheze informații de depanare în interiorul fișierului executabil și, prin urmare, acum multe câmpuri din structurile format COFF pur și simplu nu sunt folosite. În plus, formatul COFF este pe 32 de biți și ultima editie Formatul PE (numit PE32+) poate fi utilizat pe platforme hardware pe 64 de biți. Prin urmare, aparent, lucrurile se îndreaptă spre punctul în care numele COFF nu va mai fi folosit deloc.

Este interesant de remarcat faptul că fișierele executabile în formatele vechi NE și LE sunt încă acceptate de Windows. Fișierele executabile în format NE pot fi rulate sub NTVDM (NT Virtual DOS Machine), iar formatul LE este utilizat pentru driverele de dispozitiv virtual (

Formatul de fișier executabil al sistemului de operare reflectă în mare măsură ipotezele și comportamentele încorporate în sistemul de operare. Legătura dinamică, comportamentul încărcării de pornire și gestionarea memoriei sunt doar trei exemple de proprietăți specifice sistemului de operare care pot fi înțelese atunci când studiați formatul fișierului executabil.

Fișierul executabil de pe disc și modulul primit după încărcare sunt foarte asemănătoare. Încărcătorul folosește pur și simplu fișiere Win32 mapate în memorie pentru a încărca părțile corespunzătoare ale fișierului PE în spațiul de adrese al programului. Încărcarea DLL-urilor este la fel de simplă. Odată ce modulul EXE sau .DLL este încărcat, Windows îl tratează la fel ca și alte fișiere mapate în memorie.

În Win32, prin contrast, memoria folosită pentru programe, date, resurse, tabele de intrare, tabele de ieșire și alte elemente este o matrice liniară continuă de spațiu de adrese. Tot ceea ce este suficient pentru a ști în acest caz este adresa la care încărcătorul a mapat fișierul executabil în memorie. Apoi, pentru a găsi orice element al modulului, este suficient să urmăriți pointerii care sunt stocați ca parte a mapării.

Antetul MS-DOS

Antetul MS-DOS ocupă primii 64 de octeți ai fișierului PE. Structura reprezentând conținutul antetului MS-DOS este următoarea:


typedef struct _IMAGE_DOS_HEADER (antet //DOS .EXE
USHORT e_magic; //MZ
USHORT e_cblp; //Octeți pe ultimul
//pagină fișier
USHORT e_cp; //Pagini din fișier
USHORT e_crlc; //Setări
USHORT e_cparhdr; //Dimensiunea antetului în
//paragrafe
USHORT e_minalloc; //Memorie minimă alocată
USHORT e_maxalloc; //Memorie maximă alocată
USHORT e_ss; //Inițială (relativă)
//Valoare SS
USHORT e_sp; //Valoarea SP inițială
USHORT e_csum; //Verificați suma
USHORT e_ip; //Valoarea IP inițială
USHORT e_cs; //Inițială (relativă)
//Valoare CS
USHORT e_lfarlc; //adresa fișierului tabelului de configurare
USHORT e_ovno; //Număr suprapus
USHORT e_res ; //Cuvinte rezervate
USHORT e_oemid; //Identificator OEM (pentru
//e_oeminfo)
USHORT e_oeminfo; //informații OEM; e_oemid
//specific
USHORT e_res2 ; //Cuvinte rezervate
LONG e_lfanew; //adresa decalajului antetului PE
) IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;

Antetul principal al fișierului PE reprezintă o structură de tip IMAGE_NT_HEADERS, definită în fișierul WINNT.H. Structura IMAGE_NT_HEADERS în memorie este ceea ce Windows folosește ca bază de date a modulelor în memorie. Fiecare fișier EXE sau DLL încărcat este reprezentat în Windows de structura IMAGE_NT_HEADERS. Această structură constă dintr-un cuvânt dublu și două substructuri, după cum se arată mai jos:

Semnătura DWORD;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;

Semnătura fișierului PE

Câmpul Signature, reprezentat ca un cod ASCII, este PE\0\0 (doi zero octeți după PE). Dacă câmpul e_lfanew din antetul DOS a indicat desemnarea NE în acest loc în loc de desemnarea PE, atunci lucrați cu un fișier Win16 NE. În mod similar, dacă desemnarea LE este specificată în câmpul Signature, atunci acesta este un fișier VxD (VirtualDeviceDriver - driver dispozitiv virtual). Denumirea LX se referă la un fișier de la vechiul rival al Windows 95, OS/2.

Antet fișier PE

Cuvântul dublu - semnătura PE - din antetul fișierului PE este urmat de o structură de tip IMAGE_FILE_HEADER. Câmpurile acestei structuri conțin doar cele mai multe Informații generale despre dosar.
Următoarele sunt câmpurile IMAGE_FILE_HEADER:

typedef struct _IMAGE_FILE_HEADER
{
Masina USHORT;
USHORT NumberOfSections;
ULONG TimeDateStamp;
ULONG PointerToSymbolTable;
ULONG NumberOfSymbols;
USHORT SizeOfOptionalHeader;
Caracteristici USHORT;
) IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER

Mașinărie- Acest CPU, pentru care este destinat dosarul. Sunt definiți următorii identificatori de procesor:

Intel I386 0xl4C
Intel I860 0xl4D
MIPS R3000 0x162
MIPS R4000 0x166
DEC Alpha AXP 0x184
Power PC 0x1F0 (Little Endian)
Motorola 68000 0x268
PA RISC 0x290 (Arhitectură de precizie)

NumberOfSections– numărul de secțiuni din fișierul EXE sau OBJ.

TimeDateStamp– ora la care fișierul a fost creat de către linker (sau compilator, dacă este un fișier OBJ). Acest câmp indică numărul de secunde care au trecut de la 16:00 12/31/1969

PointerToSymbolTable– offset fișier al tabelului de simboluri COFF. Acest câmp este utilizat numai în fișierele OBJ și PE cu informații de depanare COFF. Fișierele PE acceptă o varietate de formate de depanare, așa că depanatorii trebuie să se refere la intrarea IMAGE_DIRECTORY_ENTRY_DEBUG din directorul de date.

NumberOfSymbols– numărul de simboluri din tabelul de simboluri COFF.

SizeOfOplionalHeader– dimensiunea antetului opțional care poate urma această structură. În fișierele executabile, aceasta este dimensiunea structurii IMAGE_OPTIONAL_HEADER care urmează această structură.

Caracteristici– steaguri care conțin informații despre fișier. Câteva domenii importante sunt descrise aici.
0x0001 – fișierul nu conține mișcări
0x0002 – fișierul reprezintă o mapare executabilă (adică nu este un fișier OBJ sau LIB)
0x2000 – fișierul este o bibliotecă de linkuri dinamice (DLL), nu un program

Antet opțional fișier PE

A treia componentă a antetului fișierului PE este o structură de tip IMAGE_OPTIONAL_HEADER. Pentru fișierele PE această parte este obligatorie. Cele mai importante câmpuri sunt câmpurile ImageBase și Subsystem.

ImageBase- Când linkerul creează un fișier executabil, se așteaptă ca fișierul să fie mapat într-o anumită locație din memorie și această adresă este stocată în acest câmp.

Subsistemul– tipul de subsistem pe care acest executabil îl folosește pentru interfața sa cu utilizatorul. WINNT.H definește următoarele valori:
NATIVE = 1 – nu este necesar niciun subsistem (de exemplu, pentru un driver de dispozitiv)
WINDOWS_GUI = 2 – rulează în subsistemul Windows GUI
WINDOWS_GUI = 3 – rulează în subsistemul de caractere Windows (aplicație terminală)
OS2_GUI = 5 – rulează în subsistemul OS/2 (numai aplicațiile OS/2 IJC)
POSIX_GUI = 7 – rulează în subsistemul Posix

Tabelul secțiunilor

Imediat după antetul fișierului PE din memorie există o matrice de 1MAGE_SECT10N_HEADER. Aceasta masa. conține informații despre fiecare secțiune de afișare. Numărul de elemente ale acestui tablou este specificat în antetul fișierului PE (câmpul IMAGE_NT_HEADER.FileHeader.NumberOfSections). Secțiunile din afișaj sunt ordonate după adresa de pornire, nu în ordine alfabetică.
Fiecare IMAGE_SECTION_HEADER reprezintă o bază de date completă a unei secțiuni a unui fișier EXE sau OBJ și are următorul format.

#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER
{
Nume UCHAR;
uniune (
ULONG PhysicalAddress;
ULONG VirtualSize;
) Diverse;
ULONG VirtualAddress;
ULONG SizeOfRawData;
ULONG PointerToRawData;
ULONG PointerToRelocations;
ULONG PointerToLinennumbers;
USHORT NumberOfRelocations;
USHORT NumberOfLinennumbers;
ULONG Caracteristici;
) IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Nume– Un nume ANSI de 8 octeți (nu Unicode) care denumește secțiunea.

Diverse– acest domeniu are diverse scopuriîn funcție de faptul dacă apare într-un fișier EXE sau OBJ. Într-un fișier EXE, acesta conține dimensiunea virtuală a unei secțiuni de cod de program sau date. Pentru fișierele OBJ, acest câmp specifică adresa fizică a secțiunii.

Adresă virtuală– în cazul fișierelor EXE, acest câmp conține RVA unde încărcătorul ar trebui să mapeze secțiunea. Instrumentele Microsoft setează RVA implicit al primei secțiuni la 0x101. Pentru fișierele obiect, acest câmp este setat la 0.

SizeOfRawData– în fișierele EXE, acest câmp conține dimensiunea secțiunii aliniată la cea mai apropiată limită superioară a dimensiunii fișierului.
PointerToRawData – offset de fișier al zonei în care se află datele sursă pentru secțiune. Dacă utilizatorul mapează fișierul PE sau COFF în memorie (în loc să îl încarce sistemul de operare), acest câmp este mai important decât în ​​VirtualAddress.

PointerToRelocations– în fișierele obiect, acesta este decalajul fișierului al informațiilor de corecție care urmează datele sursă pentru o anumită secțiune. În fișierele EXE, acest câmp este setat la 0.

PointerToLinenumhers– offset fișier al tabelului de numere de rând. Tabelul cu numere de linii potrivește numerele de rând ale fișierului sursă cu adresele la care puteți găsi codul generat pentru o anumită linie. De obicei, numai secțiunile de cod (cum ar fi .text sau CODE) au numere de linie. În fișierele EXE, numerele de rând sunt colectate la sfârșitul fișierului, după datele sursă pentru secțiuni. În fișierele obiect, tabelul cu numere de rând pentru o secțiune urmează datele sursă ale secțiunii și tabelul de relocare pentru acea secțiune.

NumberOfRelocations– numărul de mișcări din tabelul de corecție pentru această secțiune (utilizat doar în fișierele obiect).
NumberOfLinennumbers – numărul de numere de linie din tabelul de numere de linie pentru această secțiune.
Caracteristici – un set de steaguri care indică atributele secțiunii (program/date, destinat citirii, destinat scrierii etc.).

Secțiuni frecvente

Secțiunea.text(sau COD

Această secțiune conține tot codul programului scop general, generat de un compilator sau un asamblator. Linker-ul combină toate secțiunile .text din diferitele fișiere obiect într-o secțiune mare .text din fișierul EXE.

Sectiunea.date(sau DATE, dacă fișierul PE este creat de Borland C++)

Datele inițializate intră în secțiunea .data. Datele inițializate constau din acele variabile globale și statice care au fost inițializate în momentul compilării. Acestea includ, de asemenea, șiruri literale (de exemplu, șirul " Salut Lume" într-un program C/C++). Linker-ul combină toate secțiunile .data din diferite fișiere obiect și LIB într-o singură secțiune .data din fișierul EXE. Variabilele locale sunt situate pe lanțul stivei și nu ocupă spațiu în fișierul .data. și secțiunile .bss.

Sectiunea.bss

Secțiunea .bss stochează variabile statice și globale neinițializate. Linker-ul combină toate secțiunile .bss din diferite fișiere obiect și LIB într-o singură secțiune .bss din fișierul EXE.

Sectiunea.CRT

O altă secțiune pentru date inițializate, folosită de bibliotecile runtime programe Microsoft C/C++. Datele din această secțiune sunt folosite în scopuri precum apelarea constructorilor de clasă statică C++ înainte de a apela main sau WinMain.

Sectiunea.rsrc

Secțiunea .rsrc conține resurse de modul.

Sectiunea.idata

Secțiunea.idata (sau tabelul de import) conține informații despre funcțiile (și datele) pe care modulul le importă din alte DLL-uri. Tabelul de import începe cu o matrice formată din IMAGE_IMPORT_DESCRIPTOR. Fiecare element (IMAGE_IMPORT_DESCRIPTOR) corespunde unuia dintre DLL-urile cu care este asociat implicit fișierul PE. Numărul de elemente din matrice nu este luat în calcul nicăieri. În schimb, ultima structură a matricei IMAGE_IMPORT_DESCRIPTOR are câmpuri care conțin NULL.
Structura IMAGE_IMPORT_DESCRIPTOR are următorul format

typedef struct _IMAGE_IMPORT_DESCRIPTOR (
uniune (
Caracteristici DWORD;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;

DWORD ForwarderChain;
Nume DWORD;
DWORD FirstThunk;
) IMAGE_IMPORT_DESCRIPTOR

Caracteristici/OriginalFirstThunk– Acest câmp conține offset-ul (RVA) al matricei de cuvinte duble. Fiecare dintre aceste cuvinte duble este de fapt o unire a IMAGE_THUNK_DATA. Fiecare cuvânt dublu IMAGE_THUNK_DATA corespunde unei funcții importate de acel fișier EXE sau DLL.

TimeDateStamp– o ștampilă de timp și dată care indică momentul în care a fost creat acest fișier.
ForwarderChain - acest câmp este legat de transfer, atunci când un DLL transmite un link către unele dintre funcțiile sale către un alt DLL.
Numele este RVA al șirului caractere ASCII, care se termină cu zero și care conține numele DLL-urilor care urmează să fie importate.

First Thunk– Offset RVA al matricei de cuvinte duble IMAGE_THUNK_DATA. În cele mai multe cazuri, un cuvânt dublu este tratat ca un indicator către structura IMAGE_IMPORT_BY_NAME. Această structură arată astfel:

typedef struct _IMAGE_IMPORT_BY_NAME (
WORD Sugestie;
BYTE Nume;
) IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

Aluzie– numărul de export al funcției de import.
Nume– Un șir ASCIIZ cu numele funcției importate.

Un fragment dintr-un program care citește dintr-un fișier PE o listă de funcții OS importate de program.

Următorul fragment al programului scrie o listă de biblioteci cu funcții importate de program în fișierul FunctionList.txt.

  1. void ShowImportFunction()

    BYTE *pImagine = (BYTE*) GetModuleHandle(NULL) ;

    IMAGE_DOS_HEADER *idh;

    IMAGE_OPTIONAL_HEADER *ioh;

    IMAGE_SECTION_HEADER *ish;

    IMAGE_IMPORT_DESCRIPTOR *iid;

    IMAGE_IMPORT_BY_NAME *ibn;

    IMAGE_THUNK_DATA *mulțumesc;

    int i = 0;

    DWORD j = 0 ;

    char lib = „Biblioteca importată:”;

  2. Fișier HANDLE = CreateFile(TEXT("FunctionList.txt") ,GENERIC_READ|GENERIC_WRITE,

    0 ,0 ,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS,0 ) ;

    idh = (IMAGE_DOS_HEADER*) pImagine;

    ioh = (IMAGE_OPTIONAL_HEADER*)

    (pImagine + idh->e_lfanew + 4 +

    dimensiunea (IMAGE_FILE_HEADER) );

    ish = (IMAGE_SECTION_HEADER*) ((DWORD) ioh + sizeof (IMAGE_OPTIONAL_HEADER) ) ;

    pentru (i = 0; i< 16 ; i++)

Indiferent dacă computerul este pornit sau nu, toate datele și programele sunt stocate în memoria de lungă durată (externă) a computerului sub formă de fișiere - de unde sunt încărcate în timpul execuției sau procesării.

Fişier este un anumit set de coduri care afișează o anumită cantitate de informații asociate unui tip sau scop, cărora i se atribuie un nume unic și care este stocată în memoria pe termen lung.

Textele sursă ale programelor, programe gata de executat, documente, imagini grafice și orice alte date pot fi stocate sub formă de fișiere. În funcție de tipul de organizare și de conținut, fișierele sunt împărțite în două categorii - text și binar (binar). Fișiere text în conformitate cu scopul lor, ele stochează șiruri de caractere interpretate ca texte. Fișiere executabile constau din coduri de program ale programelor gata de executare.

Numele unice oferă o modalitate de a organiza fișierele și de a le face accesibile sistemelor de operare și altor programe. Numele fișierului este format din două părți separate printr-un punct: de fapt Nume dosar și extensie , definindu-i tipul (program, date etc.) Numele fișierului este atribuit de utilizator (uneori de către sistem). Tipul de fișier este de obicei setat automat de program atunci când este creat, ceea ce vă permite să automatizați lansarea programelor în majoritatea cazurilor. De exemplu, .com, .exe– fișiere executabile (programe), .txt, .rtf . docfișiere text, .pastext original program scris în limbaj Pascal .

Pentru a organiza plasarea fișierelor pe discuri, numele acestora sunt înregistrate în fisiere speciale– directoare (în sistemele de operare moderne aceste fișiere sunt numite foldere) . Catalog acesta este un fișier tabel (stocat pe același disc cu fișierele), care stochează numele fișierelor, informații despre dimensiunea acestora, timpul ultima actualizare, atributele fișierului (proprietăți), etc. Dacă un director stochează numele unui fișier, se spune adesea că fișierul este „localizat” în acel director. În realitate, fișierul este localizat (salvat) într-o zonă de memorie de pe discul computerului, adesea sub formă de mai multe părți, fragmente pe diferite piste și discuri ale pachetului (pe zone libere ale suportului). Informațiile relevante sunt conținute în catalog.

Fiecare disc poate avea multe directoare - numărul lor este determinat de oportunitate și este limitat doar de capacitatea discului. Acest lucru este valabil și pentru numărul de fișiere din director. Toate sistemele moderne de operare pe disc asigură crearea unui sistem de fișiere conceput pentru a organiza stocarea datelor și a oferi acces la acesta. Principiul organizării sistemului de fișiere este tabelar. Suprafața unui hard disk este considerată ca o matrice tridimensională, ale cărei dimensiuni sunt numerele de suprafață, cilindru și sector. Un cilindru este înțeles ca un set de toate pistele aparținând unor suprafețe diferite și situate la o distanță egală de axa de rotație. Datele despre locul în care pe disc este înregistrat un anumit fișier sunt stocate în zona de sistem a discului în tabele speciale de alocare a fișierelor (tabele FAT).

Ordinea în care fișierele sunt stocate pe disc este determinată de organizație Sistemul de fișiere(organizarea directoarelor și metoda de descriere a plasării și atributelor fișierelor din acestea).

Sute de mii de fișiere sunt stocate pe discuri, astfel încât, pentru ușurința căutării, fișierele sunt organizate sub forma unui sistem de fișiere pe mai multe niveluri, care are structura prezentată în figură.

Directorul inițial, rădăcină, conține subdirectoare de nivelul 1, la rândul lor, fiecare dintre ele are subdirectoare de nivelul 2 etc. Fiecare director are un nume (fără extensie) și poate fi înregistrat în altul, părintească catalog. Trebuie remarcat faptul că directoarele de la toate nivelurile pot stoca nu numai directoare, ci și fișiere.

Deși datele despre locația fișierului sunt de fapt stocate sub formă de tabel, pentru confortul utilizatorului, acestea sunt prezentate ca arbore ierarhic structurile și toate conexiunile necesare sunt furnizate de sistemul de operare.

Funcțiile de întreținere a sistemului de fișiere includ următoarele operațiuni efectuate sub controlul sistemului de operare:

    crearea și denumirea fișierelor;

    crearea și denumirea directoarelor;

    redenumirea fișierelor și directoarelor;

    copierea și mutarea fișierelor între unități de computer și între directoare de pe aceeași unitate;

    ștergerea fișierelor și directoarelor;

    navigarea prin structura de fișiere pentru a accesa un anumit fișier sau director;

    gestionarea atributelor fișierelor.

Typedef struct _IMAGE_FILE_HEADER ( WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Caracteristici; ) IMAGE_FILE_HEADER;FILE_HEADER;
Voi descrie doar sec aceste domenii, pentru că... numele sunt intuitive și reprezintă semnificații directe, și nu VA, RVA, RAW și alte lucruri înfricoșătoare, intrigante despre care până acum am auzit doar de la vechii pirați. Deși am întâlnit deja RAW - acestea sunt doar decalaje față de începutul fișierului (se mai numesc și pointeri bruti sau offset fișier). Adică, dacă avem o adresă RAW, aceasta înseamnă că trebuie să trecem de la începutul fișierului la pozițiile RAW ( ptrFile+ RAW). Apoi puteți începe să citiți valorile. Un exemplu izbitor de acest tip este e_lfnew- despre care am discutat mai sus la rubrica Dos.

*Mașinărie: WORD - acest număr (2 octeți) specifică arhitectura procesorului pe care aceasta aplicație poate fi efectuată.
NumberOfSections: DWORD - numărul de secțiuni din fișier. Secțiunile (în continuare le vom numi un tabel de secțiuni) urmează imediat după antet (PE-Header). Documentația spune că numărul de secțiuni este limitat la 96.
TimeDateStamp: WORD - un număr care stochează data și ora la care a fost creat fișierul.
PointerToSymbolTable: DWORD este decalajul (RAW) față de tabelul de simboluri, iar SizeOfOptionalHeader este dimensiunea acestui tabel. Acest tabel este menit să servească pentru stocarea informațiilor de depanare, dar detașamentul nu a observat pierderea unui soldat încă de la începutul serviciului său. Cel mai adesea acest câmp este șters cu zerouri.
SIzeOfOptionHeader: WORD - dimensiunea antetului opțional (care urmează imediat celui curent) Documentația precizează că pentru un fișier obiect este setat la 0...
*Caracteristici: WORD - caracteristicile fișierului.

* - câmpuri care sunt definite de un interval de valori. Mese valori posibile prezentate în descrierea structurii de la birou. site-ul web și nu va fi listat aici, deoarece Nu au nimic deosebit de important pentru înțelegerea formatului.

Să părăsim această insulă! Trebuie să mergem mai departe. Punctul de referință este o țară numită Optional-Header.

„Unde este harta, Billy? Am nevoie de o hartă.”
(Insula comoara)

Antet opțional (IMAGE_OPTIONAL_HEADER)

Titlul acestui continent nu este foarte bun. Acest antet este obligatoriu și are 2 formate PE32 și PE32+ (IMAGE_OPTIONAL_HEADER32 și, respectiv, IMAGE_OPTIONAL_HEADER64). Formatul este stocat în câmp Magie: CUVÂNT. Antetul conține informațiile necesare pentru a descărca fișierul. Ca întotdeauna :

IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER ( WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseCodeData; DWORD Basement Image;DWORD Basement; WORD MajorOperatingSystemVersion MinorSubsystemValue izeOfStackCommit; DWORD SizeOfHeapCommit DWORD NumberOfRvaAndSizes, *PIMAGE_OPTIONAL_HEADER;


*Ca întotdeauna, vom examina doar câmpurile principale care au cel mai mare impact asupra înțelegerii descărcării și a modului de a avansa cu fișierul. Să fim de acord - câmpurile acestei structuri conțin valori cu adrese VA (adresă virtuală) și RVA (adresă virtuală relativă). Acestea nu sunt adrese RAW și trebuie să le puteți citi (sau mai degrabă numărați). Cu siguranță vom învăța cum să facem acest lucru, dar mai întâi vom analiza structurile care se succed pentru a nu ne confunda. Deocamdată, amintiți-vă - acestea sunt adrese către care, după calcule, indică loc anumeîn dosar. De asemenea, vei întâlni un nou concept - aliniere. O vom lua în considerare împreună cu adresele RVA, deoarece acestea sunt destul de strâns legate.

AddressOfEntryPoint: DWORD - adresa RVA a punctului de intrare. Poate indica orice punct din spațiul de adrese. Pentru fișierele .exe, punctul de intrare corespunde adresei de la care începe execuția programului și nu poate fi egal cu zero!
BaseOfCode: DWORD - RVA de la începutul codului programului (secțiunea cod).
BaseOfData: DWORD - RVA de la începutul codului programului (secțiuni de date).
ImageBase: DWORD - adresa de bază preferată pentru încărcarea programului. Trebuie să fie un multiplu de 64 kb. În cele mai multe cazuri, este egal cu 0x00400000.
SectionAlignment: DWORD - dimensiunea de aliniere (octeți) a secțiunii când se încarcă în memorie virtuala.
FileAlignment: DWORD - dimensiunea de aliniere (octeți) a secțiunii din interiorul fișierului.
SizeOfImage: DWORD - dimensiunea fișierului (în octeți) din memorie, inclusiv toate anteturile. Trebuie să fie un multiplu al SectionAligment.
SizeOfHeaders: DWORD - dimensiunea tuturor antetelor (DOS, DOS-Stub, PE, Secțiune) aliniate la FileAligment.
NumberOfRvaAndSizes: DWORD - numărul de directoare din tabelul de directoare (tabelul în sine este mai jos). În acest moment, acest câmp este întotdeauna egal cu constanta simbolică IMAGE_NUMBEROF_DIRECTORY_ENTRIES, care este egală cu 16.
DataDirectory: IMAGE_DATA_DIRECTORY - director de date. Mai simplu spus, aceasta este o matrice (de dimensiunea 16), fiecare element conține o structură de 2 valori DWORD.

Să ne uităm la ce este structura IMAGE_DATA_DIRECTORY:

Typedef struct _IMAGE_DATA_DIRECTORY ( DWORD VirtualAddress; DWORD Size; ) IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
Ce avem? Avem o matrice de 16 elemente, fiecare element conține o adresă și o dimensiune (ce? cum? de ce? toate într-un minut). Se pune întrebarea care sunt exact aceste caracteristici. Pentru aceasta, Microsoft are constante speciale pentru potrivire. Ele pot fi văzute chiar la sfârșitul descrierii structurii. Între timp:

// Directory Entries #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Director #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Director #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Director de resurse #define IMAGE_DIRECTORY_ENTRY_EXCEPTION Director #3 //Securitate RECTORY_ENTRY_EXCEPTION 3 ////////////////////////////////////////////////////////////////////////////////////,,,,,,,,,,,,,,, Directorul #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 / / Tabelul de relocare de bază #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Director de depanare // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (utilizare X86) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Date specifice arhitecturii #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT // IMMAGE_DIRECTORY_DEFINE_DEFINIȚI_REGLP TORY_ENTRY_TLS 9 // Director TLS # define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Încarcă directorul de configurare #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Directorul de import legat în anteturi #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Tabel de adrese de import #define IMAGE_DIRECTORY_DIRECTORY_DELAY_ENTRY_DELAY_DEFINIȚI DESCRIPTOR #IMPORTAGE_1 /Definire Descriptorul de import TORY_ENTRY_ COM_DESCRIPTOR 14 // Descriptor de rulare COM
Da! Vedem că fiecare element al matricei este responsabil pentru tabelul atașat acestuia. Dar vai și a, aceste țărmuri ne sunt încă inaccesibile, pentru că... nu știm să lucrăm cu adrese VA și RVA. Și pentru a învăța, trebuie să studiem ce secțiuni sunt. Vă vor spune despre structura și activitatea lor, după care va deveni clar de ce sunt necesare VA, RVA și aliniamente. În sensul acestui articol, ne vom referi doar la exporturi și importuri. Scopul câmpurilor rămase poate fi găsit în birou. documentaţie sau în cărţi. Deci aici este. Câmpurile reale:

Adresă virtuală: DWORD - RVA pentru tabelul căruia îi corespunde elementul de matrice.
mărimea: DWORD - dimensiunea tabelului în octeți.

Asa de! Pentru a ajunge la astfel de țărmuri exotice precum tabele de importuri, exporturi, resurse și altele, trebuie să trecem printr-o căutare cu secțiuni. Ei bine, cabane, să aruncăm o privire pe harta generală, să stabilim unde suntem acum și să mergem mai departe:

Și ne aflăm direct în fața întinderilor largi ale secțiilor. Trebuie neapărat să aflăm ce ascund ei și, în sfârșit, să ne dăm seama de un alt tip de adresare. Vrem aventuri adevărate! Vrem să mergem rapid în republici precum mesele de import și export. Bătrânii pirați spun că nu toți au putut să ajungă la ei, dar cei care au făcut-o s-au întors cu aur și femei cu cunoștințe sfinte despre ocean. Pornim și ne îndreptăm către antetul Secțiunii.

„Ești destituit, Silver! Dă-te jos din butoi!”
(Insula comoara)

Antet de secțiune (IMAGE_SECTION_HEADER)


Chiar în spatele matricei DataDirectory secțiunile se succed. Tabelul secțiunii reprezintă un stat suveran, care este împărțit în NumberOfSections orase. Fiecare oraș are propria sa ambarcațiune, propriile sale drepturi și, de asemenea, o dimensiune de 0x28 octeți. Numărul de secțiuni este indicat în câmp NumberOfSections, care este stocat în antetul fișierului. Deci, să ne uităm la structură:

Typedef struct _IMAGE_SECTION_HEADER ( BYTE Name; union ( DWORD PhysicalAddress; DWORD VirtualSize; ) Diverse; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenONumbers.WORD Numerele DWORD; caracteristici ) IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Nume: BYTE - numele secțiunii. În prezent, are 8 caractere.
VirtualSize: DWORD - dimensiunea secțiunii în memoria virtuală.
SizeOfRawData: DWORD - dimensiunea secțiunii în fișier.
Adresă virtuală: DWORD - adresa sectiei RVA.
SizeOfRawData: DWORD - dimensiunea secțiunii în fișier. Trebuie să fie multiplu FileAlignment.
PointerToRawData: DWORD - RAW offset la începutul secțiunii. Trebuie să fie și multiplu FileAlignment
Caracteristici: DWORD - atribute de acces la secțiune și reguli pentru încărcarea acesteia în virtual. memorie. De exemplu, un atribut pentru definirea conținutului unei secțiuni (date inițiale, date non-inițiale, cod). Sau accesați atributele - citiți, scrieți, executați. Aceasta nu este întreaga lor gamă. Caracteristicile sunt stabilite de constante din același WINNT.h, care încep cu IMAGE_SCN_. Vă puteți familiariza cu atributele secțiunilor mai detaliat. Atributele din cărțile lui Chris Kaspersky sunt, de asemenea, bine descrise - lista de referințe este la sfârșitul articolului.

În ceea ce privește numele, ar trebui să rețineți următoarele - secțiunea cu resurse ar trebui să aibă întotdeauna numele.rsrc. În caz contrar, resursele nu vor fi încărcate. În ceea ce privește secțiunile rămase, numele poate fi orice. De obicei, există nume semnificative, de exemplu .data, .src, etc... Dar se întâmplă și:

Secțiunile sunt o zonă care este descărcată în memoria virtuală și toată munca se întâmplă direct cu aceste date. Adresa din memoria virtuală, fără nicio compensare, se numește Adresă virtuală, prescurtată VA. Adresă preferată pentru descărcarea aplicației, setată în câmp ImageBase. Acesta este ca punctul în care începe zona aplicației din memoria virtuală. Și decalajele RVA (adresă virtuală relativă) sunt măsurate în raport cu acest punct. Adică VA = ImageBase+ RVA; ImageBaseștim mereu și având la dispoziție VA sau RVA, ne putem exprima unul prin celălalt.

Se pare că ne-am obișnuit aici. Dar aceasta este memoria virtuală! Și suntem în fizic. Memoria virtuală pentru noi acum este ca o călătorie în alte galaxii pe care ni le putem doar imagina. Deci nu putem intra în memoria virtuală momentan, dar putem afla ce va fi acolo, pentru că este luat din fișierul nostru.

Aliniere


Pentru a reprezenta corect încărcarea în virtual. memorie, este necesar să înțelegem un astfel de mecanism precum alinierea. Mai întâi, să aruncăm o privire la o diagramă a modului în care secțiunile sunt paginate în memorie.

După cum puteți vedea, secțiunea nu este încărcată în memorie în funcție de dimensiunea sa. Aici sunt folosite aliniamentele. Aceasta este o valoare care trebuie să fie un multiplu al mărimii secțiunii din memorie. Dacă ne uităm la diagramă, vom vedea că dimensiunea secțiunii este 0x28, iar dimensiunea secțiunii este 0x50. Acest lucru se datorează dimensiunii de aliniere. 0x28 „nu atinge” 0x50 și, ca urmare, secțiunea va fi descărcată, iar spațiul rămas în dimensiunea 0x50-0x28 va fi pus la zero. Și dacă dimensiunea secțiunii a fost mai mare decât dimensiunea de aliniere, atunci ce? De exemplu Dimensiunea secțiunii= 0x78, a secțiuneAliniere= 0x50, adică ramas neschimbat. În acest caz, secțiunea ar ocupa 0xA0 (0xA0 = 0x28 * 0x04) octeți în memorie. Adică o valoare care este un multiplu al secțiuneAliniereși acoperă complet Dimensiunea secțiunii. Trebuie remarcat faptul că secțiunile din fișier sunt aliniate într-un mod similar, doar după dimensiune FileAlignment. După ce am primit baza necesară, ne putem da seama cum să convertim de la RVA la RAW.

„Aceasta nu este o câmpie, clima aici este diferită.”
(V.S. Vysotsky)

O mică lecție de aritmetică


Înainte de a începe execuția, o parte a programului trebuie trimisă în spațiul de adrese al procesorului. Spațiul de adrese este cantitatea de date adresată fizic de procesor. memorie cu acces aleator. „Piesa” din spațiul de adrese în care este descărcat programul se numește imagine virtuală. Imaginea se caracterizează prin adresa de bază de descărcare (baza imaginii) și dimensiunea (dimensiunea imaginii). Deci VA (Virtual address) este adresa relativă la începutul memoriei virtuale, iar RVA (Relative Virtual Address) este relativă la locul în care a fost descărcat programul. Cum să aflați adresa de bază de descărcare a unei aplicații? În acest scop există un câmp separat în antetul opțional numit ImageBase. Acesta a fost un mic preludiu pentru a vă împrospăta memoria. Acum să ne uităm la o reprezentare schematică a diferitelor adrese:

Deci, cum puteți citi informații dintr-un fișier fără a le arunca în memoria virtuală? Pentru a face acest lucru, trebuie să convertiți adresele în Format RAW. Apoi putem păși în fișier în zona de care avem nevoie și citim datele necesare. Deoarece RVA este adresa de memorie virtuală la care au fost proiectate datele din fișier, putem face procesul invers. Pentru a face acest lucru avem nevoie de o cheie nouă pe șaisprezece aritmetică simplă. Iată câteva formule:

VA = ImageBase + RVA; RAW = RVA - sectionRVA + rawSection; // rawSection - offset față de secțiunea de la începutul fișierului // sectionRVA - RVA secțiunii (acest câmp este stocat în interiorul secțiunii)
După cum puteți vedea, pentru a calcula RAW, trebuie să stabilim secțiunea căreia îi aparține RVA. Pentru a face acest lucru, trebuie să parcurgeți toate secțiunile și să verificați următoarele condiții:

RVA >= sectionVitualAddress && RVA< ALIGN_UP(sectionVirtualSize, sectionAligment) // sectionAligment - выравнивание для секции. Значение можно узнать в Optional-header. // sectionVitualAddress - RVA секции - хранится непосредственно в секции // ALIGN_UP() - функция, определяющая сколько занимает секция в памяти, учитывая выравнивание
Punând toate puzzle-urile împreună, obținem această listă:

Typedef uint32_t DWORD; typedef uint16_t WORD; typedef uint8_t BYTE; #define ALIGN_DOWN(x, aliniați) (x și ~(aliniați-1)) #define ALIGN_UP(x, aliniați) ((x și (aliniați-1))?ALIGN_DOWN(x,aliniați)+aliniați:x) // IMAGE_SECTION_HEADER secțiuni; // init secțiuni matrice int defSection(DWORD rva) ( pentru (int i = 0; i< numberOfSection; ++i) { DWORD start = sections[i].VirtualAddress; DWORD end = start + ALIGN_UP(sections[i].VirtualSize, sectionAligment); if(rva >= start && rva< end) return i; } return -1; } DWORD rvaToOff(DWORD rva) { int indexSection = defSection(rva); if(indexSection != -1) return rva - sections.VirtualAddress + sections.PointerToRawData; else return 0; }
*Nu am inclus o declarație de tip sau o inițializare a matricei în cod, ci am furnizat doar funcții care vor ajuta la calcularea adreselor. După cum puteți vedea, codul nu a fost foarte complicat. Doar un pic confuz. Acest lucru dispare... dacă petreci puțin mai mult timp găzduind .exe prin dezasamblator.

URA! Ne-am dat seama. Acum putem merge pe tărâmurile resurselor, importam și exportăm biblioteci și, în general, oriunde dorește inima noastră. Tocmai am învățat cum să lucrăm cu un nou tip de adresare. Să pornim la drum!

"-Nu e rău nu e rău! Totuși, și-au primit rațiile pentru azi!”
(Insula comoara)

Export tabel


În primul element al matricei DataDirectory RVA este stocat în tabelul de export, care este reprezentat de structura IMAGE_EXPORT_DIRECTORY. Acest tabel este comun pentru fișierele din bibliotecă dinamică (.dll). Scopul principal al tabelului este de a lega funcțiile exportate la RVA lor. Descrierea este prezentată în birou. Specificații:

Typedef struct _IMAGE_EXPORT_DIRECTORY ( DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfFunctions; DWORDNames_ORDF; DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
Această structură conține trei indicii către trei tabele diferite. Acesta este un tabel de nume (funcții) ( AddressOfNames), ordinale( AddressOfNamesOrdinals), adrese( AddressOfFunctions). Câmpul Nume stochează RVA al numelui bibliotecii dinamice. Ordinalul este ca un intermediar între tabelul de nume și tabelul de adrese și este o matrice de indici (dimensiunea indexului este de 2 octeți). Pentru o mai mare claritate, luați în considerare diagrama:

Să ne uităm la un exemplu. Să presupunem că elementul i al matricei names indică numele funcției. Apoi adresa acestei funcții poate fi obținută accesând elementul i-lea din tabloul de adrese. Acestea. i este un ordinal.

Atenţie! Dacă luați de exemplu al 2-lea element dintr-un tabel de ordinale, nu înseamnă 2 - este un ordinal pentru tabelele de nume și adrese. Indicele este valoarea stocată în al doilea element al matricei de ordinale.

Numărul de valori din tabelele de nume ( NumberOfNames) și ordinalele sunt egale și nu coincid întotdeauna cu numărul de elemente din tabelul de adrese ( NumberOfFunctions).

„Au venit după mine. Vă mulțumim pentru atenție. Acum trebuie să ucidă!”
(Insula comoara)

Import tabel


Tabelul de import este o parte integrantă a oricărei aplicații care utilizează biblioteci dinamice. Acest tabel ajută la corelarea apelurilor la funcțiile dinamice ale bibliotecii cu adresele corespunzătoare. Importul poate avea loc în trei moduri diferite: import standard, legat și import întârziat. Deoarece Subiectul importului este destul de multifațet și merită un articol separat, voi descrie doar mecanismul standard, iar restul îl voi descrie doar ca un „schelet”.

Import standard- V DataDirectory Tabelul de import este stocat sub indexul IMAGE_DIRECTORY_ENTRY_IMPORT(=1). Este o matrice de elemente de tip IMAGE_IMPORT_DESCRIPTOR. Tabelul de import stochează (într-o matrice) numele funcțiilor/ordinalelor și unde ar trebui să scrie încărcătorul adresa efectivă a acestei funcții. Acest mecanism nu este foarte eficient, deoarece Sincer vorbind, totul se reduce la căutarea în întregul tabel de export pentru fiecare funcție necesară.

Import legat- cu această schemă de lucru în domenii (în primul element masa standard import) TimeDateStamp și ForwardChain sunt setate la -1 și informațiile de legare sunt stocate în celulă DataDirectory cu indexul IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(=11). Adică, acesta este un fel de semnalizare pentru încărcător de care trebuie să utilizați importul legat. De asemenea, „lanțul de import legat” are propriile sale structuri. Algoritmul de operare este următorul: biblioteca necesară este descărcată în memoria virtuală a aplicației și toate adresele necesare sunt „legate” în etapa de compilare. Unul dintre dezavantaje este că atunci când recompilați dll-ul, va trebui să recompilați aplicația în sine, deoarece adresele funcțiilor vor fi modificate.

Întârzie importul- prin aceasta metoda se presupune ca fisierul .dll este atasat celui executabil, dar nu se descarca in memorie imediat (ca in cele doua metode anterioare), ci doar atunci cand aplicatia acceseaza prima data simbolul (asta a descarcat elementele din bibliotecile dinamice sunt numite). Adică, programul este executat în memorie și de îndată ce procesul a ajuns la punctul de a apela o funcție dintr-o bibliotecă dinamică, se numește un handler special care încarcă dll-ul și distribuie adresele efective ale funcțiilor sale. Pentru import amânat, încărcătorul contactează DataDirectory (articolul numărul 15).

După ce am acoperit puțin metodele de import, să trecem direct la tabelul de import.

„Acesta este un marinar! Hainele lui erau nautice. - Da? Credeai că vei găsi un episcop aici?”
(Insula comorilor - John Silver)

Descriptor de import (IMAGE_IMPORT_DESCRIPTOR)


Pentru a afla coordonatele tabelului de import, trebuie să accesăm matricea DataDirectory. Și anume, la elementul IMAGE_DIRECTORY_ENTRY_IMPORT (=1). Și citiți adresa RVA a tabelului. Iată o diagramă generală a căii care trebuie luată:

Apoi obținem RAW de la RVA, în conformitate cu formulele date mai sus, apoi „pasăm” prin fișier. Acum ne aflăm chiar în fața unei serii de structuri numite IMAGE_IMPORT_DESCRIPTOR. Sfârșitul matricei este indicat de structura „zero”.

Typedef struct _IMAGE_IMPORT_DESCRIPTOR ( unire ( Caracteristici DWORD; DWORD OriginalFirstThunk; ) DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; ) IMAGE_IMPORT_DESCRIPTOR_DESCRIPTOR,*_PIDESCRIPTOR;
Nu am putut găsi un link către o descriere a structurii pe msdn, dar îl puteți vedea în fișierul WINNT.h. Să începem să ne dăm seama.

OriginalFirstThunk: DWORD - RVA al tabelului de nume de import (INT).
TimeDateStamp: DWORD - data și ora.
ForwarderChain: DWORD - indexul primului caracter transmis.
Nume: DWORD - șir RVA cu numele bibliotecii.
First Thunk: DWORD - RVA al tabelului de adrese de import (IAT).

Totul aici este oarecum similar cu exportul. De asemenea, un tabel de nume (INT) și, de asemenea, o cârpă de adrese pe el (IAT). De asemenea, RVA al numelui bibliotecii. Doar INT și IAT se referă la o matrice de structuri IMAGE_THUNK_DATA. Este prezentat în două forme - pentru sistemele 64 și 32 și diferă doar prin dimensiunea câmpurilor. Să ne uităm la x86 ca exemplu:

Typedef struct _IMAGE_THUNK_DATA32 ( unire ( DWORD ForwarderString; Funcție DWORD; DWORD Ordinal; DWORD AddressOfData; ) u1; ) IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
Este important să răspundem la asta actiunile urmatoare depinde de partea cea mai semnificativă a structurii. Dacă este setat, biții rămași reprezintă numărul caracterului care este importat (import după număr). În caz contrar (bitul cel mai semnificativ este șters), biții rămași specifică RVA simbolului care este importat (import după nume). Dacă avem un import după nume, atunci pointerul stochează adresa în următoarea structură:

Typedef struct _IMAGE_IMPORT_BY_NAME ( WORD Sugestie; BYTE Name; ) IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Aici Aluzie este numărul funcției și Nume- Nume.

Pentru ce este toate acestea? Toate aceste matrice, structuri... Pentru claritate, să luăm în considerare o diagramă minunată cu