GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

应用安全 --- 安卓安全 之 调用隐藏

可能会将调用的so中方法写入jnionload中动态注册同时将函数字符串加密存储

 

这个问题很常见,有几种可能的原因。让我帮你分析一下:

可能的原因和解决方案

1. 函数名被混淆或压缩

bash
# 查看所有导出函数,不限于Engine
readelf -s libengine.so | grep -i start
nm -D libengine.so | grep -i start
objdump -T libengine.so | grep -i start

# 查看所有JNI相关函数
readelf -s libengine.so | grep -i java
nm -D libengine.so | grep -i jni

2. 使用动态注册(RegisterNatives)

这是最常见的原因 - 函数没有使用标准的JNI命名约定:

bash
# 查找JNI_OnLoad函数(动态注册通常在这里)
objdump -t libengine.so | grep -i jni_onload
nm -D libengine.so | grep -i jni_onload

# 查看所有函数,寻找注册相关的代码
strings libengine.so | grep -i register

3. 函数没有被导出

bash
# 查看所有符号(包括未导出的)
nm -g libengine.so > all_symbols.txt
readelf -s libengine.so > all_symbols_detailed.txt

# 然后在这些文件中搜索
grep -i engine all_symbols.txt
grep -i start all_symbols.txt

4. 使用IDA Pro或Ghidra深度分析

bash
# 首先用strings查看可能的函数名线索
strings libengine.so | grep -E "(start|Start|START|engine|Engine)"

5. 检查是否是静态链接或内联

bash
# 查看文件信息
file libengine.so
readelf -h libengine.so

# 查看所有段和节
readelf -S libengine.so

动态分析方案

既然静态分析找不到,我们可以通过动态方式来定位:

方案1:使用Frida Hook JNI调用

javascript
Java.perform(function() {
    // Hook System.loadLibrary来知道so何时加载
    var System = Java.use("java.lang.System");
    System.loadLibrary.overload('java.lang.String').implementation = function(library) {
        console.log("[+] Loading library: " + library);
        if (library.indexOf("engine") !== -1) {
            console.log("[!] Target engine library loaded!");
        }
        return this.loadLibrary(library);
    };
    
    // Hook Engine.start方法
    var Engine = Java.use("com.main.engine.Engine");
    Engine.start.overload('long', 'int', 'java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d, e) {
        console.log("[+] Engine.start called!");
        
        // 打印调用栈
        console.log(Java.use("android.util.Log").getStackTraceString(
            Java.use("java.lang.Throwable").$new()
        ));
        
        return this.start(a, b, c, d, e);
    };
});

方案2:使用Frida Stalker追踪Native执行

javascript
Java.perform(function() {
    var engine_start = null;
    
    // 枚举模块中的导出函数
    Process.enumerateModules({
        onMatch: function(module) {
            if (module.name.indexOf("engine") !== -1) {
                console.log("[+] Found module: " + module.name + " at " + module.base);
                
                // 枚举该模块的所有导出函数
                Module.enumerateExports(module.name, {
                    onMatch: function(exp) {
                        console.log("  Export: " + exp.name + " at " + exp.address);
                        if (exp.name.indexOf("start") !== -1) {
                            engine_start = exp.address;
                        }
                    },
                    onComplete: function() {}
                });
            }
        },
        onComplete: function() {}
    });
});

方案3:检查JNI_OnLoad

c
// 在JNI_OnLoad中通常有这样的模式:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
    
    // 注册Native方法
    JNINativeMethod methods[] = {
        {"start", "(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*)native_start}
    };
    
    jclass clazz = (*env)->FindClass(env, "com/main/engine/Engine");
    (*env)->RegisterNatives(env, clazz, methods, 1);
    
    return JNI_VERSION_1_6;
}

建议的排查步骤

  1. 先用strings命令查看so文件中是否有相关字符串

  2. 检查JNI_OnLoad是否存在

  3. 使用Frida动态分析实际的函数调用

  4. 用IDA Pro/Ghidra进行深度静态分析

你能先运行这些命令看看输出结果吗?特别是 strings 和检查 JNI_OnLoad 的命令。

posted on 2025-10-05 19:23  GKLBB  阅读(4)  评论(0)    收藏  举报