Help us understand the problem. What is going on with this article?

Exgdb 使い方

【注意】

この記事は内容が大変古いです。
使い方はリポジトリのREADME.mdをご参照ください。

Exgdbとは

SecHack365で開発中のgdb拡張です。
リポジトリ:https://github.com/miyase256/exgdb

環境

Ubuntu 16.04 LTS

まずはgdbを使ってみる

https://qiita.com/miyagaw61/private/4a4514e2de0b458c2589

次に、gdb-pedaを使ってみる

https://qiita.com/miyagaw61/private/248a486cca671686c58c

そしてExgdb

Install

/path/toは任意のディレクトリを表します。

$ git clone https://github.com/miyagaw61/exgdb.git /path/to/exgdb
$ cd /path/to/exgdb  
$ sudo ./install.sh  

最後の質問でyを答えると~/.gdbinitというgdbの初期設定ファイルが~/.gdbinit.bakという名前でバックアップがとられ、~/.gdbinitが上書きされます。
nと答えた場合は、exgdb.git/exgdbinitの内容を~/.gdbinitに書き込んでください。

実際に使ってみる

gdbやgdb-pedaで使用したバイナリを使って解析してみます。

$ gdb test
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...(no debugging symbols found)...done.
gdb-peda$

startしてみます。

gdb-peda$ start
[----------------------------------registers-----------------------------------]
RAX: 0x400596 (<main>:  push   rbp)
RBX: 0x0
RCX: 0x0
RDX: 0x7fffffffe0b8 --> 0x7fffffffe38e ("XDG_SESSION_ID=9152")
RSI: 0x7fffffffe0a8 --> 0x7fffffffe375 ("/home/miyagaw61/tmp/test")
RDI: 0x1
RBP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
RSP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
RIP: 0x40059a (<main+4>:        sub    rsp,0x30)
R8 : 0x400660 (<__libc_csu_fini>:       repz ret)
R9 : 0x7ffff7de7ab0 (<_dl_fini>:        push   rbp)
R10: 0x846
R11: 0x7ffff7a2d740 (<__libc_start_main>:       push   r14)
R12: 0x4004a0 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe0a0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400591 <frame_dummy+33>:   jmp    0x400510 <register_tm_clones>
   0x400596 <main>:     push   rbp
   0x400597 <main+1>:   mov    rbp,rsp
=> 0x40059a <main+4>:   sub    rsp,0x30
   0x40059e <main+8>:   mov    rax,QWORD PTR fs:0x28
   0x4005a7 <main+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x4005ab <main+21>:  xor    eax,eax
   0x4005ad <main+23>:  mov    DWORD PTR [rbp-0x24],0xa
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)
0008| 0x7fffffffdfc8 --> 0x7ffff7a2d830 (<__libc_start_main+240>:       mov    edi,eax)
0016| 0x7fffffffdfd0 --> 0x0
0024| 0x7fffffffdfd8 --> 0x7fffffffe0a8 --> 0x7fffffffe375 ("/home/miyagaw61/tmp/test")
0032| 0x7fffffffdfe0 --> 0x1f7ffcca0
0040| 0x7fffffffdfe8 --> 0x400596 (<main>:      push   rbp)
0048| 0x7fffffffdff0 --> 0x0
0056| 0x7fffffffdff8 --> 0x8fa409088f82687e
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 2, 0x000000000040059a in main ()

表示は何も変わりませんね。
では、exgdbで追加された新機能を触ってみましょう。

allstackコマンドを実行してみましょう。

gdb-peda$ allstack
0000| 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)

一行だけ表示されました。
このコマンドは、現在のスタックフレーム(rspからrbpまで)を表示するコマンドです。
スタックだけを見たいなら、このコマンドを使った方が便利な時があります。

pc 10x/10i $ripに色が付いたものになります。

gdb-peda$ pc 10
=> 0x40059a <main+4>:   sub    rsp,0x30
   0x40059e <main+8>:   mov    rax,QWORD PTR fs:0x28
   0x4005a7 <main+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x4005ab <main+21>:  xor    eax,eax
   0x4005ad <main+23>:  mov    DWORD PTR [rbp-0x24],0xa
   0x4005b4 <main+30>:  movabs rax,0x67202c6f6c6c6548
   0x4005be <main+40>:  mov    QWORD PTR [rbp-0x20],rax
   0x4005c2 <main+44>:  mov    DWORD PTR [rbp-0x18],0x216264
   0x4005c9 <main+51>:  lea    rax,[rbp-0x20]
   0x4005cd <main+55>:  mov    rdi,rax

