Qt Internals & Reversing.

Introduksjon.

Halvparten av teksten i denne artikkelen kommer fra mitt storre papir «Dynamic C ++ Proposal». Jeg bestemte meg for at det var nyttig a ta del om Qt internals, sette den inn i en annen artikkel og utvide den ved a legge til en reverserende del. Pa grunn av sin natur er dette ikke den vanlige typen artikkel jeg skriver. Faktisk skrev jeg reverseringsdelen pa mindre enn en dag. Sa, dette er en veldig enkel en. Men jeg tror det er nyttig for folk som trenger a reversere en Qt-applikasjon, og absolutt ikke ville vurdere a lese mitt andre papir om Dynamic C ++, som ikke hores ut som et papir om Qt, og faktisk er det ikke et papir om Qt: avsnittet om Qt er bare en blant mange andre. Dessuten har jeg ikke sett seriose artikler om dette emnet.

Det forste som ma vurderes nar du reverserer Qt-applikasjoner, er hva Qt brakte til C ++-spraket. Hendelser (inne i Qt-rammen) er bare virtuelle funksjoner, sa ingenting nytt der. Dette er ikke en C ++ reverseringsguide. Hva er nytt i Qt er signaler og spor, som stole pa dynamikken i Qt-rammen.

Sa, det forste jeg skal vise hvordan denne dynamikken fungerer. Den andre delen fokuserer pa reversering, og pa det tidspunkt vil jeg vise hvordan du far alle metadataene du trenger nar du demonterer en Q_OBJECT-klasse.

Den eneste alvorlige C ++-rammen jeg har sett rundt, er Qt-rammen. Qt tok C ++ til et nytt niva. Selv om signaler og slots teknologi introdusert av Qt er veldig original, var det virkelig interessant i hele ideen at et objekt kunne kalle andre objekter metoder uansett objektdeklarasjonen. For a fa signaler og spor til arbeid, ble dynamikken brakt inn i C ++. Vel, selvfolgelig, nar du bruker bare signaler og spor, utvikleren ikke legger merke til denne oppforselen, det handteres alt av rammen. Denne dynamikken kan imidlertid ogsa brukes av utvikleren gjennom QMetaObject-klassen.

Jeg skal ikke forklare grunnleggende om signaler og spor. Leseren kan sjekke Qt dokumentasjonssiden. Det jeg vil gjore, er a breifly vise Qt-dynamikkens indre arbeid. Den nav rende versjonen av Qt-rammen pa det tidspunktet jeg skriver dette papiret, er 4.4.3.

La oss se pa enkle signaler og spor eksempel:

SIGNAL- og SLOT-makroen inneholder innholdet i parentes, noe som gjor det til en streng. Vel, de gjor en ting til. De legger et identifikasjonsnummer i strengen:

Sa kan man ogsa skrive:

Qt-nokkelordene og sporene, som kan finnes i klassens topptekst, er bare nyttige for Qt-metakompilatoren (moc).

Faktisk, som du kan se, oker selv makroen bare lesbarhet. Bare signaler makroen er litt annerledes, fordi den kvalifiserer Qt-signaler som beskyttede metoder, mens spor kan v re av noe slag. Den forste interessante delen er Q_OBJECT makroen:

staticMetaObject er QMetaObject, som er statisk fordi det er metadataklassen, det deles av alle forekomster av samme klasse. MetaObject-metoden returnerer bare staticMetaObject. QT_TR_FUNCTIONS er en makro som definerer alle tr-funksjoner, som brukes til flerspraklig stotte. qt_metacast utforer en dynamisk cast, gitt klassenavnet eller navnet pa en av baseklassene (Qt stole ikke pa RTTI, apenbart). qt_metacall kaller et internt signal eller spor ved sin indeks. For jeg skal diskutere koden generert av moc, er her QMetaObject-erkl ringen:

Den viktige delen av QMetaObject er den interne d-strukturen. Det forste medlemmet i denne strukturen er en QMetaObject klassepeker. Dette medlemmet peker mot moderen Qt-objektmetadataklassen. En klasse som var kan arve fra mer enn bare en klasse, men den kan bare ha en QObject (eller fra den avledede) grunnklassen, og det er superklassen. Dessuten stoler Moc pa det faktum at i en QObject-avledet klassedeklarasjon er den forste arvelige klassen en QObject (eller avledet) grunnklasse. La oss ta en Qt-dialog, som ofte bruker flere arv i implementeringen:

Som gjor at moc produserer denne koden:

Men hvis ConvDialog arver Ui :: ConvDialog for QDialog, genererer moc:

