动态添加原生钩子

问题描述 投票:0回答:2

有没有办法使用 JNI 动态添加本机钩子?我的意思是,我想重写类(或新类)中的一些方法,以便重写调用我的本机代码,而无需为此编写任何 Java 代码。

java java-native-interface
2个回答
0
投票

如果您指的是

native
方法,也许注册可能是您的答案。

比如我这里注册的是native方法

JNIEXPORT jint JNICALL addOne(JNIEnv *env, jclass obj, jint a) {
  return a + 1;
}
...
...
static JNINativeMethod methods[] = {
  {"addOne",    "(I)I", (void *)&addOne}
};
...
...
(*env)->RegisterNatives(env, cls_Main,
                        methods, sizeof(methods)/sizeof(methods[0]));

将被分配到班级

public class Main {

  public static native int addOne(int a);

  ...
  ...

}

完整示例:https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo052


0
投票

这在典型的 JVM 上是可能的,但需要在运行时从预编译的字节码定义代理类。不幸的是,下面的方法在Android上不起作用,因为Dalvik使用自己的字节码格式并且

DefineClass
没有实现。在这种情况下,您必须使用一些用 Java 编写并编译为 DEX 的最少支持代码来构建包。

举个例子,假设您想创建一个 AWT

Frame
,但在本机代码中处理窗口事件。这需要实现一个抽象的
WindowListener
接口并使用
Frame
将其传递给
addWindowListener

首先,实现一个代理类,将事件转发到本机实现:

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

class NativeWindowListener implements WindowListener
{
    public native void windowActivated(WindowEvent e);
    public native void windowClosed(WindowEvent e);
    public native void windowClosing(WindowEvent e);
    public native void windowDeactivated(WindowEvent e);
    public native void windowDeiconified(WindowEvent e);
    public native void windowIconified(WindowEvent e);
    public native void windowOpened(WindowEvent e);
}

接下来,将该类编译为字节码:

$ javac NativeWindowListener.java

使用Linux命令

xxd
或类似工具将
NativeWindowListener.class
二进制转换为C数组:

$ xxd -i NativeWindowListener.class

将生成的 C 数组嵌入到本机代码中,并使用

DefineClass
在运行时从字节码定义该类:

const unsigned char NativeWindowListener_class[] = { /* bytecode */ };

const jclass listener_class = jni->DefineClass(
    "NativeWindowListener", NULL,
    reinterpret_cast<const jbyte*>(NativeWindowListener_class),
    sizeof(NativeWindowListener_class));

一旦定义了代理类,您就可以使用

RegisterNatives
为 Java 存根提供本机实现:

// Native event handlers
void JNICALL NativeWindowListener_windowActivated(JNIEnv* jni, jobject self, jobject event);
// etc.

#define WINDOW_EVENT_LISTENER(name)                                                     \
    {                                                                                   \
        const_cast<char*>(#name), const_cast<char*>("(Ljava/awt/event/WindowEvent;)V"), \
            reinterpret_cast<void*>(&NativeWindowListener_##name)                       \
    }

const JNINativeMethod methods[] = {
    WINDOW_EVENT_LISTENER(windowActivated),
    WINDOW_EVENT_LISTENER(windowClosed),
    WINDOW_EVENT_LISTENER(windowClosing),
    WINDOW_EVENT_LISTENER(windowDeactivated),
    WINDOW_EVENT_LISTENER(windowDeiconified),
    WINDOW_EVENT_LISTENER(windowIconified),
    WINDOW_EVENT_LISTENER(windowOpened),
};

jni->RegisterNatives(listener_class, methods, sizeof(methods) / sizeof(methods[0]));

最后,您可以构造侦听器并将其传递给

Frame

// Construct the listener
const jmethodID listener_ctor = jni->GetMethodID(listener_class, "<init>", "()V");
const jobject listener = jni->NewObject(listener_class, listener_ctor);

// Add listener to the frame
const jclass frame_class = jni->FindClass("java/awt/Frame");
const jmethodID frame_ctor = jni->GetMethodID(frame_class, "<init>", "()V");
const jmethodID frame_addwindowlistener = jni->GetMethodID(frame_class, "addWindowListener", "(Ljava/awt/event/WindowListener;)V");
const jobject frame = jni->NewObject(frame_class, frame_ctor);
jni->CallObjectMethod(frame, frame_addwindowlistener, listener);

如果要存储指向某些本机上下文的指针,可以向侦听器代理添加一个

long
字段,并使用
GetFieldID
Get/SetLongField
JNI 函数访问它。

[1] https://github.com/android/ndk/wiki/JNI#unsupported-featuresbackwards-compatibility

© www.soinside.com 2019 - 2024. All rights reserved.