zoukankan      html  css  js  c++  java
  • 解决Android加固多进程ptrace反调试的思路整理

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481

    一、Android多进程反调试的原理代码

    当ptrace附加目标进程时出现失败,正常情况下有理由认为目标进程已经被别的进程ptrace附加了。像梆梆加固就是采用的这种反调试的手法,效果还是不错的。

    /******************************************************
    
    // 附加目标进程失败,说明目标进程已经被调试
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {  
    	printf("DEBUGGING... Bye
    ");  
    	return 1;  
    }  
    
    ******************************************************/
    
    
    // 多进程ptrace反调试
    void anti_ptrace(void)  
    {  
        pid_t child;  
    
        // 创建子进程
        child = fork();  
        if (child) 
    	{
    		// 返回在父进程中
    		wait(NULL);  
    	}  
        else 
    	{
    		// 获取父进程的pid
    		pid_t parent = getppid();  
    		// ptrace附加父进程
    		if (ptrace(PTRACE_ATTACH, parent, 0, 0) < 0)
    		{
    		while(1);  
    		sleep(1);  
    		} 
    
    		// 释放附加的进程
    		ptrace(PTRACE_DETACH, parent, 0, 0);  
    		// 结束当前进程
    		exit(0);  
        }  
    }  

    二、Android多进程反调试的解决方法整理


    A.方法一

    对于梆梆,之前有文章讨论可以attach到其binder线程上通过gcore、dd来dump内存中的dex
    但对于其最新版本,当使用gdb attach到主进程的任一线程上时要么permission denied要么会退出


    对其实现机理进行一下分析
    1.      主进程fork子进程c1,c1 fork子进程c2;
    2.      c1会ptrace attach到主进程的主线程与GC线程(其也会操作memory,所以需要用ptrace方法保护),

    这样当试图ptrace attach时,会有permission denied;
    3.      主进程为了能让c1这个子进程跟踪需要调用prctloption为SET_DUMPABLE。但这个API比较危险,

    其效果与AndroidManifest文件中debuggable选项设为true等价。如果不调用这个api,子进程是不能ptrace父进程的
    4.      c1也会ptrace到c2进程,来对其进行保护;
    5.      这个三个进程间彼此用pipe进行通信
    6.      c1会向主进程内存空间注入大约52字节的数据应为密钥
    7.      之后c1进入pipe读阻塞sleep
    8.      c2使用inotify监控主进程的每个clone process的mem与pagemap,于是当gdb、dd试图dump内存时mem的access事件被触发

    三个进程集体退出导致内存dump不完整;这边漏了一个,同时c2会不断打开主进程的各个status文件看其tracerpid是否为0非0则被attach--退出
    9.      c2并monitor主进程的task目录,来判断是否有新的clone process现有的已消亡来更新monitor的select loop的fd集合

    另外,梆梆还hook了write方法来阻止在进程内部将header为dex的magic code的内容写入磁盘
    破解办法
    hook  write那个好破解 ,字节流中转出来到其他进程就好了 
    或者写的时候,把dex,改成xxx就好啦。


    上文中可以看出主进程的孙子进程起到防gdb/gcore的关键,那么如何绕过它,依然使用gdb去dump出完整dex呢?
    开始时,我是重新编译了一个rom屏蔽与重定向了相关API,可以dump出来,显然这个方法相对烦躁一点。


    下面介绍一种在目前梆梆的版本上,更为简单粗暴的方法
    1. 首先通过ps找出孙子进程的pid,记为pid3;
    2. 查看/proc/<pid3>/task找出孙子进程所有的thread,通常是3个,并记录下他们的tid
    3. 使用kill -19 <tid> 将这些孙子线程挂起
    4. gdb 主进程,顺利gcore 。




    kill命令的用法连接

    http://www.cnblogs.com/peida/archive/2012/12/20/2825837.html

    http://fredrick8.blog.163.com/blog/static/11734560120126213347827/


    提供Android多进程反调试的方法的大神eewolf的链接

    http://bbs.pediy.com/showthread.php?t=198778

    http://bbs.pediy.com/showthread.php?t=198995





    prctl函数的简单说明:

    http://blog.chinaunix.net/uid-23145525-id-4026304.html

    http://www.cppblog.com/beautykingdom/archive/2009/11/08/100419.aspx




    B.方法二


    原理多进程都是通过fork出来的,因此我们修改/bionic/libc/bionic/fork.c里面的fork函数来使得目标进程fork失败,代码如下:



    作者王正飞给出的思路是正确的,但是他给出的过滤代码是有问题的,本来是想编译Android源码试试的,时间比较急没来得及去测试代码了,正确的过滤代码我已经在下面给出了,其中"com.xxxx.yyy"为需要过滤的脱壳的apk的包名,后面我会测试一下,检查一下有没有问题再修改和补充:

    // 修改Android系统的/bionic/libc/bionic/fork.c里面的fork函数,过反调试
    
    #define BUF_LENTH 1024
    
    // 进行脱壳apk进程的过滤
    int is_target_apk_pid() 
    {
    	char szBuffer[BUF_LENTH] = {0};
    	char szCmd[BUF_LENTH] = {0};
    	
    	// 格式化字符串得到/proc/pid/cmdline
    	sprintf(szCmd, "/proc/%d/cmdline", getpid());
    	
    	// 获取当前pid进程的文件名称字符串(比较粗糙)
    	int fd = open(szCmd, O_RDONLY);
    	if (read(fd, szBuffer, BUF_LENTH)) 
    	{
    		//......
    	}
    	else 
    	{
    		//......
    	}
    	close(fd);
    	
    	// 判断当前pid进程是否是需要的过滤的脱壳apk进程
    	//memset(szCmd, 0, sizeof(szCmd));
    	//sprintf(szCmd,"/data/data/%s", szBuffer);
    	if (strstr(szBuffer, "com.xxxx.yyy")) 
    	{
    		return 1;
    	}
    	
    	return 0;
    }
    
    
    // 调用脱壳apk进程的过滤函数进行相应的处理
    int fork(void) 
    {
    	int ret;
    	
    	// 添加的代码
    	if (is_target_apk_pid())
    	{
    		return -1;
    	}
    }
    
    // 重新编译Android的fork函数所在的模块,定制ROM

    结果修改fork函数后目标APK只剩下一个进程了使用gdb attach正常,效果如下:



    思路扩展
    这个get_target2函数就是作为过滤条件用的可以考虑结合注入+HOOK


    作者王正飞的方法讨论链接:

    http://bbs.pediy.com/showthread.php?t=211548


    好了,可以睡觉了,晚安~










  • 相关阅读:
    SpringMVC框架
    Spring框架
    Test_Shop项目开发练习
    MyBatis动态传参
    存储过程
    游标和触发器
    远程连接Linux系统管理
    安装Linux虚拟机
    request_html模块(下)
    request_html模块(上)
  • 原文地址:https://www.cnblogs.com/csnd/p/11800658.html
Copyright © 2011-2022 走看看