[HOME] > PC-UNIX Tips > gdbでZF値を制御
Since: 2018/10/21
PC-UNIX Tips
初めてのSECCONに向けて勉強中。 今日は、やっとgdbで条件分岐の一つZFフラグレジスタの値を制御(変更)する方法を 少し理解できた(はず...)のでまとめておきます。ただ、妖しげな点も含まれているので 参考程度で (^_^;;
最初、EFLAGSレジスターのZF値を書き換えようとしていました。これはたぶん間違い で、ZF値が目的の値になるように、今回であれば比較処理する値を変更する操作が必要だと 理解しました。以下、実例で説明します。
% gcc --version
gcc (GCC) 4.8.5
...
% gdb --version
GNU gdb (GDB) 7.5
...
Cソースコードは次の通りです。条件分岐で必ずexit()
関数が呼ばれる
ため、モーフィアスのWelcomメッセージが出力されません :-) i変数をゼロ以外に設定
すれば、ネオを現実世界に連れ戻せます。
#include <stdio.h>
#include <stdlib.h>
main(){
/*printf("Hellow World.\n");*/
int i = 0;
if(i == 0){
exit(0);
}
printf("Welcome to the real world.\n");
}
シンプルにコンパイル。
% gcc test.c
アセンブラコードを取得。Intel記法で統一します。
% objdump -d -M intel a.out
(main関数のみ抜粋)
...
000000000040053d <main>:
40053d: 55 push %rbp
40053e: 48 89 e5 mov %rsp,%rbp
400541: 48 83 ec 10 sub $0x10,%rsp
400545: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
40054c: 83 7d fc 00 cmpl $0x0,-0x4(%rbp) # 条件分岐のcmpオペコード。オペランドの[rbp-0x4]アドレス値と0x0を比較。[rbp-0x4]アドレス値をゼロ以外にすれば良い。
400550: 75 0a jne 40055c <main+0x1f> # jneオペコードはZF=0ならアドレス=40055cにジャンプ。
400552: bf 00 00 00 00 mov $0x0,%edi
400557: e8 e4 fe ff ff callq 400440 <exit@plt> # ZF=1ならexit()関数実行して終了。
40055c: bf f4 05 40 00 mov $0x4005f4,%edi # ここにジャンプできればOK。
400561: e8 aa fe ff ff callq 400410 <puts@plt>
400566: c9 leaveq
400567: c3 retq
400568: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40056f: 00
...
gdbを起動。こちらもIntel記法で統一するために~/.gdbinitを設定しています。
% cat ~/.gdbinit
set disassembly-flavor intel
% gdb a.out -q
Reading symbols from /foo/hoge/a.out...done.
(gdb)
まず、そのままステップ実行してみます。
(gdb) start #main()関数でブレイクするstartを実行。
Temporary breakpoint 1 at 0x400541
Starting program: /foo/hoge/a.out
Temporary breakpoint 1, 0x0000000000400541 in main ()
(gdb) info registers rbp eflags #rbpとeflagsのフラグレジスター値を確認。
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x246 [ PF ZF IF ]
(gdb) x/wg 0x7fffffffdf20-0x4 #cmpオペランドの[rbp-0x4]アドレス値を確認。
0x7fffffffdf1c: 0
(gdb) si #ステップ実行。アセンブラ実行単位。
0x0000000000400545 in main ()
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x202 [ IF ]
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0
(gdb) si
0x000000000040054c in main () #ポイントになるcmpオペコード実行箇所。
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x202 [ IF ]
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0 #値はゼロのまま。
(gdb) si
0x0000000000400550 in main () #jneオペコード箇所。
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x246 [ PF ZF IF ] #ZFフラグが立っています。ZF値ってどうやって確認するんじゃろ?
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0
(gdb) si
0x0000000000400552 in main () #jneオペコードでジャンプせず、そのままステップ実行。
(gdb) si
0x0000000000400557 in main ()
(gdb) si
0x0000000000400440 in exit@plt () # exit()関数実行。
(gdb) c
Continuing.
[Inferior 1 (process 6250) exited normally]
(gdb)
いよいよ、int変数にゼロ以外の数値(今回は1)を設定してネオを連れ戻します。
(gdb) start
Temporary breakpoint 1 at 0x400541
Starting program: /foo/hoge/a.out
Temporary breakpoint 1, 0x0000000000400541 in main ()
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x246 [ PF ZF IF ]
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0
(gdb) si
0x0000000000400545 in main ()
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x202 [ IF ]
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0
(gdb) si
0x000000000040054c in main ()
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x202 [ IF ]
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 0
(gdb) set *(0x7fffffffdf20-0x4)=1 #cmpオペランドに1を設定。これがポイント。
(gdb) x/wg 0x7fffffffdf20-0x4
0x7fffffffdf1c: 1
(gdb) si
0x0000000000400550 in main () #jneオペコード。いけ、jne。忌々しい記憶と共に...
(gdb) info registers rbp eflags
rbp 0x7fffffffdf20 0x7fffffffdf20
eflags 0x202 [ IF ] #あれ?ZFフラグが立っていない?これでえんかな?
(gdb) si
0x000000000040055c in main () #よっしゃあ〜。ジャンプした(^_^)
(gdb) si
0x0000000000400561 in main ()
(gdb) si
0x0000000000400410 in puts@plt ()
(gdb) c
Continuing.
Welcome to the real world. # ネオを奪還 :-)
[Inferior 1 (process 6291) exited with code 033]
(gdb)
PC-UNIX Tips
Written by kabada