PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [PE] Erstellen von Codecaves



Cardano
11.07.2010, 15:06
Hi Leute, mein erster Post und dann gleich sowas :P
Und zwar hab ich ein kleines Problem bei meinem ersten C++ Projekt. Ich arbeite daran mich ein bisschen mit dem PE-Dateiformat auseinander zu setzen. Dabei hab ich natürlich einfache Dinge wie das allgemeine Zurechtfinden im Format usw. schon hinter mir. Aktuell versuch ich mich daran Codecaves zu ERZEUGEN. Also nicht bereits vorhandene auszunutzen. Hierbei mache ich folgendes:

Ich suche mir ein RawOffset innerhalb der Datei, erstelle dort ein Codecave (Nullbytemuster) einer variablen Größe.

Sagen wir ich erzeuge beim OEP ein Codecave der Größe 0x10, also 16Byte.
Der OEP meiner Datei (weiter unten) liegt bei der RVA 0x000111BE, das entspricht dem RawOffset 0x5AE. Und am Ende der Section ist ein Codecave mit einer Größe von 428 Byte. Dann wird sozusagen ab dem OEP alle Bytes um 0x10 in dieses Codecave am Ende der Section verschoben. Die restlichen Sections bleiben unberührt. Dies ist jetzt nur der einfachste Fall den ich behandle. Aber da ich im Moment nur so arbeite, dass dieser eintrifft und bei mir Fehler aufkommen, wollet ich hier um Hilfe fragen.

Folgendes mache ich noch vor dem eben beschriebenen:
Ich bestimme das Interval der betroffenen Bytes, also in diesem Fall von dem RawOffset des OEP bis zum virtuellen Ende der Section, also
von OEP bis (HeaderDerSection->PointerToRawData + HeaderDerSection->Misc.VirtualSize)

Dann gucke ich mir alle Relocations im RelocationDirectory an und vergleiche zum einen ob die RawOffsets auf welche sie zeigen betroffen sind und zum anderen ob die Pointer die dort liegen in das betroffene Interval zeigen. Beide Pointer (RVA's) werden jenachdem mit 0x10 addiert und wieder gespeichert.

Nachdem ich die Relocations abgearbeitet habe, versetze ich den OEP um 0x10 (im Mom. möchte ich noch keinen Crypter erstellen sondern einfach nur diese Funktion zum Laufen kriegen, also teste ich erstmal ob das Programm noch läuft) und dann setze ich die neuen Daten des SectionHeaders so, dass er wieder UpToDate ist was Virtual und RawSize angeht.

Das sind zumindest alle Schritte die ich glaube machen zu müssen (ich code erst seit 5 Tagen in C++ und seit 6 Tagen beschäftige ich mich erst mit dem PE-Format)

Hier mal ein kleiner PrintOut meiner Rederections die ich setze aus meiner Konsole:

C:\_projects\justcrypt\Debug>J - Anonymous - Jm0R0CnN - Pastebin.com (http://pastebin.com/Jm0R0CnN)
(Zur Erklärung: In den ersten Zeilen lass ich mir Caves am Ende von Sections ausgeben, dann im Großen bereich lass ich mir die Relocations ausgeben, hierbei gilt das Muster
Reloc RelocationblockID - RelocationBlockEntryID : Old: <Altes Word der Relocation>, New: <ggf. neuer Wert> - RawOffset: <wohin die low 12 bit + die RVA des RelocBlocks als RawOffset zeigen> , Value(RO): <der Wert der am RawOffset liegt umgewandelt in ein RawOffset>

Hier der Code den ich in kompilierter Form verarbeite:


/*----------------------------------------------------------
HelloMsg.cpp - Gibt den Text "Hello World" in einem
Meldungsfenster aus
----------------------------------------------------------*/

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MessageBox(NULL, TEXT("Hello World"), TEXT("HelloMsg"), 0);

return 0;
}
Und hier die kompilierte Version (hello_world.exe) und die Ausgabe meines Programmes (hello_world_modified.exe)[/URL]
[url]http://rapidshare.com/files/406351136/helloworld.rar (http://rs353tl.rapidshare.com/cgi-bin/upload.cgi?rsuploadid=134120429230472830)
Wenn ihr das ganze in einem PE-Editor öffnet, werdet ihr bspw. sehen, dass zb der OEP wirklich um 0x10 versetzt wurde. Da ich leider überhaupt keine Erfahrung mit Debuggern habe bin ich auf eure Hilfe angewiesen. Hab ich was vergessen oder wurden irgendwelche Werte falsch gesetzt ?

Vielen Dank schonmal, Cardano :)

Mofo
11.07.2010, 15:20
Sehr interessant was du vorhast, die Idee ist durchaus zu gebrauchen :)
Ich versuche das auch mal und schau ob ich den Fehler finde, ich bin noch sehr verschlafen..
Wenn dann werde ichs editieren

