安卓应用脱壳逆向笔记

写一篇小笔记,记录一下第一次脱壳并逆向修改安卓应用的过程,为了避免麻烦,这里不会有被我逆向应用的信息。

为了把一个应用使用的一个库换成opencv,我这个连java都不会写的人硬着头皮消耗了一个月,甚至整个春节假期在做这个事情。

首先需要的一个工具是apktool [https://github.com/iBotPeaches/Apktool],它的作用是把应用安装包apk文件中的各个部分提取出来,变成一个apktool项目目录,且修改完之后我们也需要用它打包回apk。

以下是用ai生成的一个apktool反编译后目录的结构,另外此次逆向修改过程中也大量使用了ai,不然以我这第一次认识这些东西的知识储备,还得更久才能弄好。

your_app/
├── AndroidManifest.xml        # 应用主配置文件,声明组件、权限、包名等
├── apktool.yml                # APKTool 元数据,记录 SDK 版本、资源 ID 偏移、压缩规则等,用于重打包
├── smali/                     # 主 DEX 反编译出的 Dalvik 字节码(.smali),对应 Java/Kotlin 逻辑,如果没有选择反编译资源的话,则不会有这个目录,取而代之的是classes.dex文件
│   └── com/example/...        # 按原始包名组织的类文件
├── smali_classes2/            # (若存在)第二个 DEX 文件的 smali 代码,用于 multidex 应用
├── smali_classes3/            # (若存在)第三个 DEX,依此类推
├── res/                       # 编译后的资源目录,已还原为可读 XML
│   ├── layout/                # 界面布局文件(.xml)
│   ├── values/                # 字符串(strings.xml)、颜色(colors.xml)、样式(styles.xml)等
│   └── drawable-*/            # 图片资源,按屏幕密度分类(如 drawable-xhdpi)
├── assets/                    # (可选)原始资产目录,内容不被编译,通过 AssetManager 访问(如 .json、.db)
├── lib/                       # (可选)原生库目录,包含 .so 文件
│   ├── arm64-v8a/             # 64 位 ARM 架构原生库
│   └── armeabi-v7a/           # 32 位 ARM 架构原生库(其他架构如 x86 等也可能存在)
├── original/                  # (可选)原始 APK 中的 META-INF 目录,含签名文件(重打包时通常会被替换)
└── [resources.arsc]           # (注:此文件在反编译后不直接出现,其内容已解析并融入 res/ 目录中)

要修改应用的逻辑,则需要修改各个smali目录下的smali文件,这些是反编译出来的字节码文件。

直接看smali是很容易头晕的,要把它转换为java代码的形式来查看才更容易理解里面的逻辑,这里我们需要使用另外一个工具:jadx [https://github.com/skylot/jadx],我寻找并且尝试了好几个工具,最好感觉还是这个最好用,并且还在更新,这个工具有cli模式也有gui模式,下载时不要选择gui版本,因为它只能使用gui,不能被其它工具调用,名字不带gui的版本里面也有gui。

脱壳

当时这个应用反编译出的smali目录是这样的:

smali
└───com
    ├───qihoo
    │   └───util
    └───stub

当时我还不知道这是被加固后的项目,通过jadx转换为java代码之后,发现和此应用本身的功能没有任何关系,查了一查之后才知道这是360加固的应用,现在反编译出来的逻辑只有360加固运行并释放原始应用的逻辑,因此我们还需要先把这个应用脱壳了之后才可以继续修改原始应用的逻辑。

试了几个现成的脱壳软件和脱壳应用都失败了,这些加固都是会不断进化的,但是有一点不会改变的是,这些加固总得把原始的应用释放出来才能够运行,因此我们需要一些工具来盯着应用的执行过程,当加固程序把原始的dex文件释放进内存之后立刻从内存里提取出来,这里需要用到的工具是frida [https://github.com/frida/frida]。

frida不是单独使用的程序,它需要你有一个有root权限的安卓设备,在里面运行一个 frida-server,并且开启adb调试,然后在电脑上通过frida工具进行远程调试,另外这只是一个调试工具,需要实现什么功能就需要编写对应的js脚本,不过这放在现在并不是什么难事,我的脱壳js脚本就完全是让ai写的,自己并没有费心,不然光是学frida的用法都要一两天。让ai自动写,然后自动运行调试,它会尝试各种方法绕过360加固的检测,过了好一会儿它经过各种尝试之后终于帮我提取出了三个dex文件。

有了原始的dex文件之后,就可以通过jadx把它们反编译成smali目录,按规则命名好放到apktool解包出的目录里,注意要先把原来反编译出的smali目录删除,那是加固程序的应用逻辑,已经不再需要了。

到这一步还没有完全解决360加固的残留问题,还需要让ai帮忙生成一个360加固在多处插入的一个无用的类,这里我可以贴出来,放在任意一个smali目录中com\stub\StubApp.smali’位置,内容是:

# StubApp兼容类,继承自你的应用Application类
.class public Lcom/stub/StubApp;
.super Lcom/xxxxxx/你的应用Application类;  #你需要修改这里为你Application类的标识符
.source "StubApp.java"

# direct methods
.method public constructor <init>()V
    .registers 1

    .line 1
    invoke-direct {p0}, 你的应用Application类;-><init>()V   #你需要修改这里

    return-void
.end method

# static methods
.method public static getOrigApplicationContext(Landroid/content/Context;)Landroid/content/Context;
    .registers 1
    .param p0, "context"    # Landroid/content/Context;

    .line 1
    return-object p0
.end method

.method public static interface11(I)V
    .registers 1
    .param p0, "param"    # I

    .line 1
    return-void
.end method

.method public static interface22(I[Ljava/lang/String;[I)V
    .registers 3
    .param p0, "param1"    # I
    .param p1, "param2"    # [Ljava/lang/String;
    .param p2, "param3"    # [I

    .line 1
    return-void
.end method

如你所见这个类没有任何用处,只是用来干扰脱壳后的应用运行的,但如果要一个个去修改调用这些方法的地方又太麻烦,于是直接创建这一个没有用的类即可。

另外还有一些Activity的onCreate方法会被转换成native方法,我们也需要用ai恢复成合适的java初始化代码。

做完以上步骤,应用就已经可以在脱壳的状态下编译运行起来了,使用apktool把解包目录重新编译回apk文件,然后给apk签名并安装到设备即可测试运行。

修改功能

测试脱壳的应用可以正常运行之后,就可以开始进行修改了。这里建议使用git对apktool解包目录进行版本管理,这样既可以记录每一步修改了什么,也方便项目被ai改错之后直接恢复之前的状态。

资源文件都保留着原始的内容,因此可以直接修改,这里就不多说了。

如果要修改的是smali文件,建议安装两个vscode插件来帮助进行检查:

  • Smalise (ID: loyieking.smalise): 这个是smali文件的语法高亮插件,同时也会帮你把错误使用的指令标红,方便及时发现低级错误。
  • Smali2Java (ID: ooooonly.smali2java): 这个插件的作用是调用jadx把smali文件转换成java,虽然独立的jadx也可以完成这个任务,但这个插件把转换集成进vscode之后对照起来会方便很多,改完smali之后立即转换查看一下java逻辑是否符合自己的预期,大部分情况下转换结果无误的话就不会出别的错误了。

如果要添加额外的功能,可能直接编辑原项目的smali文件会很复杂,导致容易出错,这时候建议额外创建一个安卓模块项目,把大部分逻辑都用java写好后编译,再反编译出smali文件放进apktool目录对应的位置给原项目进行调用,这样就可以尽量少地修改smali以减少出错概率。

如果要添加额外的库,像我这次使用到了opencv,先把opencv的动态库文件’libopencv_java4.so’放进lib目录下对应的架构目录里,然后再从opencv的安卓sdk里把预编译好的”classes.jar”文件反编译成smali目录放进apktool项目里,之后再打包的时候opencv sdk就会被一起编译进去,这样就把一个额外的库塞进去了,我们在需要的地方调用sdk即可。

歪门邪道

这次我没有使用前面的方法来添加功能,因为我对安卓项目并不熟,不想创建太多额外的东西,于是我用的方法是先把jadx反编译某个smali文件得出的java文件放在和那个smali文件相同的目录下,然后修改那个java文件,在修改的地方都用注释标记好,然后让ai帮我改到smali文件中对应的位置。这样做的好处不多,坏处很大,目前ai的能力还不是很高,大概率会改错,尤其是寄存器冲突和逻辑错误,然后反反复复调试很多遍才可能可以改好一个位置,极其费时间,运气不好可能不管怎么改都改不好,最后还是我介入了一点点分析手动改才改好。



本文发布于 https://luojia.me

本站文章未经文下加注授权不得拷贝发布。

0 0 投票数
打分
订阅评论
提醒
guest
0 评论