如何监视放入标准输出缓冲区的内容,并在管道中放置特定字符串时中断?

Ros*_*ers 17 c++ linux gdb

在Linux中,使用C/C++代码,使用gdb,如何添加gdb断点来扫描传入的字符串以便打破特定的字符串?

我无法访问特定库的代码,但是我想在该库将特定字符串发送到标准输出时立即中断,这样我就可以回到堆栈并调查我调用库的代码部分.当然我不想等到缓冲区刷新发生.可以这样做吗?也许是例行公事libstdc++

Ant*_*ony 24

这个问题可能是一个很好的起点:我怎样才能在gdb中的"打印到终端上的东西"中加上断点?

因此,无论什么时候写入stdout,你都可以至少破解.该方法基本上涉及在write系统调用上设置一个断点,其条件是第一个参数是1(即STDOUT).在注释中,还提示如何检查write调用的字符串参数.

x86 32位模式

我想出了以下内容并使用gdb 7.0.1-debian进行了测试.它看起来效果很好.$esp + 8包含指向传递给字符串的内存位置的指针write,因此首先将其转换为积分,然后转换为指针char.$esp + 4包含要写入的文件描述符(1表示STDOUT).

$ gdb break write if 1 == *(int*)($esp + 4) && strcmp((char*)*(int*)($esp + 8), "your string") == 0
Run Code Online (Sandbox Code Playgroud)

x86 64位模式

如果你的过程是在X86-64模式下运行,则该参数是通过暂存寄存器传递%rdi%rsi

$ gdb break write if 1 == $rdi && strcmp((char*)($rsi), "your string") == 0
Run Code Online (Sandbox Code Playgroud)

请注意,由于我们在堆栈上使用临时寄存器而不是变量,因此会删除一级间接.

变种

以上功能strcmp可以在上面的代码段中使用:

  • strncmp如果要匹配n正在写入的字符串的第一个字符数,则非常有用
  • strstr 可用于查找字符串中的匹配项,因为您无法始终确定要查找的字符串位于通过该函数写入的字符串的开头write.

编辑:我很喜欢这个问题并找到它的后续答案.我决定写一篇关于它的博客文章.


Cir*_*四事件 5

catch+strstr条件

这种方法很酷的一点是它不依赖于使用的 glibc write:它跟踪实际的系统调用。

此外,它对printf()缓冲更具弹性,因为它甚至可以捕获跨多个printf()调用打印的字符串。

x86_64 版本:

define stdout
    catch syscall write
    commands
        printf "rsi = %s\n", $rsi
        bt
    end
    condition $bpnum $rdi == 1 && strstr((char *)$rsi, "$arg0") != NULL
end
stdout qwer
Run Code Online (Sandbox Code Playgroud)

测试程序:

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    write(STDOUT_FILENO, "asdf1", 5);
    write(STDOUT_FILENO, "qwer1", 5);
    write(STDOUT_FILENO, "zxcv1", 5);
    write(STDOUT_FILENO, "qwer2", 5);
    printf("as");
    printf("df");
    printf("qw");
    printf("er");
    printf("zx");
    printf("cv");
    fflush(stdout);
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

结果:休息时间:

  • qwer1
  • qwer2
  • fflush. 前一个printf实际上没有打印任何东西,它们被缓冲了!该writesyacall只是发生在fflush

笔记:

  • $bpnum感谢 Tromey:https ://sourceware.org/bugzilla/show_bug.cgi ? id = 18727
  • rdi:x86_64中包含Linux系统调用编号的寄存器,1用于write
  • rsi: 系统调用的第一个参数,因为write它指向缓冲区
  • strstr: 标准 C 函数调用,搜索子匹配项,如果未找到则返回 NULL

在 Ubuntu 17.10、gdb 8.0.1 中测试。

斯特拉斯

如果您感觉互动,另一种选择:

setarch "$(uname -m)" -R strace -i ./stdout.out |& grep '\] write'
Run Code Online (Sandbox Code Playgroud)

示例输出:

[00007ffff7b00870] write(1, "a\nb\n", 4a
Run Code Online (Sandbox Code Playgroud)

现在复制该地址并将其粘贴到:

setarch "$(uname -m)" -R strace -i ./stdout.out |& grep -E '\] write\(1, "a'
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是可以使用常用的 UNIX 工具来操作strace输出,并且不需要很深的 GDB-fu。

解释: