【转】andorid ndk anti-debug
思考
之前研究了下如何调试和尝试反一个别人加密的东西, 所以现在的体会就是:
其实重点不是你如何加密, 重点是如何不让别人知道你怎么加密的
因为像这种自己加密的资源运行的时候自己解密之后拿来用
的程序, 我甚至根本不用关心你到底怎么加密, 加密算法是啥, 我只需要知道, 你解密完了之后, 那个资源的内存块在哪, 写个dumper就全拿到了;
加密并不能防止被破解, 只是增加破解的难度和门槛, 加密解密是一个相互博弈的过程
把资源加个密, 那么对于那些技术比较初级, 又想简单拷贝的人, 他们就拿你没办法了; 但是对于那些懂一点原理懂一点IDA的人, 并没有什么卵用
那么好, 现在我要研究一下, 怎么样再增加那么一点点门槛
之前解密的一个重要前提就是调试, 所以, 最直接想到的增加的门槛就是反调试
.
测试
先建个android 工程, 并加入native支持
先打出pid和父pid出来看看
#include <jni.h> #include <stdio.h> #include <sys/ptrace.h> #include <unistd.h> #include "log.h" voidanti_debug() { intpid=getpid(); intppid=getppid(); LOGD("pid:%d,ppid:%d",pid,ppid); } jintJNI_OnLoad(JavaVM*vm,void*reserved) { anti_debug(); JNIEnv*env; if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK) { return-1; } returnJNI_VERSION_1_6; }
06-11 06:42:24.411: D/[android-anti-debug](1451): pid:1451,ppid:192
pid 192 是:
root 192 1 492204 38400 ffffffff b756598c S zygote
在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧
看下 /proc/1451/stat
root@mx3:/data/local/tmp # cat /proc/17204//status
Name: ndroidantidebug
State: S (sleeping)
Tgid: 17204
Pid: 17204
PPid: 2146
TracerPid: 0
Uid: 10058 10058 10058 10058
Gid: 10058 10058 10058 10058
FDSize: 256
...
用gdbserver 去attach一下看看发生什么:
root@mx3:/data/local/tmp # ps |grep blog
u0_a58 30678 2146 907884 59720 ffffffff 4005778c S com.zhaoxiaodan.blog.androidantidebug
root@mx3:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 30678
Attached; pid = 30678
Listening on port 1234
root@mx3:/data/local/tmp # cat /proc/17204//status
Name: ndroidantidebug
State: t (tracing stop)
Tgid: 17204
Pid: 17204
PPid: 2146
TracerPid: 20337
Uid: 10058 10058 10058 10058
Gid: 10058 10058 10058 10058
发现TracerPid
行由0 变为了 20337
ida这些调试工具其实都是使用ptrace
进行的, ptrace有一个很重要的特定:
一个进程只能被一个进程调试。
所以, 最简单的办法就是在JNI_OnLoad
里直接ptrace(PTRACE_TRACEME, 0, 0, 0);
方法1, 直接ptrace(PTRACE_TRACEME, 0, 0, 0);
1 1 #include <jni.h> 2 2 #include <stdio.h> 3 3 #include <sys/ptrace.h> 4 4 #include <unistd.h> 5 5 #include "log.h" 6 6 voidanti_debug() 7 7 { 8 8 ptrace(PTRACE_TRACEME,0,0,0); 9 9 } 10 10 jintJNI_OnLoad(JavaVM*vm,void*reserved) 11 11 { 12 12 anti_debug(); 13 13 JNIEnv*env; 14 14 if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK) 15 15 { 16 16 return-1; 17 17 } 18 18 returnJNI_VERSION_1_6; 19 19 }
然后再用gdbserver 去attach:
root@mx3:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 31092
Cannot attach to lwp 31092: Operation not permitted (1)
Exiting
好了, 挂不上了
但是这种方法, 用反编译打开, 很容易就找到调用ptrace
的地方, 不知道修改下汇编指令
(比如改为nilnil), 就跳过这个调用了
方法2, 暗桩
根据上面说的/proc/$pid/status
中TracerPid
行显示调试程序的pid
的原理, 可以写一个方法检查下这个值, 如果!=0就退出程序
检查函数如下:
1 voidbe_attached_check() 2 { 3 try 4 { 5 constintbufsize=1024; 6 charfilename[bufsize]; 7 charline[bufsize]; 8 intpid=getpid(); 9 sprintf(filename,"/proc/%d/status",pid); 10 FILE*fd=fopen(filename,"r"); 11 if(fd!=nullptr) 12 { 13 while(fgets(line,bufsize,fd)) 14 { 15 if(strncmp(line,"TracerPid",9)==0) 16 { 17 intstatue=atoi(&line[10]); 18 LOGD("%s",line); 19 if(statue!=0) 20 { 21 LOGD("be attached !! kill %d",pid); 22 fclose(fd); 23 intret=kill(pid,SIGKILL); 24 } 25 break; 26 } 27 } 28 fclose(fd); 29 }else 30 { 31 LOGD("open %s fail...",filename); 32 } 33 }catch(...) 34 { 35 } 36 }
可以把这个函数搞成一个宏, 然后写个程序随机的把这个宏插入到源码的各个地方, 随着代码的不断执行, 会遇到各个这样的检查点
其实也没什么卵用, 只不过桩子多了, 你拔起来就麻烦点咯
下面这个只是用线程模拟检查的过程:
1 #include "android-anti-debug.h" 2 #include <string> 3 #include <sys/ptrace.h> 4 #include <unistd.h> 5 #include <stdlib.h> 6 #include <chrono> 7 #include <thread> 8 #include "log.h" 9 voidbe_attached_check() 10 { 11 try 12 { 13 constintbufsize=1024; 14 charfilename[bufsize]; 15 charline[bufsize]; 16 intpid=getpid(); 17 sprintf(filename,"/proc/%d/status",pid); 18 FILE*fd=fopen(filename,"r"); 19 if(fd!=nullptr) 20 { 21 while(fgets(line,bufsize,fd)) 22 { 23 if(strncmp(line,"TracerPid",9)==0) 24 { 25 intstatue=atoi(&line[10]); 26 LOGD("%s",line); 27 if(statue!=0) 28 { 29 LOGD("be attached !! kill %d",pid); 30 fclose(fd); 31 intret=kill(pid,SIGKILL); 32 } 33 break; 34 } 35 } 36 fclose(fd); 37 }else 38 { 39 LOGD("open %s fail...",filename); 40 } 41 }catch(...) 42 { 43 } 44 } 45 //检查线程, 每秒检查一下 46 voidthread_task(intn) 47 { 48 while(true) 49 { 50 LOGD("start be_attached_check..."); 51 be_attached_check(); 52 std::this_thread::sleep_for(std::chrono::seconds(n)); 53 } 54 } 55 voidanti_debug() 56 { 57 // ptrace(PTRACE_TRACEME, 0, 0, 0); 58 autocheckThread=std::thread(thread_task,1); 59 checkThread.detach(); 60 } 61 jintJNI_OnLoad(JavaVM*vm,void*reserved) 62 { 63 anti_debug(); 64 JNIEnv*env; 65 if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK) 66 { 67 return-1; 68 } 69 returnJNI_VERSION_1_6; 70 }