まず、この記事の長さについて申し訳ありませんが、私はこの問題をはっきりと説明したかったのです。自己修正コード、ヒープ内のコピー/ジャンプが失敗しました
私はの自己修復プログラムを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()
関数のアドレスは、ちょうど.text
でp()
後に作成します。チャンク内のコードは、セグメンテーションフォルトを実行している
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だ
あなたの答えてくれてありがとうとあなたの時間
あなたはどんな種類のハッカーですか?お願いします!自己修正コードを書くつもりなら、あなた自身でこれをやる必要があります:-) – ControlAltDel
TL; DR ......... –