gdbでarmバイナリをデバッグできるようにする
普段はMacにVMware Fusionをいれて、仮想マシンでLinux環境を動かしているわけですが、CTFの問題を解いているとELFだったとしてもアーキテクチャがARMのものにぶち当たったりします。
GDBでARMのELFをデバッグするためのパッケージとして、gdb-multiarchというものがあります。
これを入れることで、普段だとx86-64やx86のアーキテクチャのELFしか解析できない(はず)ですが、ARMのアーキテクチャもちゃんと解析できるようになります。
環境
$ uname -a Linux forensic-virtual-machine 4.13.0-36-generic #40~16.04.1-Ubuntu SMP Fri Feb 16 23:25:58 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/os-release NAME="Ubuntu" VERSION="16.04.4 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.4 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial
解析対象
とあるCTFの問題で出てきたバイナリを対象とします(手元にちょうどあったので)
$ file bin_arm bin_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, not stripped
gdb-multiarchが入っていない場合
まずは、gdb-multiarchが入っていない状態でバイナリを解析してみます。
いつも通りデバッグ対象に上記のファイルを指定します。
gdb bin_arm
試しにmain関数を逆アセンブルした結果を見てみようと思います。
gdb-peda$ disas main Dump of assembler code for function main: 0x0001055c <+0>: add BYTE PTR [eax+0x2d],cl 0x0001055f <+3>: jmp 0xe28eb568 0x00010564 <+8>: or al,dl 0x00010566 <+10>: dec ebp 0x00010567 <+11>: loop 0x10571 <main+21> 0x00010569 <+13>: add BYTE PTR [ebx],cl 0x0001056b <+15>: in eax,0xc 0x0001056d <+17>: adc BYTE PTR [ebx],cl 0x0001056f <+19>: in eax,0x8 0x00010571 <+21>: xor BYTE PTR [ebx],bl 0x00010573 <+23>: in eax,0x1 0x00010575 <+25>: add BYTE PTR [ebx-0x1d],dl 0x00010578 <+28>: or BYTE PTR [eax],al 0x0001057a <+30>: add dl,cl 0x0001057c <+32>: pusha 0x0001057d <+33>: xor BYTE PTR [edi-0x6cffff1b],bl 0x00010583 <+39>: in eax,0xc 0x00010585 <+41>: xor BYTE PTR [ebx],bl 0x00010587 <+43>: in eax,0x0 0x00010589 <+45>: xor BYTE PTR [ebx-0x5fdffc1b],dl 0x0001058f <+51>: loope 0x105e1 <main+133> 0x00010591 <+53>: adc BYTE PTR [edi-0x891b],bl 0x00010597 <+59>: jmp 0x10599 <main+61> 0x00010599 <+61>: xor al,ah 0x0001059b <+63>: jecxz 0x105aa <main+78> 0x0001059d <+65>: add BYTE PTR [eax],al 0x0001059f <+67>: jmp 0x3004:0xe51b300c 0x000105a6 <+74>: and edx,0x0 0x000105a9 <+77>: xor BYTE PTR [ebx-0x5ffffc1b],dl 0x000105af <+83>: loope 0x10573 <main+23> 0x000105b1 <+85>: (bad) 0x000105b2 <+86>: (bad) 0x000105b3 <+87>: jmp 0x105b5 <main+89> 0x000105b5 <+89>: xor BYTE PTR [eax+0x530000e1],ah 0x000105bb <+95>: jecxz 0x105bf <main+99> 0x000105bd <+97>: add BYTE PTR [eax],al 0x000105bf <+99>: sbb ah,BYTE PTR [eax+eax*1] 0x000105c2 <+102>: lahf 0x000105c3 <+103>: in eax,0x61 0x000105c5 <+105>: (bad) 0x000105c6 <+106>: (bad) 0x000105c7 <+107>: jmp 0x105ca <main+110>
ところどころ(bad)となっており、マシンコードを適切にアセンブリコードに変換できていないことが分かるかと思います。
あと、みるからにアセンブリコードが変(いつも見ている感じとはなんか違和感がある)です。
ということで、なにも手を加えずgdbでarmバイナリに突撃すると爆死します...
gdb-multiarchを入れてやってみる
では早速入れてやってやってみましょう。いつも通りaptで入れます。
sudo apt install gdb-multiarch
準備はこれで終了です。では早速起動。
$ gdb-multiarch bin_arm 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 bin_arm...(no debugging symbols found)...done. gdb-peda$
普通にgdbと同じように起動してしまったので、「これこのまんまでいけるんか?」と不安になりますがそのまま行きます。
また、見て分かる通り、gdb-pedaを入れている方であればgdb-multiarchを起動しても同じようにいけます(おそらく同じ.gdbinitを見にいっているかと)
というわけで、逆アセンブルしたmain関数のアセンブリコードを見てみます。
gdb-peda$ disas main Dump of assembler code for function main: 0x0001055c <+0>: push {r11, lr} 0x00010560 <+4>: add r11, sp, #4 0x00010564 <+8>: sub sp, sp, #8 0x00010568 <+12>: str r0, [r11, #-8] 0x0001056c <+16>: str r1, [r11, #-12] 0x00010570 <+20>: ldr r3, [r11, #-8] 0x00010574 <+24>: cmp r3, #1 0x00010578 <+28>: bgt 0x105a0 <main+68> 0x0001057c <+32>: ldr r3, [pc, #96] ; 0x105e4 <main+136> 0x00010580 <+36>: ldr r0, [r3] 0x00010584 <+40>: ldr r3, [r11, #-12] 0x00010588 <+44>: ldr r3, [r3] 0x0001058c <+48>: mov r2, r3 0x00010590 <+52>: ldr r1, [pc, #80] ; 0x105e8 <main+140> 0x00010594 <+56>: bl 0x10374 <fprintf@plt> 0x00010598 <+60>: mvn r3, #0 0x0001059c <+64>: b 0x105d8 <main+124> 0x000105a0 <+68>: ldr r3, [r11, #-12] 0x000105a4 <+72>: add r3, r3, #4 0x000105a8 <+76>: ldr r3, [r3] 0x000105ac <+80>: mov r0, r3 0x000105b0 <+84>: bl 0x104c0 <check> 0x000105b4 <+88>: mov r3, r0 0x000105b8 <+92>: cmp r3, #0 0x000105bc <+96>: bne 0x105cc <main+112> 0x000105c0 <+100>: ldr r0, [pc, #36] ; 0x105ec <main+144> 0x000105c4 <+104>: bl 0x10350 <puts@plt> 0x000105c8 <+108>: b 0x105d4 <main+120> 0x000105cc <+112>: ldr r0, [pc, #28] ; 0x105f0 <main+148> 0x000105d0 <+116>: bl 0x10350 <puts@plt> 0x000105d4 <+120>: mov r3, #0 0x000105d8 <+124>: mov r0, r3 0x000105dc <+128>: sub sp, r11, #4 0x000105e0 <+132>: pop {r11, pc} 0x000105e4 <+136>: andeq r1, r2, r0, asr r0 0x000105e8 <+140>: andeq r0, r1, r4, ror #12 0x000105ec <+144>: andeq r0, r1, r8, ror r6 0x000105f0 <+148>: andeq r0, r1, r4, lsl #13 End of assembler dump.
ちゃんと表示されているっぽい!
ぶっちゃけARMのアセンブリの解析はほとんどやっていないので、どの命令がどの処理をするのかわかってません。
(噂だとオペランドを3つとるとかなんとか....)
なので、ひとまずはこれを使って勉強していこうと思います。