发布于

Frida JavaScript API 深度解析:掌握动态分析的核心技术

作者

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 精髓

  1. 类操作:动态加载、方法枚举、字段访问
  2. 方法 Hook:重载处理、参数修改、返回值控制
  3. 类型转换:JavaScript 与 Java 类型互转
  4. 异常处理:捕获和修改异常行为

✅ Native API 核心

  • Interceptor 函数拦截和替换
  • 内存读写和保护操作
  • 模块和符号解析
  • 指针算术和类型转换

🚀 高级功能

  • Stalker 代码跟踪和性能分析
  • 动态代码生成和修改
  • 实时调试和监控
  • 自定义工具开发

💡 最佳实践

  • 合理使用 API 避免性能影响
  • 错误处理和异常捕获
  • 内存管理和资源清理
  • 模块化和可复用的脚本设计

掌握 Frida JavaScript API,释放动态分析的无限潜力!


深入理解 Frida API 是进行高效动态分析的基础,合理运用这些工具能够大大提升分析效率。