a.outを直接修正して分岐処理を変える


アニメ『ISLAND(アイランド)』

ちょっとよく分からない、ってのが正直な感想。でも丁寧に作ってあるし、原作のゲームが面白いからこそアニメ化するわけで。
どうやら体験版で無料プレイできるみたいなので、ゲームの方をプレイしてから、アニメを見ようと思います。

今回もa.outの改造ネタです。文字列の変更ではなく、直接処理に係るデータや、命令を変更していきます。

では、改造するプログラム。分岐が入っています。

takk@deb9:~/tmp$ cat -n t.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int a=1;
     6	
     7		if(a)
     8			printf("TRUE\n");
     9		else
    10			printf("FALSE\n");
    11	
    12		return 0;
    13	}
takk@deb9:~/tmp$ 

aが1ならTRUE、0ならFALSEと表示するプログラムですが、
aの値は1から変更していないので、必ずTRUEの分岐しかしません。

実行してみます。

takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
TRUE
takk@deb9:~/tmp$ 

TRUEが表示されました。
何度やっても、TRUEしか表示しません。

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

では、改造します。aの値を変更する方法で、FALSEへ分岐してみます。

アセンブラ確認。

takk@deb9:~/tmp$ objdump -d a.out | grep -A15 'main.:'
00000000000006b0 <main>:
 6b0:	55                   	push   %rbp
 6b1:	48 89 e5             	mov    %rsp,%rbp
 6b4:	48 83 ec 10          	sub    $0x10,%rsp
 6b8:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)
 6bf:	83 7d fc 00          	cmpl   $0x0,-0x4(%rbp)
 6c3:	74 0e                	je     6d3 <main+0x23>
 6c5:	48 8d 3d a8 00 00 00 	lea    0xa8(%rip),%rdi        # 774 <_IO_stdin_used+0x4>
 6cc:	e8 8f fe ff ff       	callq  560 <puts@plt>
 6d1:	eb 0c                	jmp    6df <main+0x2f>
 6d3:	48 8d 3d 9f 00 00 00 	lea    0x9f(%rip),%rdi        # 779 <_IO_stdin_used+0x9>
 6da:	e8 81 fe ff ff       	callq  560 <puts@plt>
 6df:	b8 00 00 00 00       	mov    $0x0,%eax
 6e4:	c9                   	leaveq 
 6e5:	c3                   	retq   
 6e6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
takk@deb9:~/tmp$ 

アドレス6ccと、6daにあるcallq 560が、printf(puts)のようですね。それぞれに渡す文字列をその前の行、アドレス6c5と、6d3で設定しています。

変数aに1を入れている箇所は、アドレス6b8の行にありました。

 6b8:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)

4番目のデータの01を00にすれば、FALSEの方にしか分岐しなくなります。

4番目のアドレスは、6b8 + 4なので、6bbです。10進に変換します。

takk@deb9:~/tmp$ perl -E'say 0x6bb'
1723

a.outの1723の位置を0に書き換えます。

takk@deb9:~/tmp$ echo -ne "\x0" | dd seek=1723 bs=1 count=1 conv=notrunc of=./a.out
1+0 レコード入力
1+0 レコード出力
1 byte copied, 0.00034943 s, 2.9 kB/s
takk@deb9:~/tmp$ 

さて実行しましょう。

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

はい、FALSEと表示されました。
何回実行しても、FALSEです。

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

変数aの値を0にして分岐を変更することはできました。

次は、分岐処理そのものを改造します。
まずは、再度ビルドして、a.outを元に戻します。

takk@deb9:~/tmp$ gcc t.c

実行すると、TRUEに戻ってますね。

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

先ほどのアセンブラの、6c3の行に着目します。

 6bf:	83 7d fc 00          	cmpl   $0x0,-0x4(%rbp)
 6c3:	74 0e                	je     6d3 <main+0x23>
 6c5:	48 8d 3d a8 00 00 00 	lea    0xa8(%rip),%rdi        # 774 <_IO_stdin_used+0x4>
 6cc:	e8 8f fe ff ff       	callq  560 <puts@plt>
 6d1:	eb 0c                	jmp    6df <main+0x2f>
 6d3:	48 8d 3d 9f 00 00 00 	lea    0x9f(%rip),%rdi        # 779 <_IO_stdin_used+0x9>
 6da:	e8 81 fe ff ff       	callq  560 <puts@plt>

jeは、一致したら指定されたアドレスへジャンプする命令です。(正式に何の略なのかは知りませんが、JumpとEqualの頭文字でしょう)
je 6d3とあるので、一致したら6d3のアドレス、つまりFALSE側の処理へジャンプします。
このje命令を、逆の命令jneに書き換えます。(Jump Not Equal)
どこからjne命令が出てきたのか、思いついたのかというと、感です。
プログラマーは、プログラムを知らなくても、今持ってる情報の中から、法則を見つけてプログラムできるんです。

