- 发布于
Frida JavaScript API 深度解析:掌握动态分析的核心技术
- 作者

- 姓名
- 全能波
- GitHub
- @weicracker
Frida JavaScript API 深度解析:掌握动态分析的核心技术
Frida 提供了丰富的 JavaScript API,允许开发者在运行时对目标进程进行深度分析和修改。本文将详细介绍 Frida 的核心 API 模块和高级使用技巧。
Java API 深度解析
Java.perform 和类操作
// java_api_advanced.js - Java API 高级用法
Java.perform(function() {
console.log("[+] Java runtime available");
// 1. 类加载和使用
demonstrateClassOperations();
// 2. 方法 Hook 高级技巧
demonstrateMethodHooking();
// 3. 字段操作
demonstrateFieldOperations();
// 4. 构造函数处理
demonstrateConstructorHandling();
// 5. 异常处理
demonstrateExceptionHandling();
});
function demonstrateClassOperations() {
try {
// 基础类使用
var String = Java.use("java.lang.String");
var StringBuilder = Java.use("java.lang.StringBuilder");
// 检查类是否存在
function classExists(className) {
try {
Java.use(className);
return true;
} catch (e) {
return false;
}
}
console.log("[+] String class exists: " + classExists("java.lang.String"));
console.log("[+] NonExistent class exists: " + classExists("com.nonexistent.Class"));
// 获取类信息
var StringClass = String.class;
console.log("[+] String class name: " + StringClass.getName());
console.log("[+] String superclass: " + StringClass.getSuperclass().getName());
// 获取类的所有方法
var methods = StringClass.getDeclaredMethods();
console.log("[+] String has " + methods.length + " declared methods");
// 打印前5个方法
for (var i = 0; i < Math.min(5, methods.length); i++) {
var method = methods[i];
console.log(" Method: " + method.getName() + " - " + method.getReturnType().getName());
}
// 获取类的所有字段
var fields = StringClass.getDeclaredFields();
console.log("[+] String has " + fields.length + " declared fields");
// 创建实例
var stringInstance = String.$new("Hello Frida");
console.log("[+] Created string instance: " + stringInstance);
// 调用实例方法
var upperCase = stringInstance.toUpperCase();
console.log("[+] Uppercase: " + upperCase);
} catch (e) {
console.log("[-] Class operations failed: " + e);
}
}
function demonstrateMethodHooking() {
try {
var String = Java.use("java.lang.String");
// 1. 基础方法 Hook
String.equals.implementation = function(other) {
var result = this.equals(other);
console.log("[String.equals] '" + this + "' equals '" + other + "' -> " + result);
return result;
};
// 2. 重载方法 Hook
var StringBuilder = Java.use("java.lang.StringBuilder");
// Hook 特定重载
StringBuilder.append.overload('java.lang.String').implementation = function(str) {
console.log("[StringBuilder.append] Appending string: " + str);
return this.append(str);
};
StringBuilder.append.overload('int').implementation = function(i) {
console.log("[StringBuilder.append] Appending int: " + i);
return this.append(i);
};
// 3. Hook 所有重载
var appendOverloads = StringBuilder.append.overloads;
console.log("[+] StringBuilder.append has " + appendOverloads.length + " overloads");
appendOverloads.forEach(function(overload) {
overload.implementation = function() {
console.log("[StringBuilder.append] Called with " + arguments.length + " arguments");
return overload.apply(this, arguments);
};
});
// 4. 方法替换
String.toLowerCase.implementation = function() {
console.log("[String.toLowerCase] Method replaced - returning uppercase instead");
return this.toUpperCase();
};
// 5. 条件 Hook
String.substring.overload('int', 'int').implementation = function(start, end) {
console.log("[String.substring] Called with start=" + start + ", end=" + end);
// 条件逻辑
if (start < 0 || end < 0) {
console.log("[String.substring] Invalid parameters, returning original string");
return this.toString();
}
return this.substring(start, end);
};
} catch (e) {
console.log("[-] Method hooking failed: " + e);
}
}
function demonstrateFieldOperations() {
try {
// 创建测试类
var TestClass = Java.use("java.lang.System");
// 访问静态字段
console.log("[+] System.out: " + TestClass.out.value);
console.log("[+] System.err: " + TestClass.err.value);
// 修改静态字段(如果可能)
try {
var originalOut = TestClass.out.value;
console.log("[+] Original System.out: " + originalOut);
} catch (e) {
console.log("[-] Cannot access System.out: " + e);
}
// 实例字段操作
var ArrayList = Java.use("java.util.ArrayList");
var list = ArrayList.$new();
// 添加元素
list.add("Element 1");
list.add("Element 2");
// 访问私有字段(需要反射)
var ArrayListClass = list.getClass();
var sizeField = ArrayListClass.getDeclaredField("size");
sizeField.setAccessible(true);
var size = sizeField.get(list);
console.log("[+] ArrayList size (via reflection): " + size);
// 修改私有字段
sizeField.set(list, 10);
console.log("[+] ArrayList size after modification: " + list.size());
} catch (e) {
console.log("[-] Field operations failed: " + e);
}
}
function demonstrateConstructorHandling() {
try {
var String = Java.use("java.lang.String");
// Hook 构造函数
String.$init.overload('[C').implementation = function(charArray) {
console.log("[String.<init>] Creating string from char array of length: " + charArray.length);
this.$init(charArray);
};
String.$init.overload('java.lang.String').implementation = function(str) {
console.log("[String.<init>] Creating string from string: " + str);
this.$init(str);
};
// 创建实例并观察 Hook
var str1 = String.$new("Test String");
var charArray = Java.array('char', ['H', 'e', 'l', 'l', 'o']);
var str2 = String.$new(charArray);
console.log("[+] Created strings: '" + str1 + "' and '" + str2 + "'");
// Hook 复杂构造函数
var File = Java.use("java.io.File");
File.$init.overload('java.lang.String').implementation = function(pathname) {
console.log("[File.<init>] Creating file with path: " + pathname);
// 可以修改参数
if (pathname.startsWith("/dangerous/")) {
console.log("[File.<init>] Redirecting dangerous path to safe location");
pathname = "/safe/" + pathname.substring(11);
}
this.$init(pathname);
};
// 测试文件创建
var file1 = File.$new("/normal/path/file.txt");
var file2 = File.$new("/dangerous/path/file.txt");
console.log("[+] File paths: " + file1.getAbsolutePath() + ", " + file2.getAbsolutePath());
} catch (e) {
console.log("[-] Constructor handling failed: " + e);
}
}
function demonstrateExceptionHandling() {
try {
var Integer = Java.use("java.lang.Integer");
// Hook 可能抛出异常的方法
Integer.parseInt.overload('java.lang.String').implementation = function(str) {
console.log("[Integer.parseInt] Parsing: " + str);
try {
var result = this.parseInt(str);
console.log("[Integer.parseInt] Success: " + result);
return result;
} catch (e) {
console.log("[Integer.parseInt] Exception caught: " + e);
// 可以返回默认值而不是抛出异常
console.log("[Integer.parseInt] Returning default value: 0");
return 0;
}
};
// 测试异常处理
try {
var num1 = Integer.parseInt("123");
var num2 = Integer.parseInt("invalid");
console.log("[+] Parsed numbers: " + num1 + ", " + num2);
} catch (e) {
console.log("[-] Parsing failed: " + e);
}
// Hook 异常构造函数
var RuntimeException = Java.use("java.lang.RuntimeException");
RuntimeException.$init.overload('java.lang.String').implementation = function(message) {
console.log("[RuntimeException.<init>] Exception created with message: " + message);
this.$init(message);
};
} catch (e) {
console.log("[-] Exception handling failed: " + e);
}
}
// 高级 Java API 功能
function advancedJavaFeatures() {
Java.perform(function() {
// 1. 类枚举
enumerateLoadedClasses();
// 2. 方法枚举
enumerateClassMethods();
// 3. 动态类创建
createDynamicClass();
// 4. 接口实现
implementInterface();
});
}
function enumerateLoadedClasses() {
console.log("[+] Enumerating loaded classes...");
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className.startsWith("com.example.")) {
console.log("[Class] Found target class: " + className);
}
},
onComplete: function() {
console.log("[+] Class enumeration completed");
}
});
}
function enumerateClassMethods() {
try {
var targetClass = Java.use("java.lang.String");
// 获取所有方法
var methods = targetClass.class.getDeclaredMethods();
console.log("[+] String class methods:");
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
var modifiers = method.getModifiers();
var isPublic = (modifiers & 1) !== 0;
var isStatic = (modifiers & 8) !== 0;
if (isPublic) {
console.log(" " + (isStatic ? "static " : "") + method.getName());
}
}
} catch (e) {
console.log("[-] Method enumeration failed: " + e);
}
}
function createDynamicClass() {
try {
// 使用 Java.registerClass 创建动态类
var DynamicClass = Java.registerClass({
name: 'com.frida.DynamicClass',
superClass: Java.use('java.lang.Object'),
implements: [Java.use('java.lang.Runnable')],
fields: {
value: 'int'
},
methods: {
$init: [{
returnType: 'void',
argumentTypes: ['int'],
implementation: function(value) {
this.value.value = value;
}
}],
run: {
returnType: 'void',
argumentTypes: [],
implementation: function() {
console.log("[DynamicClass] Running with value: " + this.value.value);
}
},
getValue: {
returnType: 'int',
argumentTypes: [],
implementation: function() {
return this.value.value;
}
}
}
});
// 使用动态类
var instance = DynamicClass.$new(42);
instance.run();
console.log("[+] Dynamic class value: " + instance.getValue());
} catch (e) {
console.log("[-] Dynamic class creation failed: " + e);
}
}
function implementInterface() {
try {
var Runnable = Java.use("java.lang.Runnable");
// 实现接口
var RunnableImpl = Java.registerClass({
name: 'com.frida.RunnableImpl',
implements: [Runnable],
methods: {
run: {
returnType: 'void',
argumentTypes: [],
implementation: function() {
console.log("[RunnableImpl] Custom runnable executed");
}
}
}
});
// 创建实例并使用
var runnable = RunnableImpl.$new();
runnable.run();
// 在线程中使用
var Thread = Java.use("java.lang.Thread");
var thread = Thread.$new(runnable);
thread.start();
console.log("[+] Interface implementation successful");
} catch (e) {
console.log("[-] Interface implementation failed: " + e);
}
}
// Java 类型转换和数组操作
function javaTypeOperations() {
Java.perform(function() {
// 1. 基本类型转换
demonstrateTypeConversion();
// 2. 数组操作
demonstrateArrayOperations();
// 3. 集合操作
demonstrateCollectionOperations();
});
}
function demonstrateTypeConversion() {
try {
// JavaScript 到 Java 类型转换
var String = Java.use("java.lang.String");
var Integer = Java.use("java.lang.Integer");
var Boolean = Java.use("java.lang.Boolean");
// 字符串转换
var jsString = "Hello from JavaScript";
var javaString = String.$new(jsString);
console.log("[+] Java string: " + javaString);
// 数字转换
var jsNumber = 42;
var javaInteger = Integer.valueOf(jsNumber);
console.log("[+] Java integer: " + javaInteger);
// 布尔转换
var jsBoolean = true;
var javaBoolean = Boolean.valueOf(jsBoolean);
console.log("[+] Java boolean: " + javaBoolean);
// Java 到 JavaScript 转换
var javaStr = String.$new("Java String");
var jsStr = javaStr.toString();
console.log("[+] JavaScript string: " + jsStr);
// 包装类型操作
var intValue = Integer.valueOf(100);
var primitiveInt = intValue.intValue();
console.log("[+] Primitive int: " + primitiveInt);
} catch (e) {
console.log("[-] Type conversion failed: " + e);
}
}
function demonstrateArrayOperations() {
try {
// 创建 Java 数组
var stringArray = Java.array('java.lang.String', ['Hello', 'World', 'Frida']);
var intArray = Java.array('int', [1, 2, 3, 4, 5]);
var byteArray = Java.array('byte', [0x41, 0x42, 0x43]);
console.log("[+] String array length: " + stringArray.length);
console.log("[+] Int array length: " + intArray.length);
console.log("[+] Byte array length: " + byteArray.length);
// 访问数组元素
for (var i = 0; i < stringArray.length; i++) {
console.log(" stringArray[" + i + "]: " + stringArray[i]);
}
// 修改数组元素
stringArray[0] = "Modified";
console.log("[+] Modified string array[0]: " + stringArray[0]);
// 多维数组
var twoDArray = Java.array('[I', [
Java.array('int', [1, 2, 3]),
Java.array('int', [4, 5, 6])
]);
console.log("[+] 2D array [0][1]: " + twoDArray[0][1]);
// 数组作为方法参数
var Arrays = Java.use("java.util.Arrays");
var sortedArray = Arrays.copyOf(intArray, intArray.length);
Arrays.sort(sortedArray);
console.log("[+] Sorted array: " + Arrays.toString(sortedArray));
} catch (e) {
console.log("[-] Array operations failed: " + e);
}
}
function demonstrateCollectionOperations() {
try {
var ArrayList = Java.use("java.util.ArrayList");
var HashMap = Java.use("java.util.HashMap");
var HashSet = Java.use("java.util.HashSet");
// ArrayList 操作
var list = ArrayList.$new();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
console.log("[+] ArrayList size: " + list.size());
// 遍历 ArrayList
for (var i = 0; i < list.size(); i++) {
console.log(" list[" + i + "]: " + list.get(i));
}
// HashMap 操作
var map = HashMap.$new();
map.put("key1", "value1");
map.put("key2", "value2");
console.log("[+] HashMap size: " + map.size());
console.log("[+] HashMap get('key1'): " + map.get("key1"));
// 遍历 HashMap
var keySet = map.keySet();
var iterator = keySet.iterator();
while (iterator.hasNext()) {
var key = iterator.next();
var value = map.get(key);
console.log(" map[" + key + "]: " + value);
}
// HashSet 操作
var set = HashSet.$new();
set.add("element1");
set.add("element2");
set.add("element1"); // 重复元素
console.log("[+] HashSet size: " + set.size());
console.log("[+] HashSet contains 'element1': " + set.contains("element1"));
} catch (e) {
console.log("[-] Collection operations failed: " + e);
}
}
Native API 深度解析
Interceptor 和内存操作
// native_api_advanced.js - Native API 高级用法
console.log("[+] Starting Native API demonstrations");
// 1. Interceptor 高级用法
demonstrateInterceptorFeatures();
// 2. 内存操作
demonstrateMemoryOperations();
// 3. 模块和符号操作
demonstrateModuleOperations();
// 4. 指针和数据类型
demonstratePointerOperations();
function demonstrateInterceptorFeatures() {
try {
// 查找函数地址
var mallocPtr = Module.findExportByName("libc.so", "malloc");
var freePtr = Module.findExportByName("libc.so", "free");
var strcmpPtr = Module.findExportByName("libc.so", "strcmp");
if (mallocPtr) {
console.log("[+] malloc found at: " + mallocPtr);
// 基础 Hook
Interceptor.attach(mallocPtr, {
onEnter: function(args) {
this.size = args[0].toInt32();
console.log("[malloc] Allocating " + this.size + " bytes");
},
onLeave: function(retval) {
console.log("[malloc] Allocated at: " + retval);
// 记录分配信息
if (!this.allocations) {
this.allocations = new Map();
}
this.allocations.set(retval.toString(), this.size);
}
});
}
if (freePtr) {
// Hook free 并跟踪内存释放
Interceptor.attach(freePtr, {
onEnter: function(args) {
var ptr = args[0];
console.log("[free] Freeing memory at: " + ptr);
// 检查是否为已知分配
if (this.allocations && this.allocations.has(ptr.toString())) {
var size = this.allocations.get(ptr.toString());
console.log("[free] Freeing " + size + " bytes");
this.allocations.delete(ptr.toString());
}
}
});
}
if (strcmpPtr) {
// Hook strcmp 并分析字符串比较
Interceptor.attach(strcmpPtr, {
onEnter: function(args) {
try {
this.str1 = Memory.readUtf8String(args[0]);
this.str2 = Memory.readUtf8String(args[1]);
console.log("[strcmp] Comparing: '" + this.str1 + "' vs '" + this.str2 + "'");
} catch (e) {
console.log("[strcmp] Failed to read strings: " + e);
}
},
onLeave: function(retval) {
var result = retval.toInt32();
console.log("[strcmp] Result: " + result + " (" +
(result === 0 ? "equal" : result < 0 ? "str1 < str2" : "str1 > str2") + ")");
}
});
}
// Hook 函数并修改返回值
if (strcmpPtr) {
Interceptor.replace(strcmpPtr, new NativeCallback(function(str1, str2) {
var s1 = Memory.readUtf8String(str1);
var s2 = Memory.readUtf8String(str2);
console.log("[strcmp_replaced] Comparing: '" + s1 + "' vs '" + s2 + "'");
// 特殊处理:让所有字符串比较都返回相等
if (s1 === "password" || s2 === "password") {
console.log("[strcmp_replaced] Password comparison - forcing equal");
return 0;
}
// 调用原始函数
return s1.localeCompare(s2);
}, 'int', ['pointer', 'pointer']));
}
} catch (e) {
console.log("[-] Interceptor demonstration failed: " + e);
}
}
function demonstrateMemoryOperations() {
try {
console.log("[+] Demonstrating memory operations");
// 1. 内存分配和读写
var buffer = Memory.alloc(1024);
console.log("[+] Allocated 1024 bytes at: " + buffer);
// 写入不同类型的数据
Memory.writeU8(buffer, 0x41);
Memory.writeU16(buffer.add(1), 0x4243);
Memory.writeU32(buffer.add(3), 0x44454647);
Memory.writeU64(buffer.add(7), uint64("0x4849505152535455"));
// 读取数据
var u8 = Memory.readU8(buffer);
var u16 = Memory.readU16(buffer.add(1));
var u32 = Memory.readU32(buffer.add(3));
var u64 = Memory.readU64(buffer.add(7));
console.log("[+] Read U8: 0x" + u8.toString(16));
console.log("[+] Read U16: 0x" + u16.toString(16));
console.log("[+] Read U32: 0x" + u32.toString(16));
console.log("[+] Read U64: 0x" + u64.toString(16));
// 字符串操作
var str = "Hello Frida!";
Memory.writeUtf8String(buffer.add(15), str);
var readStr = Memory.readUtf8String(buffer.add(15));
console.log("[+] String written and read: " + readStr);
// 字节数组操作
var bytes = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello"
Memory.writeByteArray(buffer.add(50), bytes);
var readBytes = Memory.readByteArray(buffer.add(50), bytes.length);
console.log("[+] Bytes written and read: " + hexdump(readBytes));
// 2. 内存搜索
Memory.scan(Module.findBaseAddress("libc.so"), 0x1000, "48 65 6C 6C 6F", {
onMatch: function(address, size) {
console.log("[+] Found 'Hello' pattern at: " + address);
return 'stop'; // 停止搜索
},
onError: function(reason) {
console.log("[-] Memory scan error: " + reason);
},
onComplete: function() {
console.log("[+] Memory scan completed");
}
});
// 3. 内存保护
try {
Memory.protect(buffer, 1024, 'r--');
console.log("[+] Memory protection changed to read-only");
// 尝试写入(应该失败)
try {
Memory.writeU8(buffer, 0xFF);
console.log("[-] Unexpected: write succeeded on read-only memory");
} catch (e) {
console.log("[+] Expected: write failed on read-only memory");
}
// 恢复写权限
Memory.protect(buffer, 1024, 'rw-');
Memory.writeU8(buffer, 0xFF);
console.log("[+] Write succeeded after restoring write permission");
} catch (e) {
console.log("[-] Memory protection failed: " + e);
}
} catch (e) {
console.log("[-] Memory operations failed: " + e);
}
}
function demonstrateModuleOperations() {
try {
console.log("[+] Demonstrating module operations");
// 1. 枚举模块
var modules = Process.enumerateModules();
console.log("[+] Found " + modules.length + " loaded modules");
// 显示前5个模块
for (var i = 0; i < Math.min(5, modules.length); i++) {
var module = modules[i];
console.log(" Module: " + module.name + " @ " + module.base + " (size: 0x" + module.size.toString(16) + ")");
}
// 2. 查找特定模块
var libcModule = Process.findModuleByName("libc.so");
if (libcModule) {
console.log("[+] libc.so found:");
console.log(" Base: " + libcModule.base);
console.log(" Size: 0x" + libcModule.size.toString(16));
console.log(" Path: " + libcModule.path);
// 枚举导出函数
var exports = libcModule.enumerateExports();
console.log("[+] libc.so has " + exports.length + " exports");
// 显示前10个导出函数
for (var i = 0; i < Math.min(10, exports.length); i++) {
var exp = exports[i];
console.log(" Export: " + exp.name + " @ " + exp.address + " (type: " + exp.type + ")");
}
// 枚举导入函数
var imports = libcModule.enumerateImports();
console.log("[+] libc.so has " + imports.length + " imports");
// 显示前5个导入函数
for (var i = 0; i < Math.min(5, imports.length); i++) {
var imp = imports[i];
console.log(" Import: " + imp.name + " from " + imp.module + " @ " + imp.address);
}
}
// 3. 符号解析
var mallocAddr = Module.findExportByName("libc.so", "malloc");
if (mallocAddr) {
console.log("[+] malloc address: " + mallocAddr);
// 反向查找符号
var symbol = DebugSymbol.fromAddress(mallocAddr);
console.log("[+] Symbol at malloc address: " + symbol);
}
// 4. 内存范围操作
var ranges = Process.enumerateRanges('r--');
console.log("[+] Found " + ranges.length + " readable memory ranges");
// 显示前3个范围
for (var i = 0; i < Math.min(3, ranges.length); i++) {
var range = ranges[i];
console.log(" Range: " + range.base + "-" + range.base.add(range.size) +
" (size: 0x" + range.size.toString(16) + ", protection: " + range.protection + ")");
}
} catch (e) {
console.log("[-] Module operations failed: " + e);
}
}
function demonstratePointerOperations() {
try {
console.log("[+] Demonstrating pointer operations");
// 1. 指针创建和操作
var addr = ptr("0x12345678");
console.log("[+] Created pointer: " + addr);
console.log("[+] Pointer as number: 0x" + addr.toString(16));
// 指针算术
var addr2 = addr.add(0x100);
var addr3 = addr.sub(0x50);
console.log("[+] addr + 0x100: " + addr2);
console.log("[+] addr - 0x50: " + addr3);
// 指针比较
console.log("[+] addr == addr: " + addr.equals(addr));
console.log("[+] addr < addr2: " + addr.compare(addr2) < 0);
// 2. NULL 指针处理
var nullPtr = ptr(0);
console.log("[+] NULL pointer: " + nullPtr);
console.log("[+] Is NULL: " + nullPtr.isNull());
// 3. 指针和整数转换
var intValue = 0x41424344;
var ptrFromInt = ptr(intValue);
var intFromPtr = ptrFromInt.toInt32();
console.log("[+] Int to pointer to int: 0x" + intValue.toString(16) + " -> " + ptrFromInt + " -> 0x" + intFromPtr.toString(16));
// 4. 64位指针操作
var addr64 = ptr("0x123456789ABCDEF0");
console.log("[+] 64-bit pointer: " + addr64);
console.log("[+] As uint64: " + addr64.toString());
// 5. 指针数组
var ptrArray = [ptr("0x1000"), ptr("0x2000"), ptr("0x3000")];
console.log("[+] Pointer array:");
for (var i = 0; i < ptrArray.length; i++) {
console.log(" [" + i + "]: " + ptrArray[i]);
}
} catch (e) {
console.log("[-] Pointer operations failed: " + e);
}
}
// 辅助函数
function hexdump(data, options) {
options = options || {};
var offset = options.offset || 0;
var length = options.length || data.byteLength;
var header = options.header !== false;
var ansi = options.ansi !== false;
var result = "";
if (header) {
result += " 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n";
}
var bytes = new Uint8Array(data);
for (var i = 0; i < length; i += 16) {
var addr = (offset + i).toString(16).padStart(8, '0').toUpperCase();
var hex = "";
var ascii = "";
for (var j = 0; j < 16; j++) {
if (i + j < length) {
var byte = bytes[i + j];
hex += byte.toString(16).padStart(2, '0').toUpperCase() + " ";
ascii += (byte >= 32 && byte <= 126) ? String.fromCharCode(byte) : ".";
} else {
hex += " ";
ascii += " ";
}
}
result += addr + ": " + hex + " " + ascii + "\n";
}
return result;
}
高级 API 功能
Stalker 和代码跟踪
// stalker_api.js - Stalker API 高级用法
console.log("[+] Starting Stalker demonstrations");
// 1. 基础代码跟踪
demonstrateBasicStalking();
// 2. 高级跟踪功能
demonstrateAdvancedStalking();
// 3. 性能分析
demonstratePerformanceProfiling();
function demonstrateBasicStalking() {
try {
console.log("[+] Basic stalking demonstration");
var targetModule = Process.findModuleByName("libc.so");
if (!targetModule) {
console.log("[-] Target module not found");
return;
}
var mallocAddr = Module.findExportByName("libc.so", "malloc");
if (!mallocAddr) {
console.log("[-] malloc not found");
return;
}
console.log("[+] Starting to stalk thread: " + Process.getCurrentThreadId());
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: true, // 跟踪函数调用
ret: true, // 跟踪函数返回
exec: false, // 不跟踪每条指令
block: false, // 不跟踪基本块
compile: false // 不跟踪编译事件
},
onReceive: function(events) {
console.log("[Stalker] Received " + events.length + " events");
// 解析事件
var calls = Stalker.parse(events, {
annotate: true,
stringify: false
});
// 处理前10个事件
for (var i = 0; i < Math.min(10, calls.length); i++) {
var event = calls[i];
if (event[0] === 'call') {
var target = event[1];
var symbol = DebugSymbol.fromAddress(target);
console.log("[Call] " + target + " (" + symbol + ")");
} else if (event[0] === 'ret') {
var target = event[1];
console.log("[Ret] " + target);
}
}
},
transform: function(iterator) {
var instruction = iterator.next();
do {
// 只跟踪目标模块内的指令
if (instruction.address.compare(targetModule.base) >= 0 &&
instruction.address.compare(targetModule.base.add(targetModule.size)) < 0) {
// 在 malloc 调用处插入回调
if (instruction.address.equals(mallocAddr)) {
iterator.putCallout(function(context) {
console.log("[Stalker] malloc called with size: " + context.x0);
});
}
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
}
});
// 运行一段时间后停止跟踪
setTimeout(function() {
Stalker.unfollow(Process.getCurrentThreadId());
console.log("[+] Stalking stopped");
}, 5000);
} catch (e) {
console.log("[-] Basic stalking failed: " + e);
}
}
function demonstrateAdvancedStalking() {
try {
console.log("[+] Advanced stalking demonstration");
var targetFunction = Module.findExportByName("libc.so", "strcmp");
if (!targetFunction) {
console.log("[-] Target function not found");
return;
}
var callCount = 0;
var executionTimes = [];
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: true,
ret: true,
exec: true // 跟踪每条指令执行
},
onReceive: function(events) {
// 批量处理事件以提高性能
var parsedEvents = Stalker.parse(events);
for (var i = 0; i < parsedEvents.length; i++) {
var event = parsedEvents[i];
if (event[0] === 'call' && event[1].equals(targetFunction)) {
callCount++;
event.startTime = Date.now();
} else if (event[0] === 'ret' && event.startTime) {
var duration = Date.now() - event.startTime;
executionTimes.push(duration);
}
}
},
transform: function(iterator) {
var instruction = iterator.next();
var basicBlockStart = instruction.address;
var basicBlockSize = 0;
do {
basicBlockSize++;
// 在特定指令处插入自定义代码
if (instruction.mnemonic === 'call') {
iterator.putCallout(function(context) {
console.log("[Stalker] Call instruction at: " + instruction.address);
});
}
// 代码覆盖率跟踪
if (instruction.address.equals(basicBlockStart)) {
iterator.putCallout(function(context) {
// 记录基本块执行
recordBasicBlockExecution(basicBlockStart, basicBlockSize);
});
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
}
});
// 定期报告统计信息
var reportInterval = setInterval(function() {
console.log("[Stalker] Function calls: " + callCount);
if (executionTimes.length > 0) {
var avgTime = executionTimes.reduce((a, b) => a + b, 0) / executionTimes.length;
console.log("[Stalker] Average execution time: " + avgTime.toFixed(2) + "ms");
}
}, 2000);
// 清理
setTimeout(function() {
clearInterval(reportInterval);
Stalker.unfollow(Process.getCurrentThreadId());
console.log("[+] Advanced stalking stopped");
}, 10000);
} catch (e) {
console.log("[-] Advanced stalking failed: " + e);
}
}
function demonstratePerformanceProfiling() {
try {
console.log("[+] Performance profiling demonstration");
var functionStats = new Map();
var callStack = [];
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: true,
ret: true
},
onReceive: function(events) {
var parsedEvents = Stalker.parse(events, {
annotate: true,
stringify: true
});
for (var i = 0; i < parsedEvents.length; i++) {
var event = parsedEvents[i];
if (event[0] === 'call') {
var target = event[1];
var symbol = event[2] || target.toString();
// 记录调用开始时间
callStack.push({
address: target,
symbol: symbol,
startTime: Date.now()
});
} else if (event[0] === 'ret' && callStack.length > 0) {
var call = callStack.pop();
var duration = Date.now() - call.startTime;
// 更新函数统计
if (!functionStats.has(call.symbol)) {
functionStats.set(call.symbol, {
callCount: 0,
totalTime: 0,
minTime: Infinity,
maxTime: 0
});
}
var stats = functionStats.get(call.symbol);
stats.callCount++;
stats.totalTime += duration;
stats.minTime = Math.min(stats.minTime, duration);
stats.maxTime = Math.max(stats.maxTime, duration);
}
}
}
});
// 定期生成性能报告
var profileInterval = setInterval(function() {
console.log("\n[Performance Profile]");
console.log("Function".padEnd(40) + "Calls".padEnd(10) + "Total(ms)".padEnd(12) + "Avg(ms)".padEnd(10) + "Min(ms)".padEnd(10) + "Max(ms)");
console.log("-".repeat(92));
// 按总时间排序
var sortedStats = Array.from(functionStats.entries()).sort((a, b) => b[1].totalTime - a[1].totalTime);
for (var i = 0; i < Math.min(10, sortedStats.length); i++) {
var [symbol, stats] = sortedStats[i];
var avgTime = stats.totalTime / stats.callCount;
console.log(
symbol.substring(0, 39).padEnd(40) +
stats.callCount.toString().padEnd(10) +
stats.totalTime.toFixed(2).padEnd(12) +
avgTime.toFixed(2).padEnd(10) +
stats.minTime.toFixed(2).padEnd(10) +
stats.maxTime.toFixed(2)
);
}
}, 5000);
// 清理
setTimeout(function() {
clearInterval(profileInterval);
Stalker.unfollow(Process.getCurrentThreadId());
console.log("\n[+] Performance profiling stopped");
}, 15000);
} catch (e) {
console.log("[-] Performance profiling failed: " + e);
}
}
function recordBasicBlockExecution(address, size) {
// 记录基本块执行情况
if (!this.basicBlockCoverage) {
this.basicBlockCoverage = new Map();
}
var key = address.toString();
if (!this.basicBlockCoverage.has(key)) {
this.basicBlockCoverage.set(key, {
address: address,
size: size,
hitCount: 0
});
}
this.basicBlockCoverage.get(key).hitCount++;
}
// Stalker 实用工具
var StalkerUtils = {
// 跟踪特定函数的执行
traceFunction: function(functionAddress, options) {
options = options || {};
var maxEvents = options.maxEvents || 1000;
var events = [];
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: true,
ret: true
},
onReceive: function(receivedEvents) {
var parsed = Stalker.parse(receivedEvents);
events = events.concat(parsed);
if (events.length >= maxEvents) {
Stalker.unfollow(Process.getCurrentThreadId());
if (options.onComplete) {
options.onComplete(events);
}
}
},
transform: function(iterator) {
var instruction = iterator.next();
do {
if (instruction.address.equals(functionAddress)) {
iterator.putCallout(function(context) {
console.log("[Trace] Function called at: " + functionAddress);
});
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
}
});
},
// 生成调用图
generateCallGraph: function(rootFunction, depth) {
depth = depth || 3;
var callGraph = new Map();
var currentDepth = 0;
Stalker.follow(Process.getCurrentThreadId(), {
events: { call: true, ret: true },
onReceive: function(events) {
var parsed = Stalker.parse(events, { annotate: true });
for (var i = 0; i < parsed.length; i++) {
var event = parsed[i];
if (event[0] === 'call') {
var caller = event[1];
var callee = event[2];
if (!callGraph.has(caller)) {
callGraph.set(caller, new Set());
}
callGraph.get(caller).add(callee);
}
}
}
});
return callGraph;
}
};
总结
Frida JavaScript API 的核心要点:
🎯 Java API 精髓
- 类操作:动态加载、方法枚举、字段访问
- 方法 Hook:重载处理、参数修改、返回值控制
- 类型转换:JavaScript 与 Java 类型互转
- 异常处理:捕获和修改异常行为
✅ Native API 核心
- Interceptor 函数拦截和替换
- 内存读写和保护操作
- 模块和符号解析
- 指针算术和类型转换
🚀 高级功能
- Stalker 代码跟踪和性能分析
- 动态代码生成和修改
- 实时调试和监控
- 自定义工具开发
💡 最佳实践
- 合理使用 API 避免性能影响
- 错误处理和异常捕获
- 内存管理和资源清理
- 模块化和可复用的脚本设计
掌握 Frida JavaScript API,释放动态分析的无限潜力!
深入理解 Frida API 是进行高效动态分析的基础,合理运用这些工具能够大大提升分析效率。