Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】利用GDB调试ARM代码

0
0
【技术分享】利用GDB调试ARM代码

2017-08-02 13:13:28

阅读:735次
点赞(0)
收藏
来源: azeria-labs.com





【技术分享】利用GDB调试ARM代码

作者:shan66






【技术分享】利用GDB调试ARM代码

译者:shan66

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


在本文中,我们将简要介绍如何利用GDB完成ARM二进制文件的编译和基本调试。当然,阅读过程中,如果读者想要对ARM汇编代码动手进行实验的话,则需要准备一个备用的ARM设备,或者在虚拟机中设置相应的实验室环境,具体操作请参考How-To这篇文章。

此外,您还将通过下面取自 Part 7 – Stack and Functions这篇文章中的代码来熟悉GDB的基本调试功能。

.section.text .global_start _start: push{r11,lr}/*Startoftheprologue.SavingFramePointerandLRontothestack*/ addr11,sp,#0/*Settingupthebottomofthestackframe*/ subsp,sp,#16/*Endoftheprologue.Allocatingsomebufferonthestack*/ movr0,#1/*settinguplocalvariables(a=1).Thisalsoservesassettingupthefirstparameterforthemaxfunction*/ movr1,#2/*settinguplocalvariables(b=2).Thisalsoservesassettingupthesecondparameterforthemaxfunction*/ blmax/*Calling/branchingtofunctionmax*/ subsp,r11,#0/*Startoftheepilogue.ReadjustingtheStackPointer*/ pop{r11,pc}/*Endoftheepilogue.RestoringFramepointerfromthestack,jumpingtopreviouslysavedLRviadirectloadintoPC*/ max: push{r11}/*Startoftheprologue.SavingFramePointerontothestack*/ addr11,sp,#0/*Settingupthebottomofthestackframe*/ subsp,sp,#12/*Endoftheprologue.Allocatingsomebufferonthestack*/ cmpr0,r1/*Implementationofif(a<b)*/ movltr0,r1/*ifr0waslowerthanr1,storer1intor0*/ addsp,r11,#0/*Startoftheepilogue.ReadjustingtheStackPointer*/ pop{r11}/*restoringframepointer*/ bxlr/*Endoftheepilogue.JumpingbacktomainviaLRregister*/

就个人而言,我更喜欢使用作为GDB增强版的GEF,它用起来要更加得心应手,具体下载地址https://github.com/hugsy/gef

将上述代码保存到名为max.s的文件中,然后使用以下命令进行编译:

$asmax.s-omax.o $ldmax.o-omax

这个调试器是一个强大的工具,可以:

在代码崩溃后加载内存dump(事后剖析调试)

附加到正在运行的进程(用于服务器进程)

启动程序并进行调试


根据二进制文件、核心文件或进程ID启动GDB:

附加到一个进程:$ gdb -pid $(pidof <process>)

调试二进制代码:$ gdb ./file

检查内核(崩溃)文件:$ gdb -c ./core.3243

$gdbmax

如果您安装了GEF,将会显示gef>提示符。

可以通过下列方式获取帮助:

(gdb) h

(gdb) apropos <search-term>

gef>aproposregisters collect--Specifyoneormoredataitemstobecollectedatatracepoint core-file--UseFILEascoredumpforexaminingmemoryandregisters infoall-registers--Listofallregistersandtheircontents infor--Listofintegerregistersandtheircontents inforegisters--Listofintegerregistersandtheircontents maintenanceprintcooked-registers--Printtheinternalregisterconfigurationincludingcookedvalues maintenanceprintraw-registers--Printtheinternalregisterconfigurationincludingrawvalues maintenanceprintregisters--Printtheinternalregisterconfiguration maintenanceprintremote-registers--Printtheinternalregisterconfigurationincludingeachregister's p--PrintvalueofexpressionEXP print--PrintvalueofexpressionEXP registers--Displayfulldetailsonone setmay-write-registers--Setpermissiontowriteintoregisters setobserver--Setwhethergdbcontrolstheinferiorinobservermode showmay-write-registers--Showpermissiontowriteintoregisters showobserver--Showwhethergdbcontrolstheinferiorinobservermode tuiregfloat--Displayonlyfloatingpointregisters tuireggeneral--Displayonlygeneralregisters tuiregsystem--Displayonlysystemregisters 断点命令:

break (or just b) <function-name>

break <line-number>

break filename:function

break filename:line-number

break *<address>

break +<offset>

break –<offset>

tbreak (设置一个临时断点)

del <number> (删除编号为x的断点)

delete (删除所有断点)

delete <range>(删除指定编号范围内的断点)

disable/enable <breakpoint-number-or-range> (不删除断点,只是启用/禁用它们)

continue (or just c) – (继续执行,直到下一个断点)

continue <number> (继续,但忽略当前断点指定次数。对循环内的断点非常有用)

finish继续,直至函数末尾)

gef>break_start gef>infobreak NumTypeDispEnbAddressWhat 1breakpointkeepy0x00008054<_start> breakpointalreadyhit1time gef>del1 gef>break*0x0000805c Breakpoint2at0x805c gef>break_start

这将删除第一个断点,并在指定的内存地址处设置一个断点。当您运行程序时,它将在这个指定的位置停下来。 如果不删除第一个断点,然后又设置一个断点并运行,则它还是在第一个断点处停下来。

启动和停止:

启动一个程序,从头开始执行

run

r

run <command-line-argument>

停止程序的运行

kill

退出GDB调试器

quit

q

gef>run
【技术分享】利用GDB调试ARM代码

现在,我们的程序在指定的位置停下来了,这样就可以开始检查内存了。 命令“x”可以用来以各种格式显示内存内容。

语法 : x/<count><format><unit>

格式单位

x – 十六进制 b - 字节

d - 十进制h - 半字(2字节)

i - 指令w - 字(4字节)

t - 二进制(two)g - 巨字(8字节)

o - 八进制

u - 无符号整数

s - 字符串

c - 字符

gef>x/10i$pc =>0x8054<_start>:push{r11,lr} 0x8058<_start+4>:addr11,sp,#0 0x805c<_start+8>:subsp,sp,#16 0x8060<_start+12>:movr0,#1 0x8064<_start+16>:movr1,#2 0x8068<_start+20>:bl0x8074<max> 0x806c<_start+24>:subsp,r11,#0 0x8070<_start+28>:pop{r11,pc} 0x8074<max>:push{r11} 0x8078<max+4>:addr11,sp,#0 gef>x/16xw$pc 0x8068<_start+20>:0xeb0000010xe24bd0000xe8bd88000xe92d0800 0x8078<max+4>:0xe28db0000xe24dd00c0xe15000010xb1a00001 0x8088<max+20>:0xe28bd0000xe8bd08000xe12fff1e0x00001741 0x8098:0x616561000x010069620x0000000d0x01080206 用于单步调试的命令:单步执行下一条命令。可以进入函数内部

stepi

s

step <number-of-steps-to-perform>

执行下一行代码。不会进入函数内部

nexti

n

next <number>

继续处理,直到达到指定的行号、函数名称、地址、文件名函数或文件名:行号

until

until <line-number>、

显示当前行号以及所在的函数

where

gef>nexti5 ... 0x8068<_start+20>bl0x8074<max><-$pc 0x806c<_start+24>subsp,r11,#0 0x8070<_start+28>pop{r11,pc} 0x8074<max>push{r11} 0x8078<max+4>addr11,sp,#0 0x807c<max+8>subsp,sp,#12 0x8080<max+12>cmpr0,r1 0x8084<max+16>movltr0,r1 0x8088<max+20>addsp,r11,#0

使用info registers或i r命令检查寄存器的值

gef>inforegisters r00x11 r10x22 r20x00 r30x00 r40x00 r50x00 r60x00 r70x00 r80x00 r90x00 r100x00 r110xbefff7e83204446184 r120x00 sp0xbefff7d80xbefff7d8 lr0x00 pc0x80680x8068<_start+20> cpsr0x1016

命令“info registers”能够提供当前的寄存器状态。 我们可以看到,这里包括通用寄存器r0-r12,专用寄存器SP、LR和PC,以及状态寄存器CPSR。 函数的前四个参数通常存储在r0-r3中。 在这种情况下,我们可以通过手动方式将其值移动到r0和r1。

显示进程内存映射:

gef>infoprocmap process10225 Mappedaddressspaces: StartAddrEndAddrSizeOffsetobjfile 0x80000x90000x10000/home/pi/lab/max 0xb6fff0000xb70000000x10000[sigpage] 0xbefdf0000xbf0000000x210000[stack] 0xffff00000xffff10000x10000[vectors] 通过命令“disassemble”,我们可以查看函数max的反汇编输出。
gef>disassemblemax Dumpofassemblercodeforfunctionmax: 0x00008074<+0>:push{r11} 0x00008078<+4>:addr11,sp,#0 0x0000807c<+8>:subsp,sp,#12 0x00008080<+12>:cmpr0,r1 0x00008084<+16>:movltr0,r1 0x00008088<+20>:addsp,r11,#0 0x0000808c<+24>:pop{r11} 0x00008090<+28>:bxlr Endofassemblerdump.

GEF特有的命令(可以使用命令“gef”查看更多命令):

将所有已加载的ELF镜像的所有节dump到进程内存中

X档案

proc map的增强版本,包括映射页面中的RWX属性

vmmap

给定地址的内存属性

xinfo

检查运行的二进制文件内置的编译器级保护措施

checksec

gef>xfiles StartEndNameFile 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max 0x000080540x00008094.text/home/pi/lab/max gef>vmmap StartEndOffsetPermPath 0x000080000x000090000x00000000r-x/home/pi/lab/max 0xb6fff0000xb70000000x00000000r-x[sigpage] 0xbefdf0000xbf0000000x00000000rwx[stack] 0xffff00000xffff10000x00000000r-x[vectors] gef>xinfo0xbefff7e8 ----------------------------------------[xinfo:0xbefff7e8]---------------------------------------- Found0xbefff7e8 Page:0xbefdf000->0xbf000000(size=0x21000) Permissions:rwx Pathname:[stack] Offset(frompage):+0x207e8 Inode:0 gef>checksec [+]checksecfor'/home/pi/lab/max' Canary:No NXSupport:Yes PIESupport:No RPATH:No RUNPATH:No PartialRelRO:No FullRelRO:No

故障排除

为了更高效地使用GDB进行调试,很有必要了解某些分支/跳转的目标地址。 某些(较新的)GDB版本能够解析分支指令的地址,并能显示目标函数的名称。 例如,下面是缺乏这些功能的GDB版本的输出内容:

... 0x000104f8<+72>:bl0x10334 0x000104fc<+76>:movr0,#8 0x00010500<+80>:bl0x1034c 0x00010504<+84>:movr3,r0 ...

而下面则是提供了上述功能的GDB版本的的输出结果:

0x000104f8<+72>:bl0x10334<free@plt> 0x000104fc<+76>:movr0,#8 0x00010500<+80>:bl0x1034c<malloc@plt> 0x00010504<+84>:movr3,r0

如果您的GDB版本中没有提供这些功能,可以升级linux(前提是它们提供了更新的GDB),或者自己编译较新的GDB。 如果您选择自己编译GDB,可以使用以下命令:

cd/tmp wgethttps://ftp.gnu.org/gnu/gdb/gdb-7.12.tar.gz tarvxzfgdb-7.12.tar.gz sudoapt-getupdate sudoapt-getinstalllibreadline-devpython-devtexinfo-y cdgdb-7.12 ./configure--prefix=/usr--with-system-readline--with-python&&make-j4 sudomake-j4-Cgdb/install gdb--version

我使用上面提供的命令来下载、编译和运行Raspbian(jessie)上的GDB,并且没有遇到任何问题。同时,这些命令也将取代以前版本的GDB。如果您不想这样做的话,请跳过以单词install结尾的命令。此外,我在QEMU中模拟Raspbian时也是这样做的,不过这个过程非常耗时,大概需要几个小时,因为模拟环境的资源(CPU)有限。 我使用的GDB版本为7.12,但是你还可以使用更高的版本,为此可以点击此处查看其他版本。




【技术分享】利用GDB调试ARM代码
【技术分享】利用GDB调试ARM代码
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://azeria-labs.com/debugging-with-gdb-introduction/

Viewing all articles
Browse latest Browse all 12749