一、Java 层
-
目标:Android Java 应用层代码(Java/Kotlin)
-
方式:通过
Java.perform()
进入 Java 虚拟机上下文,使用Java.use()
获取类,重写方法实现。 -
用途:拦截 Java 方法调用、修改参数和返回值、监控对象状态、Hook 构造函数、静态方法等。
1. Java.use
加载并操作 Java 层的类,是进行任何 Java Hook 的第一步。必须传入完整类名,返回的是一个可 Hook 的类对象。
Java.perform(function () {var TargetClass = Java.use("com.example.app.MainActivity");console.log("[*] Loaded Class:", TargetClass);
});
2. overload
Android 支持方法重载,这个函数用于指定具体参数类型的重载方法。必须精确指定参数类型,才能 Hook 到正确的函数版本。
Java.perform(function () {var Target = Java.use("com.example.app.MainActivity");Target.login.overload("java.lang.String", "java.lang.String").implementation = function (user, pass) {console.log("[*] login called with:", user, pass);return this.login(user, pass);};
});
3. $init(构造函数)
Hook 构造函数的专用方法,$init 代表 Java 类的构造函数。overload 与 $init 搭配可以精确匹配构造方法签名。
Java.perform(function () {var Target = Java.use("com.example.app.MainActivity");Target.$init.overload().implementation = function () {console.log("[*] 构造函数被调用");return this.$init();};
});
4. implementation(修改函数实现)
用于替换原方法的逻辑,在里面可以打印参数、修改返回值或调用原方法。是最常用的 Hook 手段之一。
Java.perform(function () {var Target = Java.use("com.example.app.MainActivity");Target.getData.implementation = function () {console.log("[*] getData 被调用");return "FakeData";};
});
5. 调用实例方法或类方法(主动调用)
可以在 Frida 中调用原始类或实例方法,以辅助逆向分析。常用于主动调用以触发逻辑。
Java.perform(function () {var Target = Java.use("com.example.app.MainActivity");var result = Target.getAppVersion();console.log("[*] App Version:", result);
});
6. Java.choose
枚举运行时实例,找出已加载的类对象,可操作现有实例。常用于找出 Activity、Context 或网络类实例进行分析。
Java.perform(function () {Java.choose("com.example.app.MainActivity", {onMatch: function (instance) {console.log("[*] 找到实例:", instance);},onComplete: function () {console.log("[*] 枚举完成");}});
});
7. Java.openClassFile + Dex 注入
向目标应用注入一个新的 dex 文件,可以让我们加载自定义代码。常用于突破反调试或绕过壳逻辑。
Java.perform(function () {Java.openClassFile("/data/local/tmp/hook.dex").load();var Injected = Java.use("com.inject.HookUtil");Injected.doWork();
});
8. Java.enumerateLoadedClasses
枚举所有已加载的 Java 类名,可以用于类发现与模糊匹配。搭配关键字搜索可以快速定位目标类。
Java.perform(function () {Java.enumerateLoadedClasses({onMatch: function (name) {if (name.indexOf("MainActivity") >= 0) console.log("[*] 类:", name);},onComplete: function () {console.log("[*] 枚举完成");}});
});
9. Java.enumerateClassLoaders
枚举 ClassLoader 是在多 dex/动态加载的应用中找到目标类的关键方法。可结合 loader.findClass 精确定位类存在于哪个 loader 中。
Java.perform(function () {Java.enumerateClassLoaders({onMatch: function (loader) {try {if (loader.findClass("com.example.app.MainActivity")) {console.log("[*] 找到合适的 ClassLoader");}} catch (e) {}},onComplete: function () {}});
});
10. Java.cast(强制类型转换)
将一个 Java 对象强制转换为指定类型,便于调用其方法。通常用于从 generic Object 中获取真实类。
Java.perform(function () {var activityThread = Java.use("android.app.ActivityThread");var currentApp = activityThread.currentApplication();var context = Java.cast(currentApp, Java.use("android.content.Context"));console.log("[*] 当前 context:", context);
});
二、Native 层
-
目标:Android 原生库(
.so
文件)、系统调用、本地方法 -
方式:通过
Module.findExportByName()
找到函数地址,Interceptor.attach()
监控函数调用,或用Interceptor.replace()
替换函数实现。 -
用途:Hook 加密算法、本地校验、系统调用、内存读写等。
1. Module.findExportByName
查找某个模块导出的函数地址,是 Native Hook 的前提。第一个参数是模块名,第二个是函数名。
Java.perform(function () {var addr = Module.findExportByName("libc.so", "open");console.log("[*] open() 地址:", addr);
});
2. Interceptor.attach
在函数执行前后插入自定义逻辑,是最常用的 Native Hook 工具。可以监控参数、修改返回值。
Java.perform(function () {var openPtr = Module.findExportByName("libc.so", "open");Interceptor.attach(openPtr, {onEnter: function (args) {console.log("[*] open 调用, path:", Memory.readUtf8String(args[0]));},onLeave: function (retval) {console.log("[*] 返回值:", retval);}});
});
3. Interceptor.replace
替换整个原生函数的实现,是一种高强度 Hook 方法。可以实现完全绕过或伪造原始行为。
Java.perform(function () {var strlen = Module.findExportByName(null, "strlen");Interceptor.replace(strlen, new NativeCallback(function (str) {return 42;}, "int", ["pointer"]));
});
4. NativeFunction
用于主动调用 native 层的导出函数,比如调用 puts、malloc 等。需要指定函数地址、返回值类型和参数类型。
Java.perform(function () {var putsAddr = Module.findExportByName(null, "puts");var puts = new NativeFunction(putsAddr, "int", ["pointer"]);puts(Memory.allocUtf8String("Hello from NativeFunction"));
});
5. Memory.allocUtf8String
将字符串写入内存中,返回的是指针,可用于传递给 native 函数。常与 NativeFunction 搭配使用。
Java.perform(function () {var ptr = Memory.allocUtf8String("test string");console.log("[*] 内存地址:", ptr);
});
6. Memory.readUtf8String / readByteArray
从指定内存地址读取字符串或字节数组。常用于读取参数、数据包、返回值。
Java.perform(function () {var addr = ptr("0x12345678");var str = Memory.readUtf8String(addr);console.log("[*] 读取到:", str);
});
7. Memory.scan
扫描模块内存找特定字节序列,常用于找签名、关键代码段。可配合模块基地址使用。
Java.perform(function () {var base = Module.findBaseAddress("libtarget.so");Memory.scan(base, 0x10000, "12 34 ?? 56", {onMatch: function (address, size) {console.log("[*] 匹配地址:", address);},onComplete: function () {console.log("[*] 扫描完成");}});
});
8. Thread.backtrace 打印栈信息
获取当前执行点的调用栈信息,便于调试函数调用路径。可用 Accurate 模式获取详细调试信息。
Java.perform(function () {var openAddr = Module.findExportByName(null, "open");Interceptor.attach(openAddr, {onEnter: function (args) {console.log("[*] 调用栈:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n"));}});
});
9. Module.findBaseAddress
获取模块的加载基地址,是 Memory 操作的基础。搭配 scan、偏移可精确定位函数或数据。
Java.perform(function () {var base = Module.findBaseAddress("libc.so");console.log("[*] libc.so 基地址:", base);
});
10. Module.enumerateExports
枚举模块导出的函数和变量,可用于分析可 Hook 的原生接口。常用于配合 findExportByName 精准定位。
Java.perform(function () {var exports = Module.enumerateExports("libc.so");exports.forEach(function (e) {if (e.name.indexOf("open") >= 0) console.log(e.name, e.address);});
});
11. Module.enumerateImports
枚举模块导入的函数,是分析动态链接依赖的好方法。可找出目标模块调用了哪些外部函数。
Java.perform(function () {var imports = Module.enumerateImports("libtarget.so");imports.forEach(function (imp) {console.log("[*] import:", imp.name, imp.module);});
});
12. Module.load
加载一个未自动加载的 so 文件,用于自定义注入。搭配 findExportByName 使用。
Java.perform(function () {var mymod = Module.load("/data/local/tmp/libcustom.so");var addr = mymod.findExportByName("custom_function");console.log("[*] 地址:", addr);
});
三、Objective-C / Swift 层(iOS 特有)
-
目标:iOS 应用 Objective-C Runtime
-
方式:通过
ObjC.classes
访问类,重写方法实现。 -
用途:Hook iOS 应用方法、系统 API、私有 API。
1. Objective-C 实例方法
-
表示实例方法,重写该方法实现可以打印参数、修改返回值或绕过逻辑。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;MyClass["- myInstanceMethod:"].implementation = function (arg) {console.log("[*] 实例方法 myInstanceMethod: 被调用,参数:", arg.toString());var ret = this["- myInstanceMethod:"](arg); // 调用原始实现console.log("[*] 原始返回值:", ret);return ret;};} else {console.log("[-] ObjC 环境不可用");}
});
2. Objective-C 类方法
+
表示类方法,常用于 Hook 工厂方法、单例获取等。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;MyClass["+ myClassMethod"].implementation = function () {console.log("[*] 类方法 myClassMethod 被调用");var ret = this["+ myClassMethod"]();return ret;};}
});
3. Swift 方法(借助 ObjC Runtime)
Swift 编译后方法名通常带有混淆,但用 ObjC 方式访问可 Hook。
Swift 与 ObjC 混合编译项目中,可以用 ObjC runtime 方式 Hook Swift 暴露的方法。
Java.perform(function () {if (ObjC.available) {var SwiftClass = ObjC.classes.SwiftClassName; // 先找类名// 查看所有方法名,辅助确定函数签名var methods = SwiftClass.$ownMethods;console.log("[*] SwiftClass 方法列表:", methods);// 假设要 Hook 方法 -someSwiftMethod:SwiftClass["- someSwiftMethod:"].implementation = function (arg) {console.log("[*] Swift 方法被调用,参数:", arg);return this["- someSwiftMethod:"](arg);};}
});
4. Objective-C 构造方法(init 系列)
可以 Hook init 系列构造方法,监控对象创建过程。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;MyClass["- init"].implementation = function () {console.log("[*] MyClass init 构造方法被调用");return this["- init"]();};}
});
5. Objective-C Block(闭包)
Hook 传入 Block 的方法,可以监控回调执行,修改回调行为。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;MyClass["- methodTakingBlock:"].implementation = function (block) {console.log("[*] 收到 Block 参数");var blockObj = new ObjC.Block(block);blockObj.implementation = function () {console.log("[*] Block 被调用");return blockObj.apply(this, arguments);};return this["- methodTakingBlock:"](blockObj);};}
});
6. Objective-C 属性 Getter / Setter
属性访问本质是 getter/setter 方法,Hook 它们可监控属性读取和修改。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;// Hook getterMyClass["- propertyName"].implementation = function () {var ret = this["- propertyName"]();console.log("[*] 获取属性 propertyName:", ret);return ret;};// Hook setterMyClass["- setPropertyName:"].implementation = function (value) {console.log("[*] 设置属性 propertyName 为:", value);this["- setPropertyName:"](value);};}
});
7. Objective-C 消息发送(Message Send)
Hook 底层消息发送函数,可以捕获所有 ObjC 方法调用,范围极广,适合全局监控。
Java.perform(function () {if (ObjC.available) {var objc_msgSend = Module.findExportByName(null, "objc_msgSend");Interceptor.attach(objc_msgSend, {onEnter: function (args) {var receiver = new ObjC.Object(args[0]);var selector = ObjC.selectorAsString(args[1]);console.log("[*] objc_msgSend 目标类:", receiver.$className, "方法:", selector);}});}
});
8. Objective-C 动态方法解析(resolveInstanceMethod)
动态方法解析机制,用于补救运行时未实现的方法,Hook 该方法可以监控运行时添加方法行为。
Java.perform(function () {if (ObjC.available) {var MyClass = ObjC.classes.MyClass;MyClass["+ resolveInstanceMethod:"].implementation = function (sel) {var selectorName = ObjC.selectorAsString(sel);console.log("[*] 动态方法解析:", selectorName);return this["+ resolveInstanceMethod:"](sel);};}
});
9. Swift 属性访问(通过 getter/setter)
Swift 属性也会编译成 getter/setter 方法,可以用和 Objective-C 一样的方式 Hook。
Swift 属性访问实际上是方法调用,Hook getter/setter即可。
Java.perform(function () {if (ObjC.available) {var SwiftClass = ObjC.classes.SwiftClassName;// 假设属性名为 swiftProperty,对应 getterSwiftClass["- swiftProperty"].implementation = function () {console.log("[*] Swift 属性 getter 被调用");return this["- swiftProperty"]();};// setterSwiftClass["- setSwiftProperty:"].implementation = function (value) {console.log("[*] Swift 属性 setter 被调用,值:", value);this["- setSwiftProperty:"](value);};}
});
10. Objective-C Runtime 函数
Hook Runtime 函数用于捕获底层动态行为,如方法交换、添加等,是深度逆向利器。
Java.perform(function () {var runtimeFunctions = ["class_addMethod","method_exchangeImplementations","objc_msgSend"];runtimeFunctions.forEach(function (funcName) {var addr = Module.findExportByName(null, funcName);if (addr) {Interceptor.attach(addr, {onEnter: function (args) {console.log("[*] 调用 runtime 函数:", funcName);}});}});
});
四、内存操作层(简)
-
目标:直接对进程内存读写,扫描字节模式,操作指针
-
方式:使用
Memory.allocUtf8String()
、Memory.read*()
、Memory.write*()
,Memory.scan()
-
用途:查找函数指针,修改数据内容,动态插桩。
1. malloc
-
用于动态申请指定大小的内存空间,是最基础的内存申请函数。
-
Hook malloc 可以监控程序内存申请的大小和调用时机,常用于分析程序内存使用。
var mallocPtr = Module.findExportByName(null, "malloc");
Interceptor.attach(mallocPtr, {onEnter: function (args) {console.log("[malloc] size: " + args[0].toInt32());},onLeave: function (retval) {console.log("[malloc] returned ptr: " + retval);}
});
2. free
-
用来释放之前申请的内存,防止内存泄漏。
-
Hook free 可以跟踪内存释放操作,帮助发现潜在的内存安全问题(如重复释放)。
var freePtr = Module.findExportByName(null, "free");
Interceptor.attach(freePtr, {onEnter: function (args) {console.log("[free] address: " + args[0]);}
});
3. memcpy
-
用于内存拷贝,把一块内存的数据复制到另一块内存。
-
Hook memcpy 可以监控敏感数据复制、篡改行为,尤其是密码学或逆向时非常重要。
var memcpyPtr = Module.findExportByName(null, "memcpy");
Interceptor.attach(memcpyPtr, {onEnter: function (args) {console.log("[memcpy] dest: " + args[0] + ", src: " + args[1] + ", size: " + args[2].toInt32());}
});
五、线程和调用栈层(简)
-
目标:跟踪调用栈,分析调用路径
-
方式:
Thread.backtrace()
,DebugSymbol.fromAddress()
-
用途:调试和分析调用链路、定位问题点。
1. Thread.backtrace
-
获取当前线程的调用栈,能够清晰看到函数调用顺序和调用地址。
-
非常适合调试定位程序执行路径和逆向分析调用关系。
Java.perform(function () {var openPtr = Module.findExportByName(null, "open");Interceptor.attach(openPtr, {onEnter: function (args) {var stack = Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n");console.log("[Thread.backtrace] 调用栈:\n" + stack);}});
});
2. Thread.sleep
-
线程休眠函数,使线程暂停指定时间。
-
Hook 可以用来检测和干扰程序的定时等待行为,或加快逆向调试流程。
var sleepPtr = Module.findExportByName(null, "sleep");
Interceptor.attach(sleepPtr, {onEnter: function (args) {console.log("[sleep] 线程休眠秒数: " + args[0].toInt32());}
});
3. pthread_create
-
POSIX 线程创建函数,用于创建新线程执行指定函数。
-
Hook pthread_create 可以监控程序线程创建行为,帮助分析多线程并发流程。
var pthreadCreate = Module.findExportByName(null, "pthread_create");
Interceptor.attach(pthreadCreate, {onEnter: function (args) {console.log("[pthread_create] 新线程创建, 线程函数地址: " + args[2]);}
});
六、指令级 Hook 和追踪(Stalker)(简)
-
目标:CPU 指令级追踪和分析
-
方式:使用
Stalker
跟踪指定线程的指令执行,支持条件跳转、寄存器值监控。 -
用途:深度分析代码执行路径,绕过反调试,动态分析复杂逻辑。
1. Stalker.follow
-
让 Frida 跟踪指定线程的指令执行,实时捕获 CPU 指令流。
-
用于动态分析程序执行路径和汇编指令变化,适合逆向复杂逻辑。
Java.perform(function () {var threadId = Process.getCurrentThreadId();console.log("[Stalker] 开始跟踪线程ID:", threadId);Stalker.follow(threadId, {events: {call: true, // 跟踪调用指令ret: true, // 跟踪返回指令exec: false,block: false,compile: false},onCallSummary: function (summary) {console.log("[Stalker] 调用汇总:", JSON.stringify(summary));},onEvent: function (event) {// 这里可自定义处理事件}});
});
2. Stalker.detach
-
停止对某个线程的跟踪,释放资源。
-
通常用于跟踪完成后清理,避免性能影响。
Java.perform(function () {var threadId = Process.getCurrentThreadId();Stalker.follow(threadId);setTimeout(function () {console.log("[Stalker] 停止跟踪线程ID:", threadId);Stalker.detach(threadId);}, 5000); // 5秒后停止跟踪
});
3. Stalker.queueDrain
-
手动触发 Stalker 队列的处理,及时输出跟踪数据。
-
用于高频事件场景,防止跟踪数据积压。
Java.perform(function () {var threadId = Process.getCurrentThreadId();Stalker.follow(threadId, {onEvent: function (event) {console.log("[Stalker Event] 类型:", event.type);Stalker.queueDrain(); // 强制刷新事件队列}});
});
其他辅助层(简)
-
ClassLoader 管理层:切换和操作不同 ClassLoader,解决多 Dex 多 ClassLoader 的 Hook 问题。
-
动态代码注入:
Java.openClassFile()
加载外部 Dex,动态注入代码扩展 Hook 功能。
1. DebugSymbol.fromAddress
-
将内存地址解析成函数名、模块名等符号信息。
-
方便分析调用栈或跟踪时对地址的理解,提升逆向效率。
Java.perform(function () {var addr = Module.findExportByName("libc.so", "open");var symbol = DebugSymbol.fromAddress(addr);console.log("[DebugSymbol] 地址对应符号:", symbol.name, "模块:", symbol.moduleName);
});
2. Interceptor.flush
-
手动刷新所有挂载的拦截器。
-
当动态修改 Hook 或存在大量数据缓存时,确保拦截器立即生效。
Java.perform(function () {var openPtr = Module.findExportByName("libc.so", "open");Interceptor.attach(openPtr, {onEnter: function (args) {console.log("[Interceptor] open called");}});// 强制刷新,立即生效所有拦截器Interceptor.flush();
});
3. Process.getCurrentThreadId
-
获取当前执行线程的 ID,便于精确定位线程级 Hook。
-
常用于配合 Stalker 或多线程环境的精细控制。
Java.perform(function () {var tid = Process.getCurrentThreadId();console.log("[Thread] 当前线程ID:", tid);
});