2012-04-11 15 views
5

Najpierw przepraszam za długość tego wpisu, ale chciałem jasno wyjaśnić problem.Własny kod modyfikujący, kopiowanie/przeskakiwanie w stosie nie powiodło się

Próbuję napisać coś w rodzaju małego programu modyfikującego w C, ale mam pewne problemy i nie wiem dokładnie dlaczego.

plateform jest: Ubuntu/Linux 2.6.32-40 x86_64 PROG wykorzystać x86 łuku gcc (4.4.3-4ubuntu5.1 Ubuntu) 4.4.3 GNU LD (GNU BINUTILS Ubuntu) 2,20 0,1-system.20100303

Celem programu jest stworzenie odczyt/zapis/wykonanie kawał pamięci (z memalign (3) i MPROTECT (2)), skopiuj małą funkcję o nazwie p() (zdefiniowanej w segmencie .text) w tym kawałku pamięci, a następnie wykonaj skopiowaną funkcję za pomocą wskaźnika. Funkcja p() wyświetla komunikat tylko za pomocą printf(puts).

W celu uzyskania początkowy i końcowy adres kodeksu p() (go skopiować), używam adres samej funkcji i adresu dummy() funkcji tworzyć tylko po p() w .text.

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

tworzenie pamięci Chunk i kopia zostały pomyślnie wykonane, ale gdy kod w kawałku jest uruchomienie segfault występuje. Używając gdb jest jasne, że wpisujemy kod porcji (treść kopiowanej funkcji), ale wywołanie printf nie powiodło się. Podczas demontażu funkcji p() i kodu w kawałku widzę, że użycie adresu w "wywołaniu" nie jest takie samo.

I nie wiem, dlaczego adres jest niepoprawny, kiedy kod jest kopiowany, jest wyświetlany i jest taki sam, jaki dał mi objdump (lub gdb), kiedy demontuję funkcję p().

Plik binarny jest tworzony z -static, aby uniknąć potencjalnego problemu z got/plt lub z procesem relokacji ld.so. Wydaje się, że uruchomienie kodu na kodzie heap nie jest problemem, ponieważ rozpoczyna się kopiowanie funkcji (sprawdź pod numerem gdb).

uproszczony Src programu:

<... 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; 
} 

Funkcja p() przeprowadzi objdump(1):

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>: 
.... 

Kiedy program jest wykonywany pod gdb (1) kopiowany kod jest taki sam (heks wartość) niż objdump (1) powyżej:

# 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(); 

Jeśli spojrzymy w głównym następnej przejść do fragmentu:

(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. 

Ale gdy p() i klocek są demontowane, mamy call 0x80d2c90 w kawałku pamięci zamiast call 0x8048f50 <puts> jak w p() funkcja ?Z tego powodu wyświetlany adres nie jest taki sam.

(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. 

Kiedy pamięć jest zaznaczone, kody wydają się być identyczne. W tym momencie nie rozumiem, co się dzieje, na czym polega problem? interpretacja gdb nie powiodła się, kopia kodu lub co?

(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 

Jeśli punkt przerwania jest ustawiony to wykonanie kontynuowane w kawałku pamięci:

(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 

więc w tym momencie albo program działa tak, a segfault występuje lub $ EIP zostanie zmieniony i program kończy się bez błędów.

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

Program exited normally. 
(gdb) 

Nie rozumiem, co się dzieje, a co nie. Kopia kodu wydaje się być w porządku, skok do kawałka pamięci, więc dlaczego adres (połączenia) nie jest dobry?

Dzięki za odpowiedzi i Twój czas

+2

Jakiego rodzaju jesteś hackerem ??? Proszę! Jeśli masz zamiar napisać samodefiniujący się kod, musisz to zrobić sam :-) – ControlAltDel

+0

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

Odpowiedz

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

To relative CALL (do + 0xC7E). Po przeniesieniu tej instrukcji do innego EIP należy zmodyfikować przesunięcie.

+2

Należy również pamiętać, że wiele nowoczesnych systemów operacyjnych zabrania pamięci RWX. Musisz zrobić to RW - na czas modyfikacji i R-X, kiedy będziesz gotowy do uruchomienia. –

+0

@DCoder: Dzięki za odpowiedź, naprawdę muszę obliczyć właściwe przesunięcie lub użyć bezwzględnego połączenia – hodj

Powiązane problemy