C++ 我如何监控什么';当特定管柱沉积在管道中时,是否将其放入标准输出缓冲区并断开?

C++ 我如何监控什么';当特定管柱沉积在管道中时,是否将其放入标准输出缓冲区并断开?,c++,linux,gdb,conditional-breakpoint,C++,Linux,Gdb,Conditional Breakpoint,在Linux中,使用C/C++代码,使用gdb,如何添加gdb断点来扫描传入字符串,以便在特定字符串上中断 我没有访问特定库的代码的权限,但我想在该库向standard out发送特定字符串时立即中断,以便我可以返回堆栈并调查调用库的代码部分。当然,我不想等到缓冲区刷新发生。这能做到吗?也许是libstdc++中的一个例程?这个问题可能是一个很好的起点: 所以你至少可以在有东西写入stdout时中断。该方法主要涉及在write系统调用上设置断点,条件是第一个参数为1(即STDOUT)。在注释中,

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


我没有访问特定库的代码的权限,但我想在该库向standard out发送特定字符串时立即中断,以便我可以返回堆栈并调查调用库的代码部分。当然,我不想等到缓冲区刷新发生。这能做到吗?也许是libstdc++中的一个例程?

这个问题可能是一个很好的起点:

所以你至少可以在有东西写入stdout时中断。该方法主要涉及在
write
系统调用上设置断点,条件是第一个参数为
1
(即STDOUT)。在注释中,还有一个提示,说明如何检查
write
调用的字符串参数

x86 32位模式 我提出了以下内容,并用GDB7.0.1-debian对其进行了测试。它似乎工作得很好
$esp+8
包含一个指向传递给
write
的字符串的内存位置的指针,因此首先将其转换为整数,然后转换为指向
char
的指针<代码>$esp+4包含要写入的文件描述符(1表示标准输出)

x86 64位模式 如果进程在x86-64模式下运行,则参数将通过暂存寄存器
%rdi
%rsi

$ gdb break write if 1 == $rdi && strcmp((char*)($rsi), "your string") == 0
请注意,由于我们在堆栈上使用的是暂存寄存器而不是变量,因此删除了一级间接寻址

变体 除了strcmp以外的函数可以在上述代码段中使用:

  • 如果要匹配正在写入的字符串的第一个
    n
    字符数,则此选项非常有用
  • 可以用于查找字符串中的匹配项,因为您不能始终确定要查找的字符串是否位于通过
    write
    函数写入的字符串的开头

编辑:我喜欢这个问题,并找到了它的后续答案。我决定对此做些调查。

安东尼的回答太棒了。根据他的回答,我在Windows(x86-64位Windows)上尝试了另一种解决方案。我知道这个问题是针对Linux上的GDB的,但是我认为这个解决方案是对这类问题的补充。这可能对其他人有帮助

Windows上的解决方案 在Linux中,调用
printf
将导致调用API
write
。由于Linux是一个开源操作系统,我们可以在API中进行调试。但是,Windows上的API不同,它提供了自己的API。由于Windows是商业非开源操作系统,因此无法在API中添加断点

但是VC的一些源代码是与visualstudio一起发布的,因此我们可以在源代码中找到最终调用
WriteFile
API并在那里设置断点的位置。在对示例代码进行调试后,我发现
printf
方法可能导致调用
\u write\u nolock
,其中调用了
WriteFile
。该功能位于:

your_VS_folder\VC\crt\src\write.c
原型是:

/* now define version that doesn't lock/unlock, validate fh */
int __cdecl _write_nolock (
        int fh,
        const void *buf,
        unsigned cnt
        )
与Linux上的
write
API相比:

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count); 
注意:这里仍然有一个问题,我测试了两种不同的方法将内容写入标准输出:
printf
std::cout
printf
会立即将所有字符串写入
\u write\u nolock
函数。但是,
std::cout
只将字符逐个传递给
\u write\u nolock
,这意味着API将被调用
strlen(“您的字符串”)
次。在这种情况下,该条件不能永远激活

Win32解决方案 当然,我们可以使用与Anthony提供的方法相同的方法:通过寄存器设置断点的条件

对于Win32程序,解决方案与Linux上的
GDB
几乎相同。您可能会注意到在
\u write\u nolock
的原型中有一个装饰
\u cdecl
。这种呼叫约定意味着:

  • 参数传递顺序是从右到左
  • 调用函数将从堆栈中弹出参数
  • 名称修饰约定:名称的前缀为下划线字符(389;)
  • 未执行案例翻译
有一个描述。还有一个用于显示微软网站上的寄存器和堆栈的。结果是可以找到的

那么设置断点的条件就很容易了:

  • \u write\u nolock
    中设置断点
  • 限制条件:

    fh == 1 && strstr((char *)buf, "Hello World") != 0
    
    *(int *)($esp + 4) == 1 && strstr(*(char **)($esp + 8), "Hello") != 0
    
    $rcx == 1 && strstr((char *)$rdx, "Hello") != 0
    
  • 这与Linux上的方法相同。第一个条件是确保字符串被写入
    stdout
    。第二个是匹配指定的字符串

    x64溶液 从x86到x64的两个重要功能是64位寻址功能和一组供通用的16个64位寄存器。随着寄存器的增加,x64仅用作调用约定。前四个整数参数在寄存器中传递。在堆栈上传递参数5及更高

    你可以参考微软网站上的页面。四个寄存器(按从左到右的顺序)是
    RCX
    RDX
    R8
    R9
    。因此,很容易限制条件:

    fh == 1 && strstr((char *)buf, "Hello World") != 0
    
    *(int *)($esp + 4) == 1 && strstr(*(char **)($esp + 8), "Hello") != 0
    
    $rcx == 1 && strstr((char *)$rdx, "Hello") != 0
    
  • \u write\u nolock
    中设置断点

    注意:与上面的可移植解决方案不同,我们可以只将断点的位置设置为函数,而不是函数的第一行。原因是所有寄存器都已在入口初始化

  • 限制条件:

    fh == 1 && strstr((char *)buf, "Hello World") != 0
    
    *(int *)($esp + 4) == 1 && strstr(*(char **)($esp + 8), "Hello") != 0
    
    $rcx == 1 && strstr((char *)$rdx, "Hello") != 0
    
  • 我们需要演员阵容和替补阵容的原因
    #include<string.h>
    #include<stdio.h>
    #include<stdarg.h>
    
    #define MAX_SIZE 0xFFFF
    
    int printf(const char *format, ...){
        char target_str[MAX_SIZE];
        int i=0;
    
        va_list args1, args2;
    
        /* RESOLVE THE STRING FORMATING */
        va_start(args1, format);
        vsprintf(target_str,format, args1);
        va_end(args1);
    
        if (strstr(target_str, "Hello World")){ /* SEARCH FOR YOUR STRING */
           i++; /* BREAK HERE */
        }   
    
        /* OUTPUT THE STRING AS THE PROGRAM INTENTED TO */
        va_start(args2, format);
        vprintf(format, args2);
        va_end(args2);
        return 0;
    }
    
    int puts(const char *s) 
    {   
       return printf("%s\n",s);
    }