Android的inline hook

arm平臺與其他平臺的inline hook原理一致,均爲函數arm字節碼替換
由於指令集的特點,arm平臺的inline hook較爲複雜:
1、存在arm指令集和thumb指令集
2、無法單純使用B指令直接跳轉,因爲b指令跳轉範圍優先
3、存在literal指令,與當前PC值相關,需要對指令進行修正

以arm指令集作爲hook實例
由於arm架構特性,每次修改代碼需要刷新cache,因爲指令icache和數據dchche是分開的。

Arm和Thumb模式的區別

在Arm版本7及以上的體系中,其指令集分爲Arm指令集和Thumb指令集。Arm指令爲4字節對齊,每條指令長度均爲32位;Thumb指令爲2字節對齊,又分爲Thumb16、Thumb32,其中Thumb16指令長度爲16位,Thumb32指令長度爲32位。
在對一個函數進行Inline Hook時,首先需要判斷當前函數指令是Arm指令還是Thumb指令,指令使用目標地址值的bit[0]來確定目標地址的指令類型。bit[0]的值爲1時,目標程序爲Thumb指令;bit[0]值爲0時,目標程序爲ARM指令。其相關實現代碼爲以下宏:

// 設置bit[0]的值爲1
#define SET_BIT0(addr)		(addr | 1)
// 設置bit[0]的值爲0
#define CLEAR_BIT0(addr)	(addr & 0xFFFFFFFE)
// 測試bit[0]的值,若爲1則返回真,若爲0則返回假
#define TEST_BIT0(addr)		(addr & 1)

跳轉指令

因爲b系列指令跳轉範圍有限,所以我們使用LDR PC, [PC, ?]構造跳轉指令

Arm處理器採用3級流水線來增加處理器指令流的速度,也就是說程序計數器R15(PC)總是指向“正在取指”的指令,而不是指向“正在執行”的,即PC總是指向當前正在執行的指令地址再加2條指令的地址。比如當前指令地址是0×8000, 那麼當前pc的值,在thumb下面是0×8000 + 2 2, 在arm下面是0×8000 + 4 2。

對於Arm指令集,跳轉指令爲:

LDR PC, [PC, #-4]
addr

對應的機器碼爲:0xE51FF004,addr爲要跳轉的地址。該跳轉指令範圍爲32位,對於32位系統來說即爲全地址跳轉。

對於Thumb32指令集,跳轉指令爲:

LDR.W PC, [PC, #0]
addr

LDR.W PC, [PC, #0]對應的機器碼爲:0x00F0DFF8,addr爲要跳轉的地址。同樣支持任意地址跳轉。

注意arm要4字節對齊,thumb要2字節對齊。不是對齊的要填充nop對齊。

// thumb Mode
if (TEST_BIT0(item->target_addr)) {
	int i;

	i = 0;

//首先通過TEST_BIT0宏判斷目標函數的指令集類型,其中若爲Thumb指令集,多了下面一個額外處理,對bit[0]的值清零,若其值4字節不對齊,則添加一個2字節的NOP指令,使得後續的指令4字節對齊。這是因爲在Thumb32指令中,若該指令對PC寄存器的值進行了修改,則該指令必須是4字節對齊的,否則爲非法指令。
	if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
		((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00;  // NOP
	}
//下面這4行,前2行是LDR.W PC, [PC, #0]對應的機器碼爲:0x00F0DFF8,後兩行是addr,要跳轉的地址
	((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF;//litterending
	((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000;	// LDR.W PC, [PC]
	((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF;
	((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16;
}
// arm Mode
else {
	((uint32_t *) (item->target_addr))[0] = 0xe51ff004;	// LDR PC, [PC, #-4]
	((uint32_t *) (item->target_addr))[1] = item->new_addr;
}

然後就是該內存屬性爲可寫,刷新cache

mprotect((void*)(target_addr & ~4095), 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);//改權限,加寫
	
	
	clearcache((void*)target_addr, (void*)(target_addr + 4));

後續如果比如需要修正PC,因爲如果hook替換函數要在我們代碼裏寫原來被hook覆蓋代碼使用到了pc相關內容,需要修復,可以參考博客

http://ele7enxxh.com/Android-Arm-Inline-Hook.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章