Dette avhenger av kompilatoren som opprinnelig ble brukt, da hver skaper litt forskjellige oppsett. Du kan finne veiledninger for de fleste kompilatorer på nettet. Jeg kommer til å fokusere MSVC, da det er den jeg har erfaring med, og siden det gir en skjult kompilatorbryter som skriver ut hvordan klasser skal legges ut i minnet som jeg skal bruke til illustrasjon.
Som du kanskje allerede har gjettet, må du gjenskape C ++ - klasser med C-strukturer. Dette er mulig, men plagsomt, spesielt hvis du har så mange klasser som du sa. Noen tips for å gjøre det mindre irriterende:
- Fokuser de du er interessert i.
- Start med "base-most" klassene, f.eks. de øverst i hierarkitreet. Hvis du ødelegger noe der, kan det være dyrt å fikse det senere :; forestill deg å legge til et glemt medlem i noe sånt som en smart pekerbaseklasse som er arvet av hundrevis av klasser, og nå må du justere alle disse.
- Det kan være lurt å definere
struct kode> s med C-lignende kode i Lokale typer undervisningen først og ikke i Strukturer undervisningen. Følgende eksempler kan limes inn som lokale typer.
- Hvis du er usikker på å reversere en struktur, ødelegger en konstruktør vanligvis vftable og utformingen av noen medlemmer; en funksjon som kaller det, kan gi totalstørrelsen på objektet ved å reservere nok byte til det.
- IDA 7.0 hjelper deg mye ved automatisk å gjenkjenne og merke RTTI-data (hvis tilgjengelig) som ødelegger klassehierarkiet og vftables, men det markerer feilaktig hvilken vftable som hører til hvilken basisklasse i tilfelle flere arv, et problem IDA6-skriptet det er basert på hadde ikke. Jeg skrev den om i IDAPython for 7.0 for å fikse dette og andre MSVC-relaterte problemer, det kan også automatisk opprette strengene som forklart nedenfor.
- IDA 7.2 har enda bedre støtte for automatisk å oppdage og gjenskape C ++ strukturer, men følgende svar ble skrevet med 7,0 i tankene og ved bruk av 7.2 navngivning.
Avhengig av hvor fancy polymorfismen din er, må C-strukturene dine være mer eller mindre fancy også. Så la oss begynne med de enkle sakene først og jobbe oss mot de mer kompliserte.
Ingen arv (basisklasse)
Den enkleste saken bruker ikke noen arv i det hele tatt, bare noen medlemmer og (rene) virtuelle metoder til å begynne med:
klasse Animal {int _age; Dyr () {_age = 0; } int getAge () {return _age; } virtuelt tomrom setAge (int-verdi) {_age = verdi; } virtuelt tomrom makeSound () = 0;};
MSVC-layout:
klasse Dyrestørrelse (8): + --- 0 | {vfptr} // peker til vftable (s. nedenfor) 4 | _age // medlemmer av Animal + --- Animal :: $ vftable @: | &Animal_meta // ignorere meta for våre eksempler | 0 // den (rene) virtuelle metoden følg 0 | &Animal :: setAge 1 | &Animal :: makeSound
IDA-representasjon:
struct Animal; struct Animal_vtbl {void (__thiscall * setAge) (Animal * this, int value); ugyldig (__thiscall * makeSound) (Animal * this);}; struct Animal_mbrs {int _age;}; struct Animal {Animal_vtbl * __ vftable; Animal_mbrs __members;};
- Forklar erklær klassestrukturen for å bruke den i
denne
-parameteren til vftable. - Oppringningskonvensjonen
__thiscall
kreves for å lage klassemetoder i MSVC. Den sender implisitt en peker til klasseinstansen i ecx
-registeret i tillegg til alle andre parametere. - Det er ikke nødvendig å oppgi navn på parametrene.
-
makeSound
vil være en purecall
, og setAge
vil være en typisk ukjent sub som endrer medlemmet vårt. - Plass medlemmer i en egen struktur for arv (s. nedenfor).
Enkelt arv
La oss raskt avle en Hund
som arver fra Animal
, implementer makeSound
-metoden, og legg til en ny virtuell metode for å angi pelsfargen:
klasse Hund: offentlig dyr {int _furColor; virtuelt tomrom setAge (int-verdi) {_age = verdi; } virtuelt tomrom makeSound () {cout << "Woof Woof"; } virtuelt tomrom setFurColor (int farge) {_furColor = farge; }};
MSVC-layout: Baseklassen Animal
er ganske enkelt innebygd i klassen Dog
. Den innebygde Animal
vftable griper også alle de virtuelle Dog
-metodene og legger dem til på slutten. Medlemmene av Dog
vises bak Animal
s medlemmer:
klasse Hundestørrelse (12): + --- 0 | + --- (basisklasse Animal) 0 | | {vfptr} 4 | | _alderen | + --- 8 | _furColor + --- Hund :: $ vftable @: | &Dog_meta | 0 0 | &Dog :: setAge 1 | &Dog :: makeSound 2 | &Dog :: setFurColor // Lagt til bak dyremetodene!
IDA-representasjon: La Animal
-strukturer være urørt, vi legger til følgende:
struct Dog; struct Dog_vtbl: Animal_vtbl {void (__thiscall * setFurColor) (Dog * this, int color);}; struct Dog_mbrs: Animal_mbrs {int _furColor;}; struct Dog {Dog_vtbl * __ vftable; Dog_mbrs __members;};
- Gjenbruk
Animal
vftable ved å la Dog
vftable arve fra den (arve en struktur i IDA betyr ganske enkelt å forhåndsinstallere den), og legg deretter til Dog
spesifikke virtuelle funksjoner.
- Det samme skjer med medlemmene, det er grunnen til at jeg skilte dem tidligere.
Flere arv
Dette krever litt mind-mangling. For dette gjør vi det mulig å drepe hunden vår (beklager hvis det er grusomt for deg, jeg er dårlig til å lage glade eksempler):
class Killable {bool _isDead; virtual void kill () {makeDeathSound (); _isDead = true; } virtuelt tomrom makeDeathSound () = 0;}; klasse Hund: offentlig Dyr, offentlig Drepbar {int _furColor; virtuelt tomrom setAge (int-verdi) {_age = verdi; } virtuelt tomrom makeSound () {cout << "Woof Woof"; } virtuelt tomrom setFurColor (int farge) {_furColor = farge; } virtuelt tomrom makeDeathSound () {cout << "Jeg vil kalle WWF, bark-blerg"; }};
MSVC-layout: Som med Animal
basisklasse, legger den den andre Drapbar
basisklassen inn i Hunden
klassen også, og holder Hunden
medlemmene atskilt. Mens Dog
-spesifikke virtuelle metoder fortsatt slås sammen med Animal
vftable (også kalt den første baseklassen), Drapbar
relaterte er i en egen Killable
-relatert vftable, så vi har nå:
klasse Hundestørrelse (20): + --- 0 | + --- (basisklasse Animal) 0 | | {vfptr} 4 | | _alderen | + --- 8 | + --- (basisklasse drepbar) 8 | | {vfptr} 12 | | _isDead | | <alignment member> (størrelse = 3) // siden _isDead er en 1-byte bool | + --- 16 | _furColor + --- Hund :: $ vftable @ Animal @: | &Dog_meta | 0 0 | &Dog :: setAge 1 | &Dog :: makeSound 2 | &Dog :: setFurColor // Hundemetoder fusjoneres fortsatt med Animal! Dog :: $ vftable @ Killable @: // Alle Killable-relaterte metoder her | -8 // offset for `denne` pekeren i drepbare metoder for å få en hundepeker
0 | &Killable :: kill 1 | &Dog :: makeDeathSound
IDA-representasjon: Vi holder en Dog
-spesifikk vftable i starten som gjenbruker Animal
vftable internt, som de virtuelle metodene Dog
er fortsatt lagt til Animal
s vftable. Så følger Animal
sine medlemmer som vanlig. Nå følger de urørte Killable
-strengene, siden ingenting blir slått sammen med dem. På slutten følger våre Dog
medlemmer. Hvis du sammenligner dette med forskyvningene som MSVC skrives ut, er det fornuftig:
struct Killable; struct Killable_vtbl {void (__thiscall * kill) (Killable * this); ugyldig (__thiscall * makeDeathSound) (Killable * this);}; struct Killable_mbrs {bool _isDead;}; struct Killable {Killable_vtbl * __vftable; Killable_mbrs __members;}; struct Dog; struct Dog_vtbl: Animal_vtbl {void (__thiscall * setFurColor) (Dog * this, int color);}; struct Dog_mbrs {// Ingen flere basedyrmedlemmer da de er delt opp nå! int _furColor; }; struct Dog {Dog_vtbl * __ vftable; // Inneholder fremdeles dyremetoder. Animal_mbrs __members_Dyr; // Dyrmedlemmer kommer hit hver for seg. Killable_vtbl * __ vftable_Killable; Killable_mbrs __members_Killable; Dog_mbrs __members;};
IDA 7.2 bruker litt annen navngiving for vftables som deltar i flere arv. Jeg synes det er plagsomt å håndtere manuelt, så vi bruker ikke det her.
La oss se hvordan dette ser ut i Hund :: ctor
pseudokode:
Dog * __ thiscall Dog :: ctor (Dog * this) {j_Dyr :: ctor ((Animal *) this); j_Killable :: ctor ((Killable *) &this->__vftable_Killable); this->__vftable = (Dog_vftable *) &Dog :: `vftable '; this->__vftable_Killable = (Killable_vftable *) &Dog :: `vftable '; returner dette;}
Som typisk for konstruktører kalles baseklasskonstruktører først. Deretter er vftables som kreves for Dog
angitt. Men noe gir ikke mening: Hvorfor er Dog :: vftable
tilordnet __vftable_Killable
vår? Vel, jeg brukte IDA 7.0s navngivning her, og det jeg tidligere nevnte er at det ikke merker hvilke vftable kart til hvilken basisklasse lenger (i motsetning til skriptet). Med IDA 6.0 eller IDAPython-skriptet mitt vil det si:
Dog * __ thiscall Dog :: ctor (Dog * this) {j_Animal :: ctor ((Animal *) this); j_Killable :: ctor ((Killable *) &this->__vftable_Killable); this->__vftable = (Dog_vftable *) &Dog :: `vftable for Animal '; this->__vftable_Killable = (Killable_vftable *) &Dog :: `vftable for Killable '; returner dette;}
Nå er ikke navnene de samme og gir mer mening, så ikke bli ødelagt av denne IDA 7-irritasjonen, dobbeltklikk navnene for å sjekke hvor de vil faktisk føre.
Jeg anbefaler ikke å sette den faktiske typen av vftables til deres lokasjoner / navn: Du får ingenting ut av det, det gjør bare demonteringsutgangen rotete.
Bonus: Klasser arvet fra klasser ved bruk av flere arv (wew)
Denne holdt meg litt forvirret i noen tid, kanskje fordi det ikke er dekket i de fleste opplæringsprogrammer på nettet som jeg stoler på, eller kanskje fordi jeg bare er dum. La oss arve fra Dog
med klassen Terrier
.
class Terrier: public Dog {int _annoyanceLevel; virtuelt tomrom setAge (int-verdi) {_age = verdi; } virtuelt tomrom makeSound () {cout << "Bark Bark not Woof Woof"; } virtuelt tomrom irritere () {_annoyanceLevel ++; }};
MSVC-layout: Det virker ikke så spesielt. Animal
vftable still slår sammen de nye virtuelle metodene til Terrier
, og alt annet har sin egen vftable:
klasse Terrier-størrelse (24): + --- 0 | + --- (basisklasse Hund) 0 | | + --- (basisklasse Animal) 0 | | | {vfptr} 4 | | | _alderen | | + --- 8 | | + --- (basisklasse drepbar) 8 | | | {vfptr} 12 | | | _isDead | | | <alignment member> (størrelse = 3) | | + --- 16 | | _furColor | + --- 20 | _annoyanceLevel + --- Terrier :: $ vftable @ Animal @: | &Terrier_meta | 0 0 | &Terrier :: setAge 1 | &Terrier :: makeSound 2 | &Dog :: setFurColor 3 | &Terrier :: irritere // Animal tar til og med Terrier-metodene (grådig!) Terrier :: $ vftable @ Killable @: | -8 0 | &Killable :: kill 1 | &Dog :: makeDeathSound
IDA-representasjon: Det ligner ganske mye på vår første Dog
struct-opprettelse, bare at vi trenger å respektere den andre baseklassen av Dog
også.
struct Terrier_vtbl: Dog_vtbl {void (__thiscall * annoy) (Terrier * this);}; struct Terrier_mbrs: Dog_mbrs {int _annoyanceLevel;}; struct Terrier {Terrier_vtbl * __vftable; Animal_mbrs __members_Dyr; Killable_vtbl * __ vftable_Killable; Killable_mbrs __members_Killable; Terrier_mbrs __members;};