天天看點

安卓刷量技術揭秘(二) 進階攻防技巧

估計廠商也認識到了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

安卓刷量技術揭秘(二) 進階攻防技巧

③ 打開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架構做不到的。