では、当たりをつけたjneで命令が存在するか検索してみましょう。

takk@deb9:~/tmp$ objdump -d a.out | grep jne
 647:   75 27                   jne    670 <__do_global_dtors_aux+0x30>
 68b:   75 0b                   jne    698 <frame_dummy+0x18>
 744:   75 ea                   jne    730 <__libc_csu_init+0x40>
takk@deb9:~/tmp$

3つほどヒットしました。3つに共通するデータは75ですので、おそらくこの75が、jneの意味でしょう。

ちなみに、他のどんなジャンプ命令があるか気になります。jから始まる命令を検索してみます。

takk@deb9:~/tmp$ objdump -d a.out | grep j
 53e:   74 02                   je     542 <_init+0x12>
 556:   ff 25 b4 0a 20 00       jmpq   *0x200ab4(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
 560:   ff 25 b2 0a 20 00       jmpq   *0x200ab2(%rip)        # 201018 <puts@GLIBC_2.2.5>
 56b:   e9 e0 ff ff ff          jmpq   550 <.plt>
 570:   ff 25 82 0a 20 00       jmpq   *0x200a82(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
 5c9:   76 15                   jbe    5e0 <deregister_tm_clones+0x30>
 5d5:   74 09                   je     5e0 <deregister_tm_clones+0x30>
 5d8:   ff e0                   jmpq   *%rax
 616:   74 18                   je     630 <register_tm_clones+0x40>
 622:   74 0c                   je     630 <register_tm_clones+0x40>
 625:   ff e0                   jmpq   *%rax
 647:   75 27                   jne    670 <__do_global_dtors_aux+0x30>
 655:   74 0c                   je     663 <__do_global_dtors_aux+0x23>
 68b:   75 0b                   jne    698 <frame_dummy+0x18>
 68d:   e9 5e ff ff ff          jmpq   5f0 <register_tm_clones>
 6a2:   74 e9                   je     68d <frame_dummy+0xd>
 6ab:   e9 40 ff ff ff          jmpq   5f0 <register_tm_clones>
 6c3:   74 0e                   je     6d3 <main+0x23>
 6d1:   eb 0c                   jmp    6df <main+0x2f>
 724:   74 20                   je     746 <__libc_csu_init+0x56>
 744:   75 ea                   jne    730 <__libc_csu_init+0x40>
takk@deb9:~/tmp$

今回検索したa.outではje、jmp、jne、jmpq、jbeって命令が見つかりました。
実際はもっとたくさんあると思います。

では、jeをjneに変更します。jeの位置を確認。

 6c3:	74 0e                	je     6d3 <main+0x23>

6c3を10進変換。

takk@deb9:~/tmp$ perl -E 'say 0x6c3'
1731
takk@deb9:~/tmp$ 

74が入ってた位置に75を上書きします。

takk@deb9:~/tmp$ echo -ne "\x75" | dd seek=1731 bs=1 count=1 conv=notrunc of=a.out
1+0 レコード入力
1+0 レコード出力
1 byte copied, 0.000394754 s, 2.5 kB/s
takk@deb9:~/tmp$

置換後のアセンブラ確認。

takk@deb9:~/tmp$ objdump -d a.out | grep -A15 'main.:'
00000000000006b0 <main>:
 6b0:	55                   	push   %rbp
 6b1:	48 89 e5             	mov    %rsp,%rbp
 6b4:	48 83 ec 10          	sub    $0x10,%rsp
 6b8:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)
 6bf:	83 7d fc 00          	cmpl   $0x0,-0x4(%rbp)
 6c3:	75 0e                	jne    6d3 <main+0x23>
 6c5:	48 8d 3d a8 00 00 00 	lea    0xa8(%rip),%rdi        # 774 <_IO_stdin_used+0x4>
 6cc:	e8 8f fe ff ff       	callq  560 <puts@plt>
 6d1:	eb 0c                	jmp    6df <main+0x2f>
 6d3:	48 8d 3d 9f 00 00 00 	lea    0x9f(%rip),%rdi        # 779 <_IO_stdin_used+0x9>
 6da:	e8 81 fe ff ff       	callq  560 <puts@plt>
 6df:	b8 00 00 00 00       	mov    $0x0,%eax
 6e4:	c9                   	leaveq 
 6e5:	c3                   	retq   
 6e6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
takk@deb9:~/tmp$ 

je命令があったアドレス6c3は、見事にjneに置換されました。

 6c3:	75 0e                	jne    6d3 <main+0x23>

では実行。

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

改造成功しました。

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA