Pitanje:
Kako se struktura nalazi unutar rastavljenog programa?
Ken Bellows
2013-03-20 00:24:54 UTC
view on stackexchange narkive permalink

Prije nekoliko ljeta pohađao sam osnovni tečaj obrnutog inženjerstva od 40 sati. Dok nas je učio koristiti IDAPro, nastavnik je pokazao, prilično brzo i bez puno objašnjavanja, kako označiti određene varijable u ASM-u kao članove strukture, u osnovi jednake dobroj staromodnoj struct u C / C ++ i tretirajte ih kao takve gdje god se vide u ostatku koda. Ovo mi se čini prilično korisnim.

Međutim, ono što on nije pokrivao jest kako identificirati strukturu. Kako znati kada skupina varijabli zapravo čini strukturu, a ne samo skup povezanih varijabli? Kako možete biti sigurni da je autor tamo koristio struct (ili nešto slično)?

Iz znatiželje, je li to bio moj (Rolf Rolles) razred?
Ne nije.
Vjerujem da sam predavao dio tečaja na koji mislite (iako vjerojatno ne i kad ste ga pohađali). Općenito sada trošimo više vremena na tu temu nego prije. Oprostite na zabuni!
Ovo je bio privatni tečaj za moju tvrtku, pa ako vjerojatno ne budete zaposleni kod istih ljudi i aktivno radite na ovom polju, to je malo vjerojatno. Pretpostavljam da ih ima puno u blizini.
Sedam odgovori:
#1
+22
Andrew
2013-03-20 00:54:04 UTC
view on stackexchange narkive permalink

Ne možete. U C-u postoje strukture za čitatelje programa C i njihova je upotreba u slici programa nekako neobavezna. Potpuno je moguće da je u originalnom programu neki ludi kreten odlučio sve raditi sa savršeno velikim char * odbojnicima te bacati i dodavati na odgovarajući način, a vi nikada ne biste saznali razliku.

Označavanje 'struct' u potpunosti je u vašu korist kao pregledača koda. Moglo bi biti da su strukturne oznake koje primjenjujete na program zapravo dvije varijable uvijek pohranjene jedna pored druge. To neće biti važno sve dok vas ipak ne dovede do lažnih zaključaka o tome što program radi.

Vrlo zanimljivo. Možete li dati primjer slučaja kada bi definiranje strukture moglo dovesti do pogrešnog zaključka?
@KenB Rizikujete donošenje lažnih zaključaka čak i ako je programer koristio strukturu, na primjer ako je struktura u uniji, tako da struktura podataka može imati različite izglede ovisno o putovima koda ili stupnju u životnom ciklusu objekta.
jedan (vrsta) izmišljenog primjera kojeg se mogu sjetiti je identifikacija funkcije prema vrsti argumenta. postoji poziv eax, a vi znate da je parametar pokazivač koji se predaje kao argument, a budući da vjerujete da pokazivač pokazuje na strukturu tipa baz, tada vjerujete da bi „eax“ mogao sadržavati funkciju s potpisom 'void (* ) (struct baz *); '. ali ako je vaša identifikacija strukture netočna, onda to ne mora biti slučaj.
U istom smislu kao odgovor @Gilles ne možete dobiti potpunu strukturu ako unutar strukture postoje skupine neiskorištenih varijabli. Inače bi automatizirani alati vidjeli jednu veliku strukturu koju je programer definirao kao skup manjih struktura na temelju uzoraka korištenja i stoga.
Šteta što je ovaj odgovor prebrzo prihvaćen i sad je dobio glas samo zbog njega. Iako to nije uvijek moguće učiniti pouzdano, definitivno je moguće oporaviti ili barem pogoditi izgled strukture u * mnogim * slučajevima, što dokazuju i drugi odgovori.
Mislim da nije konstruktivno reći da to nije moguće. Iako ne možete sa sigurnošću reći je li to bila struktura, kao što kažete, može biti od velike "koristi kao preglednik koda". Morate biti konzervativni u pogledu zaključaka koje izvučete - prvo slijedite put izvršenja! - ali može biti vrlo koristan alat.
Igor i Robert - S obzirom na to da je moje konačno pitanje bilo "Kako možete * biti sigurni * da je autor koristio` strukturu`? ", Ovaj je odgovor apsolutno točan. Već razumijem da je moguće korisno pretpostaviti. Jednostavno sam se pitao postoji li način da se * definitivno * utvrdi da je autor koristio `strukturu` i prirodu` strukture`.
#2
+22
user1354557
2013-03-22 05:00:44 UTC
view on stackexchange narkive permalink

