Android逆向——Android逆向进阶(4)
# 0x00 前言有价之宝:[戳这里](http://blog.csdn.net/qq_36869808/article/details/79290420)
无价之宝:[戳这里](http://blog.csdn.net/qq_36869808/article/details/79270225)
## 说明
在[上一篇](http://blog.csdn.net/qq_36869808/article/details/79344870)中进行了一个demo的练习,本来是静态分析的,但是动态分析也是可以的,为了进行一个练习,所以就拿来用了,这里感谢鬼哥。
## 内容
由于我们正在动态调试,所以要面对的一个问题就是反调试,所以不只是要进行动态调试,还要对反调试进行越过。
所以为了越过反调试,就要先了解一下反调试有哪些。
1.利用ptrace防止附加(1)
2.针对IDA。
3.总结
4.demo
# 0x01利用ptrace 反调试
## 原理
ida等都是用ptrace来进行附加的。ptrace 有一个重要的地方就是,一个进程只能被一个进程调试。这是一个非常重要的地方。
## 知识补充
稍安勿躁,如果着急的话,直接看实现就好。
### 知识点1:
在执行系统调用之前,内核会先检查当前进程是否处于被“跟踪”(traced)的状态。如果是的话,内核暂停当前进程并将控制权交给跟踪进程,使跟踪进程得以察看或者修改被跟踪进程的寄存器。
### 知识点2:
ptrace 函数有四个参数。
第一个参数决定了ptrace的行为与其他参数的使用方法。
我们这里使用的是
PTRACE_TRACEME
其他的之后遇到再说。
我们使用这个调用是为了告诉内核,当前进程已经正在被 traced。
### 知识点3:
ida等调试工具使用的是ptrace。
以上
## 实现
### NDK编程
#### 说明
这个实现,使用了自身进行进程的ptrace。
#### 首先是函数实现。
```
void anti_debug(){
ptrace(PTRACE_TRACEME,0,0,0);
}
```
#### JIN_OnLoad 调用
```
jint JNI_OnLoad(JavaVM* vm,void * reserved)
{
anti_debug();
JNIEnv *env;
if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK){
return -1;
}
return JNI_VERSION_1_6;
}
```
本来这一句
```
if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK){
return -1;
}
```
困扰我很久,最后知道了,这个就是注册Native。
## 测试
![这里写图片描述](http://t1.aixinxi.net/o_1c733sd0g1m3s1qh9141q150619qaa.png-j.jpg)
## 反调试测试
ida调试
首先adb连接root好的手机
我懒的用线,直接用wifi连接。
手机shell
```
su
setprop service.adb.tcp.port 5555
stop adbd
start adbd
```
输入指令即可。随便下载一个终端模拟器就可以了。
为了熟悉流程,我们来进行android_server的转发
最好还是自己进行敲一遍不要进行复制粘贴
转发完成。
ida进行调试
首先要知道包名,使用apkHelper。
```
com.example.hanlei.demo
```
![这里写图片描述](http://t1.aixinxi.net/o_1c7371svm16ofoiq1434j8csgka.png-j.jpg)
这里有一个非常重要的事情
就是调试的手机你要先安装app。
然后进行attach
发现出现了这样的问题
![这里写图片描述](http://t1.aixinxi.net/o_1c7378r17ior11tq120c12e5129ga.png-j.jpg)
The debugger could not attach to the selected process.
This can perhaps indicate the process was just terminated,or thiat you don't have the necessary privileges.
调试器无法连接到选定的进程。
这或许可以说明过程就终止,或者的时候你没有必要的特权。
看到了这里,就很有可能是出现了反调试。
所以我们现在进行反调试的去除。
## 反调试去除
明确一点,就是一般反调试,都是在JNI_OnLoad中进行的。
我们首先来看函数表。
![这里写图片描述](http://t1.aixinxi.net/o_1c737l60k1p7hjam1p2nt2pitga.png-j.jpg)
我们要解决的是反动态调试。
来看JNI_OnLoad
![这里写图片描述](http://t1.aixinxi.net/o_1c737qd0qre1b40vkfkimjja.png-j.jpg)
这里调用了一个函数。
这个函数就很有可能是反调试的地方。
```
j__Z10anti_debugv ; CODE XREF: JNI_OnLoad+24↓p
.plt:0000065C ADR R12, 0x664
.plt:00000660 ADD R12, R12, #0x2000
.plt:00000664 LDR PC, ! ; anti_debug(void)
```
看到这里只是进行一个返回。我们接着往下看
![这里写图片描述](http://t1.aixinxi.net/o_1c7383cl9ki31j1hufm15er13l5a.png-j.jpg)
看到了ptrace,那么也就差不多找到了重点了。
我们去除反调试的方法这时候就知道最基础的就是两种,一种就是不调用,一种就是清空函数。
我们来进行修改。
![这里写图片描述](http://t1.aixinxi.net/o_1c7387lvg1kdlfc31q6j9lh22a.png-j.jpg)
进行修改。
然后替换,然后重新签名。
然后重新安装。
我android studio有问题,这里就不动态调试了。
## 优点(自己总结)
1.拦截新手。
2.消耗一点时间。(我瞎掰掰的)
3.可以当做练习(这个也是我瞎掰掰的)
## 缺点
1.直接可以静态去除。
# 0x02 针对IDA的android_server
```
pid_t GetPidByName(const charchar *as_name) {
DIR *pdir = NULL;
struct dirent *pde = NULL;
FILEFILE *pf = NULL;
char buff;
pid_t pid;
char szName;
// 遍历/proc目录下所有pid目录
pdir = opendir("/proc");
if (!pdir) {
perror("open /proc fail.\n");
return -1;
}
while ((pde = readdir(pdir))) {
if ((pde->d_name < '0') || (pde->d_name > '9')) {
continue;
}
sprintf(buff, "/proc/%s/status", pde->d_name);
pf = fopen(buff, "r");
if (pf) {
fgets(buff, sizeof(buff), pf);
fclose(pf);
sscanf(buff, "%*s %s", szName);
pid = atoi(pde->d_name);
if (strcmp(szName, as_name) == 0) {
closedir(pdir);
return pid;
}
}
}
closedir(pdir);
return 0;
}
```
我的电脑没有办法调试,自己可以尝试。
对应的破解方法就是改变android_server名称就可以了
其他的就先不进行测试了。
# 0x03 总结
## 1.针对逆向工具
防:比如检测android_server
攻:改名字就可以了
### 思路
1.检测android_server
2.检测gdbserver
3.软件使用自己已经不能用的server
## 2.ptrace
防:
(1)自身调用
(2)子进程调用
(3)孙子进程调用子进程,子进程调用父进程,父进程进行调用
攻:
(1)线程
(2)其他方法
## 3./proc/$pid/status
简单实现。
```
void be_attached_check()
{
try
{
const int bufsize = 1024;
char filename;
char line;
int pid = getpid();
sprintf(filename, "/proc/%d/status", pid);
FILE* fd = fopen(filename, "r");
if (fd != nullptr)
{
while (fgets(line, bufsize, fd))
{
if (strncmp(line, "TracerPid", 9) == 0)
{
int statue = atoi(&line);
LOGD("%s", line);
if (statue != 0)
{
LOGD("be attached !! kill %d", pid);
fclose(fd);
int ret = kill(pid, SIGKILL);
}
break;
}
}
fclose(fd);
} else
{
LOGD("open %s fail...", filename);
}
} catch (...)
{
}
}
```
防:检测字段是否为0
攻:删除检测即可
## 4.针对调试要求
我们知道调试要求有两个,满足其中一个就是。
1.androidmainfest.xml的android:debuggable=true;
2.build.prop中ro.debuggable=true。
可以对这个地方进行检测。
## 5.IDA反附加和GDB反附加
搜索IDA或者GDB进行检测。在附加的时候停止工作。
### 说明
其他的如果在之后会遇到的话,那么就进行分析好了。
关于反调试暂时以上。
# 0x04 demo
网上好多的资料都是这个
![这里写图片描述](http://t1.aixinxi.net/o_1c73fgchi1lnd5j12fn17bh109ma.png-j.jpg)
我也来一刀吧
按照主要流程来。
## 预热
### 验证
重新签名,安装测试。
失败。证明可能有签名验证。
反编译。
尴尬,反编译发现没有检测签名的。
本来想偷懒的,用模拟器,但是好像模拟器运行失败了。到真机测试一下。发现可以运行。
这里没有签名验证。
好像可以直接开始
## start java层分析
### 反编译
![这里写图片描述](http://t1.aixinxi.net/o_1c73h66ku1m6q1ca51g9s8m01bnja.png-j.jpg)
这个就是那个按钮的监听事件了。
我们去$smali里找找。
```
.method public onClick(Landroid/view/View;)V
.locals 5
.param p1, "v" # Landroid/view/View;
.prologue
.line 33
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
iget-object v2, v2, Lcom/yaotong/crackme/MainActivity;->inputCode:Landroid/widget/EditText;
invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object v2
invoke-interface {v2}, Landroid/text/Editable;->toString()Ljava/lang/String;
move-result-object v1
.line 34
.local v1, "result":Ljava/lang/String;
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
invoke-virtual {v2, v1}, Lcom/yaotong/crackme/MainActivity;->securityCheck(Ljava/lang/String;)Z
move-result v2
if-eqz v2, :cond_0
.line 35
new-instance v0, Landroid/content/Intent;
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
const-class v3, Lcom/yaotong/crackme/ResultActivity;
invoke-direct {v0, v2, v3}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
.line 36
.local v0, "i":Landroid/content/Intent;
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
invoke-virtual {v2, v0}, Lcom/yaotong/crackme/MainActivity;->startActivity(Landroid/content/Intent;)V
.line 42
.end local v0 # "i":Landroid/content/Intent;
:goto_0
return-void
.line 39
:cond_0
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
invoke-virtual {v2}, Lcom/yaotong/crackme/MainActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v2
const-string v3, "\u9a8c\u8bc1\u7801\u6821\u9a8c\u5931\u8d25"
.line 40
const/4 v4, 0x0
.line 39
invoke-static {v2, v3, v4}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v2
.line 40
invoke-virtual {v2}, Landroid/widget/Toast;->show()V
goto :goto_0
.end method
```
这里我们看到。
```
:cond_0
iget-object v2, p0, Lcom/yaotong/crackme/MainActivity$1;->this$0:Lcom/yaotong/crackme/MainActivity;
invoke-virtual {v2}, Lcom/yaotong/crackme/MainActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v2
const-string v3, "\u9a8c\u8bc1\u7801\u6821\u9a8c\u5931\u8d25"
```
cond_0才是失败才要跳转的
```
if-eqz v2, :cond_0
```
这里进行跳转
```
invoke-virtual {v2, v1}, Lcom/yaotong/crackme/MainActivity;->securityCheck(Ljava/lang/String;)Z
move-result v2
```
上面一句是这样的
也就是说securityCheck是重点函数。
```
.method public native securityCheck(Ljava/lang/String;)Z
```
这里看到了是一个Native层的函数
那么密码没有在smali那么就一定是在so层了。不然它又不能上天。
## start Native层分析
找到函数
![这里写图片描述](http://t1.aixinxi.net/o_1c73hukfj1fkabjoqu81nm0iq4a.png-j.jpg)
![这里写图片描述](http://t1.aixinxi.net/o_1c73i5qmh1fvvkeo15lm30v9sa.png-j.jpg)
看到了这样一句话,我就是答案
那么这就是我们要弄的重点了。
先要知道包名 Android killer里有。
![这里写图片描述](http://t1.aixinxi.net/o_1c73jljrrsm61du219va1f2tgaa.png-j.jpg)
动态调试的结果并不理想,很有可能是因为有反调试。
反调试一般出现的地方是JNI_OnLoad
所以我们要进行越过。
## 反调试,动态调试
### 第一步,动态调试环境配置。
```
android:debuggable="true"
```
回编译
### 转发android_server
adb shell su /data/local/tmp/android_server
### 转发端口
adb forward tcp:23946 tcp:23946
### 使用调试
如果我们按照之前的调试方式的话,JNI_OnLoad就已经运行过了,所以我们要在运行之前进行调试。
adb shell am start -D -n com.yaotong.crackme/com.yaotong.crackme.MainActivity
### 转发端口
adb forward tcp:8700 jdwp:15127
### IDA挂起 attach
![这里写图片描述](http://t1.aixinxi.net/o_1c73mp1n817au1h391dip1n991eaa.png-j.jpg)
### 正常进入调试
![这里写图片描述](http://t1.aixinxi.net/o_1c73muheujat1vtnl2q7ru1ebsa.png-j.jpg)
### libdvm.so断点
### jdb
```
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
```
### 总结
不知道是什么原因,我的调试总是出问题。
尝试下直接去掉验证好了。
但是流程是对的。
之后看来还要对jdb调试进行一下学习呢。
## 动态调试
### 转发android_server
### 进入so文件,进入函数
![这里写图片描述](http://t1.aixinxi.net/o_1c73vhqgn122o1k2s1hic1o1n4j1a.png-j.jpg)
### 下段,运行
![这里写图片描述](http://t1.aixinxi.net/o_1c73vl3a7aqlbni1d41683b6ja.png-j.jpg)
F8运行到这个位置,我们发现寄存器里的数值,R0存的是我们输入的数字。
R3里就是密码了。
![这里写图片描述](http://t1.aixinxi.net/o_1c745j22t1gsi1a858jnlv9nh0a.png-j.jpg)
### 然后就是在这个位置
![这里写图片描述](http://t1.aixinxi.net/o_1c745h0bcnii1m654t0r7deq5a.png-j.jpg)
拿到我们的答案。
# 以上 暖楼,谢谢楼主图文并茂的教程{:1_921:} {:301_1003:}大佬讲的很详细,学习了 大佬,真牛批,学习了!!! 大佬讲的很详细,学习了!!! 谢谢分享
thanks for sharing. 不错的教程,支持楼主。 好详实的教程啊,收藏学习啦! 辛苦了,好东西收藏
页:
[1]
2