linux或者android样本ptrace反调试绕过
LD_PRELOAD是linux系统的一个环境变量,它可以影响程序运行时装载的动态链接库。
装载动态链接库,一般情况下按照以下顺序进行:
LD_PRELOAD>LD_LIBRARY_PATH>/etc/ld.so.cache>/lib>/usr/lib
这里通过编译一个包含ptrace()的动态链接库,并将其设为LD_PRELOAD环境变量,从而替换掉原来的ptrace()函数,从而达到绕过ptrace()反调试的目的。
linux
linux 系统gdb等调试器,都是通过ptrace系统调用实现。Android加固中,ptrace自身防止调试器附加是一种常用的反调试手段。
调试时一般需要手工在ptrace处下断点,通过修改ptrace返回值过掉反调试。下面提供另一种思路,降低手工操作复杂度:
测试代码(反调试程序)
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
void a()
{
if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1)
{
printf("don't trace me !!\n");
exit(1);
}
// normal execution
puts("hello girl.");
}
int main()
{
a();
return 0;
}
编写辅助库:
/* Type of the REQUEST argument to `ptrace.' */
enum __ptrace_request
{
PTRACE_TRACEME = 0,
#define PT_TRACE_ME PTRACE_TRACEME
};
long ptrace(enum __ptrace_request request, unsigned long pid,
void *addr, void *data)
{
return 0;
}
gcc -shared -fPIC helper.c -o helper.so 编译动态库。
设置环境变量:
export LD_PRELOAD=$PWD/helper.so
测试:
(gdb) r
Starting program: /home/kiiim/lab/a.out
hello girl.
During startup program exited normally.
以上,将helper.so放到APP的libs目录,并设置LD_PRELOAD环境变量,即可绕过所有ptrace的反调试。
也可以patch一些其它函数,如open函数我们需要patch后不影响其正常功能:
#define _GNU_SOURCE
#include <dlfcn.h>
typedef int (*_open)(const char *pathname, int flags);
int open(const char *pathname, int flags, ...)
{
/* Some evil injected code goes here. */
_open fake_open;
fake_open = (_open)dlsym(RTLD_NEXT,"open");
return fake_open(pathname,flags);
}
android
ptrace 反调试的原理是进程只能被一个进程附加。
当一个应用使用了 ptrace 反调试的时候,以 objection 为例,附加时候会出现这种现象
查看进程 /proc/self/status 目录,会看到 TracerPid 不为0,其值为附加它的父进程 pid ,这里是 zygote进程。

它的原理也很,加上 ptrace(PTRACE_TRACEME); 就行。
过掉它的方法很多,下面介绍使用 ptrace 怎么过掉 ptrace :
1、使用 ptrace 附加 zygote 进程。
2、拦截 zygote 的 fork 调用,在 fork 子进程时候获取当前子进程名称,判断是不是我们想要的那个应用,如果是,就保存子进程 pid
3、获取到子进程 pid 后,再拦截子进程的系统调用,判断此系统调用是不是 ptrace,并且参数是 PTRACE_TRACEME
4、拦截到指定系统调用后,修改调用参数,让 ptrace(PTRACE_TRACEME); 执行失败。
// 拦截 zygote 进程的 fork
if (ptrace(PTRACE_SETOPTIONS, pid, (void *)1, (void *)(PTRACE_O_TRACEFORK))) {
printf("FATAL ERROR: ptrace(PTRACE_SETOPTIONS, ...)");
return -1;
}
ptrace(PTRACE_CONT, pid, (void *)1, 0);
int t;
int stat;
int child_pid = 0;
for (;;) {
t = waitpid(-1, &stat, __WALL | WUNTRACED);
// 判断当前 fork 程序是不是我们指定的应用
if (t != 0 && t == child_pid) {
if (debug > 1)
printf(".");
char fname[256];
sprintf(fname, "/proc/%d/cmdline", child_pid);
int fp = open(fname, O_RDONLY);
if (fp < 0) {
ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
continue;
}
read(fp, fname, sizeof(fname));
close(fp);
if (strcmp(fname, appname) == 0) {
if (debug)
printf("zygote -> %s\n", fname);
// detach from zygote
ptrace(PTRACE_DETACH, pid, 0, (void *)SIGCONT);
// now perform on new process
pid = child_pid;
break;
} else {
ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
continue;
}
}
if (zygote) {
// 获取到指定进程pid后,拦截它的system_call
ptrace(PTRACE_SYSCALL, pid, 0, 0);
while (1) {
waitpid(pid, NULL, 0);
//系统调用前修改调用参数
hookSysCallBefore(pid);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, NULL, 0);
// 修改系统调用结果
hookSysCallAfter(pid);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
}
void hookSysCallBefore(pid_t pid) {
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
if (sysCallNo == __NR_ptrace) {
regs.ARM_r0 = 0; // 伪造一个成功的返回值
ptrace(PTRACE_SETREGS, pid, NULL, regs);
printf("__NR_ptrace: %d\n", regs.ARM_r0);
}
}
void hookSysCallAfter(pid_t pid) {
struct pt_regs regs;
int sysCallNo = 0;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
sysCallNo = getSysCallNo(pid, ®s);
if (sysCallNo == __NR_ptrace) {
regs.ARM_r0 = PTRACE_PEEKTEXT;
ptrace(PTRACE_SETREGS, pid, NULL, regs);
printf("__NR_ptrace: %ld\n", regs.ARM_r0);
}
}
最后效果如下,可以看到 TracerPid = 0 了
参考资料
https://blog.csdn.net/weixin_28883589/article/details/116973476
https://www.cnblogs.com/gm-201705/p/9864051.html
https://bbs.pediy.com/thread-212404.htm
https://www.dazhuanlan.com/2020/03/31/5e824a09dbf9b/
更多推荐


所有评论(0)