如何用ptrace拦截系统调用并替换为自定义代码
tracer能够改变系统调用参数,改变系统调用的返回值,甚至屏蔽特定的系统调用,将特定系统调用替换为自定义的helloworld函数。
ptrace系统调用的拦截替换原理为:利用ptrace函数使父进程跟踪子进程,当子进程在执行系统调用时,断住子进程,获取并保存当前系统调用的寄存器信息。然后备份ip寄存器指向的指令块A,替换为我们要执行的code指令块B,B执行后ip地址值恢复为指令块A及寄存器内容。
下面用例子实现上面的系统调用拦截替换功能。首先我们的目标是将一个打开文件的系统调用拦截,并替换为打印输出“helloworld”的字符串。
1.先得到自定义代码的二进制码
汇编实现文件ptrace_helloworld_asm.c
运行指令
gcc ptrace_helloworld_asm.c -o ptrace_helloworld_asm.o
./ptrace_helloworld_asm.o
objdump -d ptrace_helloworld_asm.o
目标二进制代码为:
2.ptrace拦截系统调用并替换
其中的CODE变量如下:
这样的执行结果如下:
(按任意键后)
源码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <sys/user.h>
#include <stdlib.h>
#define CODE \
"\xeb\x19\x5e\x48\xc7\xc0\x01\x00" \
"\x00\x00\x48\xc7\xc7\x02\x00\x00" \
"\x00\x48\xc7\xc2\x0c\x00\x00\x00" \
"\x0f\x05\xcc\xe8\xe2\xff\xff\xff" \
"\x48\x65\x6c\x6c\x6f\x20\x57\x6f" \
"\x72\x6c\x64\x0a"
#define REG_IP regs.rip
#define CODE_SIZE (sizeof(CODE)-1)
void putdata(pid_t pid, unsigned long addr, void *vptr, int len)
{
int count = 0;
long word;
while (count < len)
{
memcpy(&word, vptr+count, sizeof(word));
word = ptrace(PTRACE_POKEDATA, pid, addr+count, word);
count += sizeof(word);
}
}
/* 读pid进程addr地址处的数据 */
void getdata(pid_t pid, unsigned long addr, void *vptr, int len)
{
int i = 0, count = 0;
long word;
unsigned long *ptr = (unsigned long*)vptr;
while (count < len)
{
word = ptrace(PTRACE_PEEKDATA, pid, addr+count, NULL);
count += sizeof(word);
ptr[i++] = word;
}
}
int main()
{
pid_t child;
struct user_regs_struct regs;
char backup[CODE_SIZE+1];
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL); //系统调用了execve()
}
else {
wait(NULL);
// 获取被注入进程当前寄存器值
ptrace(PTRACE_GETREGS, child, NULL, ®s);
// 备份*ip寄存器指向的指令块
getdata(child, REG_IP, backup, CODE_SIZE);
// 替换为CODE指令块
putdata(child, REG_IP, CODE, CODE_SIZE);
// 恢复被注入进程的执行
ptrace(PTRACE_CONT, child, NULL, NULL);
printf("continue to execute the orginal process, press any key ..\n");
getchar();
putdata(child, REG_IP, backup, CODE_SIZE);
// 恢复被注入进程寄存器值
ptrace(PTRACE_SETREGS, child, NULL, ®s);
// detach,被注入进程恢复正常执行
ptrace(PTRACE_DETACH, child, NULL, NULL);
}
return 0;
}