Postoje vrlo česti obrasci koje ćete pronaći u kodu koji označava uporabu strukture.

Odstupanja od odstupanja:

Ako imate pokazivač koji je dereferenciran na nekom odmaku koji nije nula , vjerojatno imate posla sa strukturom. Potražite uzorke poput:

  mov eax, [ebp-8]; Učitajte lokalnu varijablu u eaxmov ecx, [eax + 8]; ** Preusmjeravanje dworda na eax + 8 **  

U ovom primjeru imamo varijablu koja sadrži pokazivač, ali nam je stalo do sadržaja memorije u određenom pomaku ispred pokazivača. Točno se tako koriste strukture: Dobivamo pokazivač na strukturu, a zatim preusmjeravamo pokazivač plus neki pomak za pristup određenom članu. U C-u je sintaksa za ovo: pMyStruct->member_at_offset_8.

Napomena : Ne miješajte preusmjeravanje u odmaku neke varijable s preusmjeravanjem na pomaci pokazivača sloga ili pokazivača okvira ( esp ili ebp ). Naravno, o lokalnim varijablama i argumentima funkcije možete razmišljati kao o jednoj velikoj strukturi, ali u C-u oni nisu izričito definirani kao takvi.

Suptilniji pomaci pokazivača:

Zapravo ne trebate dereferencirati da biste otkrili člana strukture. Na primjer:

  mov eax, [ebp-8]; Učitajte lokalnu varijablu u eaxpush 30h; num = 30hpush aSampleString; src = "Uzorak niza" dodaj eax, 0Chpush eax; dst = eax + 0xCcall strncpy  

