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