吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12272|回复: 16
收起左侧

[Android 原创] Android逆向——Android逆向进阶(4)

[复制链接]
BubblePig 发表于 2018-2-24 23:11

0x00 前言

有价之宝:戳这里
无价之宝:戳这里

说明

上一篇中进行了一个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。

测试

这里写图片描述

反调试测试

ida调试

首先adb连接root好的手机

我懒的用线,直接用wifi连接。

手机shell

su
setprop service.adb.tcp.port 5555
stop adbd
start adbd

输入指令即可。随便下载一个终端模拟器就可以了。

为了熟悉流程,我们来进行android_server的转发

最好还是自己进行敲一遍不要进行复制粘贴

转发完成。

ida进行调试

首先要知道包名,使用apkHelper。

com.example.hanlei.demo

这里写图片描述

这里有一个非常重要的事情

就是调试的手机你要先安装app。

然后进行attach

发现出现了这样的问题

这里写图片描述

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中进行的。

我们首先来看函数表。

这里写图片描述

我们要解决的是反动态调试。

来看JNI_OnLoad

这里写图片描述

这里调用了一个函数。

这个函数就很有可能是反调试的地方。

j__Z10anti_debugv                       ; CODE XREF: JNI_OnLoad+24↓p
.plt:0000065C                 ADR             R12, 0x664
.plt:00000660                 ADD             R12, R12, #0x2000
.plt:00000664                 LDR             PC, [R12,#(_Z10anti_debugv_ptr - 0x2664)]! ; anti_debug(void)

看到这里只是进行一个返回。我们接着往下看

这里写图片描述

看到了ptrace,那么也就差不多找到了重点了。

我们去除反调试的方法这时候就知道最基础的就是两种,一种就是不调用,一种就是清空函数。

我们来进行修改。

这里写图片描述

进行修改。

然后替换,然后重新签名。

然后重新安装。

我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[128];  
        pid_t pid;  
        char szName[128];  
        // 遍历/proc目录下所有pid目录    
        pdir = opendir("/proc");  
        if (!pdir) {  
                perror("open /proc fail.\n");  
                return -1;  
        }  
        while ((pde = readdir(pdir))) {  
                if ((pde->d_name[0] < '0') || (pde->d_name[0] > '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[bufsize];
        char line[bufsize];
        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[10]);
                    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

网上好多的资料都是这个

这里写图片描述

我也来一刀吧

按照主要流程来。

预热

验证

重新签名,安装测试。

失败。证明可能有签名验证。

反编译。

尴尬,反编译发现没有检测签名的。

本来想偷懒的,用模拟器,但是好像模拟器运行失败了。到真机测试一下。发现可以运行。

这里没有签名验证。

好像可以直接开始

start java层分析

反编译

这里写图片描述
这个就是那个按钮的监听事件了。

我们去$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层分析

找到函数

这里写图片描述

这里写图片描述

看到了这样一句话,我就是答案

那么这就是我们要弄的重点了。

先要知道包名 Android killer里有。

这里写图片描述

动态调试的结果并不理想,很有可能是因为有反调试。

反调试一般出现的地方是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

这里写图片描述

正常进入调试

这里写图片描述

libdvm.so断点

jdb

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

总结

不知道是什么原因,我的调试总是出问题。
尝试下直接去掉验证好了。

但是流程是对的。

之后看来还要对jdb调试进行一下学习呢。

动态调试

转发android_server

进入so文件,进入函数

这里写图片描述

下段,运行

这里写图片描述

F8运行到这个位置,我们发现寄存器里的数值,R0存的是我们输入的数字。
R3里就是密码了。

这里写图片描述

然后就是在这个位置

这里写图片描述
拿到我们的答案。

以上

免费评分

参与人数 2威望 +1 吾爱币 +13 热心值 +2 收起 理由
饭没吃 + 1 + 1 谢谢@Thanks!
qtfreet00 + 1 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

chinasmu 发表于 2018-2-24 23:29
暖楼,谢谢楼主图文并茂的教程
xiaokezyj 发表于 2018-2-25 00:33
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 来自手机
辛苦了,好东西收藏
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 00:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表