2012-04-11 7 views
5

まず、この記事の長さについて申し訳ありませんが、私はこの問題をはっきりと説明したかったのです。自己修正コード、ヒープ内のコピー/ジャンプが失敗しました

私はの自己修復プログラムをCで書こうとしていますが、私はいくつかの問題を抱えており、なぜ正確にわからないのですか?

Plateformがある:のUbuntu/Linuxの2.6.32-40 x86_64で、progはx86アーキテクチャ上に構築され、GCC(Ubuntuの4.4.3-4ubuntu5.1)4.4.3、GNU ldの(UbuntuのためのGNU Binutilsの)2.20 0.1-system.20100303

は、プログラムの目的は、小さな関数をコピーし、メモリのチャンク(あるmemalign(3)MPROTECT(2))を実行/書き込み/読み出しを生成することです(.textセグメントで定義されています)と呼ばれ、ポインタを介してコピーされた関数を実行します。 p()関数は、printf(puts)を使用してメッセージを表示するだけです。

(それをコピーするために)開始を取得し、p()のコードのアドレスを終了するために、私は、関数自体のアドレスを使用してdummy()関数のアドレスは、ちょうど.textp()後に作成します。チャンク内のコードは、セグメンテーションフォルトを実行している

int p() { ... }  <- address where the copy starts 
int dummy() { ... } <- address where the copy stops 
チャンクメモリの作成とコピーが正常に行われ

が、を発生します。 gdbを使用すると、チャンクのコード(コピーされた関数の本体)に入ることは明らかですが、printfへの呼び出しはに失敗しました。チャンクのp()関数とコードを逆アセンブルすると、 'call'のアドレス使用が同じではないことがわかります。

アドレスが間違っている理由はわかりません。コードがコピーされて表示され、p()関数を逆アセンブルしたときにobjdump(またはgdb)が与えたものと同じです。

got/pltまたはld.soの潜在的な問題を回避するために、バイナリは-staticで作成されます。 heapでコードを実行するのは問題ではないようです。コピーされた関数の先頭が実行されるからです(gdbをチェックしてください)。

プログラムの簡略化されたSRC:

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

objdump(1)によって分解p()機能:

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

プログラムはGDBの下で実行されると、(1)コピーしたコードが同じ(16進数でありますobjdump(1)が上記に提供する値より大きい:

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

しかし、P()とチャンクを分解したとき、我々は、p()関数のように代わりcall 0x8048f50 <puts>のメモリチャンク内call 0x80d2c90を持っている:0我々はメインに見れば、私たちは次のチャンクに入ります?そのため、表示されるアドレスは同じではありません。メモリをチェックすると

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

、コードは同じように見えます。この時点で、私は何が起こっているのか理解していない、問題は何ですか? gdbの解釈に失敗しました。コードのコピーまたは何ですか?ブレークポイントが設定されている場合、

(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 

実行はメモリチャンクに継続:だから

(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 

をこの時点でいずれかのプログラムがそのように動作し、プログラムをセグメンテーション違反が発生した場合、または$ EIPが変更され、エラーなく終了します。

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

Program exited normally. 
(gdb) 

私は何が起こっているのか、何が失敗したのか分かりません。コードのコピーは大丈夫だと思われますが、メモリーチャンクにも飛び込んでしまいます(呼び出しのアドレス)が良いのではないのはなぜですか? (+ 0xC7Eへ)relative CALL

あなたの答えてくれてありがとうとあなたの時間

+2

あなたはどんな種類のハッカーですか?お願いします!自己修正コードを書くつもりなら、あなた自身でこれをやる必要があります:-) – ControlAltDel

+0

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

答えて

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

。その命令を別のEIPに移動するときは、オフセットを変更する必要があります。

+2

多くの現代のオペレーティングシステムはRWXメモリを禁止しています。あなたはそれを修正の期間中はRWにする必要があり、実行する準備ができたらR-Xにする必要があります。 –

+0

@DCoder:あなたの答えをありがとう、本当に私は正しいオフセットを計算するか絶対的な呼び出しを使用する必要があります – hodj

関連する問題