天天看点

用DexClassLoader实现加壳

本文对dex加壳机制中的DexClassLoader部分做了稍微详细的介绍,解决了我的一些疑问,收藏之!

原文地址:http://blog.csdn.net/beyond296089727/article/details/45418803

JAVA 运行所需要依赖的API的是动态链接的,这个跟C/C++不一样,C/C++开发的程序可以是静态链接的也可以是动态链接的,其中静态链接是在编译时已经做好的,

动态链接的运行时才链接的,也就是window 的DLL文件,linxu的SO文件都是动态链接库。java是动态链接,也就是说我们代码中 new一个对象,

static Class 调用,Class.forNmae(),Class.loderclass()这些动作,JVM先把这个class文件

加载到内存然后产生一个对象并吧这对象的地址返回给new,这样我们就可以调用对象的成员了。

其中JVM把class文件加载到内存就是用classloader了,关于classloader的详解可以参考这里http://blog.chinaunix.net/uid-21227800-id-65885.html

很高兴的是JAVA API中为我们提供了ClassLoader这个类,我们用这个类可以通过反射加载自己的class,正因为如初使得JAVA有很强的生命力,在防破解

加壳自定义插件方面发挥的很好,比如ecplise的思想就是“一切都是插件”,他的功能都是通过安装插件完成的,扩展性很强,是很多公司都为他开发插件作为IDE

,也很受开发者欢迎,其中ecplise就是靠ClassLoader加载插件了,这就看出了ClassLoader的强大。

在android中虽然android 的APK是用JAVA开发的,但是android 的虚拟机dalvik并不是java标准的JVM,android的可执行文件是DEX,而JVM的是.class他们并不兼容,

关于他们的区别可以参考这里http://www.w2bc.com/Article/14646  。自然的用JAVA 标准的classloader 不能加载DEX,但是谷歌还是基本支持全部JAVA的特性,

因此classloder这个很好的JAVA特性谷歌不会放过,所以谷歌也提供给我们加载DEX的classloader,他的特性和用法跟JAVA标准的classloader差不多。

一个是DexClassLoader另一个是PathClassLoader,他们的区别可以参考这里http://www.apkbus.com/forum.php?mod=viewthread&tid=138499

这里只介绍DexClassLoader,那我们怎样用她来加壳免杀?

用DexClassLoader实现加壳

先看看API文档的说明他有四个参数,dexpath表示所要加载DEX文件的路径,

optimizedDirectory便是优化的DEX存放路径,

libraryPath表示DEX中如果调用自己的SO文件的路径,parent表示为自己的DexClassLoader指定父classloader

我们知道Classloader是双亲委派机制的,这是因为每个classloader只会在指定的文件了寻找和加载class,比如 上面如果在dexpath参数里指定了/sdcard/test.dex,那么这个dexclassloader只会在/sdcard/test.dex查找class, 如果我们的DEXclassloader通过反射加载了/sdcard/test.dex里的一个class com.my.test,而com.my.test如果 有 这样一句 String str="22555",我们知道JAVA是动态连接语言,这是如果 String类需要加载,那么他默认的classloader也是所属class的classloader,比如这里所属类是com.my.test,而com.my.test的classloader是我们的 DExclassloader,而如果我们的DExclassloader去自己的文件里test.dex找String类自然找不到,就抛出ClassNotFoundExcpiton,所以双亲委派机制就解决这个问题,当一个classloaer去加载一个class时他先调用他的parent Classloader去加载,如果parent Classloader没有在自己的文件里找到这个classs那么子classloader才去加载。

那么怎么加壳和免杀?

其实思路很简单,把我们所要加壳保护的代码导出成DEX,关于导出DEX看着里 http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html 然后加密,放到服务器或者APK其他文件夹, 运行时通过再读取文件解密然后再用dexclassloader加载运行,这样就可以保护我们的代码的, 其实爱加密,360等加固都是这样的基本思路

比如爱加密的就这这样

用DexClassLoader实现加壳

箭头指向的就是原DEX,就是被加密保护的,至于加密解密其实很简单,我举个最简单的例子,把要保护的DEX文件

的每个字节值+1,解密时-1,这样即使拿到DEX也没用。加密我们可以事先通过算法加密,然后才放到APK里,

关键是解密,我们都知道APK的入口是application类,这时我们这样在applicatian类里运行加密程序完成DEx的解壳然后用dexclassloader加载并运行,

当然了处于安全,解壳和加载DEx的算法应该放在SO,在application里通过JNI调用,其实爱加密也是这样做

用DexClassLoader实现加壳

图中的箭头指向就是他的解密SO

关于爱加密详细原理可以参考这里

http://blog.csdn.net/chengyingzhilian/article/details/38372601

所以用dexclassloader加壳就这么简单

至于包名的跟不用说了,每个APK都有自己唯一的包名,没有源码情况下

改包名麻烦,也就是说包名一般不该,所以给杀毒软件提供了很好的特征

依据,也就是说这个包名的APK如果被检测为木马的,哪怕代码在修改

包名不改很容易看出。

不过这里我不提倡大家用于实质非法活动,这是供大家学习研究,对杀毒

安全的研究和理解

关于用dexclassloader动态加载DEx存在的问题,我们知道dexclassloader

加载的类生命周期是虚拟机直接管理,但是对于组件类比如activity他们的声明周期不是由虚拟机直接管理,而是由各种对应的框架层manager管理,比如activity是有activitymanger

用DexClassLoader实现加壳

管理这就是为什么我们不能new activity的原因,只能通过startActivity()反射加载。

但是最终activity也是用户classloader加载的。之所以这个组件类是用各框架层管理器

管理是有原因的,因为这些类的声明周期是随机的,不可预知的,比如接受短信的

BroadcastRecivier,当有短信来时系统收到了,交给广播管理器广播intent,如果我们的

的androimainfest.xml定义了一个<recivier>,来接受短信,这是广播管理器会通知我们APP的主线程new 我们的BroadcastRecivier类并且调用onReciver()函数,这一切都是通过反射加载的。也就是new BroadcastRecivier是有短信来了才NEW,而 短信是随机的,所以我们不可能在代码中new ,你知道短信仕么时候来吗?!那么你NEW?!

这里就关键了,BroadcastRecivier组件类最终也是用classloader加载的,如果这个classloader是我们定义的Dexclassloader那么不管我们的组件类放在哪里都会被正常加载并且不存在生命周期问题,然而加载这些组件类的都是都APK的主线程ActivityThread来做,他用的是他自己的classloader加载,我们只把他的classloader替换成我们移动一的就得了

关于实现方案我是参考jack_jia的来做http://blog.csdn.net/androidsecurity/article/details/8809542

红色箭头是包含有activity等组件类的DEX,椭圆椭圆圈是替换classloaderder

用DexClassLoader实现加壳

继续阅读