BubblePig 发表于 2018-2-24 23:11

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)
拿到我们的答案。

# 以上

chinasmu 发表于 2018-2-24 23:29

暖楼,谢谢楼主图文并茂的教程{:1_921:}

xiaokezyj 发表于 2018-2-25 00:33

{:301_1003:}大佬讲的很详细,学习了

crackspad 发表于 2018-2-25 10:33

大佬,真牛批,学习了!!!

xjh88232259 发表于 2018-2-25 12:52

大佬讲的很详细,学习了!!!

wangdong123 发表于 2018-2-25 12:54

谢谢分享

ipaint 发表于 2018-2-25 14:56

thanks for sharing.

夜曲 发表于 2018-2-26 11:11

不错的教程,支持楼主。

firestarman 发表于 2018-3-3 00:24

好详实的教程啊,收藏学习啦!

天会再蓝 发表于 2018-3-3 00:34

辛苦了,好东西收藏
页: [1] 2
查看完整版本: Android逆向——Android逆向进阶(4)