【转】andorid ndk anti-debug

思考

之前研究了下如何调试和尝试反一个别人加密的东西, 所以现在的体会就是:

其实重点不是你如何加密, 重点是如何不让别人知道你怎么加密的

因为像这种自己加密的资源运行的时候自己解密之后拿来用的程序, 我甚至根本不用关心你到底怎么加密, 加密算法是啥, 我只需要知道, 你解密完了之后, 那个资源的内存块在哪, 写个dumper就全拿到了;

加密并不能防止被破解, 只是增加破解的难度和门槛, 加密解密是一个相互博弈的过程

把资源加个密, 那么对于那些技术比较初级, 又想简单拷贝的人, 他们就拿你没办法了; 但是对于那些懂一点原理懂一点IDA的人, 并没有什么卵用

那么好, 现在我要研究一下, 怎么样再增加那么一点点门槛

之前解密的一个重要前提就是调试, 所以, 最直接想到的增加的门槛就是反调试.

测试

先建个android 工程, 并加入native支持

android-anti-debug

先打出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(受精卵)的原因吧

具体参见:Android系统进程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/statusTracerPid行显示调试程序的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 }

  

原文地址:https://www.cnblogs.com/dependence/p/4710558.html