Frida inlineHook原理分析及简单设计一款AArch64 inlineHook工具

本文为看雪论坛精华文章
看雪论坛作者ID:abucs
一
Frida inlineHook 思路分析
动态替换需要 Hook 的指令片段为一段经过设计的跳板指令,即 trampoline ,目标为我们设计好的一段 shellCode
在内存中设计并生成一段 shellCode ,这是我们的可控 shellCode ,在该 shellCode 中需要实现 Hook 的功能函数(即打印/替换-参数/结果)
shellCode 的设计原则是保持 Hook 前后的栈平衡,并保护寄存器状态(即Hook结束后,保持与Hook开始前一致的栈布局与寄存器状态)
在 shellCode 中完成原函数的执行工作,被替换的掉的指令中若包含计算 PC-relative address ( 如 Branch 指令 ),需要对其正确解析执行
Step1:
1、Frida Hook 函数开头指令(即直接 Hook 导出函数)
2、Hook 函数中间指定位置的指令

//## hookTest1: Hook 导出函数->Java_com_example_x64_JNI_aalfunction hookTest1() {var helloAddr = Module.findExportByName("libx64.so", "Java_com_example_x64_JNI_aal");console.log(helloAddr);if(helloAddr != null){Interceptor.attach(helloAddr,{onEnter: function(args){console.log("hook1 on enter");},onLeave: function(retval){console.log("hook1 on leave");}});}}//## hookTest2: Hook 指定位置->0x000000000000BBA0function hookTest2() {var libutilityAddr = Module.findBaseAddress("libx64.so");var getOriginalStringAddr = libutilityAddr.add(0x000000000000BBA0);console.log(getOriginalStringAddr);if(getOriginalStringAddr != null){Interceptor.attach(getOriginalStringAddr,{onEnter: function(args){console.log("hook2 on enter");},onLeave: function(retval){console.log("hook2 on leave");}});}}

Step2:




Step3:
Java_com_example_x64_JNI_aal 函数入口: 0x7fac430b70


首先 mmap 了一段匿名内存( 7face7c000-7face83000 rwxp ),在 0x7face7c600 位置放置了以下几条汇编指令构成第二段跳板。
> ldr x17, =0x7facec12e0
> ldr x16, =0x7face7c000
> br x16
其中 x17 寄存器装载了一个地址( 0x7facec12e0 ),这个地址内部保存着 0x7fac430b70 ,正是 Java_com_example_x64_JNI_aal 函数入口地址。
而 x16 寄存器装载了此番生成的 shellCode 的地址( 0x7face7c000 ),将该段内存 dump 下来,拖入 ida 进行分析:



之后再次使用 x16 寄存器跳转至 0x7fac430b80,即函数 Java_com_example_x64_JNI_aal 开头偏移 0x10 的位置,以完成原函数的执行动作。
> ldr x17, =0x7facec12e0
> ldr x16, =0x7face7c100
> br x16

最后由 BR X16 返回 Java_com_example_x64_JNI_aal 函数被调用时真正的 LR。
二
AArch64 inlineHook 开发
Hook 导出函数:即在函数开头进行 Hook ,能够执行原函数,并提供 onEnter 以及 onLeave 两层代码注入点,达到类似 Frida 那种 "代码托管" 一样的效果
Hook 函数内指定地址:Hook 指定位置的汇编指令,仅提供 onEnter 一层代码注入点,因为考虑到在指定位置上 X30( LR ) 寄存器可能已经发生变化,此时用该寄存器做返回判断并不准确,故放弃 onLeave
在 onEnter 中提供入参的打印/修改操作 ( 本质是寄存器/堆栈内存打印/修改操作 )
在 onLeave 中提供返回值的打印/修改操作 ( 本质是寄存器/堆栈内存打印/修改操作 )
Step1:
_trampoline_:LDR X16, x64code0BR X16x64code0:_jmp_addr_:.dword 0x1111111111111111


