news 2026/5/8 10:16:50

Frida-Gum的Interceptor实战:从ARM64寄存器到Android Native函数参数与返回值的完整提取指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Frida-Gum的Interceptor实战:从ARM64寄存器到Android Native函数参数与返回值的完整提取指南

Frida-Gum的Interceptor实战:从ARM64寄存器到Android Native函数参数与返回值的完整提取指南

在逆向工程和移动安全分析领域,能够准确捕获和解析Native层函数调用是突破复杂防护的关键。当面对经过混淆或虚拟化保护的64位Android应用时,传统的Hook方法往往难以应对参数传递的复杂性。本文将深入ARM64调用约定与Frida-Gum的Interceptor机制,手把手教你构建精准的参数提取方案。

1. ARM64调用约定与寄存器布局

ARM64架构采用AAPCS64调用标准,其核心规则直接影响着Hook时的参数定位:

  • 整数参数:前8个参数通过X0-X7寄存器传递,超出部分通过栈传递
  • 浮点参数:前8个参数通过V0-V7寄存器传递,同样遵循"寄存器优先"原则
  • 返回值:X0/V0寄存器存储返回值,X8用于间接结果返回位置

典型场景中容易忽略的细节包括:

  1. this指针:C++成员函数的首个参数始终是对象指针(存储在X0)
  2. 结构体传递:小于16字节的结构体可能拆分为多个寄存器传递
  3. 栈对齐:SP必须保持16字节对齐,计算偏移时需注意填充
// 函数原型示例 void complexFunc( MyClass* obj, // X0 int32_t count, // X1 double threshold, // V0 const char* filter, // X2 Result& output // X3 );

2. Interceptor上下文操作指南

Frida的this.context对象是寄存器操作的入口,但直接读取原始寄存器可能引发类型错位。推荐采用类型化读取方式:

Interceptor.attach(target, { onEnter: function(args) { // 安全读取指针参数 const objPtr = this.context.x0.toUInt64(); const filterStr = Memory.readUtf8String(this.context.x2); // 处理浮点参数 const threshold = this.context.v0.toDouble(); // 结构体参数解析(假设16字节) const structData = Memory.readByteArray(this.context.x3, 16); }, onLeave: function(retval) { // 返回值类型转换 const status = this.context.x0.toInt32(); } });

常见陷阱处理方案

问题类型现象解决方案
浮点参数错位V寄存器读取为0检查函数原型是否包含float/double
结构体损坏读取到非法内存使用Memory.protect()临时解除保护
隐式参数缺少this指针确认函数是否包含C++名称修饰

3. 复杂参数类型的深度解析

3.1 指针链式追踪

当参数为多级指针时,需要递归解析内存引用:

function derefPointer(addr, depth = 3) { let current = addr; for (let i = 0; i < depth; i++) { if (Memory.readPointer(current).isNull()) break; current = Memory.readPointer(current); } return current; } // 使用示例 const finalAddr = derefPointer(this.context.x3);

3.2 回调函数处理

针对函数指针参数(如X1存储回调地址),可建立二级Hook:

const callbackAddr = this.context.x1; Interceptor.attach(callbackAddr, { onEnter: function(args) { console.log(`Callback triggered from ${this.returnAddress}`); } });

3.3 变长参数解析

对于printf等变参函数,需要结合格式字符串动态解析:

const fmtStr = Memory.readUtf8String(this.context.x0); const args = []; let regIndex = 1; // 从X1开始 fmtStr.match(/%[dfsu]/g).forEach(spec => { switch(spec) { case '%d': args.push(this.context[`x${regIndex++}`].toInt32()); break; case '%f': args.push(this.context[`v${regIndex-1}`].toDouble()); regIndex++; break; // 其他类型处理... } });

4. 高级调试技巧与性能优化

4.1 寄存器快照比对

在关键函数前后捕获寄存器状态差异:

let preRegs = {}; Interceptor.attach(target, { onEnter: function() { for (let i = 0; i < 8; i++) { preRegs[`x${i}`] = this.context[`x${i}`].toString(); } }, onLeave: function() { for (let i = 0; i < 8; i++) { const post = this.context[`x${i}`].toString(); if (post !== preRegs[`x${i}`]) { console.log(`X${i} changed: ${preRegs[`x${i}`]} -> ${post}`); } } } });

4.2 内存访问监控联动

结合MemoryAccessMonitor追踪参数关联内存:

const paramPtr = this.context.x2; MemoryAccessMonitor.enable({ base: paramPtr, size: 8 }, { onAccess: function(details) { console.log(`Memory accessed by ${details.from}`); } });

4.3 性能敏感场景处理

当Hook影响执行速度时,可采用条件拦截:

Interceptor.attach(target, { onEnter: function(args) { if (this.context.x1.toInt32() > 1000) { // 阈值过滤 this.skipTracing = true; return; } // 正常处理逻辑 } });

5. 实战:解析加密函数参数

以常见的AES加密函数为例,演示完整参数提取流程:

const aesEncryptAddr = Module.findExportByName("libcrypto.so", "AES_encrypt"); Interceptor.attach(aesEncryptAddr, { onEnter: function(args) { // 参数解析 const inPtr = this.context.x0; const outPtr = this.context.x1; const keyPtr = this.context.x2; // 读取输入输出数据 const input = Memory.readByteArray(inPtr, 16); const key = Memory.readByteArray(keyPtr, 32); // 结构化输出 console.log(JSON.stringify({ type: "AES_encrypt", input: Array.from(input), key: Array.from(key), outputAddr: outPtr.toString() }, null, 2)); }, onLeave: function(retval) { // 捕获加密结果 const output = Memory.readByteArray(this.context.x1, 16); console.log("Encrypted:", Array.from(output)); } });

关键点处理清单

  • 确认函数原型匹配调用约定(标准AES_encrypt使用3个参数)
  • 处理可能的内存对齐要求(AES要求16字节对齐)
  • 注意ARM64下long类型为64位,与x86区别

在分析某金融类APP时,发现其关键加密函数采用自定义调用约定,通过X0传递结构体指针,其中包含实际参数。此时需要先解析结构体:

const customStruct = { algorithm: this.context.x0.add(0x00).readU32(), mode: this.context.x0.add(0x04).readU32(), keyPtr: this.context.x0.add(0x08).readPointer(), ivPtr: this.context.x0.add(0x10).readPointer() };

这种深度解析能力,正是高级逆向工程区别于基础Hook的关键所在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 10:16:43

树莓派CM4驱动的Doly AI机器人开发全解析

1. Doly机器人项目概述Doly是由Limitbit公司开发的一款基于树莓派CM4模块的自主AI机器人&#xff0c;专为STEM教育和开发者平台设计。这款外形可爱的机器人配备了两条连续履带、两个由舵机控制的小机械臂、两个圆形彩色显示屏&#xff08;作为眼睛&#xff09;以及多种传感器。…

作者头像 李华
网站建设 2026/5/8 10:16:32

Zotero GPT插件:5步搭建你的终极AI文献助手,让科研效率翻倍!

Zotero GPT插件&#xff1a;5步搭建你的终极AI文献助手&#xff0c;让科研效率翻倍&#xff01; 【免费下载链接】zotero-gpt GPT Meet Zotero. 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-gpt Zotero GPT插件是一款革命性的智能文献管理工具&#xff0c;它将…

作者头像 李华
网站建设 2026/5/8 10:16:04

字节 LeetCode 912.排序数组

前置题目&#xff1a;可先学习相对更简单的快速选择算法&#xff0c;重点掌握划分&#xff08;partition&#xff09;的原理&#xff0c;题目见hot100 215.数组中的第K个最大元素。 思路&#xff1a;手写快排。 1.快速排序的核心思想&#xff1a; &#xff08;1&#xff09;…

作者头像 李华
网站建设 2026/5/8 10:16:02

考公行测图形推理:用‘箭头法’搞定六面体四面体,保姆级避坑教程

考公行测图形推理&#xff1a;用‘箭头法’搞定六面体四面体&#xff0c;保姆级避坑教程 图形推理作为行测考试中的高频题型&#xff0c;常常让考生感到头疼。尤其是立体图形推理中的六面体和四面体空间重构题&#xff0c;更是让不少考生在考场上手足无措。本文将详细介绍一种高…

作者头像 李华