android 內聯 hook
阿新 • • 發佈:2018-07-27
bit != fff andro html -a 速度 git 其它 先回顧下 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] :0xE51FF004
後面就與 x86 方式一樣了修改地址:
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 字節)
後面就與 x86 方式一樣了修改地址:
x86
call 0Xxxxx
arm
LDR PC, [PC, #-4]
Thumb(32位)
addr
LDR.W PC, [PC, #0]
addr
最後要註意一個非常容易忽略的點就是地址一般為 4 個字節,也就是說Thumb(32位)的每條指令是 2 個字節對齊的因此只能 2 個字節的寫。而 arm 每條指令為 4 字節對齊。因此應該考慮有時候只有 2 字節,也就必須後倆個字節用 nop填充。因為指令沒有 4 字節對齊為非法指令。
也就是寫法為:
//開始 hook //arm 方式就一種 //thumb 方式有倆種 一種是轉成arm 再 hook 一種是直接 hook static 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