本文共 3951 字,大约阅读时间需要 13 分钟。
那么我们上篇文章中提及了安全性问题, ,那么本篇文章提及一点,so动态库的安全性与重要性。
首先我们要知道, .so动态库是做什么用的,它不像.smail文件可修改,它是属于汇编语言,如果直接去修改,文件会发生错乱。早上有人来问我,游戏打入渠道sdk之后发生错误,且只有armeabi里发生错误,这种情况可以断定.so动态库中有了兼容的冲突。
通常我们会看到libs下面有这么几个文件夹,mips、armeabi、armeabi-v7a和x86,其实是代表着不同的CPU类型,那么在arm下有不同的指令,想要了解的可以参考这篇文章 。
实现步骤:
Tips:
我们以 趣头条.apk 为例,反向思维来看下这个包是如何加密的,首先我们来看目录结构:
我们可以看到,smail里面是没多少代码的,主要的是so动态库和两个jar包,打开jar包来看看:(bdxadsdk.jar 和 gdtadv2.jar ) 什么都没有,可以肯定的是,这个是加了壳的,加密方式是怎么样的呢,我们来参考网上一张图: 我们先去看下 smail文件里面有什么线索没 “libjiagu” 顾名思义,加固,我们发现 ,这个包还加固了。来,继续往下走“DexOptJobService_DexOptimization” 动态加载了Dex
那么动态加载dex技术是如何处理的?引用一张图来看看,顺便过下这个知识点关于PathClassLoader,API中提及: Android uses this class for its system class loader and for its application class loader(s) —->> Android应用就是用它来加载;
DexClass可以加载apk,jar,及dex文件,但PathClassLoader只能加载已安装到系统中(即/data/app目录下)的apk文件。
子节点都是从继承BaseDexClassLoader中来的,那我们去看看源码:
@Override protected Class findClass(String name) throws ClassNotFoundException { ListsuppressedExceptions = 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, Listsuppressed) { 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动态库?
等我稍后整理下图片,先发布了,持续更新。