U ovom primjeru kopiramo do 0x30 znakova iz nekog izvornog niza u eax + 0xC (pogledajte strncpy). To nam govori da eax vjerojatno pokazuje na strukturu s međuspremnikom niza (od najmanje 0x30 bajtova) s pomakom 0xC. Na primjer, struktura može izgledati otprilike ovako:

  struct _MYSTRUCT {DWORD a; // + 0x0 DWORD b; // + 0x4 DWORD c; // + 0x8 CHAR d [0x30]; // + 0xC ...}  

U tom bi slučaju uzorak koda izgledao ovako:

  strncpy (&pMyStruct->d, "Sample String", sizeof (pMyStruct->d));  

Dodatna napomena: Moguće je (iako malo vjerojatno) da bismo mogli kopirati u veliki međuspremnik niza s pomakom + 0xC, ali to biste mogli odrediti kroz kontekst. Ako su, na primjer, ofset + 0x8, na primjer, bili cijeli broj, onda je to definitivno struct. Ali ako smo kopirali niz fiksne duljine 0xC za adresu eax , a zatim smo kopirali drugi niz za adresu eax + 0xC , to je vjerojatno jedan gigantski niz.

Sva čitanja / sva pisanja:

Recimo da imate strukturu ( ne pokazivač na strukturu) kao lokalnu varijablu vašeg stoga. Većinu vremena IDA ne zna razliku između strukture na hrpi ili gomile pojedinačnih lokalnih varijabli. Ali ogromna dojava da imate posla sa strukturom jest ako ikada čitate iz varijable bez pisanja u nju ili (manje) ako u varijablu pišete samo bez čitanja iz nje. Evo primjera svakog:

  lea eax, [ebp + var_58]; Učitajte ADRESU lokalne varijable u eaxpush eaxcall some_functionmov eax, [ebp + var_54]; Recimo da nikada prije nismo dodirnuli var_54 ... test eax, eax; ... Ali mi provjeravamo njegovu vrijednost! Jz negdje ...  

U ovom primjeru čitamo iz var_54 , a da mu nikada nismo ništa napisali (unutar ove funkcije). To vjerojatno znači da je član strukture kojoj se pristupilo iz nekog drugog poziva funkcije. U ovom se primjeru podrazumijeva da bi var_58 mogao biti početak te strukture, jer je njegova adresa gurnuta kao argument za some_function . To možete provjeriti slijedeći logiku some_function i provjeravajući je li njegov argument ikad dereferenciran (i modificiran) s pomakom + 0x4. Naravno, to se ne mora nužno dogoditi u some_function - to se može dogoditi u nekoj od njegovih podređenih funkcija ili nekoj od njegovih podređenih funkcija itd.

Sličan primjer postoji za pisanje:

  xor eax, eaxmov [ebp + var_28], eax; Recimo da je ovo * samo * vrijeme kada se var_28 dodirne lea eax, [ebp + var_30] push eaxcall some_other_function ...  

Kad vidite da se postavljaju lokalne varijable, a zatim se više nikad ne referencira, ne mogu samo zaboraviti na njih, jer bi vrlo vjerojatno mogli biti članovi strukture koja se prenosi na drugu funkciju. Ovaj primjer podrazumijeva da se u strukturu (koja započinje s var_30 ) zapisuje na offset + 0x8 prije nego što se adresa te strukture prenese u some_other_function .

Oba ova primjera u C-u mogu izgledati ovako:

  neka_funkcija (&myStruct); if (myStruct.member_at_offset_4) ...  

i

  myStruct.member_at_offset_8 = 0; neke_druge_funkcije (&myStruct);  

Popratna napomena: Iako je svaki od ovih primjera koristio lokalne varijable, iste logika se odnosi na globalne.

Dokumentirane funkcije koje očekuju strukture:

Ovo je vjerojatno očito, a IDA će to riješiti gotovo cijelo vrijeme, ali jednostavan način da saznate kada imate strukturu u kodu je ako pozovete dokumentiranu funkciju koja očekuje određenu strukturu. Na primjer, CreateProcessW očekuje pokazivač na strukturu STARTUPINFOW . Za ovo ne bi trebao biti potreban primjer.

Kako da znam da li ti uzorci zapravo ukazuju na upotrebu strukture?

Posljednja točka koju želim istaknuti je da je u svim tim slučajevima, da, tehnički, autor programa mogao napisati njihov kôd bez upotrebe struktura. Također su mogli napisati svoj kod definirajući svaku funkciju kao __declspec (goli) s velikim redom __asm ​​. Nikad ne biste mogli reći. Ali vjerojatno, nije važno. Ako postoje logičke skupine vrijednosti koje se neprekidno pohranjuju u memoriju i prenose iz funkcije u funkciju, još uvijek ih je smisleno označiti kao strukture. Gotovo cijelo vrijeme, ovako je ionako autor napisao svoj kod.

Ako trebate nešto detaljnije objasniti, javite mi.

Vau. Ovo je impresivno temeljito.
** "Naravno, o lokalnim varijablama i argumentima funkcije možete razmišljati kao o jednoj velikoj strukturi, ali u C-u oni nisu izričito definirani kao takvi." ** Međutim, također vrijedi istaknuti da IDA-ino sučelje za specificiranje stack varijable unutar funkcije gotovo je isto kao i za definiranje struktura. Povezivanje dvaju riječi u C nije toliko korisno, ali u IDA-i je dobro prepoznati sličnosti.
#3
+11
Yifan
2013-03-20 01:32:14 UTC
view on stackexchange narkive permalink

Pronalaženje struktura je nezgodno, ali može puno pomoći u razumijevanju koda. Kao što je Andrew rekao, strukture su samo C apstrakcija, a u sklopu su to samo blok memorije i ne postoji besprijekoran način identificiranja struktura. Međutim, za jednostavnije programe neke heuristike mogu biti korisne. Na primjer, "mali" nizovi imaju veću vjerojatnost da budu konstrukcije od divovskih nizova. Ako se, na primjer, očitaju intovi pročitani iz petlje, čini se da je to niz, dok bi čitanje nekoliko intova s ​​konstantnim pomakom izgledalo više poput strukture. Drugi je način vidjeti istu grupu preusmjeravanja koja se događa na različitim područjima koda. Ako dvije različite funkcije uzimaju neki pokazivač kao parametar i obje pokušavaju odstupati 0x10 nakon čega slijedi 0x18 nakon čega slijedi 0x14 ili nešto slično, to bi mogla biti polja za postavljanje koda u strukturi. Također, svaki pristup podacima različitih veličina koji se preusmjeravaju iz jednog unesenog pokazivača dobar je pokazatelj.

#4
+9
Jesper.Reenberg
2013-03-20 01:10:18 UTC
view on stackexchange narkive permalink

Najlakši način da saznate kada imate posla sa strukturom je kada kod poziva funkcije za koje znate (ili navodi dokumentaciju) uzima strukturu kao argument.

Na primjer, in_addr struktura funkcije inet_ntoa .

S obzirom na to da IDA to uopće nije shvatio.

Vrlo dobra stvar.
#5
+8
Wesley McGrew
2013-03-20 04:45:15 UTC
view on stackexchange narkive permalink

Pokušavam potražiti situacije u kojima se pokazivač na komad podataka prenosi u funkciju, a onda kada se koristi u toj funkciji, različiti pomaci od nje tretiraju se kao različite vrste podataka. To mi ukazuje na to da 1) to nije niz jednog tipa podataka, i 2) na njega se upućuje s osnovne adrese, a ne kao zasebni parametar. Također pomaže kada vidite da se dinamički dodjeljuje (malloc ili novi), a zatim ispunjava podacima različitih vrsta.

