2012-04-11 4 views
5

Zuerst entschuldige ich mich über die Länge dieses Beitrags, aber ich wollte das Problem klar erklären.Selbst modifizierender Code, Kopieren/Springen im Heap fehlgeschlagen

Ich versuche, eine Art kleine selbstmodifizierenden Programm in C schreiben, aber ich habe einige Probleme, und ich weiß nicht genau warum.

Plateform ist: Ubuntu/Linux 2.6.32-40 x86_64- ist prog auf x86 arch bauen, gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3, GNU ld (GNU Binutils für Ubuntu) 2.20 .1-system.20100303

der Zweck des Programms ist es, ein Lese/Schreib/Ausführungs Teil des Speichers (mit memalign (3) undmprotect (2)) zu erstellen, kopieren, um eine kleine Funktion genannt p() (definiert in der .text Segment) in diesem Stück Speicher und dann die kopierte Funktion über einen Zeiger ausführen. Die p() Funktion zeigt nur eine Nachricht mit printf(puts).

Um die Start- und End-Adresse des Codes von p() (zu kopieren) zu bekommen, verwende ich die Adresse der Funktion selbst und die Adresse einer dummy() Funktion erstellen Sie einfach nach p() in .text.

int p() { ... }  <- address where the copy starts 
int dummy() { ... } <- address where the copy stops 

Chunk Speicher Erstellung und Kopie erfolgreich durchgeführt werden, aber , wenn der Code in dem Brocken einen segfault ausgeführt wird auftritt. Durch die Verwendung von gdb ist klar, dass wir den Code des Chunks eingeben (den Rumpf der kopierten Funktion), aber der Aufruf an printf ist fehlgeschlagen. Bei der Zerlegung der p()-Funktion und des Codes im Chunk sehe ich, dass die Adressverwendung im 'call' nicht die selbe ist.

Und ich weiß nicht, warum die Adresse falsch ist, wenn der Code kopiert wird, wird es angezeigt und es ist das gleiche, das objdump (oder gdb) gab mir, wenn ich die p() Funktion zerlegen.

Die Binärdatei wird mit -static erstellt, um ein potenzielles Problem mit got/plt oder mit dem Umzugsprozess von ld.so zu vermeiden. Es scheint kein Problem zu sein, Code auf dem heap auszuführen, weil der Anfang der kopierten Funktion ausgeführt wird (überprüfen Sie unter gdb).

Das vereinfachte src des Programms:

<... skip include/checks ...> 
#define newline() putchar('\n') 

/* - function copied in the chunk */ 
int p() 
{ 
    printf("hello world\n"); 
    return 0; 
} 

/* - dummy function to get last address of p() */ 
int dummy() { return 0; } 

int main() 
{ 
    char *s, *code, *chunk; 
    unsigned int pagesz, sz; 
    int (*ptr)(void); 

    pagesz = sysconf(_SC_PAGE_SIZE); 
    chunk = (char*)memalign(pagesz, 4 * pagesz); 
    mprotect(chunk, 4 * pagesz, PROT_WRITE|PROT_EXEC|PROT_READ); 

    /* - get size, display addr */ 
    sz = (char *)dummy - (char *)p; 
    printf("Copy between : %p - %p\n", (char *)p, (char *)dummy); 
    printf("Copy in chunk : %p - %p\n", chunk, chunk + sz, sz); 

    /* - copy code (1 byte in supp) */ 
    printf("Copied code : "); 
    for(s = (char *)p, code = chunk; \ 
      s <= (char *)dummy; s++, code++) { 

     *code = *s;  
     /* - write in console -- to check if code of p() after disas 
     * it with objdump(1) is the same, RESULT : ok */ 
     printf("%02x ", *(unsigned char *)code); 
    } 
    newline(); 

    /* - run once orginal function */ 
    ptr = p; 
    ptr(); 

    /* - run copied function (in chunk) */ 
    ptr = (int (*)(void))chunk; 
    ptr(); 

    newline(); 
    free(chunk); 
    return 0; 
} 

Die p() Funktion durch objdump(1) zerlegt:

080483c3 <p>: 
80482c0:  55      push %ebp 
80482c1:  89 e5     mov %esp,%ebp 
80482c3:  83 ec 18    sub $0x18,%esp 
80482c6:  c7 04 24 a8 d9 0a 08 movl $0x80ad9a8,(%esp) 
80482cd:  e8 7e 0c 00 00   call 8048f50 <_IO_puts> 
80482d2:  b8 00 00 00 00   mov $0x0,%eax 
80482d7:  c9      leave 
80482d8:  c3      ret  

080483dc <dummy>: 
.... 

Wenn das Programm unter GDB ausgeführt wird (1) die kopierten Code ist derselbe (hex Wert) als objdump (1) oben angeben:

# gcc -m32 -o selfmodif_light selfmodif_light.c -static -g -O0 

# gdb -q ./selfmodif_light 
Reading symbols from /path/.../selfmodif_light...done. 

(gdb) list 55 
50   /* - run once orginal function */ 
51   ptr = p; 
52   ptr(); 
53 
54   /* - run copied function (in chunk) */ 
55   ptr = (int (*)(void))chunk; 

<<< The problem is here >>> 

56   ptr(); 
57  
58   newline(); 
59   free(chunk); 

(gdb) br 56 
Breakpoint 1 at 0x8048413: file tmp.c, line 56. 

(gdb) run 
Starting program: /path/.../selfmodif_light 
Copy between : 0x80482c0 - 0x80482d9 
Copy in chunk : 0x80d2000 - 0x80d2019 
Copied code : 55 89 e5 83 ec 18 c7 04 24 a8 d9 0a 08 e8 7e 0c 00 00 b8 00 00 00 00 c9 c3 55 
hello world 

Breakpoint 1, main() at tmp.c:56 
56   ptr(); 

Wenn wir in Haupt sehen wir als nächstes in den Brocken gehen:

(gdb) disas main 
Dump of assembler code for function main: 
    0x080482e3 <+0>: push %ebp 
    ... <skip> ... 

=> 0x08048413 <+304>: mov 0x18(%esp),%eax 
    0x08048417 <+308>: call *%eax 

    ... <skip> ... 
    0x08048437 <+340>: ret 
End of assembler dump. 

Aber wenn p() und Brocken zerlegt werden, haben wir ein call 0x80d2c90 im Speicher chunk anstelle eines call 0x8048f50 <puts> wie in der p() Funktion ?Aus diesem Grund ist die angezeigte Adresse nicht identisch.

(gdb) disas p 
Dump of assembler code for function p: 
    0x080482c0 <+0>:  push %ebp 
    0x080482c1 <+1>:  mov %esp,%ebp 
    0x080482c3 <+3>:  sub $0x18,%esp 
    0x080482c6 <+6>:  movl $0x80ad9a8,(%esp) 
    0x080482cd <+13>: call 0x8048f50 <puts> <<= it is not the same address 
    0x080482d2 <+18>: mov $0x0,%eax 
    0x080482d7 <+23>: leave 
    0x080482d8 <+24>: ret 
End of assembler dump. 
(gdb) disas 0x80d2000,0x80d2019 
Dump of assembler code from 0x80d2000 to 0x80d2019: 
    0x080d2000: push %ebp 
    0x080d2001: mov %esp,%ebp 
    0x080d2003: sub $0x18,%esp 
    0x080d2006: movl $0x80ad9a8,(%esp) 
    0x080d200d: call 0x80d2c90    <<= than here (but it should be ??) 
    0x080d2012: mov $0x0,%eax 
    0x080d2017: leave 
    0x080d2018: ret 
End of assembler dump. 

Wenn der Speicher aktiviert ist, scheinen Codes identisch zu sein. An diesem Punkt verstehe ich nicht, was passiert ist, was ist das Problem? gdb's Interpretation fehlgeschlagen, Kopie des Codes oder was?

(gdb) x/25bx p // code of p in .text 
0x80482c0 <p>: 0x55 0x89 0xe5 0x83 0xec 0x18 0xc7 0x04 
0x80482c8 <p+8>: 0x24 0xa8 0xd9 0x0a 0x08 0xe8 0x7e 0x0c 
0x80482d0 <p+16>: 0x00 0x00 0xb8 0x00 0x00 0x00 0x00 0xc9 
0x80482d8 <p+24>: 0xc3 

(gdb) x/25bx 0x80d2000 // code of copy in the chunk 
0x80d2000: 0x55 0x89 0xe5 0x83 0xec 0x18 0xc7 0x04 
0x80d2008: 0x24 0xa8 0xd9 0x0a 0x08 0xe8 0x7e 0x0c 
0x80d2010: 0x00 0x00 0xb8 0x00 0x00 0x00 0x00 0xc9 
0x80d2018: 0xc3 

Wenn ein Breakpoint gesetzt wird dann weiterhin die Ausführung in dem Speicher chunk:

(gdb) br *0x080d200d 
Breakpoint 2 at 0x80d200d 
(gdb) cont 
Continuing. 

Breakpoint 2, 0x080d200d in ??() 
(gdb) disas 0x80d2000,0x80d2019 
Dump of assembler code from 0x80d2000 to 0x80d2019: 
    0x080d2000: push %ebp 
    0x080d2001: mov %esp,%ebp 
    0x080d2003: sub $0x18,%esp 
    0x080d2006: movl $0x80ad9a8,(%esp) 
=> 0x080d200d: call 0x80d2c90 
    0x080d2012: mov $0x0,%eax 
    0x080d2017: leave 
    0x080d2018: ret 
End of assembler dump. 
(gdb) info reg eip 
eip   0x80d200d 0x80d200d 
(gdb) nexti 
0x080d2c90 in ??() 
(gdb) info reg eip 
eip   0x80d2c90 0x80d2c90 
(gdb) bt 
#0 0x080d2c90 in ??() 
#1 0x08048419 in main() at selfmodif_light.c:56 

Deshalb an dieser Stelle entweder das Programm läuft so, und ein segfault auftritt oder eip $ geändert wird und das Programm endet ohne Fehler.

(gdb) set $eip = 0x8048f50 
(gdb) cont 
Continuing. 
hello world 

Program exited normally. 
(gdb) 

Ich verstehe nicht, was passiert ist, was fehlgeschlagen. Die Kopie des Codes scheint in Ordnung zu sein, der Sprung in den Speicherblock auch, also warum ist die Adresse (des Anrufs) nicht das Gute?

Vielen Dank für Ihre Antworten und Ihre Zeit

+2

Was für ein Hacker bist du überhaupt ??? Bitte! Wenn Sie selbst modifizierenden Code schreiben wollen, müssen Sie dies selbst tun :-) – ControlAltDel

+0

TL; DR ......... –

Antwort

7
80482cd:  e8 7e 0c 00 00   call 8048f50 

dass ein relative CALL ist (bis + 0xC7E). Wenn Sie diese Anweisung in eine andere EIP verschieben, müssen Sie den Offset ändern.

+2

Beachten Sie auch, dass viele moderne Betriebssysteme RWX-Speicher verbieten. Sie müssen RW-für die Dauer der Änderung und R-X, wenn Sie bereit sind, es zu starten. –

+0

@DCoder: Danke für Ihre Antwort, in der Tat muss ich den richtigen Offset berechnen oder einen absoluten Anruf verwenden – hodj

Verwandte Themen