如今不少App不講武德了,爲了防止 openat 、read、kill 等等底層函數被hook,乾脆就直接經過syscall的方式來作系統調用,致使沒法hook。git
應對這種狀況有兩種方案:github
咱們今天採用第二種方法,用frida來實現typescript
. 備份SWI/SVC部分的指令,重寫成爲跳轉指令安全
. 跳轉到咱們新的代碼空間,把以前備份的指令執行一下。而後執行咱們本身的邏輯。 (打印參數之類的)微信
. 跳回原程序空間,繼續往下跑markdown
此次hook使用 frida ArmWriter 來實現,用 putBranchAddress 函數寫個跳轉指令,須要花20個字節,咱們先看看修改以前的狀況。函數
// 咱們定位的錨是 svc指令, 0x9374C1F8 它前面還有8個字節的指令這裏一併替換
const address = syscallAddress.sub(8);
// 備份這20個字節,立刻它們就要被替換了
const instructions = address.readByteArray(20);
if (instructions == null) {
throw new Error(`Unable to read instructions at address ${address}.`);
}
// 把舊的20個字節打印出來
console.log(" ==== old instructions ==== " + address);
console.log(instructions);
// 開始替換成跳轉指令,跳轉的地址是 createCallback 裏面建立的新的代碼空間地址。
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);
複製代碼
跑一下結果oop
==== 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, 木有問題。spa
而後返回的地址是 0x93762204, 剛好也是要回來的地址。
執行備份指令比較簡單,可是咱們本身的邏輯可不能用Arm彙編來寫,frida已經幫咱們想好了,能夠建立一個 NativeCallback, 執行備份指令以後,直接能夠跳轉到 firida的 NativeCallback。 聽起來很牛的樣子。
// Hook 邏輯,這裏只打印 參數
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']));
......
// 建立一個新的代碼空間,放咱們本身的代碼
let frida = Memory.alloc(Process.pageSize);
// 開始寫程序了
writer = new ArmWriter(code, { pc: frida });
// 執行備份的指令
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} // 狀態寄存器入棧
writer.putInstruction(0xE10FA000);
writer.putInstruction(0xE92D0400);
// instructions.size = 20 + 5條指令
// 修改lr寄存器,保障執行咱們本身的邏輯以後還能回來繼續向下執行。
writer.putLdrRegAddress("lr",frida.add(20 + 5*4));
writer.putBImm(callback);
// 00 04 BD E8 LDMFD SP!, {R10} // 狀態寄存器出棧
// 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
複製代碼
本文來自github.com/AeonLucid/f… ,(對,就是AndroidNativeEmu的做者。 Orz) ,做者實現了 arm64下面的hook,咱們把arm32的hook補上了,因此調試的時候須要在 arm32的手機上去調試。
frida 腳本採用 typescript project, 調試和編譯腳本的時候須要參照 github.com/oleavr/frid… 。
frida-syscall-interceptor和frida-agent-example在同級目錄。
生成js文件時,會提示ArmWriter的putBranchAddress函數找不到,其實這個函數是存在的,只是庫文件沒有更新, 手工在 declare class ArmWriter 裏面增長一下 putBranchAddress 的聲明。
咱們的實現只是把參數和結果打印出來了,在咱們本身的 NativeCallback 中並不能方便的去修改這些入參和返回值。這個就給你們留做業了。 參照 github.com/zhuotong/An… 的原理,那就想怎麼玩就怎麼玩。
往往剖開本身寫過的代碼,裏面都應有血流出來。
TIP: 本文的目的只有一個就是學習更多的逆向技巧和思路,若是有人利用本文技術去進行非法商業獲取利益帶來的法律責任都是操做者本身承擔,和本文以及做者不要緊,本文涉及到的代碼項目能夠去 奮飛的朋友們 知識星球自取,歡迎加入知識星球一塊兒學習探討技術。有問題能夠加我wx: fenfei331 討論下。
關注微信公衆號: 奮飛安全,最新技術乾貨實時推送