U IDA-i uvijek savjetujem svojim studentima da nastave i kreiraju IDA-ine strukture za stvari za koje sumnjaju da su strukture i ispunjavaju ih elementima kako vide da se na njih referira, čak i ako kasnije ispadnu biti u krivu / u zabludi. To je iterativni postupak, ispunjavajući ga s više detalja kako steknete više razumijevanja o programu i načinu na koji koristi te podatke dok idete, tako da je važno označiti stvari koje shvatite kako ih shvatite.

#6
+8
Robert Mason
2013-03-20 17:29:35 UTC
view on stackexchange narkive permalink

Postoji nekoliko metoda koje možete koristiti (na većinu su natuknuli prethodni odgovori), ali radi cjelovitosti navest ću ih ovdje.

  1. Potražite pozive knjižnicama koje očekuju strukture. Ako izvršna datoteka ne radi nešto što zapravo ne bi trebala i lijeva pokazivače, a zatim ih ubacuje u funkciju knjižnice i nadajući se da se neće srušiti (malo vjerojatno), tada ovdje možete dobro osjetiti koji su dijelovi memorije strukture.

  2. Pogledajte kako se dijelovi memorije prenose / vraćaju iz funkcija. Neki sastavljači / konvencije / platforme pozivanja prolaze i vraćaju male strukture (koje mogu stati u nekoliko registara) drugačije od ostalih vrsta podataka. Na primjer, ako vidite da se podaci vraćaju i u eax i u edx , onda vjerojatno imate posla s malom strukturom.

  3. Pogledajte kako se rješava memorija. Ako se čini da se dijelovi memorije mijenjaju preko jedne veličine registra kroz pokazivač, to je najvjerojatnije (ako ne rade nešto neobično) niz ili strukturu. Također, kao što je Yifan rekao, ako postoji puno pristupa putem stalnog pomaka, vjerojatno gledate strukturu ili mali niz koji se koristi na način sličan strukturi (kao što je unsigned char rgb [3] ). A ako promatrate dijelove podataka različite veličine, to je gotovo definitivno struktura.

Međutim, uz bilo koji scenarij što-ako, sve dok upotreba podaci su u skladu s vašim modelom, nije važno je li u izvornom kodu samo niz bajtova s ​​nekim konvencijama o tome što kamo ide ili potpuno razvijena struktura. Upotrijebite bilo koji model koji vam pomaže u obrazloženju koda.

Da bih riješio problem sindikata o strukturama koje imaju različit raspored memorije u različitim granama koda, obratit ću se dvijema najčešćim oblicima unija (prema mom iskustvu):

  • Upišite punning: u ovom slučaju to vjerojatno nećete ni vidjeti u kompiliranom kodu. Iako je AFAIK tehnički nedefinirano ponašanje, većina kompajlera tretirat će ga kao reinterpret_cast<>()

  • Algebarske vrste: Obično je to popraćeno oznakom koja olakšava prepoznavanje. Ako na nekoliko mjesta vidite kôd koji provjerava neki cijeli broj, a zatim memoriju tretira različito na temelju te vrijednosti, tada možete pretpostaviti da se to događa. Ako određene funkcije ne provjere oznaku, tada vam to također govori nešto o funkciji - pod pretpostavkom da autor nije lijen i želi da im se kôd razbije, vjerojatno smatraju invarijantom da će se ta funkcija pozivati ​​samo u granama koje već znam da je određene vrste.

FWIW, eax: edx return * obično * znači 64-bitni cijeli broj, a ne malu strukturu.
#7
+1
SW_user2953243
2015-01-19 20:19:29 UTC
view on stackexchange narkive permalink

Kao što su drugi rekli, obično tražim poziv funkcije gdje se prosljeđuje referenca - ali tražim i slučajeve kada se vrati malloc'ed međuspremnik (ovako znate / potvrđujete veličinu strukture) - a različiti članovi 'međuspremnika' su postavljeni / inicijalizirani.



Ova pitanja su automatski prevedena s engleskog jezika.Izvorni sadržaj dostupan je na stackexchange-u, što zahvaljujemo na cc by-sa 3.0 licenci pod kojom se distribuira.
Loading...