2012-05-05 4 views
7

Ich denke, meine Frage mag ein wenig seltsam scheinen, aber hier geht es; Ich versuche, ein Programm dynamisch in C++ zu erstellen (hauptsächlich aus Spaß, aber auch aus programmatischen Gründen) und es ist nicht so schwer, wie es klingen mag. Um dies zu tun, müssen Sie Montag im laufenden Betrieb wie folgt verwenden:Ein gültiges Muster in Assembly für variadic Argumente

byte * buffer = new byte[5]; 
*buffer = '0xE9'; // Code for 'jmp' 
*(uint*)(buffer + 1) = 'address destination'; // Address to jump to 

Das ist viel einfacher, als es scheinen mag, weil ich nur eine Plattform und Compiler Ziel; GCC mit Linux 32bit (und auch nur eine Aufrufkonvention, cdecl). Ich versuche also, eine dynamische Assembly-Funktion zu erstellen, um Aufrufe von Triggern umzuleiten, so dass ich Klassenmethoden als Callbacks verwenden kann (sogar mit C-API-Bibliotheken (natürlich mit cdecl)). Ich brauche das nur um Zeiger und native Typen zu unterstützen (char, int, short etc ...).

Die obige Funktion ist diejenige, die ich in reiner Assembly erstellen möchte (im Speicher mit C++). Da die Funktion sehr einfach ist, ist ihr ASM auch einfach (abhängig von Argumenten).

55      push %ebp 
89 e5     mov %esp,%ebp 
83 ec 04    sub $0x4,%esp 
8b 45 08    mov 0x8(%ebp),%eax 
89 04 24    mov %eax,(%esp) 
e8 00 00 00 00   call <address> 
c9      leave 
c3      ret 

Also in meinem Programm, ich habe einen ASM Mustergenerator erstellt (da ich weiß, ASM nicht besonders gut, ich nach Mustern zu suchen). Diese Funktion kann Assembler-Code (in Bytes, für den genauen obigen Fall, d. H. Eine Funktion, die umleitet und zurückgibt) durch Spezifizieren der Menge von Argumenten, die die Funktion benötigt, erzeugen. Dies ist ein Ausschnitt aus meinem C++ Code.

std::vector<byte> detourFunc(10 + stackSize, 0x90); // Base is 10 bytes + argument size 

// This becomes 'push %ebp; move %esp, %ebp' 
detourFunc.push_back(0x55);  // push %ebp 
detourFunc.push_back(0x89);  // mov 
detourFunc.push_back(0xE5);  // %esp, %ebp 

// Check for arguments 
if(stackSize != 0) 
{ 
    detourFunc.push_back(0x83);  // sub 
    detourFunc.push_back(0xEC);  // %esp 
    detourFunc.push_back(stackSize); // stack size required 

    // If there are arguments, we want to push them 
    // in the opposite direction (cdecl convention) 
    for(int i = (argumentCount - 1); i >= 0; i--) 
    { 
     // This is what I'm trying to implement 
     // ... 
    } 

    // Check if we need to add 'this' 
    if(m_callbackClassPtr) 
    { 

    } 
} 

// This is our call operator 
detourFunc.push_back(0xE8);  // call 

// All nop, this will be replaced by an address 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 

if(stackSize == 0) 
{ 
    // In case of no arguments, just 'pop' 
    detourFunc.push_back(0x5D); // pop %ebp 
} 

else 
{ 
    // Use 'leave' if we have arguments 
    detourFunc.push_back(0xC9); // leave  
} 

// Return function 
detourFunc.push_back(0xC3);  // ret 

Wenn ich Null als stackSize angeben dies wird der Ausgang sein:

55      push %ebp 
89 e5     mov %esp,%ebp 
e8 90 90 90 90   call <address> 
5d      pop %ebp 
c3      ret 

Wie Sie, das ist völlig gültig 32-Bit-ASM, und fungiert als ‚MyRedirect‘ sehen können, wenn es hatte null Argumente und keinen Bedarf für einen 'diesen' Zeiger. Das Problem ist, ich möchte den Teil implementieren, wo es ASM-Code generiert, abhängig von der Anzahl der Argumente, die ich angeben, dass die "Redirect" -Funktion erhalten wird. Ich habe das erfolgreich in meinem kleinen C++ - Programm von mir gemacht (knackte das Muster).

Diese Funktion gibt das exakt gleiche Muster aus wie der von 'objdump' generierte ASM-Code. Also meine Frage ist; wird dies in allen Fällen gültig sein, wenn ich nur eine Umleitungsfunktion wie die oben, egal die Argumente wollen, wenn es nur unter Linux 32bit, oder gibt es irgendwelche Fallstricke, über die ich wissen müssen? Beispielsweise; Wäre der generierte ASM anders als "shorts" oder "chars" oder funktioniert das (ich habe nur mit Ganzzahlen getestet), und auch wenn ich eine Funktion aufrufen würde, die "void" zurückgibt (wie würde das den ASM betreffen)?

ich ein bisschen unscharf alles erklärt haben könnte, so wenden Sie sich bitte an Stelle von Missverständnissen fragen :)

HINWEIS: Ich will nicht, Alternativen wissen, ich meine aktuelle Implementierung genießen und denke, es ist sehr interessant ist, ich Ich würde Ihre Hilfe zu diesem Thema sehr schätzen.

EDIT: Bei Interesse sind hier einige Deponien für die oben genannten C++ Code: link

+6

Eine sehr gute Alternative zum manuellen Codieren von Anweisungen ist die [asmjit] (http://code.google.com/p/asmjit/) -Bibliothek (im Gegensatz zu dem Namen ist dies kein JIT-Compiler, nur etwas, das JIT-Compiler können verwenden). –

+0

@afishwhoswimsaround Diese Bibliothek sah großartig aus. Buchstäblich. Das scheint genau zu sein, was ich brauche. Schnelle Frage; kann ich eine Funktion dynamisch mit dieser Bibliothek (mit malloc oder etwas) erstellen, so kann ich (mit Assembly 'jmp') andere Funktionen 'umleiten' ('haken') sie zu den generierten Funktionen? –

+1

Ja, schau dir die Beispiele an http://code.google.com/p/asmjit/wiki/Examples –

Antwort

1

Wie Dan schon sagt, müssen Sie den Speicher als ausführbar markieren. I wrote some code you can use. (Es funktioniert unter GNU/Linux und Windows.) Wenn Sie niemals ARM, x86-64 oder andere Plattformen unterstützen wollen, dann sehe ich keinen Fehler in Ihrem Code (mit dem ausführbaren Teil) und es scheint so sollte "immer funktionieren". (Vorausgesetzt, alles andere funktioniert natürlich einwandfrei.)

#include <sys/mman.h> 

... 

n = <size of code buffer>; 
p = mmap(0, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); 

'Fisch' vorgeschlagen, Sie verwenden asmjit. Dem muss ich zustimmen; es ist tragbarer als deine Methode. Sie haben jedoch gesagt, dass Sie an Alternativen nicht interessiert sind.

Sie könnten Interesse an etwas namens "Thunking" (Art von) haben. Es versucht im Grunde, "einen C-Callback mit einer C++ - Methode zu ersetzen". Das ist eigentlich ziemlich nützlich, aber es ist nicht wirklich ein gutes Design für Ihre Anwendungen.

Hoffe, dass hilft.