Som er feil, fordi Ui :: ConvDialog ikke er en avledet klasse av QObject og dermed ikke har et staticMetaObject-medlem. A gjore det vil resultere i en kompileringsfeil.

Det andre medlemmet av d-strukturen er et karbonatrise, som inneholder klassens bokstavelige metadata. Det tredje medlemmet er et usignert int array. Dette oppsettet er et bord, og det inneholder alle metadataforskyvninger, flagg osv. Sa hvis man onsker a oppregne sporene og signalene til en klasse, ma man ga gjennom dette bordet og fa de riktige offsetene for a fa metodene navnene fra strengdata-arrayen. Det refererer ogsa egenskaper og enums. Det fjerde medlemmet er et null terminert utvalg av QMetaObject-klasser. Dette medlemmet gir lagring for metadatainformasjon for tilleggsklasser. Jeg har aldri sett den brukt, men den refereres av funksjonen QMetaObject_findMetaObject.

Denne funksjonen blir bare kalt ved eiendomsmetoden, som i sin tur blir kalt av propertyCount, propertyOffset og indexOfProperty.

Og her er moc-generert kode for var counter-klasse:

Metoden qt_metacall kaller andre interne metoder for klassen etter deres indeks. Qt-dynamikken er avhengig av indekser, og unngar pekere. Jobben til faktisk a ringe metoder er overlatt til kompilatoren. Denne implementeringen gjor signalene og slots mekanismen ganske rask.

Argumenter sendes gjennom en peker til pointer-array og kastes hensiktsmessig nar man ringer metoden. A bruke poeng er selvsagt den eneste maten a sette alle typer typer i en matrise. Argumenter starter fra posisjon 1, fordi posisjon 0 er reservert for dataene som skal returneres. Signaler og spor i eksemplet er erkl rt ugyldige og har dermed ingen data a returnere. Hvis et spor hadde data a returnere, ville koden i bryteren se slik ut:

Den andre interessante metoden som genereres av moc er valueChanged, som representerer koden som er utfort for a avgive valueChanged-signalet. Denne koden kaller aktiveringsmetoden til QMetaObject, som bare er en overbelastning av denne aktiveringsmetoden:

Denne metoden gjor mange ting, inkludert a sjekke om den nav rende tilkoblingen skal behandles umiddelbart eller settes inn i hendelsekoen. Hvis det er tilfelle, kalles det den aktuelle aktiveringsmetodvarianten, og fortsetter deretter med neste tilkobling i ConnectionList. Ellers, hvis forbindelsen skal behandles umiddelbart, blir ID-en av metoden som skal ringes, retrivet fra den nav rende tilkoblingen, og mottakerens qt_metacall-metode blir kalt. For a forenkle utforelsen flyt:

Og dette forteller oss alt vi trenger a vite om internaler av signaler og spor. Nar du kaller tilkoblingsfunksjonen, konverteres signatur- og spor-signaturene til deres ids, som deretter lagres i tilkoblingsklassen. Hver gang et signal sendes ut, blir forbindelsene for signalets ID hentet, og deres spor blir kalt av deres ids.

Den siste delen som ma diskuteres er dynamiske pakaller. QMetaObject-klassen tilbyr invokeMethod-metoden for a dynamisk kalle en metode. Denne metoden er litt annerledes enn signaler og spor, fordi den ma bygge en signatur for metoden a ringe fra returtype, navn og argumenttyper, og deretter sla opp metadataene for a hente ID-et, for du kaller qt_metacall-metoden for objektet.

Metoden id hentes gjennom indexOfMethod. Hvis metoden underskrift ikke kan bli funnet, returnerer invokeMethod false.

Og dette bor gi deg en generell ide om Qt internals.

Ved reversering onsker vi a bruke alle metadata Qt-tilbudene. For a gjore det, la vi revidere metadatabordet:

Som man kan legge merke til, forteller tabellen oss ikke bare metodetellingen, men ogsa motsetningen av metodene (10). Her er C ++-deklarert overskrift av denne strukturen:

Denne strukturen er inneholdt i «qmetaobject.cpp» som resten av informasjonen ma vi analysere metadata tabellen. For du tar hensyn til egenskaper og enums, la oss vurdere metoder. Vi har allerede metoden telle og kompensere, det som na ma analyseres er dataene til metodene selv. Disse dataene er delt inn i fem heltall, som representerer i henhold til moc: signatur, parametere, type, tag, flagg. Signaturfeltet er en offset i strengdataene og refererer til en delvis deklarasjon (uten returtype) av metoden: valueChanged (int). Parameterfeltet er et annet kompensert til navnene pa argumentene. Ja, navnene: ‘newValue’. Navnene er adskilt av kommaer, sa hvis sporet hadde to argumenter, ville det vise: ‘newValue1, newValue2’. Typefeltet refererer til returmetoden til metoden. Hvis det er en tom streng, som i vart tilfelle, er metoden deklarert som ugyldig. Etikettfeltet er ubrukt for oyeblikket. Jeg limer inn beskrivelsen i kildene: «Merker er spesielle makroer anerkjent av moc som gjor det mulig a legge til ekstra informasjon om en metode. For oyeblikket stotter moc ikke noen spesielle tagger. «. De siste feltflaggene er ikke en offset, men, som navnet antyder, flagg. Her er listen over mulige flagg:

