2016-05-19 6 views
-1

Problem schreiben:

ich eine neue (einzelne Seite) Textsegment im Speicher zugeordnet werden soll, und führen Sie es ohne viel Montage zu schreiben, aus einem C-Programm. Um dies zu tun, bin ich eine C-Funktion des Programms selbst zu duplizieren, und ich setze ein Flag, also wenn ich zu ihm springen einen anderen Pfad in der Funktion aufgerufen wird. Sie können es als dumm denken, aber es ist das gleiche, was wir tun, wenn wir zum Beispiel fork() einen Prozess.C: Führen Sie eine manuell mmaped Textsegment ohne viel Montage

Warum

ich eine minimale Erfassung und Wiedergabe-Mechanismus auf Android implementiert haben, link. Derzeit verwende ich eine nicht so elegante Möglichkeit, echte Android-Apps wiederzugeben, die ASLR deaktiviert, und abgesehen von Sicherheitslücken (die jetzt einfach ignoriert werden können), verursacht es uns einige andere Probleme. Also von einem reinen C-Prozess möchte ich einen Übergang machen und eine Android-Funktion wiedergeben. Wenn ich den folgenden Ansatz zur Arbeit mache, wird es viel besser sein, dass der aktuelle, der auf die erste Android-Funktion eingreift, die aufgerufen wird und zum Hauptthread gehört, und .. bla bla ..!

Ansatz:

In einem C-Programm I ein Textsegment am abbildet, die im Grunde eine Kopie einer C-Funktion des Programms selbst ist. Um dies zu tun, rufe ich eine Funktion namens entrypoint einmal, bekomme ich den Programmzähler, setzen Sie ein Flag und zurück. Dann mmap ich eine Seite im Speicher (die Code-Größe der Einstiegspunkt-Funktion sollte nicht größer sein, dass dies) sagen wir bei 0xabc000, und ich kopiere darin eine Seite von dem PC aus, die ich habe. Das bedeutet, dass die Seite abc ein Duplikat des Textes/Codes vom PC bis mindestens zum Ende der Einstiegspunktfunktion enthalten sollte (eine Seite, die grundsätzlich von pc ausgeht) Ich setze die Berechtigungen der Seite abc auf ausführbar, und ich springe zu es.

Und ich bekomme eine Segmentierung Verletzung. Aber ich möchte wissen, WARUM?

Beispielcode:

bool entered_ = false; 
void *pc_ = 0x0; 

void entrypoint() { 
if(!entered_){ 
entered = 1; 
pc_ = GET_PROGRAM_COUNTER(); 
return; 
} 

// .. 
// SPECIAL CODE 
// .. 

} 

int main(..) { 

entrypoint(); 
// map a new rdwr private page (4096) 
newtextseg = mmap(..args..); 

// copy 1 page of text from this application's text segment, 
// starting from the point where I got the pc 
memcpy(newtextseg, pc, PAGE_SIZE); 

// give exec permissions to newtextseg 
mprotect(..args); 

// Jump to the code, and expect the 
// SPECIAL CODE to run 
JUMP(newtextseg); 

} 

Zusätzliche Informationen

Die C-Bibliothek ist statisch mit meiner Anwendung verknüpft. Ich führe dies auf einem Android-Gerät mit Arm-Architektur. Leider gibt das Betriebssystem keine Informationen über die Segmentierung, da es sich um einen reinen C-Prozess handelt, der statisch mit libc verknüpft ist. Es gibt nichts, was Android dort betrifft. Nicht einmal llog.

Annahmen

Montagevorgänge korrekt sind (für immer und PC Springen). Auch die Ergebnisse der Aufrufe von mmap und mprotect werden überprüft, und ich überprüfe die Datei proc/self/maps.

Zufällige Gedanken:

Da ich statisch mit libc verbunden bin, und ich bin einige printf ‚s zum entrypoint für Debugging-Zwecke zu tun, könnte es der Fall sein, dass die Zeiger auf die Funktionen von Offset einige behoben haben der Anfang des ursprünglichen Code-Segments (das, das ich entrypoint von kopiert habe), also, wenn ich versuche, aus einem anderen Offset Dinge zu brechen brechen?

+2

Sie sollten zumindest als Position unabhängigen Code kompilieren. Um den PC zu bekommen, können Sie einfach den Namen der Funktion verwenden. Wenn Sie einen segfault erhalten, müssen Sie einen Debugger verwenden und uns zeigen, wo der Fehler liegt. – Jester

+0

@Jester Leider lief ich dies auf einem Android-Gerät und kein Ereignis wird vom Betriebssystem für die 'SEGV' generiert. Nicht einmal ein "Tombstone" wird erstellt, was passiert, wenn ein Prozess unter Android abstürzt. – Paschalis

+0

Ich denke, dass ich es auf x86 mit dem richtigen Debuggen testen und dann auf Arm übertragen muss. – Paschalis

Antwort

1

Sie können dies mit einem Funktionszeiger machen.