上から三つめの0x4005a7番地を始点として10個の命令列を見たい場合は、code 0x4005a7 10というコマンドを実行します。x/10i 0x4005a7に色がつきます。

gdb-peda$ code 0x4005a7 10
=> 0x4005a7 <main+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x4005ab <main+21>:  xor    eax,eax
   0x4005ad <main+23>:  mov    DWORD PTR [rbp-0x24],0xa
   0x4005b4 <main+30>:  movabs rax,0x67202c6f6c6c6548
   0x4005be <main+40>:  mov    QWORD PTR [rbp-0x20],rax
   0x4005c2 <main+44>:  mov    DWORD PTR [rbp-0x18],0x216264
   0x4005c9 <main+51>:  lea    rax,[rbp-0x20]
   0x4005cd <main+55>:  mov    rdi,rax
   0x4005d0 <main+58>:  call   0x400460 <puts@plt>
   0x4005d5 <main+63>:  mov    eax,0x0

第二引数を1にすると0x4005a7番地の命令だけを見れます。

gdb-peda$ code 0x4005da 1
=> 0x4005da <main+68>:  mov    rdx,QWORD PTR [rbp-0x8]

"rbp-0x8"という文字列があることがわかると思います。
この"rbp-0x8"という文字列を含む命令をもつアドレスまで進んでみましょう。

gdb-peda$ uu rbp-0x8
[----------------------------------registers-----------------------------------]
RAX: 0x4d9fbbbfcb981a00
RBX: 0x0
RCX: 0x0
RDX: 0x7fffffffe0b8 --> 0x7fffffffe38e ("XDG_SESSION_ID=9152")
RSI: 0x7fffffffe0a8 --> 0x7fffffffe375 ("/home/miyagaw61/tmp/test")
RDI: 0x1
RBP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
RSP: 0x7fffffffdf90 --> 0x0
RIP: 0x4005a7 (<main+17>:       mov    QWORD PTR [rbp-0x8],rax)
R8 : 0x400660 (<__libc_csu_fini>:       repz ret)
R9 : 0x7ffff7de7ab0 (<_dl_fini>:        push   rbp)
R10: 0x846
R11: 0x7ffff7a2d740 (<__libc_start_main>:       push   r14)
R12: 0x4004a0 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe0a0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400597 <main+1>:   mov    rbp,rsp
   0x40059a <main+4>:   sub    rsp,0x30
   0x40059e <main+8>:   mov    rax,QWORD PTR fs:0x28
=> 0x4005a7 <main+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x4005ab <main+21>:  xor    eax,eax
   0x4005ad <main+23>:  mov    DWORD PTR [rbp-0x24],0xa
   0x4005b4 <main+30>:  movabs rax,0x67202c6f6c6c6548
   0x4005be <main+40>:  mov    QWORD PTR [rbp-0x20],rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf90 --> 0x0
