【发布时间】:2014-09-14 02:02:30
【问题描述】:
我试图在 ptrace 内部发生 malloc 时进行陷阱。
我已经能够在调用 malloc 时进行挂钩,所以我应该能够通过一些自定义模块capture;但是,那是在使用动态库时(不使用 -static 标志)。
有没有一种方法可以让我以通用的方式做到这一点?
如果我们查看以下程序集,我知道我需要捕获的位置。我只是不知道如何:
.file "new.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $4, %edi
call malloc ;<= TRAP HERE
movq %rax, -8(%rbp)
movl $.LC0, %edi
call puts
movq -8(%rbp), %rax
movq %rax, %rdi
call free
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]"
.section .note.GNU-stack,"",@progbits
来自ptrace(2),
PTRACE_SINGLESTEP
像 PTRACE_CONT 一样重新启动停止的跟踪对象,但安排跟踪对象分别在系统调用的下一个入口或退出时停止,或在执行单个指令后停止。 (被跟踪者也会像往常一样在收到信号后停止。)`
所以我相当肯定我需要那个选项。从我读过的tutorial,我可以单步执行;但是,没有任何输出有任何意义。特别是如果我有某种输出语句。这是有输出时的简短输出:
RIP: 7ff6cc4387c2 Instruction executed: 63158b48c35d5e41
RIP: 7ff6cc4387c4 Instruction executed: 2f0663158b48c35d
RIP: 7ff6cc4387c5 Instruction executed: 2f0663158b48c3
RIP: 400c38 Instruction executed: 7500e87d83e84589
RIP: 400c3b Instruction executed: b93c7500e87d83
RIP: 400c3f Instruction executed: ba00000000b93c75
RIP: 400c41 Instruction executed: ba00000000b9
RIP: 400c46 Instruction executed: be00000000ba
RIP: 400c4b Instruction executed: bf00000000be
RIP: 400c50 Instruction executed: b800000000bf
RIP: 400c55 Instruction executed: fe61e800000000b8
RIP: 400c5a Instruction executed: bafffffe61e8
RIP: 400ac0 Instruction executed: a68002015a225ff
RIP: 400ac6 Instruction executed: ff40e90000000a68
RIP: 400acb Instruction executed: 9a25ffffffff40e9
RIP: 400a10 Instruction executed: 25ff002015f235ff
RIP: 400a16 Instruction executed: 1f0f002015f425ff
RIP: 7ff6ccf6c160 Instruction executed: 2404894838ec8348
RIP: 7ff6ccf6c164 Instruction executed: 244c894824048948
RIP: 7ff6ccf6c168 Instruction executed: 54894808244c8948
RIP: 7ff6ccf6c16d Instruction executed: 7489481024548948
....
Hello world
....
为什么 IP 的价值会发生如此剧烈的变化?这是因为我事先处于内核模式吗?
此外,执行指令的输出似乎没有正确排列(就像它被分割成多行一样),但这可能只是我试图在没有的地方放置一个模式。
无论如何,这是我运行到该输出的程序: 警告,讨厌的 C\C++ 混合
#include <iostream>
#include <sys/ptrace.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <iomanip>
using namespace std;
///for when dealing with different archectures.
#if __WORDSIZE == 64
#define REG(reg) reg.orig_rax
#else
#define REG(reg) reg.orig_eax
#endif
int main()
{
pid_t child;
long orig_eax;
const int long_size = sizeof(long);
child = fork();
long ins;
if(child == 0)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("./dummy", "dummy", NULL);
}
else
{
ptrace(PTRACE_ATTACH, child, NULL, NULL);
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
int status;
union u {
long val;
char chars[long_size];
}data;
struct user_regs_struct regs;
int start = 0;
long ins;
while(1)
{
wait(&status);
if(WIFEXITED(status))
break;
ptrace(PTRACE_GETREGS,child, NULL, ®s);
ins = ptrace(PTRACE_PEEKTEXT, child, regs.rip, NULL);
cout << "RIP: " << hex << regs.rip << " Instruction executed: " << ins << endl;
ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
}
ptrace(PTRACE_DETACH, child, NULL, NULL);
}
}
如果需要任何其他信息,请告诉我。我知道我有点冗长,但如果这个问题得到回答,希望它会为下一个尝试学习 ptrace 的人提供足够的信息。
【问题讨论】:
-
malloc(3)是 C 库的函数而不是系统调用。 -
@BSH 正确,这是否意味着我不能用 ptrace 捕获它?编辑:重新阅读手册页,它没有指定它只适用于系统调用,这可能就是为什么有单步功能以及 peek 和 poke 的原因。
-
单步执行时,程序执行完每条指令后都会停止。但是,当进程处于内核模式时,不会发生这种跟踪(单步执行)。这就是文档仅在系统调用进入和退出时停止的意思。请注意,系统调用有一个小存根,它在切换到内核模式之前在用户模式下执行,因此将跟踪这个存根中的指令。你想通过挂钩
malloc来完成什么? -
@RossRidge 挂钩 malloc 时,我可以(理论上)让新的 malloc 进行虚假的系统调用,该调用会被跟踪器捕获。问题是,如果在编译时使用了
-static标志,那么我将无法判断是否\何时调用了 malloc。 -
为什么要让
malloc被跟踪器捕获?为什么你想知道malloc是否或何时被调用?
标签: c assembly malloc glibc ptrace