2012-04-11 17 views
5

İlk olarak bu gönderinin uzunluğu hakkında üzgünüm, ancak sorunu açık bir şekilde açıklamak istedim.Kendini değiştiren kod, yığın içinde kopyala/atla başarısız oldu

C'da bir tür küçük kendini değiştirme programı yazmaya çalışıyorum ama bazı sıkıntılarım var ve nedenini tam olarak bilmiyorum.

plateform olup: Ubuntu/Linux 2.6.32-40 x86_64, Prog 86 kemer üzerine inşa edilmiş, gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3 GNU ld (Ubuntu için GNU Binutils) 2.20 .1-system.20100303

programının amacı/a okuma/yazma oluşturmak bellek yığın çalıştırmak için (memalign (3) ve mprotect (2)), küçük bir işlev kopya olduğunu Bu yığın bellekte p() (.text segmentinde tanımlanmıştır) denir ve ardından kopyalanan işlevi bir işaretçi aracılığıyla yürütür. p() işlevi sadece printf(puts) kullanarak bir mesaj görüntüler. başlangıç ​​ve p() (kopyalamak) kod bitiş adresini almak için

, ben .text içinde hemen sonra p() oluşturmak fonksiyonu kendisinin adresini ve bir dummy() fonksiyonun adresini kullanın. yığın kod segfault çalıştırılır

int p() { ... }  <- address where the copy starts 
int dummy() { ... } <- address where the copy stops 
Chunk bellek oluşturma ve kopyalama başarıyla yapılmaktadır

ama oluşur. gdb kullanarak öbek kodunun (kopyalanan işlevin gövdesi) girdiğimiz açıktır, ancak printf çağrısı başarısız oldu. p() işlevinin parçalara ayrılmasında ve yığıntaki kodda, 'çağrı' içinde adresin kullanılmasının aynı olmadığını görüyorum.

Ve adresin neden yanlış olduğunu bilmiyorum, kod kopyalandığında görüntüleniyor ve p() işlevini devre dışı bıraktığımda bu nesnenin bana (veya gdb) verdiğiyle aynı.

got/plt ile ilgili olası sorunlardan veya ld.so yeniden yerleştirme işleminden kaçınmak için ikili, -static ile oluşturulur. Kopyalanan işlevin başlangıcı (gdb'un altında kontrol edin) çalıştırıldığı için heap kodunun çalıştırılması sorun olmayacaktır.

programın basitleştirilmiş src: p() fonksiyonu objdump(1) tarafından demonte

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

:

programı gdb altında çalıştırılır
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>: 
.... 

(1) kopyalanan kod aynıdır (heks (1) objdump'dan (1) yukarıdaki değeri belirtin:

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

Fakat p() ve yığın demonte edildiğinde, bunun yerine p gibi bir call 0x8048f50 <puts> bellek öbekte bir call 0x80d2c90 var() fonksiyonu: ana bakarsanız 0 önümüzdeki yığın gitmek ?Hangi nedenle görüntülenen adres aynı değildir. Bellek işaretlendiğinde

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

, kodlar özdeş görünmektedir. Bu noktada ne olduğunu anlamıyorum, sorun nedir? gdb'nin yorumu başarısız oldu, kodun kopyası ya da ne? Bir kesme noktası ayarlanırsa,

(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 

yürütme bellek öbekte devam: Yani

(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 

bu noktada her iki programın böyle çalışır ve programı bir segfault oluşur veya $ eip değiştirilir ve hatasız biter.

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

Program exited normally. 
(gdb) 

Neler olup bittiğini anlamıyorum, ne başarısız oldu. Kodun kopyası tamam gibi görünüyor, bellek yığınının içine atlamak da, neden adres (çağrı) iyi değil?

cevaplar için teşekkürler ve (+ 0xC7E kadar) bir relative CALL var zaman

+2

Zaten ne tür bir hacker vardır ??? Lütfen! Kendinizi değiştirecek kod yazacaksanız, bunu kendiniz yapmalısınız :-) – ControlAltDel

+0

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

cevap

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

. Bu talimatı farklı bir EIP'ye taşıdığınızda, ofseti değiştirmeniz gerekir.

+2

Ayrıca, birçok modern işletim sistemi RWX belleğini yasaklar. Bunu çalıştırmak için hazır olduğunuzda, RW-modifikasyonu ve R-X için yapmanız gerekir. –

+0

@DCoder: Cevabınız için teşekkürler, gerçekten doğru ofseti hesaplamalıyım veya mutlak bir çağrı kullanmalıyım – hodj

İlgili konular