最近看到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的方式,必須找到一種替換其方法的辦法,只是可惜最后沒有找到,僅僅只是實現了一個可用於部分訪問成員的代碼注入而已。