Step2:
//## Hook目标函数extern "C" JNIEXPORT jstring JNICALLJava_com_cs_inline_MainActivity_stringFromJNI(JNIEnv* env,jobject /* thisobj */,jstring jstr) {std::string hello = "Hello from C++: ";hello.append(env->GetStringUTFChars(jstr, nullptr));return env->NewStringUTF(hello.c_str());}//## 该函数内部完成了对Java_com_cs_inline_MainActivity_stringFromJNI函数的inlineHookextern "C" JNIEXPORT void JNICALLJava_com_cs_inline_MainActivity_inlineHook1(JNIEnv* env,jobject /* thisobj */){//## Hook target函数为:Java_com_cs_inline_MainActivity_stringFromJNIu_long func_addr = (u_long)Java_com_cs_inline_MainActivity_stringFromJNI;extern u_long _shellcode_start_, _the_func_addr_, _end_func_addr_, _ori_ins_set1_, _retback_addr_, _shellcode_end_, _trampoline_, _jmp_addr_, _shellcode_part2_;//## 计算shellcode整体长度u_long total_len = (u_long)&_shellcode_end_ - (u_long)&_shellcode_start_;LOGD(ANDROID_LOG_DEBUG, "[+] ShellCode len: %d, target func: %p", total_len, func_addr);//## 使用mmap分配匿名内存存放shellcodeu_long page_size = getpagesize();u_long shellcode_mem_start = (u_long)mmap(0, page_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);memset((void *)shellcode_mem_start, 0, page_size);memcpy((void *)shellcode_mem_start, (void *)&_shellcode_start_, total_len);LOGD(ANDROID_LOG_DEBUG, "[+] shellcode_mem_start: %p", shellcode_mem_start);//## 设置trampoline跳转的目标地址*(u_long*)&_jmp_addr_ = shellcode_mem_start;u_long mem_the_func_addr_ = (u_long)&_the_func_addr_ - (u_long)&_shellcode_start_ + shellcode_mem_start;u_long mem_end_func_addr_ = (u_long)&_end_func_addr_ - (u_long)&_shellcode_start_ + shellcode_mem_start;u_long mem_ori_ins_set1_ = (u_long)&_ori_ins_set1_ - (u_long)&_shellcode_start_ + shellcode_mem_start;u_long mem_retback_addr_ = (u_long)&_retback_addr_ - (u_long)&_shellcode_start_ + shellcode_mem_start;if(!off_shellcode_part2_)off_shellcode_part2_ = (u_long)&_shellcode_part2_ - (u_long)&_shellcode_start_;//## 设置onEnter及onLeave函数*(u_long*)mem_the_func_addr_ = (u_long)on_enter_1;*(u_long*)mem_end_func_addr_ = (u_long)on_leave_1;//## 设置返回地址为距离Hook点0x10长度的指令地址,即偏移为trampoline的长度*(u_long*)mem_retback_addr_ = (u_long)func_addr + 0x10;//## 原指令保存,并未做任何解析,PC-relative address相关指令暂不支持*(u_long*)mem_ori_ins_set1_ = *(u_long*)func_addr;*(u_long*)(mem_ori_ins_set1_ + 8) = *(u_long*)(func_addr + 8);//## 页权限修改并完成inlineHooku_long entry_page_start = (u_long)(func_addr) & (~(page_size-1));mprotect((u_long*)entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);*(u_long*)func_addr = *(u_long*)&_trampoline_;*(u_long*)(func_addr + 8) = *(u_long*)(((u_long)&_trampoline_) + 8);
//## 使用线程局部存储保存原始返回地址LR(X30)u_long thread_local ori_lr = 0;u_long off_shellcode_part2_ = 0;void on_enter_1(u_long sp){//## sp回到初始位置,取出返回地址LRsp = sp + 0x60;u_long lr = *(u_long*)(sp - 8);u_long lr_ptr = sp - 8;u_long pc = *(u_long*)(sp - 0x20);pc -= 0x20;//## 使用TLS保存LRori_lr = lr;//## 一般来说8个参数顶天了u_long arg1 = *(u_long*)(sp - 0x28);u_long arg2 = *(u_long*)(sp - 0x30);u_long arg3 = *(u_long*)(sp - 0x38);u_long* arg3_ptr = (u_long*)(sp - 0x38);u_long arg4 = *(u_long*)(sp - 0x40);u_long arg5 = *(u_long*)(sp - 0x48);u_long arg6 = *(u_long*)(sp - 0x50);u_long arg7 = *(u_long*)(sp - 0x58);u_long arg8 = *(u_long*)(sp - 0x60);//## sp上还有参数的话照下面这么写u_long arg9 = *(u_long*)(sp);u_long arg10 = *(u_long*)(sp + 0x8);//## 打印String参数JNIEnv* env = reinterpret_cast<JNIEnv *>(arg1);jstring jstr = reinterpret_cast<jstring>(arg3);LOGD(ANDROID_LOG_INFO, "[+] arg3: %s", env->GetStringUTFChars(jstr, nullptr));//## 替换String参数jstring jstr_new = env->NewStringUTF("--This is on_enter_1 !");*arg3_ptr = reinterpret_cast<u_long>(jstr_new);//## 修改LR寄存器,保证原始函数执行完毕会回到on_leave_1函数*(u_long*)lr_ptr = pc + off_shellcode_part2_;LOGD(ANDROID_LOG_WARN, "[+] on_enter_1: %p", on_enter_1);}void on_leave_1(u_long sp){//## sp回到初始位置sp = sp + 0x10;u_long x0 = *(u_long*)(sp - 8);u_long* x0_ptr = (u_long*)(sp - 8);u_long lr = *(u_long*)(sp - 0x10);u_long* lr_ptr = (u_long*)(sp - 0x10);//## do_something ...LOGD(ANDROID_LOG_DEBUG, "[+] on_leave_1: %p", on_leave_1);//## 取回LR并返回*(u_long*)lr_ptr = ori_lr;}
三
效果展示及总结

Git链接: https://github.com/zzyccs/inlineHook

看雪ID:abucs
https://bbs.pediy.com/user-home-772122.htm
# 往期推荐


球分享

球点赞

球在看

点击“阅读原文”,了解更多!
[广告]赞助链接:
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注KnowSafe微信公众号随时掌握互联网精彩
- 加州大学研发新技术:高性能鼠标可被转化为窃听工具
- Memos现代、开源、自托管的笔记和知识管理平台
- 研究人员称谷歌reCAPTCHA验证机制很容易被机器人攻破
- ScopeSentry 资产测绘、信息收集、漏洞扫描工具
- WordPress页面编辑器有哪些
- FBI承认:未获授权购买居民位置数据
- 交通银行与华为签署深化战略合作协议
- 华为高层谈 35 岁危机,程序员如何破年龄之忧?
- 诸子云|话题:如何应对供应链攻击?怎样落实数据存储加密?如何解密https做流量分析?
- Ant Design 遭删库!
- 盘点 | 2020网络安全热词:疫情阻不住关注和期待
- 我国首次举办工业互联网“人机对抗”竞赛 检验独创理论独有技术有效性