/edit :Ok die Idee ist kaum umzusetzen vor allem nicht mit meinen Asm-Kenntissen :/ , ich denke ich bleibe weiterhin beim ausnutzen vorhandener Caves , oder schaffe mir zusätzlichen Platz am Ende der PE :P

Cardano
11.07.2010, 15:44
Vielen Dank schonmal =)
Ich würde mich am Liebsten selber daran machen den Fehler zu beheben. Nur leider hab ich bisher noch gar nicht mit Debuggern gearbeitet. Und wenn man die hello_world_modified herunterlädt dann sieht man, dass sie abstürzt ohne einen Fehler auszugeben. Es wäre echt schon hilfreich wenn mir jemand sagen könnte warum sich Datei abschießt, bzw. wobei. Schafft der Loader die Datei zu laden und sie verendet erst beim Ausführen von Code am neu gesetzte OEP oder verendet der Loader schon bei irgendetwas ? Wie zum Beispiel an dem Relocations oder sonst irgendwas.

Hab ich vielleicht sonst noch etwas vergessen ?
Wie gesagt es wurden folgende Werte verändert:
Pointer in das betroffene Interval, Addresse des OEPs und RawSize und VirtualSize der Section im SectionHeader

l0dsb
11.07.2010, 15:59
Eins vorweg: Wenn du dich bereits mit Themen wie PE-Modifikation beschäftigen willst, ist der Umgang mit einem Debugger auf Binary-Basis mehr oder weniger ein "Muss". Lena151 hilft da gerne weiter, was OllyDbg betrifft.

Du verschiebst nicht alle Relocations richtig. Am OEP befindet sich bereits ein JMP, der über deine Codecave springt (Ziel außerhalb des Intervalls, wenn ich deine Beschreibung oben richtig verstanden habe) und somit 0x10 Byte unter seinem eigentlichen Ziel landet.

Entwickelst du das System nur zum Spaß oder was möchtest du damit erreichen? Unter Umständen gibt es eine elegantere Möglichkeit, das ähnlich umzusetzen.

Muss die Cave mitten im Code liegen (erhöhter Aufwand, wenn sich irgendwann noch die Boundaries anderer Sections verschieben, da diese auch angepasst werden müssten) oder was spricht gegen Caves am Ende einer Section, Backup des OEPs und Cave in der ersten Section, Hinzufügen einer Section, ...

EBFE
11.07.2010, 16:19
Verstehe ich es richtig: du "schiebst" 0x10 Bytes "mittenrein" in den Code und passt dann diese über Relocations an? Dann sehe ich auf Anhieb folgende Probleme: zum einen sind Relocations bei Exen eher "optional" und werden nicht von allen Compilern/Linkern produziert.
Zum anderen: Relocations an sich decken den "Anpassbedarf" bei weitem nicht ab. Denn viele JMPs und CALLs (auf Maschinencodelevel) sind relativ - also in der Form "Springe/Calle zu der Stelle: _aktuelle_position + XYZ Bytes". Und diese werden keineswegs von Relocations irgendwie korriegiert (denn aufgrund ihrer "Relativität" laufen sie eher mit jeder ImageBase). D.h dass alle relativen JMPs und CALLs in und aus dem "verschobenen" Bereich dann dementsprechend 0x10 Bytes daneben gehen.

Cardano
11.07.2010, 16:23
Muss die Cave mitten im Code liegen (erhöhter Aufwand, wenn sich irgendwann noch die Boundaries anderer Sections verschieben, da diese auch angepasst werden müssten) oder was spricht gegen Caves am Ende einer Section, Backup des OEPs und Cave in der ersten Section, Hinzufügen einer Section, ...

Es MUSS nirgendwo liegen :p Ich schreibe zum Spaß, um mich an dem Umgang mit C++ zu gewöhnen (ich code seit einem halben Jahr mit PHP und fang jetzt mal mit C++ an) an diesem Projekt. Die von dir beschriebenen Fälle decke ich ebenfalls ab. Ich versuche das ganze so komplett wie möglich zu gestalten mit dem Endziel vielleicht IRGENDWANN mal damit einen Crypter zu basteln. Ich versuche mich mit jedem einzelnen Teil der PE Spezifikation auseinander zu setzen um so eine lib zu entwickeln mit der ich einfach PE Datein modifizieren, kombinieren und neu zusammensetzen kann.

Zum 2. Abschnitt:
Könntest du genauer beschreiben was du meinst?
Achso sry, die oben im Text beschriebenen angaben sind zu einer anderen Datei =(, Hier nochmal ein Print in dem ich mehr Informationen gleich ausgeben lasse, eben hatte ich einige deaktiviert, so kriegt ihr gleich Informationen zur richtigen Datei hello_word.exe):
Es passen 8 SecionHeader in da - Anonymous - DYt7kwdc - Pastebin.com (http://pastebin.com/DYt7kwdc)

Wenn du hier auch einen Fehler findest, sag bitte die RelocBlock ID und die RelocBlockEntry ID die du meintest.

Vielen Dank schonmal für die Mühe

btw.: Wer ist Lena151?

EDIT: Ah danke EBFE. Heißt das ich habe mit dem Unterfangen schon vorloren ? Oder kann man es schaffen diese JMP's aufzuspüren und ebenfalls anzupassen ?

l0dsb
11.07.2010, 16:38
Wenn sich die Boundaries verschieben, musst du dich zwangsweise mit jedem Teil der Spezifikation auseinandersetzen, da du dann z. B. auch das Resource-Directory relocaten musst. ;)

Lena151 hat eine Einführung in die Verwendung von OllyDbg gegeben (zum Reverse Engineering). Nichtsdestotrotz interessant für dich, wobei du dir noch Assembler ansehen solltest.

EBFE hat natürlich recht im Bezug auf die short jumps. Diese hundertprozentig aufzuspüren und anzupassen halte ich für utopisch (oder schlichtweg für zu viel Mehraufwand in Anbetracht der zu erledigenden Aufgabe).

Um Daten an eine PE-Datei anzuhängen, wird üblicherweise die Resource-Section verwendet (auch prima für Code zu verwenden, vgl. 29a eZine), die letzte Section erweitert oder eine neue Section eingefügt.

Damit der Entrypoint nicht in der letzten Section liegt (heuristic detection), können ein paar Byte des OEPs in die letzte Section verschoben werden und ein Sprung (nicht wortwörtlich, aber funktional) zum Code in der letzten Section ausgeführt werden (vgl. ASProtect).

EBFE
11.07.2010, 16:39
EDIT: Ah danke EBFE. Heißt das ich habe mit dem Unterfangen schon vorloren ? Oder kann man es schaffen diese JMP's aufzuspüren und ebenfalls anzupassen ?Zuerst müssten wir sichergehen, dass wir nicht aneinander vorbeireden ;)
D.h ich denke, dass du folgendes möchtest:


040 1000 code
040 1002 code
<--- hier wird eine 16 Byte Codecave eingeschleußt
040 1003 + 0x16 nun ist der restliche Code quasi um 16 Bytes verschoben
Um richtig zuverlässig zu arbeiten, müsste dein Code mit einem Disassembler durch den kompletten Code durchgehen und alle CALLS und JMPs in den nun verschobenen Bereich um die Intervallänge korrigieren. Dasselbe gilt für CALLs/JMPs aus diesem Bereich in den "restlichen" Code (allerdings nicht für Sprünge innerhalb des Bereichs). Wie schwer es ist, wirklich zuverlässig zu disassemblieren, braucht man glaub ich nicht zu erwähnen. Obwohl die üblichen Disassemblerengines in 90% aller Fälle sehr brauchbare Ergebnisse liefern sollten. Die Frage ist, ob du solche Komplexität möchtest? Denn letzendlich ist es 100 mal einfacher, die Codecave am Ende der Seciton einzufügen, in dem man den Unterschied zwischen RawSize und VirtualSize sowie SectionAlignment vs. FileAlignment ausnutzt (die Sectionheader Korrektur muss ja auch bei deiner Methode erfolgen). Zudem du die "Boundarys" der nachfolgenden Sections eher nicht verschieben kannst (bei Verschiebung ergeben sich noch heftigere Probleme, als gerade mit der Codeverschiebeung ;) )

