native层函数没有导出时,如何获得相应函数地址?

文章讲述了在Android应用中,由于native函数的地址在每次App运行时可能会变化,但相对于基地址的偏移保持不变。通过Frida的Module.findBaseAddress和add方法可以获取函数地址。以一个具体的例子展示了如何动态注册native方法stringFromJNI3,并使用Frida的hook(RegisterNatives)脚本来找到函数的偏移地址,然后编写hookNative.js脚本进行函数拦截。强调了App重新编译可能导致函数偏移地址改变,需要更新hook脚本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

每次App重新运行后native函数加载的绝对地址是会变化的,唯一不变的是函数相对于基地址的偏移,因此我们可以在获取模块的基地址后加上固定的偏移地址获取相应函数的地址,Frida中也正好提供了这种方式:先通过Module.findBaseAddress(name)或Module.getBaseAddress(name)两个API函数获取对应模块的基地址,然后通过add(offset)函数传入固定的偏移offset获取最后的函数绝对地址。以下使用stringFromJNI3中的stringFromJNI3作为具体案例讲解下思路和注意的点。

动态注册的函数

// MainActivity.java
package com.roysue.ndktest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.roysue.ndktest.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'ndktest' library on application startup.
    static {
        System.loadLibrary("ndktest");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.i("r0ysue", stringFromJNI3());
        }
    }

    /**
     * A native method that is implemented by the 'ndktest' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native String stringFromJNI2();

    public native String stringFromJNI3();
}
// native-lib.cpp
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_roysue_ndktest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

JNIEXPORT jstring JNICALL
Java_com_roysue_ndktest_MainActivity_stringFromJNI2(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

jstring JNICALL
sI3(JNIEnv *env, jobject /*this*/) {
    std::string hello = "Hello from C++ stringFromJNI3 r0ysue...";
    return env->NewStringUTF(hello.c_str());
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    JNINativeMethod methods[] = {
            {"stringFromJNI3", "()Ljava/lang/String;", (void *) sI3},
    };
    env->RegisterNatives(env->FindClass("com/roysue/ndktest/MainActivity"), methods, 1);
    return JNI_VERSION_1_6;
}

获取stringFromJNI3函数的偏移地址

使用objection遍历ndktest.so模块的导出函数,会发现找不到stringFromJNI3()字符串相关函数了。这里介绍一个项目 frida_hook_libart,仓库地址为frida_hook_libart。这个项目中包含着一些对JNI函数和art相关函数的Frida Hook脚本,这里要使用的是RegisterNatives()函数的Hook脚本:hook_RegisterNatives.js

执行以下命令进行遍历

frida -UF -f com.roysue.ndktest -l hook_RegisterNatives.js

在这里插入图片描述

注意:fnoffset不是地址偏移值,箭头指向的0x1f5bc才是正确的偏移量

hookNative.js

Hook脚本如下

function hook_nattive3() {
    var libnative_addr = Module.findBaseAddress("libndktest.so");
    console.log("libndktest_addr is =>", libnative_addr);
    var stringFromJNI3 = libnative_addr.add(0x1f5bc);
    console.log("stringFromJNI3 address is=>", stringFromJNI3);

    Interceptor.attach(stringFromJNI3, {
        onEnter: function (args) {
            console.log("jnienv pointer =>", args[0]);
            console.log("jobj pointer =>", args[1]);
        },
        onLeave: function (retval) {
            console.log("retval is =>", Java.vm.getEnv().getStringUtfChars(retval, null).readCString());
            console.log("============================")
        }
    })
}

function main() {
    hook_nattive3();
}

setImmediate(main)

执行Hook命令:

frida -UF -l hookNative.js

Hook结果

注意:App不重新编译,基地址就不会变,如果App代码发生改变,并重新编译运行,那么新的App的函数的偏移地址是会发生改变的,此时应当重新使用hook_RegisterNatives.js脚本获取相应函数的新偏移地址并修改Hook脚本,然后再进行注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诗雅颂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值