Rajko.
Nenad <x...@xxx.xxx> wrote in message news:a5m2g3$qjn$1...@news.eunet.yu...
Nije bas tako jednostavno kako mislis. EXE file se sastoji od mnogo
razlicitih
blokova informacija (staticki podaci za program, masinski kod,
resources,...)
Dalje.. svaki exe file je pravljen da se izvrsava od adrese 0, pa se u fajlu
nalazi
i tablica za relokaciju, pomocu koje se sam masinski kod prepravlja u fazi
ucitavanja u memoriju.
Ni sam neznam koliko masinskih instrukcija ima Intelov procesor. Znam samo
da
svaka instukcija moze da ima 1 ili vise adresnih modova, kao i razlicit broj
operanada.
Kada se sve to stavi na papir... ima mnogo toga... (mozda oko 1000, neka me
neko ispravi
ako gresim).
Veruj mi jos jednu stvar... Ako naucis na kraju sve te instrukcije,
uhvatices sebe kako
blenes u 10 linija koda 30 min, pokusavajuci da shvatis kako to parce koda
radi...
A koda ima mnogo....
yooyo
masinski jezik je naprosto niz nula i jedinica u nekom formatu sa kojim
procesor ume da radi(da iz njega uzima jednu po jednu instrukciju).Jedan bit
je jedna 0 ili 1, a 8 njih cine bajt(1024 bajta kilobajt i tako dalje).Svaka
instrukcija procesora se sastoji iz opcoda iliti koda instrukcije i niza
parametara za tu specificnu instrukciju, a to mogu biti konkretne
vrednosti(neki broj), adrese u memoriji, ili offseti, i opet njih moze
0,1,2,3.....e sad, to nije bas sve tako u samom fajlu jer exe fajlovi imaju
opet neki svoj format, znaci prvo bi morao da znas taj format, pa da onda
vadis podatke...ali to je zaludan posao, nabavi neki deasembler, koji ce ti
to prevesti u asemblerski kod koji je ipak mnogo citljiviji(ovo mnogo je
samo u odnosu na hex varijantu, inace je i on vrlo nezgodan)
Sto se literature tice potrazi na netu, ima toga dosta...pogledaj tutorijale
za crackovanje, tu ima dosta tih detalja....
Portable EXE
by Weird
Sadrzaj:
- Rec autora
- Uvod
- Pregled strukture PE EXE fajla
- Struktura PE EXE fajla (opis)
- Standardne sekcije
- Kernel32 problem
- Kacenje
- Download
Rec autora
Ovaj projekat je napisan iskljucivo za edukativne svrhe. Autor ne odgovara
za bilo kakve posledice prouzrokovane citanjem i/ili koriscenjem projekta.
Projekat je jedinstven i bez ikakvog preterivanja se moze reci da se neke
ovde opisane stvari ne mogu vise nigde naci. Cak su i dobri virusi
zaboravili ponesto :) Smisao projekta nije da uputi citaoca samo u tajne
Win95 virusa. Naprotiv, postoji puno stvari koje mogu da proisteknu iz ovog
projekta: omiljena ideja je kriptor PE fajlova u cilju zastite programa.
Uvod
Poznato je da se razvoj Windows NTa zasniva na VAX, VMS i UNIX nasledu.
Mnogi Windows NT kreatori su ucestvovali u razvoju pomenutih operativnih
sistema pre nego sto su pristupili Micro$oftu. Logicno je da su u Windows NT
ugradili osobine koje su vec bile ranije napisane i testirane. Format
izvrsnih i objektnih fajlova koji se oni koristili bio je COFF (Common
Object File Format). On je sam za sebe bio dobra polazna tacka ali ga je
bilo potrebno izmeniti da bi odgovarao potrebama 'modernog' operativnog
sistema. Tako je nastao PE (Portable Executable) format izvrsnih fajlova.
Nazvan je 'portable' zato sto sve implementacije Windows NT-a na razlicitm
platformama: x86, MIPS, Alpha... koriste isti format izvrsnih fajlova.
Naravno, postoje razlike, na pr. u kodiranju CPU instrukcija itd, ali glavni
adut je to sto loader operativnog sistema (i razni alati) ne moraju da se
kompletno iznova pisu za svaku platformu.
Struktura PE fajla je opisana u WINNT.H header fajlu (za Borlandova
programerska okruzenja to je: NTIMAGE.H). Tu su date sve strukture koje
sluze za rad sa PE fajlovima. Na zalost, imena clanica struktura su dugacka
i deskriptivna, cesto ne odgovaraju funkciji clanice, strukture su velike,
ugnjezdene itd. Ali, zar se tako nesto ne ocekuje, s obzirom od koga je???
Mala zen poruka za buduce tvorce operativnih sistema: struktura izvrsnog
fajla nekog operativnog sistema je ujedno i pokazatelj njegovih
karakteristika.
Pregled struktura PE EXE fajla
Ovde nece biti detaljno opisana cela struktura PE fajla, vec samo (veci) deo
koji je znacajan za temu ovog projekta. Kompletni opis PE strukture se moze
naci na Internetu, mada nisam nasao ni jedan koji je ceo bio
zadovoljavajuci. Ipak, ubedljivo se istice opis koji je sastavio, ko bi
drugi nego Matt Pietrek (Nu-Mega Techologies).
Struktura PE fajla generalno izgleda ovako:
+-------------------------------+
| MS-DOS MZ header |
+-------------------------------+
| MS-DOS Real-Mode Stub program |
+-------------------------------+
| PE EXE Header |
+-------------------------------+
| PE EXE Optional Header |
+-------------------------------+
| section header #1 |
+-------------------------------+
| section header #2
+-------------------------
:
:
+------------------------------+
| section #1 |
+------------------------------+
| section #2
+--------------------
:
:
Prva vazna stvar koju treba znati je da je izgled PE fajla na disku vrlo
slican njegovom izgledu u memoriji, kada ga Loader (deo Windows KERNELa
zaduzen za ucitavanje izvrsnih fajlova) ucita. Loader koristi mehanizam
memorijski mapiranih fajlova (MMF) da mapira sve delove PE fajla u virtuelni
adresni prostor. Dakle, sve sto je potrebno znati je gde i kako Loader
mapira delove PE fajla u za to odredenom memorijskom bloku.
U vezi sa ovim, vreme je i da se objasni pojam relativne virtuelne adrese
RVA (Relative Virtual Address). RVA je jednostavno ofset nekog podatka u
odnosu na virtuelnu adresu gde je pocetak mapiranog PE fajla. Na primer: ako
Loader mapira PE fajl pocevsi od adrese 0x40000, a ako je RVA nekog podatka
0x464 tada se on moze naci u memoriji na virtuelnoj adresi: 0x40000 +
0x00464 = 0x40464. Treba uociti da postoji veza RVA nekog podatka i njegovog
fajl ofseta u PE EXE fajlu!
Jos jedan pojam treba objasniti: sekcije PE fajla (section). Sekcije su
kontinualni delovi PE fajla proizvoljne duzine koji ostaju kontinualni i u
memoriji, nakon ucitavanja fajla. Sekcije , u stvari, nose tkzv. 'sirove'
(raw) podatke: kod, data, resource itd. Kada Loader ucita PE fajl i mapira
ga u za to spremljen memorijski blok on pri tome na odreden nacin razmesta
sekcije PE fajla po tom bloku. Sama sekcija se ne dira i njen sadrzaj ostaje
istovetan sadrzaju sekcije u PE fajlu. Svaka sekcija ima odredenu namenu.
Tako postoje sekcije koje sadrze kod ili podatke, ali ima sekcija koje
generise sam linker i koje su vezane za operativni sistem.
Struktura PE EXE fajla (opis)
Sledi detaljna struktura PE EXE fajla sa stanovista programa koji bi se
'kacio' na njega, iz ma kakvog (korisnog ili stetnog:) razloga. Opis se jako
puno oslanja na strukture definisane u standardnom WINNT.H fajlu. Sve
strukture koje se spominju su jasno oznacene drugim fontom. Elementi
strukture koji su od znacaja su oznaceni crvenom zvezdicom.
MS-DOS MZ Header
Prvih 64 bajta svakog EXE fajla zauzima standardno MS-DOS zaglavlje
(header). Ono je moralo da ostane radi ocuvanja kompatibilnosti sa
prethodnim verzijama Micro$oft-ovog operativnog sistema. Njegova struktura
je: IMAGE_DOS_HEADER. Od svih elemenata strukture vredi spomenuti sledece:
WORD e_magic
Tkzv. magicni broj: prva dva bajta svakog EXE fajla moraju biti slova 'M' i
'Z' (to su inace inicijali programera koji je radio na stvaranju prvih
verzija DOSa). Ono sto se ne zna je da ova dva slova mogu biti data i u
obrnutnom rasporedu: 'Z', 'M'.
LONG e_lfanew [*]
Fajl ofset PE EXE (ujedno i RVA) odakle pocinje 'PE EXE Header'. Tacnije,
pokazuje 4 bajta ispred te strukture, na dword koji sluzi kao znak
raspoznavanja (signature) fajla.
MS-DOS Real-Mode Stub program
Ovo je obican DOS program ciji je zadatak da ispise odgovarajucu poruku kada
se PE fajl startuje iz starije verzije DOSa (stub program). Medutim, ovo
moze biti bilo kakav MS DOS program tako da se ovako moze dobiti program
koji radi na dva operativna sistema: DOSu i Windows9x!
PE EXE Header
Ovde konkretno pocinje PE EXE. Pre ove strukture u fajlu (i memoriji) se
nalazi podatak tipa dword na koga pokazuje gore pomenuti e_lfanew. Ovaj
dword sluzi kao znak raspoznavanja (signature) tipa izvrsnog fajla: da li je
PE, LE, NE itd. Za PE fajlove vrednost dworda mora biti 0x00004550 (PE00).
Posle dword-a sledi PE EXE header. Opisan je u strukturi: IMAGE_FILE_HEADER.
Elementi ove strukture su:
WORD Machine
CPU indetifikacija (ID broj): Intel i860 (0x14D), Intel i386 (0x14C), MIPS
R3000 (0x162), MIPS R4000 (0x166), DEC Alpha AXP (0x183).
WORD NumberOfSections [*]
Ukupan broj sekcija.
DWORD TimeDateStamp
Vreme kreiranja fajla u sekundama od 31-dec-1969 u 4:00 PM.
DWORD PointerToSymbolTable
DWORD NumberOfSymbols
Nisu od interesa.
WORD SizeOfOptionalHeader
Velicina strukture 'PE File Optional Header' koja sledi odmah iza ove.
WORD Characteristics
Nije od znacaja.
PE EXE Optional Header
Ova struktura sledi odmah iza pethodne i predstavlja njen nastavak. Iako u
njenom imenu stoji rec 'Optional' ona je ipak obavezna! Rec je o tome da
jedan deo ove strukture razlicito izgleda u EXE fajlovima koji su namenjeni
drugim procesorima. Ovaj header je opisan u strukturi:
IMAGE_OPTIONAL_HEADER. Sledi opis elemanata strukture (za Intelove
procesore):
WORD Magic
Jos jedan (nevazan) magicni broj.
BYTE MajorLinkerVersion
BYTE MinorLinkerVersion
Verzije linkera koji je napravio fajl. Nije od znacaja.
DWORD SizeOfCode
DWORD SizeOfInitializedData
DWORD SizeOfUninitializedData
Velicine code sekcije, data sekcije i bss sekcije (za neinicijalizovane
podatke). Nije od znacaja.
DWORD AddressOfEntryPoint [*]
RVA adresa odakle Loader startuje PE EXE. Pokazuje mesto gde program pocinje
sa radom.
DWORD BaseOfCode
DWORD BaseOfData
RVA odakle pocinju code i data sekcija. Nije od znacaja.
DWORD ImageBase [*]
Virtuelna adresa odakle se ucitava ceo PE EXE fajl (obicno je 0x400000). U
odnosu na nju se racuna virtuelna adresa svih podataka: dodavanjem njihove
RVA.
DWORD SectionAlignment
Korak poravnavanja (alignment) virtuelne adrese pocetka neke sekcije (obicno
je 0x1000).
DWORD FileAlignment [*]
Alignment velicine celog fajla (obicno je 0x200). Ovo je ujedno i alignment
sekcija.
WORD MajorOperatingSystemVersion
WORD MinorOperatingSystemVersion
WORD MajorImageVersion
WORD MinorImageVersion
WORD MajorSubsystemVersion
WORD MinorSubsystemVersion
DWORD Win32VersionValue
Razne nevazne stvari.
DWORD SizeOfImage [*]
Ovo je ukupna velicina memorije o kojoj Loader treba da vodi racuna. Ukoliko
nije ispravno podesena javlja se greska da nema dovoljno memorije i da treba
zatvoriti neke programe.
DWORD SizeOfHeaders [*]
Velicina svih PE header-a i tablice sekcija. Nakon njih pocinju 'sirovi'
(raw) podaci.
DWORD CheckSum
Trebalo bi da bude CRC ceksum fajla, ako ga linker postavi. Nije od znacaja.
WORD Subsystem [*]
Tip interfejsa koji PE EXE zahteva: NATIVE (1) - ne zahteva subsytem, na
pr.: device driver; WINDOWS_GUI (2) - zahteva Windows GUI; WINDOWS_CUI (3) -
konzolna aplikacija; OS2_CUI (5) - OS/2 konzolna aplikacija; POSIX_CUI (7) -
Posix konzolna aplikacija.
WORD DllCharacteristics
DWORD SizeOfStackReserve
DWORD SizeOfStackCommit
DWORD SizeOfHeapReserve
DWORD SizeOfHeapCommit
DWORD LoaderFlags
Nije od znacaja.
DWORD NumberOfRvaAndSizes
Broj polja (entrys) u DataDirectory. Uvek je setovano na 16.
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] [*]
Ovo je niz od 16 polja (entrys). Svako polje odreduje polozaj i velicinu
nekog od vaznih delova PE fajla: sekcije, tabele itd. Svako polje je, u
stvari, struktura IMAGE_DATA_DIRECTORY koja ima samo 2 clana:
DWORD VirtualAddress - RVA tog dela, i
DWORD Size - velicina istog.
Od svih 16 polja za sada se koristi 11, mada se u poslednje vreme koriste
jos 2 polja, ali sa tim se, jasno, ne moze racunati. Od svih polja ovde je
od znacaja drugo po redu, tj. DataDirectory[1] posto ono odreduje polozaj i
velicinu IMPORT sekcije. Sta ostala polja odreduju moze se naci u fajlu
WINNT.H u definicijama: IMAGE_DIRECTORY_ENTRY_XXX.
Od novih polja je jako korisno DataDirectory[12] koje odreduje IAT (Import
Address Table), ali je, na zalost, ono zastupljeno tek sa novijim verzijama
linkera i mora se racunati da na to da PE fajl ne mora da ima ovo polje.
Kada neki deo PE fajla koga opisuje polje ne postoji onda su njegov polozaj
i velicina setovani na nulu.
Inace, smisao DataDirectory-ja je da omoguci Loader-u brzo lociranje nekog
od vaznih delova PE fajla.
Section Table
Odmah posle PE headera, a pre 'sirovih' podataka sekcija, sledi niz Section
Headera za svaku od sekcija koja postoji u PE fajlu. Dakle, svaka sekcija
koja postoji mora biti ovde zastupljena svojim 40 bajtnim headerom. Ukupan
broj sekcija je dat ranije u fajlu. Section header je opisan u strukturi:
IMAGE_SECTION_HEADER. Ona izgleda ovako:
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]
8 bajtno ANSI ime sekcije. Obicno ime sekcije pocinje sa tackom ('.').
Takode, linkeri pojedine sekcije imenuju prilicno deskriptivno (.text, .data
itd) ali se u to ne sme pouzdati. Treba primetiti da ukoliko je ime duzine
svih 8 bajtova onda ne postoji termiator 0 na kraju stringa imena.
union {
DWORD PhysicalAddress
DWORD VirtualSize [*]
} Misc;
Ovo je jedan od najzanimljivijih elemenata PE fajla. Od ova dva elementa
unije za PE EXE fajlove se koristi VirtualSize (PhysicalAddress se koristi
za OBJ fajlove). On odreduje kolika je raw velicinu sekcije. Znaci, u
pitanju je nezaokruzena tj. non-alignment velicina sekcije!
Ono sto se ne zna je da ova velicina moze da bude i 0! U tom slucaju Loader
predpostavlja da je raw velicina jednaka alignment velicini (koja je data
malo kasnije u strukturi). Watcom linker generise PE EXE fajlove kod kojih
je VirtualSize setovano na 0. Posto virusi gotovo sigurno koriste ovaj
podatak, a kako se malo zna da on moze da bude i 0, moze se desiti da se
virus lose zakaci na EXE fajl! Startovanje takvog lose zarazenog fajla
obicno daje poznatu Windows poruku da je nastupila greska i program nece
raditi! Eto kako se moze postici jednostavna unutrasnja zastita od virusa,
bez ikakvog dodatnog koda! Malo inteligentnija zastita bi bila ona koja pre
startovanja programa proverava da li je ovo polje setovano na 0, pa ako nije
da onda da informaciju o mogucem postojanju virusa.
DWORD VirtualAdress [*]
RVA adresa sekcije. Na osnovu nje se odreduje virtuelna adresa sekcije
njenim sabiranjem sa vrednoscu ImageBase, koja je data ranije. Napomena:
ovde RVA nije jednako fajl ofsetu sekcije u PE EXE, ali postoji veza.
DWORD SizeOfRawData [*]
Ovo je zaokruzena (alignment) velicina sekcije. Kao korak zaokruzivanja se
uzima ranije data vrednost FileAlignment.
DWORD PointerToRawData [*]
Fajl ofset na kome pocinju sirovi (raw) podaci sekcije. Kao sto je bilo
receno, konkretni sadrzaji sekcija se nalaze odmah iza ovog niza Section
header-a. Prilikom ucitavanja PE EXE fajla u memoriju, sadrzaj sekcija se
cita iz fajla sa ovde navedenog fajl ofseta i smestaju se na adresu koju
odreduje gore navedeno polje VirtualAdress.
DWORD PointerToRelocations
DWORD PointerToLineNumbers
DWORD NumberOfRelocations
DWORD NumberOfLineNumbers
Nisu od znacaja.
DWORD Characteristics [*]
Sadrzi karakteristiku (skup atributa) pojedine sekcije. Svaka sekcija ima
svoje karakteristike, zavisno od toga cemu sluzi. Code sekcije obicno ima
setovane sledece atribute: executable, readable, code. Data sekcija obicno
setuje sledece atribute, izmedu ostalih: readable i writeable.
Ovde je od znacaja prvenstveno atribut 'writeable' koji odreduje da li je, u
toku izvrsavanja programa moguce upisivati podatke u neku sekciju ili ne.
Sections
Konacno, posle PE header-a i niza Section Headera sledi i sam program,
raspodeljen na sekcije. Svaka sekcija obicno sadrzi jednu funkcionalnu
celinu programa: podatke, sam kod, resource podatke, import, export podatke
itd. Medutim, moguce je postojanje vise sekcija iste funkcionalnosti, mada
se to ne praktikuje: 2 code sekcije, na primer.
Standardne sekcije
Sledi spisak uobicajenih sekcija, pri cemu su posebno obradene code, import
i export sekcija.
Code sekcija
Linkeri obicno sav kod iz raznih OBJ fajlova smestaju u jednu sekciju. Ova
sekcija se cesto zove '.text'. U njoj se, dakle, nalazi kod programa.
Ono sto treba nauciti vezano za Code sekciju je kako se poziva importovana
funkcija iz nekog DLLa tj. funkcija koju sam Loader uvozi (importuje) iz
nekog DLLa prilikom startovanja programa. Takve funkcije su, na primer, sve
WinAPI funkcije i one se importuju iz sistemskih DLLova. Posto se potrebni
DLLovi ne nalaze na svaki put istim adresama, a moguce je i da ne budu
ucitani u memoriju, Loader mora da nade efikasan nacin da pravilno
preusmerava ovakve pozive importovanih funkcija. Ono sto Loader (a prethodno
i linker) radi je da sve te pozive preusmerava na specijalno mesto u samoj
Code sekciji na kome se nalaze instrukcije tipa:
JMP DWORD PTR [XXXXXXXX]
gde je XXXXXXXX adresa negde unutar Import sekcije programa. Pri ucitavanju
PE EXE fajla Loader popuni sva ta mesta unutar Import sekcije odgovarajucim
ofsetima funkcija iz DLLova koje se koriste u programu. Ovaj postupak
predstavlja importovanje funkcija. Na ovaj nacin, Loader ne mora da
patch-uje svaku CALL instrukciju u programu, vec mora da samo jednom za
svaku importovanu funkciju popuni za nju odgovarajuce mesto u Import sekciji
njenim trenutnim memorijskim ofsetom. Nedostatak ovog metoda je to sto se u
programu ne moze direktno uzeti adresa importovane funkcije, ali to se vec
moze postici na drugi nacin.
Neka se, kao primer ovog mehanizma, negde u programu poziva WinAPI funkcija
GetMessage iz sistemskog USER32.DLL. Evo dijagrama koji opisuje ovaj
mehanizam:
Program USER32.DLL
| |
+-Import-----------------+
+-----------------+
| : | 0x77879426: | :
|
0x401042: | 0x77879426 | -------------------> | GetMessage code
|
| : | | :
|
+------------------------+
+-----------------+
| |
| |
+-Code-------------------+
| : |
0x404408: | JMP DWORD PTR [401042] |
| : |
| : |
| CALL GetMessage |
(call 404408)
| : |
+------------------------+
| |
Data sekcija
Data sekcija sadrzi sve globalne i staticke promenljive koje se
inicijalizuju prilikom kompajliranja. Tu takode spadaju stringovi. Linker i
ovde obicno kombinuje sve raspolozive data sekcije u jednu, koja se obicno
zove '.data'. Lokalne varijable se smestaju na lokalnom steku thread-a i ne
ulaze u data i bss sekciju.
BSS sekcija
U ovu sekciju se smestaju sve neinicijalizovane staticke i globalne
varijable. Ova sekcija ne zauzima fizicki prostor u fajlu, pa je kod nje
PointerToRawData i/ili SizeOfRawData jednako 0. Borlandovi linkeri obicno
nemaju ovu sekciju, tj. ona se nalazi u okviru Data sekcije. Standardan
naziv ove sekcije je '.bss'.
Resource sekcija
Obicno se zove '.rsrc' i tu su smesteni resource podaci.
Import sekcija
U Import sekciji (cesto se zove '.idata') se nalaze informacije koji sluze
Loaderu da odredi adrese svih importovanih funkcija koje program koristi i
da patch-uje deo sekcije, kako bi program radio. Import sekcija pocinje
nizom IMAGE_IMPORT_DESCRIPTOR struktura, za svaki navedeni DLL cije su
funkcije importovane. Jedan DLL moze biti naveden vise puta, svaki put sa
razlicitom grupom funkcija. Da bi se znalo gde se ovaj niz zavrsava, na
samom kraju se nalazi jos jedna takva struktura ciji su svi elementi
resetovani (NULL). Sledi opis elemata strukture.
DWORD Characteristic [*]
Mali propust Micro$oft-a: ovo nije nikakva karakteristika vec RVA adresa
niza pointera. Ovaj niz pointera se naziva HintName Array (HNA) i ima slicnu
ulogu kao i IAT (Import Address Table) Medutim, za razliku od IAT, kada se
program startuje Loader ne menja sadrzaj ove tabele kao sto to radi sa IAT.
DWORD TimeDateStamp
Nije od znacaja.
DWORD ForwarderChain
Ovaj element sluzi za posebnu tehniku importovanja funkcija: tkzv
'forwarding'. Forwarding je tehnika kada se importovane funkcije (tj.
njihove adrese) ne uzimaju iz direktno naznacenog DLLa vec iz forwardovanog.
Na primer, NTDLL.DLL ima exporte nekih funkcija koje su u stvari iz
KERNEL32.DLL. Kada se one pozivaju iz programa, a pri tome su naznacene da
su importovane iz NTDLL.DLL, Windows ce ih izvrsiti ne u kodu NTDLL.DLL vec
u kodu KERNEL32.DLL, gde se stvarno nalaze. Ovo polje moze biti od vaznosti
u nekim specijalnim slucajevima.
DWORD Name [*]
RVA adresa ASCIIZ stringa koji sadrzi ime importovanog DLLa.
PIMAGE_THUNK_DATA FirstThunk [*]
RVA adresa Import Address Table (IAT). IAT je bukvalno niz pointera koji
posredno u fajlu pokazuju na imena importovanih funkcija i njihov ordinalni
broj. Ordinalni broj je jedinstven broj funkcije u DLLu. Zove se jos i Hint.
Znaci, svaki pointer iz IAT-a posredno pokazuje u PE fajlu na strukturu
IMAGE_IMPORT_BY_NAME koja ima pomenuta dva elementa:
WORD Hint - ordinalni broj, i
BYTE Name[?] - ASCIIZ ime importovane funkcije.
Ovde postoji i jedna nedokumentovana osobina: postoje slucajevi kada PE EXE
importuje pojedine specijalne funkcije samo po njihovom ordinalnom broju i
tada nije dato ime te funkcije. To je najcesce slucaj sa sistemskim
programima (na pr.: EXPLORER.EXE) koji koriste neke nedokumentova funkcije
operativnog sistema. Takvi slucajevi se detektuju tako sto pointer iz IAT
pokazuje na signed int koji je manji od 0. Tacnije, pointer pokazuje na
DWORD koji sadrzi ordinalni broj specijalne import funkcije ali i setovani
najvisi bit (na pr: 0x80000012 za ordinalni broj 0x12).
Prilikom startovanja programa Loader koristi ove informacija na sledeci
nacin: za svaki pointer iz IAT Loader cita ime funkcije koja se importuje i
nalazi njenu adresu u DLLu ciji IAT se trenutno obraduje. Loader zatim menja
sadrzaj IAT tako da njeni clanovi sada direktno pokazuju na kod importovanih
funkcija!
Sledi dijagram koji opisuje jednu standardnu import strukturu PE EXE fajla.
HNA IAT
+---+ +------------------+ +---+
/->| |--->| 44 |<---| |<---\
| | | | "GetMessage" | | | |
| +---+ +------------------+ +---+ |
IMAGE_IMPORT_DESCRIPTOR | | |--->| 72 |<---| | |
+-----------------+ | | | | "LoadIcon" | | | |
| Characteristics |---/ +---+ +------------------+ +---+ |
+-----------------+ | |--->| 19 |<---| | |
| TimeDateStamp | | | |"TranslateMessage"| | | |
+-----------------+ +---+ +------------------+ +---+ |
| ForwarederChain | : : : |
+-----------------+ |
| Name |---> "USER32.DLL" |
+-----------------+ |
| FirstThunk |------------------------------------------------/
+-----------------+
:
Kao sto je receno, ovo je izgled stanja u PE fajlu. Kada Loader ucita ovaj
PE EXE, on promeni IAT tabelu tako da ona pokazuje na kod import funkcija
GetMessage, LoadIcon itd. koje se nalaze u USER32.DLL.
Nazalost, postoje i dva izuzetka, o kojima se jako malo zna. Prvi izuzetak
potice od Borland-a: kod programa linkovanih TLINK32 linkerom HNA pointer je
NULL. Na taj nacin se stedi malo prostora. Razlog zasto Borland ukida HNA
tabele je taj sto HNA i IAT prakticno pokazuju na istu stvar. Medutim,
razlika postoji posto Loader menja samo IAT, tako da se i posle ucitavanja
programa HNA i dalje pokazuje na imena imporotvanih funkcija i njihove
hintove. Ipak, Borland je odlucio da ukine HNA.
Drugi izuzetak potice od Micro$oft-a. Tice se IAT-a: u cilju optimizacije
pojedini programi imaju IAT tabelu ciji elementi vec pokazuju na impotovanih
funkcija (primer: explorer.exe iz win97), tj sadrze njihove adrese! Ovo je u
suprotnosti sa izjavama da se DLLovi ucitavaju svaki put na drugoj adresi! U
svakom slucaju, za rad sa import funkcijama treba koristiti HNA tabelu, pri
cemu paralelno treba pratiti i IAT tabelu, posto ce ona sigurno pokazivati
na importovane DLL funkcije kada Loader ucita program.
Kada se govori o Import sekciji moze se uociti jos jedna stvar: posto je ova
sekcija obicno sa setovanim read-write atributima lako je presretati pozive
funkcija koje EXE ili DLL importuju iz nekog drugog DLLa! Jednostavno treba
patch-ovati zeljena polja u IAT tako da pokazuju na presretajucu funkciju,
dok se iz nje zatim poziva originalna import f-ja. Nema potrebe da se
modifikuje ni pozivajuci ni pozivani kod - sjajno, nije moglo lakse!
Export sekcija
Ova sekcija (obicno se zove '.edata') je karakteristicna za DLLove. Ona
sadrzi informacije o export funkcijama koje neki drugi EXE ili DLL koriste u
vidu import funkcija. Na pocetku ove sekcije se naalzi struktura
IMAGE_EXPORT_DIRECTORY a odmah iza se nalaze sirovi podaci na koje ova
struktura pokazuje. Sledi prikaz elemenata strukture.
DWORD Characteristics
DWORD TimeDateStamp
DWORD MajorVersion
DWORD MinorVersion
Nisu od znacaja.
DWORD Name
RVA adresa ASCIIZ stringa koji sadrzi ime tog DLLa.
DWORD Base
Pocetni ordinal broj prve export funkcije. On se mora dodati na ordinalne
brojeve koji se dobijaju pomocu AddressOfNameOrdinals.
DWORD NumberOfFunctions [*]
Broj funkcija koje se dobijaju pomocu AddressOfFunctions. On teoretski moze
da bude razlicit od vrednosti NumberOfNames, ali su ta dva elementa uvek
setovana na istu vrednost.
DWORD NumberOfNames
Broj imena koji se dobijaju pomocu AddressOfNames.
PDWORD *AddressOfFunctions [*]
RVA adresa niza koji sadrzi adrese funkcija. Ove adrese su RVA adrese za
svaku exportovanu funkciju tog modula (DLLa).
PDWORD *AddressOfNames [*]
RVA na niz pointera na stringove. Stringovi sadrze imena exportovanih
funkcija.
PWORD *AddressOfNameOrdinals
RVA na niz pointera na WORD koji sadrze ordinalni broj exportovanih
funkcija. Ordinalni broj dobijen iz ovog polja treba uvecati za gore datu
vrednost Base.
Ovde se vidi da se Export sekcija sastoji od tri razlicita niza pointera
koji zasebno pokazuju na ime funkcije, njenu adresu i ordinalni broj. Ova
tri niza su medusobno 'paralelna'. Proces pronalazenja funkcije zeljenog
imena svodi se na pronalazenje indeksa tog imena u nizu pointera na imena
export funkcija. Taj indeks (tj redni broj) se zatim iskoristi u drugom nizu
koji sadrzi pointere na adrese export funkcija.
Kernel32 problem
Vreme je da se posveti paznja samom problemu kacenja nekog koda na PE EXE
fajl, koji se u ovom procesu naziva host (domacin). Posto kod koji se kaci
na host obicno radi nesto konkretno, ocigledno je da on mora da poziva
WinAPI funkcije. I tu nastaje problem.
Ranije je receno da Loader prilikom ucitavanja PE EXE fajla cita IAT ciji
elementi pokazuju na imena svih importovanih funkcija, sto znaci i na imena
importovanih WinAPI funkcija. Zatim Loader dobavlja adrese svih importovanih
funkcija i prepravlja IAT tako da njegovi elementi sada pokazuju na ove
funkcije. Kada se u hostu nade poziv neke importovane funkcije ona se poziva
indirektno bas preko sadrzaja IAT. O kreiranju IAT i ostatka PE EXE fajla
brine linker prilikom njegovog kreiranja, tako da programer ne mora o tome
da vodi racuna: on u svom sorsu pise standardno: call .
Problem nastaje u kodu koji se kaci na host: kako pozvati WinAPI funkcije? U
procesu kacenja ocigledno nema linkovanja koje ce u dodatom kodu pravilno
preusmeriti pozive tako da koriste sadrzaj IAT!
Resenje problema je da se pre poziva bilo koje WInAPI f-je iz dodatog koda
prvo nadu adrese svih potrebnih f-ja. Za to je potrebno prvo locirati
KERNEL32.DLL, posto on sadrzi adrese svih WinAPI f-ja koje su neophodne u
dodatom kodu. Medutim, KERNEL32.DLL i ostali sistemski DLL-ovi se ne nalaze
na fiksnim adresama. Ovo je namerno uradeno izmedu ostalog i da bi se
onemogucilo pisanje virusa za Windows platforme. Ali, rupa uvek postoji....
... i zove se GetModuleHandle. Ova WinAPI f-ja vraca adresu zeljenog modula.
U ovom slucaju je to KERNEL32.DLL. Ali kako je, sto mu gromova, moguce
pozvati GetModuleHandle() ako prethodno nije lociran KERNEL32.DLL i iz njega
nije izvucena adresa ove funkcije ???
Resenje je jednostavno: f-ja GetModuleHandle se odreduje iz IATa ucitanog
hosta. Kada Loader zavrsi posao, tj. prepravi IAT, sto znaci na pocetku
programa, potrebno je procitati sadrzaj IAT koji odgovara importovanoj
funkciji: GetModuleHandle. Inace, ova funkcija u EAX vraca adresu (tj.
handle) KERNEL32.DLL.
Uocava se i slaba tacka ovog pristupa: host mora da ima GetModuleHandle medu
svojim importovanim funkcijama. Ipak, ovo i nije bas toliko slaba tacka, cak
sta vise, skoro i da ne postoji program koji ne importuje ovu funkciju.
Dobro, KERNEL32.DLL je lociran, kako sada doci do tako zeljenih adresa
funkcija koje zakaceni kod koristi? Jednostavno: tu je GetProcAddress. Ova
funkcija kao parametre ima handle DLLa koji sadrzi funkciju i pointer na
njeno ime. Rezultat je adresa funkcije. Ali i ovde prethodno treba znati
adresu same GetProcAddress.
I da i ne. Posto je GetProcAddress cesta medu importovanim funkcijama u EXE
fajlovima, njena adresa se moze dobaviti iz IAT, na isti nacin kao i adresa
GetModuleHandle. Medutim, znacajan je broj programa koji ne importuju ovu
funkciju - u tom slucaju se pribegava pronalazenju ove adrese na razne
nacine. Jedan od najelegantnijih je koriscenje sopstvene funkcije koja radi
identicno kao i GetProcAddress.
Kada su poznate adrese GetModuleHandle i GetProcAddress moguce je pronaci
adrese svih ostalih WinAPI funkcija koje zakaceni kod koristi. Ovo
dobavljanje adresa se obicno vrsi na samom pocetku zakacenog koda.
U delu za download je dat program 'Getproc.exe' koji nalazi adrese zeljenih
funkcija. Dat je i sors. Program je napisao jqwerty iz poznate koderske
grupe 29A koja je izdala puno dobrih virusa.
Kacenje
A sada malo o samom kacenju. Naime, kod koji se kaci na PE EXE fajl ne sme
da ugrozi strukturu PE fajla. Postoje razna resenja kako se vrsi dodavanje
koda. Sledi opis dva najcesca resenja. Sva ostala predstavljaju njihove
varijante.
Prvi nacin koji pada na pamet je da se kod doda kao nova sekcija, na kraju
EXE fajla. Medutim, ako se malo bolje pogleda uocava se da je za ovo resenje
potrebno prilicno izmeniti originalni PE EXE fajl. Potrebno je pre svega
dodati novi Section Header i to usred PE EXE fajla: prilicna komplikacija,
pogotovo za virusolike programe. Zato postoji i drugi, elegantniji nacin.
Drugi nacin se sastoji u tome da se kod za kacenje doda na poslednju
sekciju. Analizom se zakljucuje da predasnja struktura PE EXE fajla ostaje
ista. Kako je ovaj nacin primamljiviji sledi njegova detaljna analiza.
Medutim, bilo koji da se nacin koristi, treba voditi racuna da EXE fajlovi
mogu da imaju Overlay. To je dodatak na kraju EXE fajla i njegova velicina
nije uracunata u strukturi PE EXE fajla. Tipican primer su razni
instalacioni programi: program koji vrsi instalaciju ima dodat Overlay koji
sadrzi sam program koji ce biti instaliran. Kod koju se kaci treba da se
doda na sam kraj PE EXE fajla, u ovom slucaju znaci iza Overlaya, pa tako da
i o tome treba voditi racuna.
Algoritam
Sledi detaljno objasnjen postupak 'kacenja' nekog koda na originalni PE EXE
fajl, koji se u ovom procesu cesto zove i host (domacin). Deklaracija
promeljivih, obrada greski i slicne 'sporedne' stvari se nece pominjati da
ne bi skretali paznju sa vaznih detalja.
Sam pocetak kacenja je otvaranje host fajla i njegovo mapiranje u memoriju.
Neka je filemap pointer tipa char* na pocetak mapirnog hosta.
[***] Napomena: Ostatak projekta je, za sada, dat samo opisno. Ako se pokaze
interesovanje i podrska, projekat ce biti zavrsen sa detaljnim izvodima iz
sorsa pravog virusa.
Provera da li je fajl validan PE EXE fajl: proverava se MZ (tj. ZM) potpis
na samom pocetku, kao i PE00 potpis pre PE headera.
Preuzima se broj sekcija. Ide se u PE Optional Header.
Proverava se da li je aplikacija za GUI. Pamti se EntryPoint, ImageBase,
FileAlignment (koji predstavlja alignment sekcija) i preuzma se pointer na
DirectoryData i na SizeOfImage.
Ako postoji IMPORT sekcija u DirectoryData (a obicno postoji) odreduje se
pozicija Import sekcije i iz nje preuzimaju RVA adrese za KERNEL32.DLL
funkcije GetModuleHandleA i, ako postoji, za GetProcAddress.
Sada treba naci poslednju sekciju u host fajlu. Ovo ne mora da bude i
poslednja navedena sekcija medu sekcijama u Section Table!!! Naime, javlja
se slucaj kada je poslednja navedena BSS sekcija koja fizicki ne cini deo
fajl hosta! Kod nje je PointerToRawData i/ili SizeOfRawData jednako 0.
Nalazi se fajl ofset gde treba dodati virus. Kao sto je receno ranije
VirtualSize moze biti 0, tako da o tome treba voditi racuna.
Konacno, sledi unos izmena. Treba izmeniti:
- RVA adresu EntryPointa tako da pokazuje na mesto gde je dodati kod;
- velicinu poslednje sekcije (SizeOfRawData);
- velicinu poslednje sekcije u DirectoryData, ako tamo postoji;
- karakteristike poslednje sekcije: setovati EXECUTE, READ i WRITE.
- velicinu poslednje sekcije (VirtualSize) ako je razlicita od 0;
- velicinu SizeOfImage;
- velicinu celog fajla
Pre samog upisivanja koda u host fajl u njega (kod) se upisuju podaci
vezanih za njegovo izvrsavanje. To su, pre svega, nadene adrese KERNEL32
funkcija GetModuleHandleA i GetProcAddress, kao i stari, originalni
EntryPoint.
Kod za kacenje
Ostaje jos prodiskutovati i sam kod za kacenje. Pre svega, on se ceo,
zajedno sa podacima, nalazi u jednom segmentu, tako da ne postoji posebno
data segment, a posebno segment za kod. Zato se i setuju READ & WRITE
atributi sekcije u koju se kod dodaje.
[***] Napomena: Ostatak projekta je, za sada, dat samo opisno. Ako se pokaze
interesovanje i podrska, projekat ce biti zavrsen sa detaljnim izvodima iz
sorsa pravog virusa.
Kod za kacenje se generalno izvrsava na sledeci nacin: prvo se odredi adresa
pocetka dela podataka koda za kacenje. Odatle se citaju zapamcene adrese
KERNEL32 funkcija. Zatim se odreduju i adrese svih ostalih WinAPI funkcija
koje su potrebne.
Posle ovoga kod moze da odradi ono za sta je namenjen: ako je, na primer, u
pitanju virus onda treba da nastavi za inficiranjem fajlova. Bilo kako bilo,
kada zavrsi sa radom kod mora da skoci na zapamceni, originalni EntryPoint
kako bi se i sam host startovao.
skini manuale za procesore, imas 3 komada, mislim da je u drugom spisak svih
instrukcija
sa njihovim opkodovima u masinskom jeziku, tu ima oko 1000 i kusur strana
to neka ti sluzi kao referenca
asembler pocni sa dosom i .com fajlovima jer su ti oni ciste instrukcije
com = copy of memory, nema headera
nemoj se previse zezati sa dos programiranjem, vec nauci neke osnove u njemu
koje
vaze svuda a najlakse ces ih nauciti bas u dosu
sta te jos zanima?
Nenad <x...@xxx.xxx> wrote in message news:a5otin$bm$1...@news.eunet.yu...
Hvala Vam svima.