a.outを編集してprintfの出力を変更する

アニメ『グラゼニ』 PV

アニメ『グラゼニ』

お金はもちろん好きですから、おもしろいですこのアニメ。
コツコツってのは、お金の話に思いがちですが、コツコツ勉強するってのも将来得るお金につながります。コツコツお金を貯めた方がいいのか、投資をしてコツコツ勉強、スポーツする方がいいのか。
どちらにしても努力が必要ですね。

今回は文字列数Byteのケチなネタです。
a.out直接編集すると実行できる?」では、a.outを1Byteだけ書き換えて、HELLOプログラムをTELLOと表示するプログラムに改造しました。
1文字だけの変更なので簡単でしたが、さすがに出力する文字列を長くはできないでしょう。短くはできますが。

果たしてそうでしょうか。HELLOの文字列が格納されているメモリの周囲について確認していきたいと思います。

まずは、objdumpを使い、a.outのアセンブラを確認。

takk@deb9:~/tmp$ objdump -d a.out > asm.txt
takk@deb9:~/tmp$ 

冒頭部分だけ見てみます。

takk@deb9:~/tmp$ head -20 asm.txt

a.out:     ファイル形式 elf64-x86-64


セクション .init の逆アセンブル:

0000000000000530 <_init>:
 530:	48 83 ec 08          	sub    $0x8,%rsp
 534:	48 8b 05 a5 0a 20 00 	mov    0x200aa5(%rip),%rax        # 200fe0 <__gmon_start__>
 53b:	48 85 c0             	test   %rax,%rax
 53e:	74 02                	je     542 <_init+0x12>
 540:	ff d0                	callq  *%rax
 542:	48 83 c4 08          	add    $0x8,%rsp
 546:	c3                   	retq   

セクション .plt の逆アセンブル:

0000000000000550 <.plt>:
 550:	ff 35 b2 0a 20 00    	pushq  0x200ab2(%rip)        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
 556:	ff 25 b4 0a 20 00    	jmpq   *0x200ab4(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
takk@deb9:~/tmp$ 

よくわからないですよね。
では、main関数の辺りを見てみましょう。main関数は、正規表現でmain.:を探せば見つかります。

takk@deb9:~/tmp$ grep 'main.:' asm.txt -A10
00000000000006b0 <main>:
 6b0:	55                   	push   %rbp
 6b1:	48 89 e5             	mov    %rsp,%rbp
 6b4:	48 8d 3d 99 00 00 00 	lea    0x99(%rip),%rdi        # 754 <_IO_stdin_used+0x4>
 6bb:	e8 a0 fe ff ff       	callq  560 <puts@plt>
 6c0:	b8 00 00 00 00       	mov    $0x0,%eax
 6c5:	5d                   	pop    %rbp
 6c6:	c3                   	retq   
 6c7:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
 6ce:	00 00 

takk@deb9:~/tmp$ 

ん~。どこかで見たプログラムです。そうです。前回「GDBアセンブラ表示とメモリダンプ」のgdbでdisassembleしたソースと似ています。

takk@deb9:~/tmp$ gdb a.out

省略

(gdb) b main
Breakpoint 1 at 0x6b4: file t.c, line 5.
(gdb) r
Starting program: /home/takk/tmp/a.out 

Breakpoint 1, main () at t.c:5
5		printf("HELLO\n");
(gdb) disass
Dump of assembler code for function main:
   0x00005555555546b0 <+0>:	push   %rbp
   0x00005555555546b1 <+1>:	mov    %rsp,%rbp
=> 0x00005555555546b4 <+4>:	lea    0x99(%rip),%rdi        # 0x555555554754
   0x00005555555546bb <+11>:	callq  0x555555554560 <puts@plt>
   0x00005555555546c0 <+16>:	mov    $0x0,%eax
   0x00005555555546c5 <+21>:	pop    %rbp
   0x00005555555546c6 <+22>:	retq   
End of assembler dump.
(gdb) 

objdumpで出力したアセンブラと異なるところは、アドレス部が、非常に大きな数字(0x00005555555546**)になっている点と、 #の後が、同じく大きな数字(0x555555554754)となっている点です。
よく見ると、法則があります。objdumpで表示されたアドレスに、0x0000555555554000を足した数字となっています。

法則さえわかれば、怖くないです。objdumpで、HELLOの定義内容を確認してみます。

takk@deb9:~/tmp$ objdump -s a.out | grep -C3 HELLO
セクション .fini の内容:
 0744 4883ec08 4883c408 c3                 H...H....       
セクション .rodata の内容:
 0750 01000200 48454c4c 4f00               ....HELLO.      
セクション .eh_frame_hdr の内容:
 075c 011b033b 38000000 06000000 f4fdffff  ...;8...........
 076c 84000000 14feffff ac000000 24feffff  ............$...
takk@deb9:~/tmp$ 

HELLOがあるセクションは、.rodataという名前がついてますね。
アドレスは0750とありますが、実際にHELLOが配置されているのは、ダンプ内容を見ると0754です。

最初に確認したobjdumpでのアセンブラ表示のコメントのアドレス 754と一致します。

 6b4:	48 8d 3d 99 00 00 00 	lea    0x99(%rip),%rdi        # 754 <_IO_stdin_used+0x4>

さらに、
.rodataのセクションの後に、.eh_frame_hdrセクションがありますが、0x75c番地から始まっているので、少し余裕があるようです。

a.outの生ダンプを見てみます。HELLOの位置を確認すると、

takk@deb9:~/tmp$ hd a.out | grep HELLO
00000750  01 00 02 00 48 45 4c 4c  4f 00 00 00 01 1b 03 3b  |....HELLO......;|
takk@deb9:~/tmp$ 

4f の後は、00 00 00、その後に.eh_frame_hdrセクションのデータである01 1b 03 3bが続いています。
つまり、HELLOの後は3Byte分余裕があります。文字列の終端であるNULL(0)は絶対必要なので、実質2Byteです。
2Byteほど文字列を伸ばしてみましょう。HELLOをHELOOOに変更します。

takk@deb9:~/tmp$ echo OOO | dd seek=1880 bs=1 count=3 conv=notrunc of=b.out
3+0 レコード入力
3+0 レコード出力
3 bytes copied, 0.00010077 s, 29.8 kB/s
takk@deb9:~/tmp$ hd b.out | grep HELLO
00000750  01 00 02 00 48 45 4c 4c  4f 4f 4f 00 01 1b 03 3b  |....HELLOOO....;|
takk@deb9:~/tmp$ ./b.out
HELLOOO
takk@deb9:~/tmp$ 

実行できました。

コメント

タイトルとURLをコピーしました