背景故事:在
strace
下运行程序时,我注意到 '/dev/urandom' 正在被 open
' 编辑。 我想知道这个调用来自哪里(它不是程序本身的一部分,它是系统的一部分)。
因此,使用 gdb,我尝试在发出
catch syscall open
调用时中断(使用 open
)程序执行,这样我就可以看到回溯。 问题是 open
被调用了 alot,大概有几百次,所以我无法缩小打开 /dev/urandom 的具体调用范围。 我应该如何缩小特定呼叫的范围?有没有一种方法可以按参数进行过滤,如果是的话,我该如何为系统调用执行此操作?
任何建议都会有帮助——也许我的做法都是错误的。
GDB 是一个非常强大的工具,但有一点学习曲线。
基本上,您想设置一个条件断点。
首先使用 -i 标志来 strace 或 objdump -d 来查找 open 函数的地址,或者更实际地在到达那里的链中查找某些内容,例如在 plt 中。
在该地址设置一个断点(如果您有调试符号,您可以使用它们来代替,省略*,但我假设您没有 - 尽管如果没有其他的话,您很可能将它们用于库函数。
break * 0x080482c8
接下来你需要设置条件
(理想情况下,您可以将字符串参数与所需的字符串进行比较。我在尝试的前几分钟内并没有让它发挥作用)
希望我们可以假设该字符串是程序中某处或其加载的库之一的常量。 您可以查看 /proc/pid/maps 以了解加载的内容和位置,然后使用 grep 验证该字符串实际上在文件中,使用 objdump -s 查找它的地址,然后使用 gdb 验证您是否已加载实际上是通过将映射中地址的高位部分与文件中的低位部分相结合来在内存中找到它的。 (编辑:在可执行文件上使用 ldd 可能比查看 /proc/pid/maps 更容易)
接下来,您需要了解您正在使用的平台的 abi,特别是如何传递参数。 我最近一直在研究arm,这非常好,因为前几个参数只是进入寄存器r0,r1,r2...等。x86有点不太方便 - 似乎它们在堆栈上,即* ($esp+4),*($esp+8),*($esp+12)。
因此,假设我们在 x86 上,并且我们想要检查 esp+4 中的第一个参数是否等于我们为试图捕获它传递的常量找到的地址。 只是,esp+4 是一个指向 char 指针的指针。 所以我们需要取消引用它来进行比较。
cond 1 *(char **)($esp+4)==0x8048514
然后你可以输入run
并期待最好的结果 如果您捕获了断点条件,并且使用信息寄存器和 x 命令来检查内存似乎是正确的,那么您可以使用 return 命令来渗透备份调用堆栈,直到找到您识别的内容。
break open if strcmp($rdi,"/dev/urandom") == 0
可能可以完成这项工作。
按照
Chris 的回答,这是最终让我得到我想要的东西的过程: (我正在尝试查找哪些函数正在调用“/dev/urandom”上的
open
系统调用)
在可执行文件上使用 ldd 来查找加载的库
grep
rdi
break open if $rdi == _addr_
bt
另请注意,您可能需要将 rdi 寄存器中的数据转换为 (const char*) 以确保正确的行为。
总结:
b open if (int)strcmp((const char*)$rdi,"/dev/urandom") != 0
另一种方法是 catch syscall open 并测试 $rdi ,这样您就不需要忍受名为 open 的函数的大量破坏,也不需要费心去搜索真正的系统调用 open 的符号。但是,如果您希望程序进入下一个检查点,您需要在捕获后正确处理系统调用。