一恩闻、目標
現(xiàn)在很多App不講武德了,為了防止 openat 剧董、read幢尚、kill 等等底層函數(shù)被hook,干脆就直接通過syscall的方式來做系統(tǒng)調用翅楼,導致無法hook尉剩。
應對這種情況有兩種方案:
- 刷機重寫系統(tǒng)調用表來攔截內核調用
- inline Hook SWI/SVC指令
我們今天采用第二種方法,用frida來實現(xiàn)
- 內聯(lián)匯編SWI/SVC做系統(tǒng)調用毅臊, syscall
- frida inline hook
- hook syscall
- frida ArmWriter
- frida typescript project
二理茎、步驟
inline Hook 原理
. 備份SWI/SVC部分的指令,重寫成為跳轉指令
. 跳轉到我們新的代碼空間管嬉,把之前備份的指令執(zhí)行一下皂林。然后執(zhí)行我們自己的邏輯。 (打印參數(shù)之類的)
. 跳回原程序空間蚯撩,繼續(xù)往下跑
重寫成為跳轉指令
這次hook使用 frida ArmWriter 來實現(xiàn)础倍,用 putBranchAddress 函數(shù)寫個跳轉指令,需要花20個字節(jié)胎挎,我們先看看修改之前的情況著隆。
// 我們定位的錨是 svc指令, 0x9374C1F8 它前面還有8個字節(jié)的指令這里一并替換
const address = syscallAddress.sub(8);
// 備份這20個字節(jié)呀癣,馬上它們就要被替換了
const instructions = address.readByteArray(20);
if (instructions == null) {
throw new Error(`Unable to read instructions at address ${address}.`);
}
// 把舊的20個字節(jié)打印出來
console.log(" ==== old instructions ==== " + address);
console.log(instructions);
// 開始替換成跳轉指令美浦,跳轉的地址是 createCallback 里面創(chuàng)建的新的代碼空間地址。
Memory.patchCode(address, 20, function (code) {
let writer = null;
writer = new ArmWriter(code, { pc: address });
writer.putBranchAddress(createCallback(callback, instructions, address.add(20), syscallAddress));
writer.flush();
});
// 把新的指令打出來對比下
console.log(" ==== new instructions ==== " + address);
const instructionsNew = address.readByteArray(20);
console.log(instructionsNew);
跑一下結果
==== old instructions ==== 0x937621f0
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 07 c0 a0 e1 42 71 00 e3 00 00 00 ef 0c 70 a0 e1 ....Bq.......p..
00000010 01 0a 70 e3 ..p.
==== new Code Addr ====
0xa9b83000
==== retAddress ====
0x93762204
==== new instructions ==== 0x937621f0
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 01 80 2d e9 04 00 9f e5 04 00 8d e5 01 80 bd e8 ..-.............
00000010 00 30 b8 a9 .0..
指令是修改成功了项栏,但是修改的對不對呢浦辨? 這時候需要祭出 IDA,Attach一下我們的demo。對比一下流酬。
新的代碼空間地址是 0xa9b83000币厕。 從我們修改的指令來看,它會跳轉到 0xa9b83000芽腾, 木有問題旦装。
然后返回的地址是 0x93762204, 恰好也是要回來的地址摊滔。
執(zhí)行備份指令阴绢,和我們自己的邏輯
執(zhí)行備份指令比較簡單,但是我們自己的邏輯可不能用Arm匯編來寫艰躺,frida已經(jīng)幫我們想好了呻袭,可以創(chuàng)建一個 NativeCallback, 執(zhí)行備份指令之后腺兴,直接可以跳轉到 firida的 NativeCallback左电。 聽起來很牛的樣子。
// Hook 邏輯页响,這里只打印 參數(shù)
hookSyscall(address, new NativeCallback(function (dirfd, pathname, mode, flags) {
let path = pathname.readCString();
log('Called openat hook');
log('- R0: ' + dirfd);
log('- R1: ' + path);
log('- R2: ' + mode);
log('- R3: ' + flags);
return 0;
}, 'int', ['int', 'pointer', 'int', 'int']));
......
// 創(chuàng)建一個新的代碼空間篓足,放我們自己的代碼
let frida = Memory.alloc(Process.pageSize);
// 開始寫程序了
writer = new ArmWriter(code, { pc: frida });
// 執(zhí)行備份的指令
writer.putBytes(instructions);
// 寄存器入棧,這里把r0也入棧了
// FF 5F 2D E9 STMFD SP!, {R0-R12,LR} 寄存器入棧
writer.putInstruction(0xE92D5FFF);
// 00 A0 0F E1 MRS R10, CPSR
// 00 04 2D E9 STMFD SP!, {R10} // 狀態(tài)寄存器入棧
writer.putInstruction(0xE10FA000);
writer.putInstruction(0xE92D0400);
// instructions.size = 20 + 5條指令
// 修改lr寄存器闰蚕,保障執(zhí)行我們自己的邏輯之后還能回來繼續(xù)向下執(zhí)行纷纫。
writer.putLdrRegAddress("lr",frida.add(20 + 5*4));
writer.putBImm(callback);
// 00 04 BD E8 LDMFD SP!, {R10} // 狀態(tài)寄存器出棧
// 0A F0 29 E1 MSR CPSR_cf, R10
writer.putInstruction(0xE8BD0400);
writer.putInstruction(0xE129F00A);
// FF 5F BD E8 LDMFD SP!, {R0-R12,LR} 寄存器出棧
writer.putInstruction(0xE8BD5FFF);
// 我回來了 0x93762204
writer.putBranchAddress(retAddress);
writer.flush();
再跑一下,
Called openat hook
- R0: 86
- R1: /proc/self/maps
- R2: 0
- R3: 0
三陪腌、總結
本文來自https://github.com/AeonLucid/frida-syscall-interceptor ,(對烟瞧,就是AndroidNativeEmu的作者诗鸭。 Orz) ,作者實現(xiàn)了 arm64下面的hook,我們把arm32的hook補上了参滴,所以調試的時候需要在 arm32的手機上去調試强岸。
frida 腳本采用 typescript project, 調試和編譯腳本的時候需要參照 https://github.com/oleavr/frida-agent-example 。
frida-syscall-interceptor和frida-agent-example在同級目錄砾赔。
生成js文件時蝌箍,會提示ArmWriter的putBranchAddress函數(shù)找不到,其實這個函數(shù)是存在的暴心,只是庫文件沒有更新妓盲, 手工在 declare class ArmWriter 里面增加一下 putBranchAddress 的聲明。
我們的實現(xiàn)只是把參數(shù)和結果打印出來了专普,在我們自己的 NativeCallback 中并不能方便的去修改這些入?yún)⒑头祷刂得醭摹_@個就給大家留作業(yè)了。 參照 https://github.com/zhuotong/Android_InlineHook 的原理檀夹,那就想怎么玩就怎么玩筋粗。
每每剖開自己寫過的代碼策橘,里面都應有血流出來。