You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android调用预编译.so库打开串口时JNI方法找不到的问题

问题描述

我在StackOverflow上搜索相关问题但未找到直接答案。我需要在特定Android设备的应用中打开串口,该设备原有带串口功能的APK,我逆向分析后获取了system/lib64下的liboldnativelibrary.so文件,已知信息如下:

  • 串口采用Uart通信;
  • 该.so库功能正常;
  • 已掌握其基础函数名;
  • 设备运行Android 10,处理器为arm64-v8a;
  • 已将该.so添加至jniLibs并成功加载。

我没有原JNI的.c和.h文件,也无设备及串口文档,难以重新构建原生库。逆向得到的.so方法为:

jobject Java_com_the_old_serialportApp_SerialPort_open(JNIEnv *penv,undefined8 param_2,jstring inputString,speed_t baudRate,uint hiddenParam)

我编写了Kotlin代码:

package com.myself.serialporttesting

    companion object {
        init {
            try {
                Log.i("Library", "Loading library")
                System.loadLibrary("oldnativelibrary")
                Log.i("Library", "Library loaded successfully")
            } catch (e: UnsatisfiedLinkError) {
                Log.i("Library", "Library not loaded")
            }
        }

        private external fun open(
            fileName: String,
            baudRate: Int,
            hiddenValue:  Int
        ): FileDescriptor
}

运行时报错:

No implementation found for java.io.FileDescriptor com.hfad.serialporttesting.SerialPort$Companion.open(java.lang.String, int, int)

我怀疑是包名不同导致的JNI命名问题,请问是否如此?能否找到解决办法?


解决方案

你的怀疑完全正确——这就是JNI方法命名不匹配导致的问题。JNI通过全限定类名+方法名的规则绑定Java/Kotlin方法和原生函数,逆向得到的原生函数名Java_com_the_old_serialportApp_SerialPort_open对应原APK中的类路径是com.the.old.serialportApp.SerialPort,而你当前的Kotlin类路径是com.myself.serialporttesting.SerialPort$Companion(因为用了伴生对象),两者完全不匹配,所以系统找不到对应的实现。

以下是几种可行的解决办法,按优先级排序:

1. 完全匹配原类路径(最简单直接)

直接把你的Kotlin类的包名和类名改成和原APK一致:

  • 包名改为com.the.old.serialportApp
  • 类名改为SerialPort,且将external方法放到类实例中,不要用伴生对象(原函数的第二个参数对应类实例jobject,而非伴生对象的jclass)

修改后的Kotlin代码示例:

package com.the.old.serialportApp

class SerialPort {
    init {
        try {
            Log.i("Library", "Loading library")
            System.loadLibrary("oldnativelibrary")
            Log.i("Library", "Library loaded successfully")
        } catch (e: UnsatisfiedLinkError) {
            Log.i("Library", "Library not loaded")
        }
    }

    private external fun open(
        fileName: String,
        baudRate: Int,
        hiddenValue: Int
    ): FileDescriptor
}

这样JNI就能通过标准命名规则找到对应的原生函数。

2. 使用JNI动态注册(无需修改包名)

如果不想调整自己的包名,可以通过JNI动态注册绑定方法,步骤如下:

  • 创建新的JNI层文件(比如serialport_bridge.c),实现动态注册逻辑
  • 编译该文件为新的.so库,让它作为桥梁连接你的Kotlin方法和原有的liboldnativelibrary.so函数

动态注册核心代码示例:

#include <jni.h>
#include <android/log.h>

// 声明原有的原生函数
jobject Java_com_the_old_serialportApp_SerialPort_open(JNIEnv *penv, jobject thiz, jstring inputString, speed_t baudRate, uint hiddenParam);

// 方法映射:Kotlin方法名 -> 签名 -> 原生函数指针
static const JNINativeMethod methods[] = {
    {
        "open",
        "(Ljava/lang/String;II)Ljava/io/FileDescriptor;",
        (void *)Java_com_the_old_serialportApp_SerialPort_open
    }
};

// 库加载时执行注册逻辑
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    // 替换为你当前Kotlin类的全路径(注意用/分隔,伴生对象需加$Companion)
    jclass clazz = (*env)->FindClass(env, "com/myself/serialporttesting/SerialPort$Companion");
    if (clazz == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, "SerialPortBridge", "Failed to find target class");
        return JNI_ERR;
    }
    if ((*env)->RegisterNatives(env, clazz, methods, sizeof(methods)/sizeof(methods[0])) < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "SerialPortBridge", "Failed to register methods");
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

编译生成libserialport_bridge.so后,在Kotlin代码中先加载这个桥接库,再加载原库即可完成绑定。

3. 修改原.so文件的函数名(需逆向技能)

如果有逆向修改ELF文件的能力,可以用IDA Pro或Ghidra等工具,把原函数名Java_com_the_old_serialportApp_SerialPort_open修改为对应你当前类路径的名字(比如Java_com_myself_serialporttesting_SerialPort_00024Companion_open,Kotlin伴生对象会生成类似后缀)。但这种方法风险较高,容易破坏.so的结构,仅推荐有逆向经验的开发者尝试。

额外注意:

  • 原函数的第二个参数undefined8 param_2对应JNI中的jobject thiz(类实例),如果用伴生对象,该参数会变成jclass,可能引发参数类型不匹配问题,建议优先使用类实例方法而非伴生对象。
  • 原函数的speed_t和uint类型在arm64-v8a架构下均为32位整数,与Kotlin的Int类型匹配,参数类型无需调整。

内容的提问来源于stack exchange,提问作者jekth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:23:18