Dette er alt vi trenger a vite om metoder. La oss na vurdere enums og egenskaper. Til dette formal har jeg lagt til en av hver til koden eksempelet ovenfor:

Og moc genererer:

Igjen, vi har egenskapene og enums teller og deres offsets. La oss starte med egenskaper. Dataene pa egenskaper er laget av 3 heltall: navn, type, flagg. Navnet feltet refererer selvfolgelig til navnet. Typefeltet viser typen av eiendommen, i vart tilfelle: Prioritet. Til slutt inneholder flaggfeltet flagg og her er listen:

La oss vurdere na enum: navn, flagg, telle, data. Vi vet allerede hva navnet feltet er na. Flaggfeltet er ikke brukt. Tellefeltet representerer antall elementer som finnes i enummen. Datafeltet er en offset i metadatabordet og peker pa enhetens elementer. Hvert element er representert av to heltall: nokkel, verdi. Nokkelfeltet peker pa navnet pa gjeldende element, mens verdien er den faktiske verdien av elementet.

For a hente metadatainformasjonen fra en bin r fil, skrev jeg et skript for IDA. Jeg skrev skriptet i Python takket v re IDAPython plugin. Mange grunner til a bruke Python: a skrive den samme koden med IDC-skriptet, ville v re altfor mye innsats, og Python kan gjenbrukes selv utenfor IDA. Skriptet trekker ut metoder, egenskaper og enums fra oppsettet til en Q_OBJECT-klasse. Du kan laste den ned herfra. Her er koden:

Jeg skal forklare hvordan jeg skal na metadataene til en klasse i et oyeblikk. Funksjonen DisplayMetaData aksepterer to parametere: metadatabordadressen og metadatastrenger adressene. Det er fordi det noen ganger skjer at utformingen av en klasse er satt pa kjoretid som denne VC ++-applikasjonen gjor:

Noen ganger er oppsettet allerede satt i den fysiske filen:

I sa fall kan skriptet lanseres direkte pa adressen til SuperData-elementet, som er den forste av d-strukturen inne i QMetaObject. Utgangen av skriptet for den ovennevnte tellerklassen er:

— Qt MetaData Displayer av Daniel Pistelli.

— metadata av klassen: Counter.

flagg: Lesbar | Skrivbar | EnumOrFlag | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

Nice, ikke sant? Signaturen til metodene inneholder like argumentnavn (nar tilgjengelig). For a sjekke kvaliteten pa skriptet, provde jeg det pa den storre klassen QWidget inne i modulen ‘QtGui4’. Her er resultatet:

— Qt MetaData Displayer av Daniel Pistelli.

— metadata av klassen: QWidget.

2 – offentlig: tomt settDisabled (bool)

3 – offentlig: ugyldig settWindowModified (bool)

4 – offentlig: ugyldig settWindowTitle (QString)

5 – offentlig: tomt settStyleSheet (QString styleSheet)

6 – offentlig: void setFocus ()

7 – offentlig: ugyldig oppdatering ()

8 – offentlig: ugyldig repaint ()

9 – offentlig: tomt settVisible (bool synlig)

10 – offentlig: tomt settHidden (bool skjult)

11 – offentlig: ugyldig visning ()

12 – offentlig: ugyldig skjul ()

13 – offentlig: tomt settShown (bool vist)

14 – offentlighet: tomt showMinimized ()

15 – offentlig: tomt showMaximized ()

16 – offentlig: void showFullScreen ()

17 – offentlig: ugyldig showNormal ()

18 – offentlig: bool close ()

19 – offentlig: tomgang ()

20 – offentlig: ugyldig lavere ()

21 – beskyttet: ugyldig oppdateringMicroFocus ()

22 – privat: void _q_showIfNotHidden ()

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

1 – Qt :: WindowModality windowModality.

flagg: Lesbar | Skrivbar | EnumOrFlag | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

2 – bool aktivert.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

3 – QRect geometri.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

4 – QRect frameGeometry.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

5 – QRect normalGeometry.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Skrivbar | Skriptbar | ResolveEditable.

9 – QSize frameSize.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Skrivbar | Skriptbar | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

14 – QRect childrenRect.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

15 – QRegion childrenRegion.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

16 – QSizePolicy sizePolicy.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

17 – QSize minimumstorrelse.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

18 – Maksimal storrelsesstorrelse.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

19 – int minimum Bredde.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | ResolveEditable.

20 – int minimumHoyde.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | ResolveEditable.

21 – int maksimal Bredde.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | ResolveEditable.

22 – int maksimalHoyde.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | ResolveEditable.

23 – QSize sizeIncrement.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

24 – QSize baseSize.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

25 – Qpalette palett.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

27 – QCursor markor.

flagg: Lesbar | Skrivbar | Tilbakestillbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

28 – bool mouseTracking.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

29 – bool isActiveWindow.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

30 – Qt :: FocusPolicy fokusPolicy.

flagg: Lesbar | Skrivbar | EnumOrFlag | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

32 – Qt :: ContextMenuPolicy contextMenuPolicy.

flagg: Lesbar | Skrivbar | EnumOrFlag | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

33 – bool updatesEnabled.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | Lagret | ResolveEditable.

34 – bool synlig.

flagg: Lesbar | Skrivbar | StdCppSet | Skriptbar | Lagret | ResolveEditable.

35 – bool minimert.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

36 – bool maksimert.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

37 – bool fullScreen.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

38 – QSize sizeHint.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

39 – QSize minimumSizeHint.

flagg: Lesbar | Designbar | Skriptbar | Lagret | ResolveEditable.

40 – bool acceptDrops.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

41 – QString windowTitle.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

42 – QIcon windowIcon.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

43 – QString windowIconText.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

44 – dobbeltvinduetOpacity.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

45 – bool windowModified.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

46 – QString toolTip.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

47 – QString statusTip.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

48 – QString whatsThis.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

49 – QString tilgjengelig.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

50 – QString tilgjengeligDescription.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

51 – Qt :: LayoutDirection layoutDirection.

flagg: Lesbar | Skrivbar | Tilbakestillbar | EnumOrFlag | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

52 – bool autoFillBackground.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

53 – QString styleSheet.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

54 – QLocale locale.

flagg: Lesbar | Skrivbar | Tilbakestillbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

55 – QString windowFilePath.

flagg: Lesbar | Skrivbar | StdCppSet | Designbar | Skriptbar | Lagret | ResolveEditable.

Utgangen er 100% riktig sammenlignet med QWidget kildekoden.

Na skal jeg vise deg hvordan du finner metadataene til en klasse. La oss vurdere fordelingen av tellerklassen:

Den siste instruksjon av forsamlingen ovenfor setter vtabelpekeren. Vekten til en Q_OBJECT-klasse ser dette ut:

MetaObject-metoden kan brukes til a skaffe metadatabordene:

dword_406000 refererer til klasselayout av QMetaObject, som vi ma utfore skriptet.

Det neste trinnet, etter a ha hentet metadataene til en klasse, er koblingsmetode (og eiendom) navn til deres faktiske demonterte kode. Skriptet skriver ut indeksen for hver metode og egenskap. For a oppna en metodeadresse fra en indeks, er det nodvendig a vurdere qt_metacall-metoden generert av moc:

Denne metoden kan lose bade metodenavn og egenskaper for a fa / sette metodene.

Adressen til qt_metacall er hentet fra vtabelen. For du analyserer koden til denne metoden, er det nodvendig a ta hensyn til denne enmen:

Na demontering:

Hvis esi-registeret ikke er 0, er det et InvokeMetaMethod-anrop. Switich er representert av de siste instruksjonene. La oss folge «_id == 0» sak:

Sa, vi kan na fastsla at sub_401490 virkelig er verdienChanged signal:

Na er metoden forstaelig. Den samme tiln rmingen kan brukes til a lose de far / sett metodene til egenskaper. Selvfolgelig, for en stor analyse pa en fil, kan en liten parser for bryterblokker skrives og kombineres med Qt MetaData Parser-skriptet for a lose alle metoder automatisk. Men bytteblokkene er dypt plattform og kompilatoravhengig. Derfor ma man skifte skript ofte. Videre er sma metoder ofte innfort i qt_metacall-metoden, dette bor vurderes nar du skriver et slikt skript.

Konklusjoner.

Denne artikkelen tok meg bare en dag, og jeg likte a skrive den. Selv om det er enkelt, kan det hende at noen finner det nyttig, ogsa fordi, etter min mening, Qt-rammen vil bli brukt mer og mer av programvareutviklere.


Vil du spille i det største kasinoet? Vi samlet det for deg. Registrer deg nå!