热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android逆向apk的.so动态库

那么我们上篇文章中提及了安全性问题,Androidapk如何加固防止被破解(防止逆向编译),那么本篇文章提及一点,so动态库的安全性与重要性。首先我们要知道,.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 suppressedExceptiOns= new ArrayList();
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 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动态库?

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


推荐阅读
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文探讨了在一个物理隔离的环境中构建数据交换平台所面临的挑战,包括但不限于数据加密、传输监控及确保文件交换的安全性和可靠性。同时,作者结合自身项目经验,分享了项目规划、实施过程中的关键决策及其背后的思考。 ... [详细]
  • 腾讯云移动推送TPNS(Tencent Push Notification Service)为APP开发者和运营人员提供了一站式、高效、稳定的推送解决方案,帮助提升用户活跃度和运营效率。 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • 调试利器SSH隧道
    在开发微信公众号或小程序的时候,由于微信平台规则的限制,部分接口需要通过线上域名才能正常访问。但我们一般都会在本地开发,因为这能快速的看到 ... [详细]
  • 从理想主义者的内心深处萌发的技术信仰,推动了云原生技术在全球范围内的快速发展。本文将带你深入了解阿里巴巴在开源领域的贡献与成就。 ... [详细]
  • Windows操作系统提供了Encrypting File System (EFS)作为内置的数据加密工具,特别适用于对NTFS分区上的文件和文件夹进行加密处理。本文将详细介绍如何使用EFS加密文件夹,以及加密过程中的注意事项。 ... [详细]
  • 树莓派4B:安装基础操作系统指南
    本文将详细介绍如何为树莓派4B安装基础操作系统,包括所需材料、镜像下载、镜像烧录以及更换国内源等步骤。 ... [详细]
  • 本文介绍了GitHub上的一些Python开源项目,特别是IM(即时通讯)技术的应用。通过Sealtalk项目,探讨了如何利用开源SDK提升开发效率。 ... [详细]
  • 本文详细介绍了如何在Android应用中实现重复报警功能。示例代码可在以下路径找到:https://developer.android.com/samples/RepeatingAlarm/index.html。首先,我们将从Manifest文件开始分析。 ... [详细]
  • 本文介绍了如何在Linux系统中获取库源码,并在从源代码编译软件时收集所需的依赖项列表。 ... [详细]
  • 本文介绍了如何在Android应用中通过Intent调用其他应用的Activity,并提供了详细的代码示例和注意事项。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 在 Mac 上配置 NDK
    本文详细介绍了如何在 Mac 上配置 Android NDK,包括设置环境变量和解决常见问题的方法。 ... [详细]
author-avatar
丹丹6655
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有