估計廠商也認識到了APP在JAVA環境下運作收集到的資訊被篡改的幾率很大,是以很多廠商把目光标準了JNI層。
JNI層的運算是獨立于JAVA層以外的、更底層的運算,也就是說,你用XPOSED安裝了一個JAVA層的鈎子修改了機器的重要資訊A,但從JNI層讀取資訊A的時候,并不會觸發你的鈎子,是以傳回值是系統真實值。
那麼JNI層的資料如何修改呢?如何進入JNI層?進入JNI層後如何使用鈎子?
這裡我提供兩個思路 兩個思路都有完整的JAVA+JNI層的HOOK功能和架構 而且兩種方案都有自己的優缺點,靠你自己取舍。
1 Cydia Substrate
具體可以參考
http://www.cydiasubstrate.com/
http://www.cydiasubstrate.com/inject/dalvik/ (java層支援)
http://www.cydiasubstrate.com/inject/android/ (JNI/Native層支援)
cydia架構的優勢很明顯,同時支援jni和java層的HOOK功能,缺點也很明顯,相容性不好,官網上标注最高支援安卓4.3版,但實際中測試發現一些第三方的ROM會出現一些莫名其妙的問題,而據說有人用安卓原生5.0也可以完美運作。是以是否使用CYDIA架構,要根據你目前的硬體情況來選擇,如果你的手機ROM可以運作CYDIA,我更推薦使用這種完整的內建方案
使用cydia開發 搭建架構我這裡不多重複了,可以登入官網檢視文檔。
public class Main
{
public static int nTest=0;
static void initialize()
{
//函數入口
//do something........
}
}
根據cydia和xposed的架構原理來看,cydia的開發成本會更高一些。
在你第一次使用這個架構時,編譯後會出現很多莫名其妙的問題,如果你之前在c/c++語言上有過比較深的基礎并且做過hook的相關項目,這些莫名其妙的問題解決起來會很輕松.
其中有三個點需要注意:
① cydia中的所有變量都是全局變量,例如,你在App A中個修改了nTest,那麼會同步到其他App程序所有對Main.nTest的引用上。
② cydia中因為無法直接獲得目前App的資訊,你需要使用一個聰明的方式獲得你想要東西作為你是否要HOOK目标程序的判斷依據。
③ cydia引入了hookClassLoad方法,這個和xposed中的設計理念是不同的,詳細資料看文檔。
例如你可以用cydia hook捕獲到Activity的OnCreate建立過程
Class<?> resources = android.app.Activity.class;
Method method_name=HTool.GetDeclaredMethodFromClass(resources,"onCreate",Bundle.class);
if (method_name != null) {
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, method_name, new MS.MethodHook() {
@Override
public Object invoked(Object obj, Object... args) throws Throwable {
Object result_old = old.invoke(obj, args);
Log.i(CreateTag,"onCreate PackName:"+PackName);
return result_old;
}
}, old);
}
也可以使用cydia jni hook攔截getprop的傳回值
int (*old__system_property_read)(int pi, char *name, char *value);
int new__system_property_read(int pi, char *name, char *value)
{
int result=old__system_property_read(pi,name,value);
return result;
}
//程式入口
MSInitialize
{
MSImageRef image = MSGetImageByName("/system/lib/libc.so");
if (image != NULL)
{
void *hook__system_property_read=MSFindSymbol(image,"__system_property_read");
if(hook__system_property_read) MSHookFunction(hook__system_property_read,(void*)&new__system_property_read,(void **)&old__system_property_read);
else LOGE("error find __system_property_read ");
}
}
上面的例子HOOK了native層的__system_property_read方法,重定向到new__system_property_read。該方法是執行指令getprop的關鍵過程,配置好代碼可以修改getprop的所有傳回值
2 Xposed+Libc_hook.so
下面說第二種方案,如果你的安卓系統不能安裝cydia 但同時又需要jni層的HOOK 那麼我推薦你使用下面這套方案,實作成本相對第一種要複雜一些,但是跟着我所寫的步驟,一定能得到你所要的功能。
在不能安裝cydia的情況下如何進入jni層HOOK呢?很簡單,找到關鍵so庫檔案,以病毒修改elf的方式,加載自己的注入代碼,把自己的hook.so注入到該程序中即可
首先找到一個注入點,這裡我推薦libc.so 因為基本上每個程序都要用到libc,更重要的是 libc的加載時機足夠早。
① abd pull /system/lib/libc.so 先把手機上的libc.so到本地 然後使用ida打開
② 打開Function Window視窗找到dlopen函數的位址 0xEAD0
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiMzQjNxYTNzITMwATM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
③ 打開Program Segmentation視窗找到.init_array的位址 0x57480
④ 按ALT+B搜尋80個0 此位址随意 本例中找到位址為 0x51F00
⑤ 按照上面的三個數字 對應帶入下面的代碼中 使用vs2015 c#編譯通過
⑥ 運作時 把libc.so放在exe同目錄下可得到libc_ok.so
使用上面的程式生成的libc_ok.so覆寫回/system/lib/libc.so即可
以後每當程式運作,都會自動加載/system/lib/libckis.so庫檔案 可以盡情的HOOK了!
class My
{
static int dlopenAddr = 0xEAD0;
static int InitArrayAddr = 0x57480-0x1000;
static int asmAddr = 0x51F00;
static byte[] asm = { 0xFF,0x40,0x2D,0xE9,0x18,0x00,0x9F,0xE5,0x00,0x10,0xA0,0xE3,0x00,0x00,0x8F,0xE0,
0x01,0xF7,0xFF,0xEB,0xFF,0x40,0xBD,0xE8,0x08,0x00,0x9F,0xE5,0x00,0x00,0x8F,0xE0, 0x10,0xFF,0x2F,0xE1,0x9C,0x68,0x03,0x00,0x09,0xBF,0x01,0x00,0x00,0x00,0x00,0x00};
static int StrAddr = asmAddr + 0x40;
static void Main(string[] args)
{
BinaryReader br = null;
try
{
br = new BinaryReader(new FileStream("libc.so", FileMode.Open));
}
catch (IOException e)
{
Console.WriteLine(e.Message + "Cannot open file.");
return;
}
int length = Convert.ToInt32(br.BaseStream.Length);
byte[] data = br.ReadBytes(length);
br.Close();
byte[] LibCkisSo = System.Text.Encoding.ASCII.GetBytes("libckis.so");
Array.ConstrainedCopy(LibCkisSo, 0, data, StrAddr, LibCkisSo.Length);
int JmpReturnAddr = System.BitConverter.ToInt32(data, InitArrayAddr);
byte[] byAsmAddr = System.BitConverter.GetBytes(asmAddr);
Array.ConstrainedCopy(byAsmAddr, 0, data, InitArrayAddr, byAsmAddr.Length);
byte[] JmpOffset = System.BitConverter.GetBytes(JmpReturnAddr - asmAddr - 0x24);
Array.ConstrainedCopy(JmpOffset, 0, asm, 0x28, JmpOffset.Length);
byte[] StrOffset = System.BitConverter.GetBytes(StrAddr - asmAddr - 0x14);
Array.ConstrainedCopy(StrOffset, 0, asm, 0x24, StrOffset.Length);
byte[] DlopenOffset = System.BitConverter.GetBytes((dlopenAddr - asmAddr - 0x18) / 4);
Array.ConstrainedCopy(DlopenOffset, 0, asm, 0x10, StrOffset.Length - 1);
Array.ConstrainedCopy(asm, 0, data, asmAddr, asm.Length);
BinaryWriter bw = new BinaryWriter(new FileStream("libc_ok.so", FileMode.Create));
bw.Write(data);
bw.Close();
}
}
上面着重講解了cydia架構的使用以及手動注入jni層的方法,這種方法從原理上,可以100%的過目前的所有的檢測,這是單一xposed架構做不到的。