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, &regs);
    sysCallNo = getSysCallNo(pid, &regs);
 
    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, &regs);
    sysCallNo = getSysCallNo(pid, &regs);
 
    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/

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