Android 逆向apk的.so动态库

那么我们上篇文章中提及了安全性问题, Android apk如何加固防止被破解(防止逆向编译),那么本篇文章提及一点,so动态库的安全性与重要性。

首先我们要知道, .so动态库是做什么用的,它不像.smail文件可修改,它是属于汇编语言,如果直接去修改,文件会发生错乱。早上有人来问我,游戏打入渠道sdk之后发生错误,且只有armeabi里发生错误,这种情况可以断定.so动态库中有了兼容的冲突。

通常我们会看到libs下面有这么几个文件夹,mips、armeabi、armeabi-v7a和x86,其实是代表着不同的CPU类型,那么在arm下有不同的指令,想要了解的可以参考这篇文章 《Android ARM常用的汇编指令合集》

实现步骤:

  1. 使用apktool命令编译出来apk的目录
  2. 然后用IDA打开.so文件,在apk根目录的lib文件夹下
  3. 菜单中有个Search,可以用text作为入口
  4. 找到入口,一般为init方法
  5. 使用动态调试,给libdvm.so中的函数:dvmDexFileOpenPartial 下断点,然后得到dex文件在内存中的起始地址和大小,然后dump处dex数据即可
  6. 分析底层加载dex源码,知道有一个函数:dvmDexFileOpenPartial 这个函数有两个重要参数,一个是dex的其实地址,一个是dex的大小,而且知道这个函数是在libdvm.so中的。所以我们可以使用IDA进行动态调试获取信息
  7. 双开IDA开始获取内存中的dex内容,双开IDA,走之前的动态破解so方式来给dvmDexFileOpenPartial函数下断点,获取两个参数的值,然后使用一段脚本,将内存中的dex数据保存到本地磁盘中。
  8. 分析获取到的dex内容,得到了内存中的dex之后,我们在使用dex2jar工具去查看源码,但是发现保存,以为是dump出来的dex格式有问题,但是最后使用baksmali工具进行处理,得到smali源码是可以的,然后我们就开始分析smali源码。

Tips:

  • debugger模式
  • 通过dump出内存中的dex数据,其实不管apk如何加固,最后都是会加载到内存中的
  • 可以尝试调用so中的native方法,在知道了这个方法的定义之后 adb shell input text 命令来辅助我们的输入


我们以 趣头条.apk 为例,反向思维来看下这个包是如何加密的,首先我们来看目录结构:

我们可以看到,smail里面是没多少代码的,主要的是so动态库和两个jar包,打开jar包来看看:(bdxadsdk.jar 和 gdtadv2.jar )

什么都没有,可以肯定的是,这个是加了壳的,加密方式是怎么样的呢,我们来参考网上一张图:

我们先去看下 smail文件里面有什么线索没

“libjiagu” 顾名思义,加固,我们发现 ,这个包还加固了。来,继续往下走

“DexOptJobService_DexOptimization” 动态加载了Dex

那么动态加载dex技术是如何处理的?引用一张图来看看,顺便过下这个知识点

  1. 关于PathClassLoader,API中提及: Android uses this class for its system class loader and for its application class loader(s) —->> Android应用就是用它来加载;
  2. DexClass可以加载apk,jar,及dex文件,但PathClassLoader只能加载已安装到系统中(即/data/app目录下)的apk文件。

子节点都是从继承BaseDexClassLoader中来的,那我们去看看源码:

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
        for (Throwable t : suppressedExceptions) {
            cnfe.addSuppressed(t);
       }
        throw cnfe;
    }
     return c;
}

从代码中我们发现,当我们需要去找class时,是从pathList中的findClass方法中读取,查看源码,可以发现pathList是DexPathList类的一个实例,我们接着来看findClass(name, suppressedExceptions);

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if (dex != null) {
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
   }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

看完之后我们可以发现,它是遍历一个装在dex文件(每个dex文件实际上是一个DexFile对象)的数组(Element数组,Element是一个内部类),然后依次去加载所需要的class文件,直到找到为止。

public String inject(String libPath) {
    boolean hasBaseDexClassLoader = true;
    try {
        Class.forName("dalvik.system.BaseDexClassLoader");
    } catch (ClassNotFoundException e) {
        hasBaseDexClassLoader = false;
    }
    if (hasBaseDexClassLoader) {
        PathClassLoader pathClassLoader = (PathClassLoader)sApplication.getClassLoader();
        DexClassLoader dexClassLoader = new DexClassLoader(libPath, sApplication.getDir("dex", 0).getAbsolutePath(), libPath, sApplication.getClassLoader());
        try {
            Object dexElements = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader)));
            Object pathList = getPathList(pathClassLoader);
            setField(pathList, pathList.getClass(), "dexElements", dexElements);
            return "SUCCESS";
        } catch (Throwable e) {
            e.printStackTrace();
            return android.util.Log.getStackTraceString(e);
        }
    }
    return "SUCCESS";
}

看到这里,注入的解决方案也就浮出水面,假如我们将第二个dex文件放入Element数组中,那么在加载第二个dex包中的类时,应该可以直接找到。

OK,这个知识点到底结束,我们知道了这个apk的加固与加载dex方式,那如何逆向分析这个.so动态库?

等我稍后整理下图片,先发布了,持续更新。

时间: 2024-09-09 14:18:58

Android 逆向apk的.so动态库的相关文章

Android Robolectric加载运行本地So动态库

前言 Robolectric 是 Android 的单元测试框架,运行无需 Android 真机环境直接运行在 JVM 之上,所以在 test case 运行速度效率上有了很大提升,接近于 Java JUnit test(JUnit test > Robolectric ≫ androidTest).不过框架本身并不支持 so 本地库的加载使用,加载时会直接报错,因为实际上运行环境是电脑机器,而我们打出的 so 文件是给手机上用的所以当然会报错.虽然在 GitHub 上很多人问过关于使用 so

【短视频SDK】如何导入Android的AAR?动态库so文件到底怎么样放呢?

开发者在拿到短视频Android的SDK的时候会有些觉得不太一样,为什么SDK提供的是AAR文件和几个so文件呢?我们常见的SDK不是都是jar包吗?文本试图将AAR是什么,如何导入出现的问题进行一个归纳总结,希望开发者读完能够解决这种类型的问题. 1.什么是AAR文件?如何导入AAR文件? 1.1 什么是AAR文件? 开发者在开发的时候大家都遵循组件化的思路写代码,比如我们在写一个圆形的自定义组件的时候,这个组件是一个独立的组件,但是他可能不仅仅包含Java代码,还有很多资源甚至是底层so文件

android安卓-为什么我用Android逆向助手反编译知乎apk。却显示文件夹空呢。啊

问题描述 为什么我用Android逆向助手反编译知乎apk.却显示文件夹空呢.啊 为什么我用Android逆向助手反编译知乎apk.却显示文件夹空呢.反编译其他的就行啊?咋回事啊 解决方案 用的什么工具?是不是加密了,工具不支持啊 解决方案二: 省省吧,大公司的产品一般都是层层封装的,要不岂不都让对手给剽窃了,它们这些APP,你就算是反编译了,基本上都是一些无用的信息和资源 解决方案三: 说明人家的反编译做得好啊 解决方案四: 那你自己做一个反编译的呗

从源码编译Android系统的Java类库和JNI动态库的方法_Android

利用源码编译Android系统Java类库1.编写Java项目和Android.mk文件 ├── Android.mk └── src └── com └── lhw └── framework └── led └── Led.java Led.java文件 package com.lhw.framework.led; /** * LED操作库 * @author Micky Liu */ public class Led { public boolean turnOn() { return t

从源码编译Android系统的Java类库和JNI动态库的方法

利用源码编译Android系统Java类库 1.编写Java项目和Android.mk文件 ├── Android.mk └── src └── com └── lhw └── framework └── led └── Led.java Led.java文件 package com.lhw.framework.led; /** * LED操作库 * @author Micky Liu */ public class Led { public boolean turnOn() { return

Android热修复升级探索——SO库修复方案

一.前言 通常情况下,大多数人希望android下热补丁方案能够做到补丁的全方位修复,包括类修复/资源修复/so库的修复. 这里主要介绍热补丁之so库修复思路. 二.so库加载原理 Java Api提供以下两个接口加载一个so库 System.loadLibrary(String libName):传进去的参数:so库名称, 表示的so库文件,位于apk压缩文件中的libs目录,最后复制到apk安装目录下. System.load(String pathName):传进去的参数:so库在磁盘中的

android 中APK如何获取root权限?

问题描述 android 中APK如何获取root权限? android APK中如何获取到root权限,从而能切换到执行诸如exec = Runtime.getRuntime().exec(""su -c ""+abspath); 语句?eng版本 具有root权限吗?可是执行时报错:su: uid xxx not allowed to su adb root 和system root有区别吗? user版本如何仅在开发的APK中获取root权限?user版本在我

为Android的apk应用程序文件加壳以防止反编译的教程_Android

一.什么是加壳?加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作.大多数病毒就是基于此原理. 二.加壳作用加壳的程序可以有效阻止对程序的反汇编分析,以达到它不可告人的目的.这种技术也常用来保护软件版权,防止被软件破解. 三.Android Dex文件加壳原理PC平台现在已存在大量的标准的加壳和解壳工具,但是Android作为新兴平台还未出现APK加壳工具.Android Dex文件大量使用引用给加壳带来了一定的难度,但是从理论上讲,Android APK加壳

为Android的apk应用程序文件加壳以防止反编译的教程

一.什么是加壳? 加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作.大多数病毒就是基于此原理. 二.加壳作用 加壳的程序可以有效阻止对程序的反汇编分析,以达到它不可告人的目的.这种技术也常用来保护软件版权,防止被软件破解. 三.Android Dex文件加壳原理 PC平台现在已存在大量的标准的加壳和解壳工具,但是Android作为新兴平台还未出现APK加壳工具.Android Dex文件大量使用引用给加壳带来了一定的难度,但是从理论上讲,Android AP