標籤:add b16 ref 不同 write 寄存器 arm 必須 ram
先回顧下 x86 下的內聯 hook.
1.原理是找到你要 hook 的地址。
2.儲存這個地址原來的資料。(這裡要儲存至少 5 個位元組的資料因為一個 call指令為 5 個位元組
3.把這個地址修改成 call 0Xxxxx(5 個位元組 ) 也就是對應 opcode 為 E9 0Xxxxx 後面四個位元組為一個函數地址
4.填充0Xxxxx 公式為 自己的函數地址-當前地址-5
5.把原來的那個 5 個位元組的 opcode 還原重新從 hook點開始執行
arm 下
第一點有 倆種模式Arm模式與Thumb模式
在Arm版本7及以上的體系中,其指令集分為Arm指令集和Thumb指令集。Arm指令為4位元組對齊,每條指令長度均為32位;Thumb指令為2位元組對齊,又分為Thumb16、Thumb32,其中Thumb16指令長度為16位,Thumb32指令長度為32位。
第二點函數調用指令為:
B系列指令:B、BL、BX、BLX
PC寄存器 相當於 eip
第三點不這倆種模式流水線級數不同。arm一般為 3 級 。也就是說在 arm模式下 pc 指向下一條指令的向一條。因為 hook是 pc 要回退 4 個位元組(4位元組對齊)
因此對應:
LDR PC, [PC, #-4]addr
而Thumb(32位)不用回退
LDR.W PC, [PC, #0]addr
因為也就相當於 LDR.W PC, [PC, #0]/LDR PC, [PC, #-4] 對應x86 中的 call 對應 opcode
call : E9
LDR.W PC, [PC, #0]:0x00F0DFF8 (LDR.W強制以 4 位元組)
LDR PC, [PC, #-4] :0xE51FF004
後面就與 x86 方式一樣了修改地址:
x86 call 0XxxxxarmLDR PC, [PC, #-4]Thumb(32位)addrLDR.W PC, [PC, #0]addr
最後要注意一個非常容易忽略的點就是地址一般為 4 個位元組,也就是說Thumb(32位)的每條指令是 2 個位元組對齊的因此只能 2 個位元組的寫。而 arm 每條指令為 4 位元組對齊。因此應該考慮有時候只有 2 位元組,也就必須後倆個位元組用 nop填充。因為指令沒有 4 位元組對齊為非法指令。
也就是寫法為:
//開始 hook//arm 方式就一種//thumb 方式有倆種 一種是轉成arm 再 hook 一種是直接 hookstatic void doInlineHook(struct inlineHookItem *item){ //修改頁屬性 mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); if (item->proto_addr != NULL) { *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions; } //Arm指令為4位元組對齊,每條指令長度均為32位;Thumb指令為2位元組對齊, // 又分為Thumb16、Thumb32,其中Thumb16指令長度為16位,Thumb32指令長度為32位。 //這裡要注意的是: //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 // //LDR PC, [PC, #-4]對應的機器碼為:0xE51FF004,addr為要跳轉的地址。 //該跳轉指令範圍為32位,對於32位系統來說即為全地址跳轉。 //對於Thumb32指令集,跳轉指令為: // //LDR.W PC, [PC, #0] 不要回退剛好指向下一條 //addr // //LDR.W PC, [PC, #0]對應的機器碼為:0x00F0DFF8,addr為要跳轉的地址。同樣支援任意地址跳轉。 //thumb 模式(Thumb32指令集)為倆位元組對齊 if (TEST_BIT0(item->target_addr)) { int i; i = 0; //判斷是否為4 位元組對齊 用NOP填充 if (CLEAR_BIT0(item->target_addr) % 4 != 0) { ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP } //LDR偽指令 LDR.W 強制32位 //倆個位元組為一組 //LDR.W PC, [PC, #0]對應的機器碼為:0x00F0DFF8 每次一個四位元組要分為倆個2 位元組 ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF; ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC] //一個四位元組地址 同樣分為倆個2 位元組 //取低四位 ((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 模式 指令為4位元組對齊 else { //四個位元組為一組 ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4] //下一條4位元組地址 ((uint32_t *) (item->target_addr))[1] = item->new_addr; } mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); item->status = HOOKED; cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0);}
源碼:
https://github.com/ele7enxxh/Android-Inline-Hook
其它部落格地址:
http://ele7enxxh.com/Android-Arm-Inline-Hook.html
https://www.cnblogs.com/mmmmar/p/8185549.html
android 內聯 hook