[HOME] > PC-UNIX Tips > gdbでZF値を制御

gdbでZF値を制御

Since: 2018/10/21

PC-UNIX Tips

初めてのSECCONに向けて勉強中。 今日は、やっとgdbで条件分岐の一つZFフラグレジスタの値を制御(変更)する方法を 少し理解できた(はず...)のでまとめておきます。ただ、妖しげな点も含まれているので 参考程度で (^_^;;

最初、EFLAGSレジスターのZF値を書き換えようとしていました。これはたぶん間違い で、ZF値が目的の値になるように、今回であれば比較処理する値を変更する操作が必要だと 理解しました。以下、実例で説明します。

INDEX

環境

% gcc --version
gcc (GCC) 4.8.5
...
% gdb --version
GNU gdb (GDB) 7.5
...
	

ソースコード

Cソースコードは次の通りです。条件分岐で必ずexit()関数が呼ばれる ため、モーフィアスのWelcomメッセージが出力されません :-) i変数をゼロ以外に設定 すれば、ネオを現実世界に連れ戻せます。

test.c

#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起動

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