0008| 0x7fffffffdf98 --> 0x0
0016| 0x7fffffffdfa0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)
0024| 0x7fffffffdfa8 --> 0x4004a0 (<_start>:    xor    ebp,ebp)
0032| 0x7fffffffdfb0 --> 0x7fffffffe0a0 --> 0x1
0040| 0x7fffffffdfb8 --> 0x0
0048| 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)
0056| 0x7fffffffdfc8 --> 0x7ffff7a2d830 (<__libc_start_main+240>:       mov    edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004005a7 in main ()

0x4005a7 <main+17>: mov QWORD PTR [rbp-0x8],raxで止まったと思います。
このように、uuコマンドでは、引数に与えた文字列を命令に含むアドレスまで自動で進むことができます。
ちなみに、正規表現も使えます。

次に、今の命令の中にある[rbp-0x8]の中身を見てみましょう。
gdb-peda内蔵の機能では、tel $rbp-0x8というコマンドで可能です。

gdb-peda$ tel $rbp-0x8
0000| 0x7fffffffdfb8 --> 0x0

Exgdbには、現在のプログラムカウンタが指しているアドレス中の命令に含まれているデータを解析し、自動で中身を表示してくれるiiコマンドが存在します。

gdb-peda$ ii
RAX: 0xa06857ec28cd0e00
RBP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
[rbp-0x8]: 0x7fffffffdfb8 --> 0x0

「gdb-peda超入門」の記事でも紹介しましたが、gdb-peda内蔵の機能で、関数の中身を全てダンプするpdisassというコマンドが存在します。
pdisass mainを実行し、main関数の中身を全て見てみましょう。

gdb-peda$ pdisass main
Dump of assembler code for function main:
   0x0000000000400596 <+0>:     push   rbp
   0x0000000000400597 <+1>:     mov    rbp,rsp
   0x000000000040059a <+4>:     sub    rsp,0x30
   0x000000000040059e <+8>:     mov    rax,QWORD PTR fs:0x28
=> 0x00000000004005a7 <+17>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000004005ab <+21>:    xor    eax,eax
   0x00000000004005ad <+23>:    mov    DWORD PTR [rbp-0x24],0xa
   0x00000000004005b4 <+30>:    movabs rax,0x45202c6f6c6c6548
   0x00000000004005be <+40>:    mov    QWORD PTR [rbp-0x20],rax
   0x00000000004005c2 <+44>:    mov    DWORD PTR [rbp-0x18],0x62646778
   0x00000000004005c9 <+51>:    mov    WORD PTR [rbp-0x14],0x21
   0x00000000004005cf <+57>:    lea    rax,[rbp-0x20]
   0x00000000004005d3 <+61>:    mov    rdi,rax
   0x00000000004005d6 <+64>:    call   0x400460 <puts@plt>
   0x00000000004005db <+69>:    mov    eax,0x0
   0x00000000004005e0 <+74>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x00000000004005e4 <+78>:    xor    rdx,QWORD PTR fs:0x28
   0x00000000004005ed <+87>:    je     0x4005f4 <main+94>
   0x00000000004005ef <+89>:    call   0x400470 <__stack_chk_fail@plt>
   0x00000000004005f4 <+94>:    leave
   0x00000000004005f5 <+95>:    ret
End of assembler dump.

ここから、「callを呼んでいるところだけを抽出したい」とします。
そういう時は、grepコマンドが便利です。
第一引数に「何かを表示するコマンド」、第二引数に「正規表現」です。

gdb-peda$ grep "pdisass main" call
   0x00000000004005d0 <+58>:    call   0x400460 <puts@plt>
   0x00000000004005e9 <+83>:    call   0x400470 <__stack_chk_fail@plt>

少し前にuuという正規表現にマッチしたアドレスまで進むというコマンドを紹介したと思います。
あれは内部でnextiを使用しているのですが、代わりにnextcallを使用することで高速化したucコマンドも存在します。

gdb-peda$ uc puts
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdfa0 ("Hello, gdb!")
RBX: 0x0
RCX: 0x0
RDX: 0x7fffffffe0b8 --> 0x7fffffffe38e ("XDG_SESSION_ID=9152")
RSI: 0x7fffffffe0a8 --> 0x7fffffffe375 ("/home/miyagaw61/tmp/test")
RDI: 0x7fffffffdfa0 ("Hello, gdb!")
RBP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
RSP: 0x7fffffffdf90 --> 0x0
RIP: 0x4005d0 (<main+58>:       call   0x400460 <puts@plt>)
R8 : 0x400660 (<__libc_csu_fini>:       repz ret)
R9 : 0x7ffff7de7ab0 (<_dl_fini>:        push   rbp)
R10: 0x846
R11: 0x7ffff7a2d740 (<__libc_start_main>:       push   r14)
R12: 0x4004a0 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe0a0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4005c2 <main+44>:  mov    DWORD PTR [rbp-0x18],0x216264
   0x4005c9 <main+51>:  lea    rax,[rbp-0x20]
   0x4005cd <main+55>:  mov    rdi,rax
=> 0x4005d0 <main+58>:  call   0x400460 <puts@plt>
   0x4005d5 <main+63>:  mov    eax,0x0
   0x4005da <main+68>:  mov    rdx,QWORD PTR [rbp-0x8]
   0x4005de <main+72>:  xor    rdx,QWORD PTR fs:0x28
   0x4005e7 <main+81>:  je     0x4005ee <main+88>
Guessed arguments:
arg[0]: 0x7fffffffdfa0 ("Hello, gdb!")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf90 --> 0x0
0008| 0x7fffffffdf98 --> 0xa00000000 ('')
0016| 0x7fffffffdfa0 ("Hello, gdb!")
0024| 0x7fffffffdfa8 --> 0x216264 ('db!')
0032| 0x7fffffffdfb0 --> 0x7fffffffe0a0 --> 0x1
0040| 0x7fffffffdfb8 --> 0x9d1304f00b2f0b00
0048| 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)
0056| 0x7fffffffdfc8 --> 0x7ffff7a2d830 (<__libc_start_main+240>:       mov    edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

正しくputs関数で止まることが確認できました。
uc "(malloc|free)"などと書くと、エンターを押すたびにmallocかfreeで止まる(超高速)、みたいなことが可能になります。

次に、同系統のコマンドで、jmp系の命令で止まるjjコマンドも使ってみましょう。

gdb-peda$ jj
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7b042c0 (<__write_nocancel+7>:      cmp    rax,0xfffffffffffff001)
RDX: 0x0
RSI: 0x602010 ("Hello, gdb!\n")
RDI: 0x1
RBP: 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:    push   r15)
RSP: 0x7fffffffdf90 --> 0x0
RIP: 0x4005e7 (<main+81>:       je     0x4005ee <main+88>)
R8 : 0x602000 --> 0x0
R9 : 0xd ('\r')
R10: 0x7ffff7dd1b78 --> 0x602410 --> 0x0
R11: 0x246
R12: 0x4004a0 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe0a0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4005d5 <main+63>:  mov    eax,0x0
   0x4005da <main+68>:  mov    rdx,QWORD PTR [rbp-0x8]
   0x4005de <main+72>:  xor    rdx,QWORD PTR fs:0x28
=> 0x4005e7 <main+81>:  je     0x4005ee <main+88>
 | 0x4005e9 <main+83>:  call   0x400470 <__stack_chk_fail@plt>
 | 0x4005ee <main+88>:  leave
 | 0x4005ef <main+89>:  ret
 | 0x4005f0 <__libc_csu_init>:  push   r15
 |->   0x4005ee <main+88>:      leave
       0x4005ef <main+89>:      ret
       0x4005f0 <__libc_csu_init>:      push   r15
       0x4005f2 <__libc_csu_init+2>:    push   r14
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf90 --> 0x0
0008| 0x7fffffffdf98 --> 0xa00000000 ('')
0016| 0x7fffffffdfa0 ("Hello, gdb!")
0024| 0x7fffffffdfa8 --> 0x216264 ('db!')
0032| 0x7fffffffdfb0 --> 0x7fffffffe0a0 --> 0x1
0040| 0x7fffffffdfb8 --> 0x63919453d9938600
0048| 0x7fffffffdfc0 --> 0x4005f0 (<__libc_csu_init>:   push   r15)
0056| 0x7fffffffdfc8 --> 0x7ffff7a2d830 (<__libc_start_main+240>:       mov    edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004005e7 in main ()

jmp系の命令の一つであるje命令で止まったことがわかると思います。

あとは、ヒープ関連のコマンドを紹介したいのですが、ヒープは少しばかり難しいので、今回は紹介しないでおきます。
いくつかチャンクを確保しfreelistにもいくつか繋いだ状態で、ph, ci [addr], allci, cix [addr], allcixなどのコマンドを試してみてください。

おわり

以上でExgdbの基礎は終わりです。
このように、Exgdbでは、動的解析を効率化する様々なコマンドを用意しています。
日常でバイナリの動的解析をする中で、「このような機能が欲しい」と感じたら、ぜひ教えていただけると幸いです。
ご清聴(?)ありがとうございました。
enjoy your exgdb life!

miyase256
SoftwareDvelopment, LinuxKernelReading, MalwareAnalysis, Exploit, AtCoder / seccamp'17'18, SecHack365'18, GlobalCybersecurityCamp'18
https://miyase256.github.io
ipfactory
メンバーが各々の技術分野を追求するサークル、「IPFactory」のOrganizationです。それぞれのアウトプット活動を促進するために発足されました。
https://twitter.com/_ipfactory_
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away