最近看到Android手机上金山毒霸实现了一个过滤广告的功能(也不确认是不是最早的),在注入过程中还包括JAR和dex注入,这让我产生了兴趣。
有关金山的注入在看雪论坛上有简要的分析:
http://bbs.pediy.com/showthread.php?t=166151
这里主要是被JAR注入吸引到了,不知道JAVA原来也能玩注入。不过替换的本质我却产生了个误解:难道JAVA的方法能够被动态替换?当然后来问了达人,得到一个结论,C#和JAVA初始化的过程不是一样的,方法的动态替换。据说C#可以,但是JAVA不行。
首先在网上搜索了一些有关JAVA注入的资料,有2套资料比较有帮助:
1、xantorohara:http://xantorohara.blogspot.tw/2007/09/java-code-injection-via-winapis.html
2、vmattach:http://www.blogjava.net/javacap/default.html?page=5
一、在资料1中,按照作者提供的代码,(最好是XP环境下)编译完成之后,在我运行Notepad.jar之后,再注入JAR运行得到了如下图所示结果:
这的确实现了在对方的JAR(或者说java.exe)中运行了注入的JAR代码(Insider.java实现):
public class Insider implements Runnable { static { Thread t = new Thread(new Insider()); t.setPriority(Thread.MIN_PRIORITY); t.setDaemon(true); t.start(); } public void run() { int i = 0; while (true) { System.out.println(i++); try { Thread.sleep(300); } catch (InterruptedException e) { break; } } } }
Xantorohara在实现过程中包含了一共5个重要的代码文件:
Injector.java —— 用于实现注入的JAVA代码,PID值由用户输入
Injector.cpp —— 用于实现注入的JNI代码,就是利用了Win32下的CreateRemoteThread函数,在远程进程中实现了LoadLibraryA("Insider.dll"),用于加载Insider.dll
Insider.java —— 构造了一个Insider类创建线程不断的输出数字,就是图示的情况
Insider.cpp —— Insider.dll的代码,它利用了Bin2H.java将Insider.java生成了class字节码存入Insider.h的jbyte clazz[]数组,在Insider.cpp中利用DefineClass创建新类,并且激活了这个类的代码:
C语言中的env->FindClass(className)类比JAVA下的Class.forName(className)
Bin2H.java —— 主要用于将javac Insider.java得到的Insider.class的字节码存入Insider.h的jbyte clazz[]数组
这里我们可以看得出来,要实现一个JAVA的注入,我们至少需要2个DLL:一个是用于主要负责原生工作的注入代码(主要调用CreateRemoteThread);另外一个DLL主要是在注入远程进程调用LoadLibraryA后启动,用于加载或者初始化类从而完成JAR的工作。
二、资料2中,作者没有提供相对完整的原生部分代码,但是展现的代码更为注入的意义
作者的VMAttach包中包含了2个DLL,VMAttach.dll和VMattach.jar配合主要是实现在JAVA(VMAttach.jar)下调用JNI(VMAttach.dll),像远程的JAVA进程注入了worker.dll,而worker.dll中通过IDA分析其通过JNI下实现的类似于如下JAVA代码:
loader = new URLClassLoader(new URL ("jar:file:/C:/AttachDemo.jar!/AttachDemo"))
来加载了AttchDemo.jar(入口为AttachDemo)并且调用FindClass、CallStaticVoidMethod函数运行了AttachDemo.jar中的main方法。
在main方法中,远程进程通过反射的方式,获得了UseSingletonApp中 private volatile int stateNum; 的当前值,测试效果如下所示:
后记:
最后,我google了很久也没找到一个关于JAVA动态时候替换方法的办法,有一些比如说热替换的思路,但是是在特殊构造的代码情况下能够实现;除此之外就是利用开源的ASM类修改字节码实现,不过这不属于“运行时替换”吧。虽然能够注入JAVA代码,但是如果想实现类似API HOOK的方式,必须找到一种替换其方法的办法,只是可惜最后没有找到,仅仅只是实现了一个可用于部分访问成员的代码注入而已。