file
实用程序时,它显示出我被剥夺了:ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
我有哪些选择如果运行该环境的环境不允许远程IDA Pro实例连接到
gdbserver
?简而言之:您所拥有的环境在允许您执行的操作中受到限制,但是您确实拥有可信赖的旧版gdb
和可反向工程的二进制文件。#1 楼
为了方便起见,我使用了惯例和初步说明。为了简洁起见,我整理了GDB的输出,因为它通常在每届会议开始时都会显示版权和其他信息。当我重现输出时,我将从第一个(gdb)
提示符行开始,或者从第一条真正的输出行开始,以防万一或自动执行命令。为了区分在GDB提示符下输入的命令,就像在现实世界中一样,它们将具有领先的
(gdb)
。对于shell命令,它根本不是前缀,或者是$
,因为它似乎是大多数unixoid系统上的惯例。当我使用诸如
vim
这样的特定命令作为编辑器时,您可以自由使用当然要使用您自己喜欢的编辑器。无论是emacs
还是nano
,我都不会判断;)入门
本节内容是关于设置
gdb
环境并开始该过程的。我还将为新手们介绍一些花絮。您应该知道的窍门
GDB有一个很好的提示,提示您在程序中断后或任何时候停止光标您正在执行步进或类似操作。
运行GDB命令后按RETURN(又名ENTER)将再次运行相同的命令。当您单步执行
step
或next
的代码,并且只想一个接一个地继续执行时,这很有用。只要命令是明确的,就可以将其缩写。对于某些经常使用的命令,存在一个特殊的速记,尽管存在歧义,但该优先级仍然优先:
b
(对于break
)(尽管bt
和backtrace
)c
或cont
的continue
(尽管catch
,call
等)n
的next
(尽管ni
和nexti
)您可以使用命令
call
从调试程序中调用实际的库函数甚至函数。这意味着您可以尝试行为或强制行为。您可以使用
gdbtui
或gdb -tui
启动GDB,以获取(可能更方便)更直观的文本用户界面。它在顶部显示源代码,在下面显示(gdb)
提示符。您还可以通过在layout src
提示符下执行命令(gdb)
来切换到此布局。GDB与许多shell一样,具有命令行完成功能,因此请使用Tab来发挥优势,并确保在每次使用时都使用
help
或help [keyword|command]
需要帮助。shell
允许您在Shell中执行命令,以便可以从GDB会话中运行命令。在开发过程中,示例为shell make
。print
,examine
和display
知道各种格式(/FMT
),可以使用这些格式使输出更易读。当进行源代码级调试时,可以使用C类型强制转换为显示值。想象一下
void *
后面的C字符串(在这种情况下GDB知道了这些符号)。只需将其转换为(char*)
并打印:print (char*)variable
。让进程运行
由于我们要动态分析二进制文件,因此需要首先启动它。
命令行
我们不仅可以通过传递二进制文件的路径,还可以传递我们想从其开始的参数来直接从命令行执行此操作。整个过程如下所示:
$ gdb --args ./exe argument1 argument2
很容易。然后,从
(gdb)
提示符下,您可以发出run
命令(简写为r
)以使用命令行上给定的参数运行./exe
。我更喜欢这种方法,但是您的里程可能会有所不同。GDB提示
启动GDB并在
(gdb)
提示符下使用file
命令加载二进制文件,然后使用run
命令以您要传递的参数启动它:$ gdb
(gdb) file exe
(gdb) run argument1 argument2
上面的一种替代方法是使用
set args
,例如:$ gdb
(gdb) file exe
(gdb) set args argument1 argument2
(gdb) run
在任何情况下,您还可以看到参数
run
将通过发出以下命令进入启动过程:(gdb) show args
btw:如果您想了解环境变量,请使用GDB内置的
help
命令作为help set
和help show
。指针:set environment VARNAME=VALUE
和show environment [VARNAME]
和unset environment VARNAME
。但是,为什么程序会因SIGSEGV
(段错误)而停止?好吧,我们还不知道,但是看起来这个小兽想要适当的治疗。由于我们练习防御性计算,因此我们不想运行任何我们不了解的东西,对吗?因此,让我们重新开始。如果这将是恶意软件,则如果它是VM guest虚拟机,则必须刷新计算机并重新安装或还原快照。
首先,我们将要运行
info
命令,如下所示:(gdb) info file
观察:
有两个重要的信息,与我们最相关的是线路说明:
Entry point: 0x400710
好了,所以我们可以在该处设置一个断点,然后使用我们喜欢的参数对过程进行
run
。.gdbinit
赢了但是等等,这已经很乏味了。没有简单的方法以某种方式自动执行这些步骤吗?事实上有。启动时,可以使用名为
.gdbinit
的文件向GDB发出命令。您还可以使用(shell)命令行上的-x
参数,使用GDB命令传递文件。如果我有很多项目,通常它们都在每个都有.gdbinit
文件的子文件夹中。旁注:
-nx
阻止了.gdbinit
的内容在启动时执行。所以我们知道我们要传递的参数以及断点的地址,这将转换为以下
.gdbinit
文件: file exe
break *0x400710
run argument1 argument2
启动
gdb
时得到的输出没有任何其他参数是:Breakpoint 1 at 0x400710
Breakpoint 1, 0x0000000000400710 in ?? ()
(gdb)
不错!但这看起来不一样...
组装和GDB
所以您习惯于看到要执行的下一行,然后看到可靠的旧
(gdb)
提示符。但是没有这样的事情。我们没有该二进制和符号的来源。 h!因此,我们考虑在(gdb)
提示符下闪烁的插入符号,并想知道该怎么做。不用担心,GDB也可以处理汇编代码。唯一的问题,在我看来,它默认为不方便的AT&T汇编语法。我更喜欢Intel风格,并且以下命令告诉GDB做到这一点:(gdb) set disassembly-flavor intel
显示汇编代码
怎么样给我们看汇编代码?好吧,类似于TUI模式(使用gdb的标签Wiki,请使用以下命令):
>
(gdb) layout asm
还将在概述中向您显示寄存器的内容。
让我们再次运行它
所以我们出于我们的目的而得到以下
.gdbinit
:(gdb) layout regs
当我们在不带参数的情况下启动
gdb
时,我们得到的结果是:甜。这样我们就可以在遍历代码时看到反汇编。我们可以在这里得出结论,但是当然还有更多的技巧要学习,所以为什么不走得更远呢。当我们刚启动程序时,它不是太有意义,但是在以后逐步执行代码时可能很有用。
顺便说一句,如果您希望节省屏幕资源
...并且视觉效果较差,那么从GDB 7.0开始,您可以使用:
file exe
break *0x400710
set disassembly-flavor intel
layout asm
layout regs
run argument1 argument2
在GDB版本之前,您可以通过设置自动
display
来模仿行为:set disassemble-next-line on
或更短的
disp/i $pc
其中/i
是格式,您可以通过思考来最好地记住它“指令”和$pc
是指令指针,也称为程序计数器,因此也称为pc
。也很容易理解
有时在逐步组装
regs
和asm
视图时,感到无聊。只需再次执行相应的layout
命令,即可将它们恢复到以前的状态:display/i $pc
在组装级别的“调试”
在汇编模式下,您习惯于从源代码级调试中使用的某些命令根本无法使用。这是有道理的,因为单个源代码行通常意味着十几条指令或更多指令。但是,
next
和step
命令具有指令级的对应项:nexti
(简写ni
...其他想到灌木丛的人吗?)stepi
(简写为si
)从上面的反汇编中我们知道:
(gdb) layout asm
(gdb) layout regs
,实际上,这是
main
函数。当然,如果您要对恶意软件进行反向工程,则应格外小心,但在这种情况下,请务必谨慎。因此,让我们在此地址(0x40f961
)上添加一个断点,而不是在入口点上添加一个断点:可以看到:0x40072d mov rdi,0x40f961
好吧,我们要关注
examine
,所以让我们使用x
进入它。进入函数时,我们会在指令指针处立即看到另一个call
:break *0x40f961
si
将我们引向一个调用call
的函数,现在为什么要这么做呢?(gdb) x/5i $pc
x/5i $pc
=> 0x40f961: push rbp
0x40f962: mov rbp,rsp
0x40f965: mov eax,0x0
0x40f96a: call 0x40911f
0x40f96f: pop rbp
好吧,这是Mellowcandle在另一个反调试器技巧中描述的在这里进行问答:
在Linux中检测跟踪
但是我们如何解决呢?我们必须将
call
覆盖为使用ptrace(PTRACE_TRACEME, ...)
或类似的代码调用call
的函数。这就是GDB有点笨拙的地方。但是我们可以使用
ptrace()
,为我们做魔术。让我们先检查指令字节:(gdb) x/5i $pc
x/5i $pc
=> 0x40911f: call 0x400b8c
0x409124: push rbp
0x409125: mov rbp,rsp
0x409128: push r10
0x40912a: push r11
nop
是一个调用指令,我们现在知道它的长度为5个字节。因此,让我们对此进行set
。 (0xe8
表示在程序计数器处检查10个字节-默认格式已为十六进制)。所以我们在
nop
处停止时做:0x400bab call 0x4006b8 <ptrace@plt>
并验证修补位置:
(gdb) x/10b $pc
x/10b $pc
0x40911f: 0xe8 0x68 0x7a 0xff 0xff 0x55 0x48 0x89
0x409127: 0xe5 0x41
非常好。我们现在可以执行它。
给定方法的替代方法
修补的替代方法:
x/10b $pc
后跟0x40911f
操作程序计数器(指令指针):
set {unsigned int}0x40911f = 0x90909090
或更明确的set {unsigned char}0x409123 = 0x90
set $pc+=5
操纵/修补正在运行的程序的更好的方法
Tavis Ormandy还有其他类似的方法(也是一种更好的方法)。我正在复制下面的
set $pc=$pc+5
宏(以防它从另一个地方脱机):(gdb) set write
(gdb) set {unsigned int}$pc = 0x90909090
(gdb) set {unsigned char}($pc+4) = 0x90
(gdb) set write off
同样,上面的脚本片段不是我写的,但是Tavis Ormandy撰写-请参见上面的链接。
这个小小的问答结束了。
评论
很棒的文章!只是一个注意事项:-q作为cmd arg摆脱了版权内容
–0xea
13年4月27日在8:02
尽管我非常了解gdb,但我学到了很多东西。谢谢 !
–恐怖
13年4月27日在9:16
这个真棒答案不只一次挽救了我的性命。谢谢,再次感谢。
– Hackndo
2015年9月7日在8:00
我见过的最好的gdb介绍。
– RichieHH
11月21日6:47
评论
unstrip是一种工具,可以尝试恢复已知库调用的丢失符号名称。b __libc_start_main