(ich gebe offen zu, dass dies wahrscheinlich viele Teile der C Standard ... verletzt) ​​

Zuerst typedef einen Funktionszeiger-Typen, da es viel einfacher ist, mit Funktionszeigern zu behandeln, wenn Sie ein typedef verwenden:

typedef (*randomAddressFunc_t)(void); 

// define a function pointer 
randomAddressFunc_t JUMP; 

// assign an address to the function pointer: 
JUMP = (randomAddressFunc_t) newtextseg; 

// call it 
JUMP(); 

Das sollte versuchen, alle binären Bits auszuführen, auf die newtextseg zeigt.

+0

entweder durch Verwendung von Funktionszeigern oder durch Setzen des Programmzählers (wie in meinem Ansatz) fehlgeschlagen. – Paschalis

1

Da Sie auf x86 genannte Überprüfung, hier ist ein Beispiel, das für mich gearbeitet:

#include <stdio.h> 
#include <string.h> 
#include <sys/mman.h> 

void printcaller() 
{ 
    printf("caller = %p\n", __builtin_return_address(0)); 
} 

void entrypoint(void (*callback)()) 
{ 
    callback(); 
} 

int main() 
{ 
    unsigned char* block = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 
    printf("entrypoint = %p, block = %p\n", entrypoint, block); 
    memcpy(block, entrypoint, 1024); 
    printf("entrypoint():"); 
    entrypoint(printcaller); 
    printf("block():"); 
    ((void (*)())block)(printcaller); 
    return 0; 
} 

Beispielausgabe:

$ ./a.out 
entrypoint = 0x40064a, block = 0x7eff7cbd1000 
entrypoint():caller = 0x400661 
block():caller = 0x7eff7cbd1017 

Hinweis ich ausdrücklich die Rückrufadresse passieren musste, weil eine einfache Funktion Aufruf verwendet relativen Offset, der aufgrund des Kopierens bricht. Der Zugriff auf andere Daten (z. B. globale Daten, einschließlich String-Literale) kann ebenfalls dieses Problem haben.

Wie @EOF in seinem Kommentar erwähnt:

Sie können nur von einer gültigen Seite kopieren. Wenn die Startadresse nicht Seitenausrichtung ist, und die Seite, die Sie memcyp (..., ..., PAGE_SIZE) aus dem letzten Teil einer mmap() ed-Region ist, erhalten Sie einen segfault für das memcpy() selbst.

In der Tat musste ich 1024 genau aus diesem Grund verwenden.


Alternate Version Gang Funktionszeiger:

#include <stdio.h> 
#include <string.h> 
#include <sys/mman.h> 

struct functions 
{ 
    void* (*getcaller)(); 
    int (*printf)(const char*, ...); 
}; 

void* getcaller() 
{ 
    return __builtin_return_address(0); 
} 

void entrypoint(const char* fmt, const struct functions* functions) 
{ 
    functions->printf(fmt, functions->getcaller()); 
} 

int main() 
{ 
    struct functions functions; 
    unsigned char* block = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 
    printf("entrypoint = %p, block = %p\n", entrypoint, block); 
    memcpy(block, entrypoint, 1024); 
    functions.printf = printf; 
    functions.getcaller = getcaller; 
    entrypoint("entrypoint(): %p\n", &functions); 
    ((void (*)())block)("block(): %p\n", &functions); 
    return 0; 
} 
+0

danke, dass du dir die Zeit genommen hast zu antworten. Es scheint, dass ich x86 nicht anprobieren werde, während ich mit einem 'remote gdb' Server versuche. Ich erhalte den 'memcpy' Fehler nicht, also versuche ich es zu untersuchen. Ihre Vorgehensweise springt jedoch auf den ursprünglichen Code zurück, während ich in meinem Fall zu einem "unabhängigen" Codeblock springen möchte! Ich kann ohne das Datensegment leben, aber ich brauche Zugang zu Bibliotheksaufrufen! Wenn Codezeiger gebrochen sind, ist meine einzige Hoffnung, dass 'asm' Aufrufe intern' libc' nicht durchlaufen. – Paschalis

+0

Ich bin nur zurückgesprungen, also konnte ich die '__builtin_return_address' verwenden, um die Adresse des Anrufers zu bekommen, die natürlich der kopierte Code ist. Nur um zu zeigen, dass der kopierte Code ausgeführt wird. Um auf Bibliotheksaufrufe zuzugreifen, müssen Sie sie möglicherweise in einer Struktur von Funktionszeigern übergeben. – Jester

+0

Okay, ich verstehe. Dennoch setze ich direkt den 'pc' Zeiger (das ist, was' JMP' Makro in meinem Beispiel tut), der einen äquivalenten Effekt mit Ihrem hat. Das Problem ist, dass 'entrepoint' immer noch hängt. Ich werde es weiter untersuchen und berichten. 'gdb' kann nicht viel sagen, da' die Grenzen der kopierten Funktion nicht finden können (wie erwartet). – Paschalis