我正在尝试读取子进程的堆栈,但没有成功。我知道可以使用ptrace
,但是ptrace
的界面允许您一次只能读取一个单词,而我正在尝试扫描堆栈的更大部分。
我还尝试在第一次使用 ptrace 附加到它之后从文件/proc/$pid/mem
中提取的堆栈边界中读取(如建议的那样)/proc/$pid/maps
这里)但读取不断失败(即使以 root 身份运行),尽管在尝试从进程的不同部分(例如堆)读取时相同的代码会成功。
我究竟做错了什么?还有其他选择吗?
答案1
ptrace
的界面允许您一次只能读取一个单词,而我正在尝试扫描堆栈的较大部分
好吧,那就用一个循环吧。老实说,我不明白这如何构成 的问题ptrace
,我一直使用它来远程访问进程。
我用这样的东西:
static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
static int const align = sizeof(long) - 1;
while (n)
{
size_t todo = MIN(n, sizeof(long) - (src & align));
long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
if (errno)
{
perror("ptrace_peektext (memcpy_from_target)");
return -1;
}
memcpy(dest, (char *)&data + (src & align), todo);
dest += todo; src += todo; n -= todo;
}
return 0;
}
答案2
这是另一种策略,可能需要调整,但对于大数据块应该更有效。这个想法是在远程进程中执行系统调用以检索堆栈内容。它将需要特定的架构代码,但如果您只针对 x86 / x86_64,那么应该不会有太多麻烦。
- 创建一个命名管道,例如
"/tmp/fifo"
在您的调用进程中。 - 单步进入跟踪的进程,直到它从系统调用返回,使用
PTRACE_SYSCALL
单步执行、waitpid()
等待和PTRACE_GETREGS
/PTRACE_PEEKTEXT
检查当前执行的操作码。 - 备份远程进程的寄存器及其堆栈的一小部分区域。
- 通过使用您自己的数据覆盖其堆栈来在远程进程上执行系统调用:
open("/tmp/fifo")
、write()
堆栈内容、close()
描述符。 - 恢复远程进程的状态。
- 从调用进程中读取 fifo 数据。
命名管道可能有更优雅的替代方案,但我现在想不到。我只使用系统调用的原因是,由于各种安全保护措施,远程代码注入在现代系统上相当不可靠。缺点是它会挂起,直到远程进程执行系统调用(这对于某些主要进行计算的程序来说可能是一个问题)。
您可以在以下位置看到一些实现大部分工作的免费代码这个源文件。欢迎对代码提供反馈!
答案3
另一个建议。
当/如果它被主 Linux 内核树接受时,您将能够使用 Christopher Yeoh 的交叉内存连接修补。请参阅文档进程_vm_readv例如。
答案4
你可以尝试堆栈。它使用 ptrace,就像所有其他成功的“读取另一个进程的堆栈”程序一样。我无法使用 /proc/$pid/mem 读取来让程序工作。我相信你不能那样做,尽管从逻辑上讲,你应该这样做。