网秦安全盾 原理分析

发布者:我是小三
发布于:2014-11-18 17:23

环境:
手机: 中兴 u887
系统 android2.3.5
调试器: IDA pro 6.5

一:加固前后对比
加固前 classes.dex 大小如下:

加固后 classes.dex 大小如下:

那原来的 classes.dex 去那儿了呢?我们再来对比下资源目录下都多了些什么?
加固前的:

加固后的: 

Lib 目录:

加固后:

比较发现多了些文件,具体这些文件有什么用请看下面分析。

二:java 层概述
1.通过反编译看,从 AndroidManifest.xml 文件中的 application 项可以看出壳的入口为:

2.简单分析 com.nqshield.NqApplication 类都做了些什么?
.不能反编译成 java 代码,直接看 smali 代码吧。
在 NqApplication 类 method protected attachBaseContext(Landroid/content/Context;)V
中都分别调用了这些函数:
3..invoke-static{p0},Lcom/nqshield/Common;->loadXShellLib(Landroid/content/Cont
ext)V
从名字可以看出是加载 so 库,并注册 JNI 函数。
通过分析 JNI 方法注册到 Dalvik 虚拟机的过程,可以得到 java 层 native 函数与 so 层函数
对应的关系为:
native nq10 -> b9df1ce797fd03603fd7137afff2957
native nq11 -> eb5993d402df42eac47e82555b7ae31
native nq12 -> fa3f3a501e8754e88bed48c128ef90
native nq13 -> abc020d3ee6fbc05ab1ed6b4da7252d
native nq14 -> e300588cc034bcbaa7d61
(*如有不清楚如何分析这个过程的可以参考学习罗升阳 的文章)
4..invoke-static{p0},Lcom/nqshield/Common;->CopyBinaryFile(Landroid/content/Con
text;)V
该函数是将 assets 文件夹中的 DexToLoad.apk 与 nqdata 拷贝到/data/data+包名/.cache
目录中,然后调用 nq10 函数,(该函数会在下面分析)。
5..invoke-static{p0},Lcom/nqshield/jniExport;->getDexClassLoader(Landroid/conte
nt/Context;)Ljava/lang/ClassLoader;
该函数通过调用 DEXClassLoader 完成对加密的/data/data+包名/.cache/DexToLoad.apk 的
动态加载,内存中解密 DexToLoad.apk 并组合 odex, 完成动态加载。 (详细过程看 so 层分
析) 。
类似下面的样子:

6.invoke-virtual{v0,p0,v1,v2},Lcom/nqshield/jniExport;->nq12(Landroid/content/C
ontext;Ljava/lang/ClassLoader;I)V
传入 getDexClassLoader 返回值。
7.invoke-virtual{v0,v1,v2},Lcom/nqshield/jniExport;->nq13(Ljava/lang/String;I)V
so 层分析
8.最后在.method public onCreate()V 中调用
invoke-virtual {v0, v1, v2}, Lcom/nqshield/jniExport;->nq14(Ljava/lang/String;I)V
三:so 层分析:
1.通过 readelf -S libnqshield.so 查看 so 信息,发现 INIT_ARRAY 不为空,

用 IDA 打开 so,G 到 INIT_ARRAY 中的地址去看看.

对应 so 函数名被混淆了,动态分析发现这些函数都是解密字符串用的,根据传进的数字解
密相应的字符。

下面是传入要解密的字符串。

110 代表解密后的字符为: nq10
115 代表解密后的字符为: (Ljava/lang/String;Ljava/lang/String;I)V
111 代表解密后的字符为:nq11
等等...以此类推
2.JNI_OnLoad 中都做了些什么?
第一个函数 fork 一个子进程,并创建一个线程,线程函数如下所示

inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,
int wd = inotify_add_watch (fd, path, mask); 用于添加一个 watch
fd 是 inotify_init() 返回的文件描述符, path 是被监视的目标的路径名(即文件名或目
录名), mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以
使用同样的方式来修改事件掩码,即改变希望被通知的 inotify 事件。Wd 是 watch 描述
符。
在 arch-arm\usr\include\linux\ inotify.h 头文件中定如下:
#define IN_OPEN 0x00000020
,根据线程函数可以看出它是监视/proc/pid/maps 的打开事件.
(*不明白的可以点这里 http://blog.csdn.net/myarrow/article/details/7096460)

我猜测这个线程的功能可能是做反注入用的。
2.解密字符串
传入数字 79 代表解密后的字符为: com/nqshield/jniExport
3.注册 JNI 函数
对应关系
native nq10 -> b9df1ce797fd03603fd7137afff2957
native nq11 -> eb5993d402df42eac47e82555b7ae31
native nq12 -> fa3f3a501e8754e88bed48c128ef90
native nq13 -> abc020d3ee6fbc05ab1ed6b4da7252d
native nq14 -> e300588cc034bcbaa7d61
4.下面对这几个函数进行分析
nq10 函数在 java 层 CopyBinaryFile 函数中被调用到,我们在 so 中对
b9df1ce797fd03603fd7137afff2957 函数下断点,动态分析看它都做了些什么?
函数里面调用了 result = initEnv(a5, "DexToLoad.dex", "DexToLoad.apk", &s);
进入该函数分析,该函数对 libdvm.so 与 libnativehelper.so 中的操作文件等一些函数进
行 hook 如下图:

我 们 分 别 对 这 几 个 hook 函数 (myopen,myread 等 ) 下 好 断 点 , 当 java 层 调 用
getDexClassLoader 函数时会走到这些 hook 函数中,b9df1ce797fd03603fd7137afff2957
函数就分析完成了。
5.java 层的 CopyBinaryFile 函数执行完后就要执行 getDexClassLoader 函数了,该函数是
调用 DexClassesLoader 动态加载 DexToLoad.apk,继续动态走,断在 myopenh 函数中,该函数
会判断是不是打开 DexToLoad.apk 文件,如果是就会创建一个 DexToLoad.apk.zip 文件。
接着来到 myopen 函数中,判断是否打开 DexToLoad.apk,如果是会调用函数 access 判断
/data/data/yiqi.bazi/.cache/DexToLoad.apk 是否存在,存在就打开它并将其读取到指定
内存,然后解密,如下图:

6.组合生成 ODEX:
下会根据 ODEX 结构图进行组合:

(*该图来自 “ Android 软件安全与逆向分析” 一书,如有对该结构不明白的地方可以
去阅读这本书,书里有详细讲解)。
首先使用 zlib 函数 inflateInit2_与 inflate 对上面解密出来的 DexToLoad.apk 数据从
classes.dex 标志后进行解压得到 dex 数据, 在 inflateEnd 处下断 F9 执行, 如果要脱壳的
话,这个时候是最佳的 dump 时机,可得到完整的 Dex 数据。(组合完后会对 dex 做些手脚)接
下来会按照上图 odex 结构图进行组合,
在解压出来的 dex 前写入 ODEX 文件头如下图所示:

打开/data/dalvik-cache/mnt@asec@yiqi.bazi-1@pkg.apk@classes.dex 读取依赖库到 dex
后面,如下图:

打开/data/data/yiqi.bazi/.cache/nqdata 读取辅助数据到依赖库后面,如下图:

组合完后就是 vm 加载过程了。
7. 接着 java 层调用 nq12 nq13 nq14 函数
反射调用 LoadedApk.mApplication, LoadedApk.mClassLoader,
ActivityThread.mInitialApplication,
ActivityThread.mAllApplications 等值,将其重新
指向目标 Application 和 ClassLoader。确保系统
稍后构造组件时能正确的加载到目标类。
三: 到此分析完成,最后,将控制权给目标 Application。

完。

样本及文档下载

http://yunpan.cn/cA3U6hctfwfj3 (提取码:9b1e)


声明:该文观点仅代表作者本人,转载请注明来自看雪