アニメ『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$
改造成功しました。
コメント