Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (GNU/Linux, *NIX, *BSD und Co) » Assembler Injection in existierenden C-Code.

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
22.06.2010, 00:41 Uhr
~cmoetzing_
Gast


Hallo.

Ich habe hier ein abenteuerliches Konstrukt bei dem ich ein bisschen Hilfe bräuchte.

Ausgangspunkt ist ein relative großes C-Paket. Ich muss Code einfügen um das Verhalten des Paketes zu verändern. Allerdings ist das Paket zu groß um es von Hand im Quellcode zu machen.

Die Idee ist nun ein Python-Skript vor den gcc zu schalten, welches den Kompiliervorgang auseinander pflückt und zuerst alle .c-Dateien mit -S in Assembler übersetzt. Dann füge ich in die .s-Dateien meine Änderungen ein (ein Funktionsaufruf), kompiliere die zu den gewünschten .o und lasse den Compiler linken. Beim linken füge ich noch eine Objektdatei hinzu, die die Funktion enthält, die ich in Assembler aufrufe.

Soweit so gut. Das funktioniert für ein kleines Testprogramm ganz gut, für das größere Paket allerdings nicht. Da ich nicht der große Assembler-Guru bin, bräuchte ich ein paar Erklärungen, um das besser zu verstehen. Das ganze soll auf einer X86_64 Maschine laufen.


Code:
pushfq
xchgq %rax,TMP
movq $0, %rax
call func
xchgq %rax,TMP
popfq



Mit pushfq speichere ich die aktuellen Register und mit popfq stelle ich sie wieder her, richtig? Das q steht für quadword, so weit bin ich schon mal. Was passiert wenn ich auf meiner Maschine pushf aufrufe? Das ist soweit ich weis der Befehl um die Daten mit einer Wortbreite zu speichern? Muss ich beides aufrufen?

Das %rax-Register ist auch 4 Worte breit. Es gibt ausserdem noch das %eax-Register, das ist glaube ich 2 Worte breit. Muss ich das vor dem Funktionsaufruf auch sicher und danach wieder herstellen? Die Funktion func ist vom Typ void und nimmt keine Argumente entgegen. Daher der movq-Befehl, der passen sollte!?

Die Funktion func pfuscht nicht in den restlichen Daten des Programmes rum. Ich such in der Assembler-Datei noch bestimmten Befehlen und füge das Code-Stück danach ein. Welches Probleme/Seiteneffekte muss ich beachten um diesen Aufruf sicher ausführen zu könne? Sicher heißt in diesem Fall für mich, dass das restliche Programm so weiter läuft, als ob die Anweisungen nicht von mir eingefügt worden sind. Ich wäre um jede Hilfe sehr dankbar.

Grüße

Christian
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
22.06.2010, 03:29 Uhr
~cmoetzing_
Gast


Gehe ich recht in der annahme, dass die Register %ax,%eax und %rax alle den selben Speicher teilen und %ax nur die ersten 16 Bit und %eax nur die ersten 32 Bit von den 64 Bit in %rax enthalten?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
22.06.2010, 08:39 Uhr
ao

(Operator)



Zitat:
Allerdings ist das Paket zu groß um es von Hand im Quellcode zu machen.

Kannst du das bitte genauer erklären? Warum kannst du nicht ein search-and-replace über den ganzen Quelltext machen?


Zitat:
dass das restliche Programm so weiter läuft, als ob die Anweisungen nicht von mir eingefügt worden sind.

Etwas böser formuliert: Du willst im Objectcode eine geheime Funktion aufrufen, ohne Spuren im Quellcode zu hinterlassen. Und wir sollen dir dabei helfen?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
22.06.2010, 11:39 Uhr
~cmoetzing_
Gast


Hi, so schön hat das bisher noch niemand ausgedrückt. Das es echt hässlich ist merke ich schon allein daran, dass ich nun nicht mehr vernünftig debuggen kann (mein eingefügter Code wird ja im Debugger in der Source-View nicht angezeigt).

Der Grund für das Vorgehen ist recht simpel:
Ich möchte das Rundungsverhalten der FPU vor jeder floating-point-Operation verändern. Im Quellecode müsste ich das alle Anweisungen in Einzelschritte zerlegen was absolut unmöglich ist. So kann ich einfach nach floating-point-Operationen suchen und jeweils meine Funktion einfügen.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
22.06.2010, 12:14 Uhr
ao

(Operator)


Assembly-Output ist ja eigentlich nur Objectcode in lesbarer Form. Zwischen Compile- und Link-Step daran rumzufummeln halte ich für riskant. Hast du irgendwas, womit du sicherstellen kannst, dass die Berechnungen richtig sind? Was ist mit dem Optimizer? Kann es da Nebenwirkungen geben?

Lässt sich das vielleicht auch über Compiler-Optionen steuern? Schon mal nachgeguckt, ob es passende #pragmas gibt?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
22.06.2010, 12:45 Uhr
~cmoetzing_
Gast


Die Optimierung hab ich im ausgeschaltet. Sobald der Compiler anfängt und SSE-Code generiert habe ich sowieso verloren. Dass es langsamer wird macht nichts, das Ändern des Rounding-Modes kostet sowieso viel Zeit.

Es gibt leider kein #pragma.

Ich kann das Programm mit und ohne meine Änderungen ausführen. Wenn ich die Ergebnisse vergleich sehe ich ja ob es funktioniert hat. Im Moment bekomme ich leider noch einen SegFault.

Wo ich jetzt vor allem Hilfe bräuchte ist:

Zeigen diese Register alle auf den selben Speicher? %ax,%eax und %rax Anfangs dachte ich ja, nun würde ich sage nein.

pushfq sichert den aktuellen Zustand von%rax, %rbx, %rcx und popfq holt die Daten zurück?
Gleiches gilt dann für pushf und %eax... und push %ax oder?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
22.06.2010, 13:30 Uhr
ao

(Operator)


Zu der Assemblerprogrammierung kann ich nicht viel sagen, da kenne ich mich nicht aus.

Zu dem Segfault: Du könntest mal versuchen, den Fehler einzugrenzen. Zuerst machst du mal nur pushfq und popfq und prüfst im Debugger, welche CPU-Register dadurch verändert werden. Sollte nur der Program-Counter sein (oder?)

Danach fügst du xchgq und movq ein und danach einen call auf eine Funktion, die nichts tut. Nach jedem Schritt prüfen, ob es Seiteneffekte gibt, verfälschte Register o.ä., und ob der Segfault auftritt.

Als letztes einen Call auf die eigentliche Funktion

Noch ne Idee: Darfst du diese FPU-Instructions überhaupt ausführen, oder sind die vielleicht protected?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
22.06.2010, 13:49 Uhr
~cmoetzing_
Gast


Da bin ich schon dran. Ich bin jetzt auch ein ganzes Stück weiter. Ich werde einfach keine Funktion aufrufen sonder alles direkt in Assembler einfügen. Damit störe ich den Programmablauf weniger. Mal sehen.

An der FPU darf ich rumfummeln, ein simples Programm das das ein paar mal macht stürzt nicht ab.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
22.06.2010, 14:00 Uhr
RedEagle



al,ah,ax,eax und rax sind das selbe Register, nur unterschiedliche Bereiche daraus. (siehe hier)

wenn du ein push rax machst, musst du auch pop rax verwenden, sonst bleibt ein Teil des Registers auf dem Stack liegen und versaut dir diesen.

Du musst auch darauf achten das der neue Code, den du einfügst, nicht irgendwelche jumps behindert. Wenn der alte Code z.B. 5 Byte groß ist, und dein neuer Code 8 Byte, kann es sein dass ein Sprung der eigentlich über die Funktion des Codes springen soll genau reinspringt. Das würde dann zu äußerst komischen Verhalten führen - idr aber zu abstürzen.
--
MFG RedEagle
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
22.06.2010, 14:42 Uhr
~cmoetzing_
Gast


Juhu, wenn man mal weis, dass die Register alle dieselben sind, dann kann das auch funktionieren.

Jetzt habe ich noch eine inhaltliche Frage und eine rein aus Neugier:
_RR_ARRAY_ is ein array in C. In assembler sieht der Zugriff so aus:

Code:
movzwl _RR_ARRAY_(%rax,%rax), %eax


Ziel ist das %eax-Register, in %rax steht der Index meines Elementes. Was bedeutet das zweite %rax in der Klammer?

Wenn ich in C

Code:
int I = 0;
I++;


kompiliere, dann macht der Compiler selbst mit -O3 das hier daraus:


Code:
movq    I(%rip), %rax
addq    $1, %rax
movq    %rax, I(%rip)



Wieso benutzt er nicht incq? Das spart doch die 2 movq?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (GNU/Linux, *NIX, *BSD und Co) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: