我想当一个malloc发生在ptrace内部时设陷阱。
我已经能够在调用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_单步
重新启动停止的跟踪,如pTraceE.CONT,但安排跟踪在下一个条目停止或退出系统调用,或在执行一个指令之后。(如往常一样,被跟踪者也会在收到信号后停止工作。)`
所以我很确定我需要这个选择。从我读过的atutorial中,我可以单步执行;但是,没有一个输出是有意义的。尤其是如果我有某种输出语句。下面是一个简短的输出:
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
。为了钩住它,无论如何,你需要知道它的地址。唯一的方法是在可执行文件的符号表中查找malloc
,但是由于它是静态链接的,所以不能保证它有一个符号表。动态库必须有一个符号表,这样它才能被动态链接,但是由于静态链接的程序是完全链接的,所以它不需要一个符号表。
也就是说,许多静态链接的可执行文件都有一个符号表,因为没有符号表,调试几乎是不可能的。它们所占的额外大小不再是什么问题了。您可以使用nm
命令检查您可能希望与应用程序一起使用的任何可执行文件,以了解此问题可能对您产生的影响。
假设您有一个带有符号的可执行文件,下一个问题是如何实际读取程序中的符号。ELF格式不是那么简单,所以您可能想使用BFD(来自binutils)或libelf之类的格式。您也可以在命令行中使用nm
并手动为您的问题提供地址。
一旦有了malloc
地址,就可以通过在函数开头设置断点来使用ptrace
跟踪对它的调用。设置断点很简单。只需使用PTRACE_PEEKTEXT
读取函数的第一个字节,将其保存在某个地方,然后使用PTRACE_POKETEXT
将字节更改为0xCC
,这是英特尔x86断点指令(INT 3
)的操作码。然后,当调用malloc
时,跟踪进程将发送一个SIGTRAP
信号,您可以截取该信号。
那么你需要做的事情就更复杂了。您需要执行一系列步骤,如下所示:
读取寄存器和/或堆栈以找到malloc
的参数并记录它们。
使用PTRACE_POKETEXT
还原函数的原始第一个字节。
从堆栈顶部读取返回地址
在malloc
将返回的位置设置断点,保存旧值。
从程序计数器(EIP/RIP)中减去1(断点指令的大小)。
继续运行跟踪的进程。您截获的下一个SIGTRAP
将在malloc
返回之后。
通过读取返回寄存器(EAX/RAX)记录malloc
返回的值。
使用PTRACE_POKETEXT
删除返回地址处的断点
使用PTRACE_POKETEXT
将断点放回malloc
的开头
从程序计数器中减去1。
继续运行跟踪的进程。
也许有些事我还没想好,但那是你需要做的。
如果您只想使用自己编译的代码,那么有很多更简单的选项,比如使用glibc内置的对memory allocation hooks的支持。