Edit: es sind ja nicht nur short JMPs betroffen, auch "long JMPs" + Calls sind sehr oft relativ (nur sieht/merkt man das normalerweise in OllyDbg nicht, da Olly direkt die Zieladresse anzeigt).
Hier ein Auszug der CALC.exe ab den OEP - in den 190 Bytes sind gleich 3 relative Calls "nach Außen" drin und ein haufen Jmps.


01012475 > $ 6A 70 PUSH 70
01012477 . 68 E0150001 PUSH calc.010015E0
0101247C . E8 47030000 CALL calc.010127C8 ; relativer call
01012481 . 33DB XOR EBX,EBX
01012483 . 53 PUSH EBX ; /pModule => NULL
01012484 . 8B3D 20100001 MOV EDI,DWORD PTR DS:[<&KERNEL32.GetModu>; |kernel32.GetModuleHandleA
0101248A . FFD7 CALL EDI ; \GetModuleHandleA
0101248C . 66:8138 4D5A CMP WORD PTR DS:[EAX],5A4D
01012491 . 75 1F JNZ SHORT calc.010124B2 ; die short JMPs sind sowieso relativ
01012493 . 8B48 3C MOV ECX,DWORD PTR DS:[EAX+3C]
01012496 . 03C8 ADD ECX,EAX
01012498 . 8139 50450000 CMP DWORD PTR DS:[ECX],4550
0101249E . 75 12 JNZ SHORT calc.010124B2
010124A0 . 0FB741 18 MOVZX EAX,WORD PTR DS:[ECX+18]
010124A4 . 3D 0B010000 CMP EAX,10B
010124A9 . 74 1F JE SHORT calc.010124CA
010124AB . 3D 0B020000 CMP EAX,20B
010124B0 . 74 05 JE SHORT calc.010124B7
010124B2 > 895D E4 MOV DWORD PTR SS:[EBP-1C],EBX
010124B5 . EB 27 JMP SHORT calc.010124DE
010124B7 > 83B9 84000000>CMP DWORD PTR DS:[ECX+84],0E
010124BE .^ 76 F2 JBE SHORT calc.010124B2
010124C0 . 33C0 XOR EAX,EAX
010124C2 . 3999 F8000000 CMP DWORD PTR DS:[ECX+F8],EBX
010124C8 . EB 0E JMP SHORT calc.010124D8
010124CA > 8379 74 0E CMP DWORD PTR DS:[ECX+74],0E
010124CE .^ 76 E2 JBE SHORT calc.010124B2
010124D0 . 33C0 XOR EAX,EAX
010124D2 . 3999 E8000000 CMP DWORD PTR DS:[ECX+E8],EBX
010124D8 > 0F95C0 SETNE AL
010124DB . 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
010124DE > 895D FC MOV DWORD PTR SS:[EBP-4],EBX
010124E1 . 6A 02 PUSH 2
010124E3 . FF15 0C120001 CALL DWORD PTR DS:[<&msvcrt.__set_app_ty>
010124E9 . 59 POP ECX
010124EA . 830D 10500101>OR DWORD PTR DS:[1015010],FFFFFFFF
010124F1 . 830D 14500101>OR DWORD PTR DS:[1015014],FFFFFFFF
010124F8 . FF15 08120001 CALL DWORD PTR DS:[<&msvcrt.__p__fmode>]
010124FE . 8B0D 0C500101 MOV ECX,DWORD PTR DS:[101500C]
01012504 . 8908 MOV DWORD PTR DS:[EAX],ECX
01012506 . FF15 04120001 CALL DWORD PTR DS:[<&msvcrt.__p__commode>
0101250C . 8B0D 08500101 MOV ECX,DWORD PTR DS:[1015008]
01012512 . 8908 MOV DWORD PTR DS:[EAX],ECX
01012514 . A1 00120001 MOV EAX,DWORD PTR DS:[<&msvcrt._adjust_f>
01012519 . 8B00 MOV EAX,DWORD PTR DS:[EAX]
0101251B . A3 18500101 MOV DWORD PTR DS:[1015018],EAX
01012520 . E8 9D020000 CALL calc.010127C2 ; wieder relativer Call
01012525 . 391D D0490101 CMP DWORD PTR DS:[10149D0],EBX
0101252B . 75 0C JNZ SHORT calc.01012539
0101252D . 68 C2270101 PUSH calc.010127C2 ; Entry address
01012532 . FF15 FC110001 CALL DWORD PTR DS:[<&msvcrt.__setusermat>; msvcrt.__setusermatherr
01012538 . 59 POP ECX
01012539 > E8 72020000 CALL calc.010127B0 ; wieder relativer call

Cardano
11.07.2010, 17:00
Ich kann ja mal ein bisschen Code posten
C++ | bool PEFile::createCodecave(__ - Anonymous - yMHiSKHw - Pastebin.com (http://pastebin.com/yMHiSKHw)

Wie man sieht, bin ich in der Implementierung noch nicht allzu weit. Wann immer euch ein (void) 0; begegnet habe ich noch nicht weitergemacht. Ich hatte mir für den Fall, dass das Codecave nicht groß genug ist ausgedacht, dass ich "einfach" wieder ein RELOC_INTERVAL


typedef struct _RELOC_INTERVAL {
DWORD roLowBoundary;
DWORD roHighBoundary;
DWORD dwDelta;
} RELOC_INTERVAL;
erzeuge mit der Größe dwDelta, welche sich wie in Zeile 22 zu sehen
aus

iDeltaFollowingSections = Align(iCaveSize - ccAffectedSection->iCaveSize, this->pImageNTHeaders->OptionalHeader.FileAlignment);ergibt.
Dann relocate ich wieder alles von der darauf folgenden Section bis zum Ende der letzten physischen Section
Danach muss ich ggf. noch einige(viele) Angaben in den Datadirectorys anpassen, wie zum Einen natürlich die Addresse des RelocDirs an sich, ggf. ExportDir usw. und sofort.

Aber dann kommen mir natürlich nun die short calls/jumps in die Quere.
D.h., wollte ich fortfahren müsste ich mein Glück mit einer Disassemblierungsengine versuchen, die auch nur zu 90%iger Wahrscheinlichkeit mit die richtigen Befehle gibt um das Problem zu lösen, oder ?:confused:

l0dsb
11.07.2010, 17:07
Wenn die long jumps relativ sind und der Compiler Relocations erzeugt, tut er das auch für diese. Relativ sind die ja sowieso, am Offset ändert das ja nichts (00000000 ist ja genauso ein Offset wie jeder andere auch und kann dementsprechend reloziert werden).

edit
Deine letzte Frage lässt sich wohl nur bejahen, wie man schon EBFE's Beitrag entnehmen kann. Relokationsinformationen für short jumps sind in der Regel (normaler Kompiliervorgang) sinnfrei, da diese ja nur direkt abhängig von der Quell- aber nicht von der Zieladresse sind. Diese müsstest du per Disassembly finden, was nicht immer zum Erfolg führen muss.

EBFE
11.07.2010, 17:31
HalbOT:
Relocs für "long Jmps/Calls" sind imho auch recht sinnfrei :). Habe auch gleich mal nach einer Exe mit Relocs gesucht (und dosbox 0.71 "gefunden")


00401274 . /E9 878D1F00 JMP dosbox.005FA000
...
0040123E . E8 CDFEFFFF CALL dosbox.00401110
(der jmp liegt fast direkt am EP und der CALL etwas davor, beide nicht in der RelocTabelle auffindbar. Eventuell kommt es hier auch noch auf den jeweiligen Compiler/Linker an. Wobei sich die Frage imho eher "erledigt", da ein Haufen der Exen erstmal gar keine Relocs besitzt)