动态注册native_google 动态注册 native 函数 - CSDN
精华内容
参与话题
  • 1.前言在很久之前的一篇Android 开发艺术探索的笔记当中,学习了简单的jni开发流程,但是那会的步骤极其繁琐复杂,而且生成的头文件函数太长,...2. 首先声明native函数现在,我在activity里声明了一个native函数。publi

    1.前言

    在很久之前的一篇Android 开发艺术探索的笔记当中,学习了简单的jni开发流程,但是那会的步骤极其繁琐复杂,而且生成的头文件函数太长,那么,有没有方法能解决呢,让开发过程变得简单易懂。当然是有的,那就是今天的主角。JNI_OnLoad函数。顺便说一下,现在as对 jni开发的支持是越来越好了。

    2. 首先声明native函数

    现在,我在activity里声明了一个native函数。

    public native String getStringFromC();

    然后我们build一下,为啥要build呢,这个马上就说的。

    3. 创建并编写 .h头文件

    创建jni目录,并且右键->new c++class,会生出相应的.h 和.cpp 文件,我们需要稍微修改下.h 文件。

    #ifndef NDK_CORE_H
    #define NDK_CORE_H
    
    #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
    
    #include <jni.h>
    #include <stdlib.h>
    
    __BEGIN_DECLS
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
    
    
    __END_DECLS
    
    #endif //TEXT_HELLO_H
    

    代码说明如下:

    • 我们定义了一个宏NELEM,后面会用到
    • 我们用JNIEXPORT和JNICALL关键字,告诉虚拟机,这是jni函数,

    4. 编写cpp文件

    代码如下:

    jstring returnString(JNIEnv *env,jobject jobj){
      char* str = "I come from C++";
      return env->NewStringUTF(str);
    }
    
    static JNINativeMethod gMethods[] = {
        {"getStringFromC","()Ljava/lang/String;",(void *)returnString }
    };
    
    JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm,void *reserved) {
      JNIEnv *env;
      if (vm->GetEnv((void **) &env,JNI_VERSION_1_6) != JNI_OK){
        return JNI_ERR;
      }
    
      jclass javaClass = env->FindClass("com/example/hello_jni/MainActivity");
      if (javaClass == NULL){
        return JNI_ERR;
      }
      if (env->RegisterNatives(javaClass,gMethods,NELEM(gMethods)) < 0) {
        return JNI_ERR;
      }
    
      return JNI_VERSION_1_6;
    }

    先看JNI_OnLoad方法,这是注册native函数的地方。

    • 首先判断jni版本是不是JNI_VERSION_1_6
    • FindClass方法找到我们对应生命native函数的类,返回一个jclass
    • RegisterNatives 注册native函数,我这里用这个三个参数的方法,第一个表示对应jclass,第二个表示JNINativeMethod的数组,第三个,这个就是我们先前命名的宏,

    就这样,我们就注册了native函数了。接下来我们看下gMethods。

    5. JNINativeMethod数组

    这个数组里存放着这样的元素。

    {"getStringFromC","()Ljava/lang/String;",(void *)returnString }
    • 第一个参数对应的native方法名
    • 第二个参数对应 native方法的描述,我们通过javap -s class文件路径来获取。
      这里写图片描述
    • 第三个参数对应的是我嗯c++代码里对应的实现

    最后就是android.mk 和 application.mk的编写以及gradle的配置,最后编译,这里就不多说了,相关的内容下篇说明。现在给出文件内容。

    android.mk

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := hello-jni
    
    LOCAL_C_INCLUDES += $(LOCAL_PATH)
    LOCAL_SRC_FILES := hello.cpp
    
    include $(BUILD_SHARED_LIBRARY)

    application.mk

    APP_ABI := armeabi x86
    APP_PLATFORM := android-14
    APP_STL := stlport_static
    APP_OPTIM := debug

    gradle 配置

    这里写图片描述

    最后就会生出so文件了。(会自动打包进apk里)
    这里写图片描述

    展开全文
  • IDA动态调试动态注册native函数流程1.编写目的 记录IDA动态调试步骤 2使用工具 逆向工具: IDA 7.0 , Jadx 运行环境:Nexus 5 (Android 4.4) 3.原字符串信息 4.实现流程4.1 使用adb命令定位字符串位置 1.手机打开字符...

    安卓/手游逆向交流群963612891
    IDA动态调试动态注册native函数流程1.编写目的
    记录IDA动态调试步骤
    2使用工具
    逆向工具: IDA 7.0 , Jadx

    运行环境:Nexus 5 (Android 4.4)
    3.原字符串信息
    在这里插入图片描述
    4.实现流程4.1 使用adb命令定位字符串位置
    1.手机打开字符串所在界面
    2.手机连接电脑
    3.在cmd中输入”adb shell dumpsys activity top”查看当前顶层界面所在app的工程位置

    结果:
    目标所在位置 : ”com.glj.dynamicregistration/.MainActivity”
    在这里插入图片描述

    4.2 使用Jadx对APP进行逆向分析
    Jadx工具分析完后打开包名”com.glj.dynamicregistration”下的”MainActivity”
    在这里插入图片描述

    4.3 定位字符串显示位置
    1.在onCreate中只有一个TextView从getText()函数中获取字符串,再看getText()函数是经过native修饰的.那么可以肯定这是一个jni函数.再看Activity中确实加载了一个”hellojni”的so库,那么我们基本上定义这是从so库中获取的字符内容.
    在这里插入图片描述
    4.4 获取so库
    将app的apk格式改为zip压缩包格式后打开,在lib目录下进行扣取
    在这里插入图片描述
    4.4 IDA加载so库静态分析
    (1)将so文件直接拖拽进入IDA加载后点击Exports窗口是否存在java开头的函数,看下图,发现并没有.那么下一步只能去分析JNI_OnLoad函数了.

    在这里插入图片描述
    4.5 静态分析JNI_OnLoad
    (1) 将JNI_OnLoad函数的形参识别为”JavaVM*”类型
    (2) 从13行中可以看到sub_F70函数,那么我们继续跟进去.
    在这里插入图片描述
    (3) 在sub_F70中可以看见直接返回了sub_FA4函数的返回值.那么我们继续跟进去看.
    在这里插入图片描述

    (4)在sub_FA4函数终于看到了我们的RegisterNatives函数了,那么可以继续跟进去查看一番
    在这里插入图片描述
    (5)很抱歉,不能继续再跟了,那么将sub_FA4函数的代码同步到汇编里面.
    在这里插入图片描述
    在这里插入图片描述

    4.5动态JNI_OnLoad函数4.5.1 启动android-server
    在这里插入图片描述
    4.5.2 端口转发
    在这里插入图片描述

    4.5.3 使用am命令启动app
    注:命令格式为:am start -D -n 包名/.入口
    在这里插入图片描述

    使用Android Device Monitor工具查看当前APP的端口
    在这里插入图片描述

    4.5.4 打开IDA工具附加进程
    在这里插入图片描述
    选择”Debugger”->”Attach”->”Remote ARMLinux/Android debugger”进行打开附加窗口,并点击”Debug options”勾选三项

    在这里插入图片描述
    在这里插入图片描述

    在附加进程窗口搜索关键字并双击调试的进行加载.
    在这里插入图片描述

    进程附加完毕
    在这里插入图片描述
    4.5.5 定位JNI_OnLoad函数
    (1)此时继续勾选三项”Debugger”->”Debugger options”
    在这里插入图片描述
    在这里插入图片描述

    (2) 使用jdb命令开始调试
    在这里插入图片描述
    (3) 在IDA中按F9直至”libhellojni.so”加载为止.通过IDA的Output window窗口可以查看
    在这里插入图片描述

    (4) 此时在IDA的Debugger中打开Module list.并在其中使用Ctrl+F搜索hellojni.待结果处双击进入.进入后继续找到JNI_OnLoad函数继续双击进入.具体打开方式”Debugger”->”Debugger windows”->”Module list”
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    4.5.6调试JNI_OnLoad函数
    (1)在JNI_OnLoad函数中下断点后一路F9,直至进入到断点为止.
    在这里插入图片描述

    4.5.7根据基址+偏移的方式算出RegisterNatives的位置.
    基址:加载so库的初始地址.的当前地址:0x754BF000
    在这里插入图片描述
    偏移地址:静态分析时RegisterNatives所在的位置:0xFEA
    在这里插入图片描述
    绝对地址 = (754BF000+FEA)=0x754BFFEA

    在IDA中使用G键进行跳转地址到:0x754BFFEA后下断点.并F9跳转到该位置.
    在这里插入图片描述
    在这里插入图片描述

    4.5.8 对sub_754BFFEA函数进行单步调试
    通过JNI的知识可以推出R2存放的就是Native方法列表,那么进入R2寄存器查看里面的数据进行验证一下.
    在这里插入图片描述

    方法表对应的是java方法名,签名数据,C函数代码具体实现位置这三步.那么继续验证754C19D8, 754C19E0和754BFEA9地址中存放的值

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    其中看到jni_getText函数是不是一脸懵逼…其实这里只需要C或者P一下即可还原为汇编指令代码.
    在这里插入图片描述
    在这里插入图片描述

    那么我们进入R1寄存器中查看数据进行验证一下吧.

    在这里插入图片描述

    5总结
    要分析一个APP里面的native方法最有效的方法就是先静态分析一波,找到相关信息.然后在动态调试时使用基址加偏移的方式去定位函数.这样会很有效率,其次就是对JNI需要一定的了解,因为动态注册的JNI函数都是在JNI_OnLoad中进行加载的,所以在静态分析是定位到RegisterNatives函数,然后需要关注的是R0=JNIEnv* , R1=obj, R2=注册方法列表, R3=方法个数.其中方法表里的格式为Java方法名,签名信息,C函数代码实现地址.经过这一波分析,可以清楚地在JNI_OnLoad中找到相应的C函数体有一定的认知了.

    展开全文
  • 动态注册native函数

    2015-01-28 21:56:00
    Java JNI是一种静态注册native函数的方式。它的缺点是对每一个声明有native函数的java都需要生成.h,然后再做成链接库。相对繁琐。 其实java native函数和JNI函数的一一对应关系可以理解为函数和函数指针的关系,...

    Java JNI是一种静态注册native函数的方式。它的缺点是对每一个声明有native函数的java都需要生成.h,然后再做成链接库。相对繁琐。

    其实java native函数和JNI函数的一一对应关系可以理解为函数和函数指针的关系,因此可以存在一种动态注册的方式注册函数。

    Andoird 中使用了一种JNINativeMethod结构体还存放这种关联关系,即Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下:

    typedef struct {
    const char* name;         // native函数名称
    const char* signature;  // native函数签名,主要包括函数参数类型和返回值
    void* fnPtr;                       //函数指针,用于创建关联关系
    } JNINativeMethod;

    第一个变量name是Java中函数的名字。

    第二个变量signature,用字符串是描述了函数的参数和返回值

    第三个变量fnPtr是函数指针,指向C函数。

    其中比较难以理解的是第二个参数,例如

    "()V"

    "(II)V"

    "(Ljava/lang/String;Ljava/lang/String;)V"

    实际上这些字符是与函数的参数类型一一对应的。

    "()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

    "(II)V" 表示 void Func(int, int);

    具体的每一个字符的对应关系如下

    字符 Java类型 C类型

    V      void            void
    Z       jboolean     boolean
    I        jint              int
    J       jlong            long
    D      jdouble       double
    F      jfloat            float
    B      jbyte            byte
    C      jchar           char
    S      jshort          short

    数组则以"["开始,用两个字符表示

    [I       jintArray      int[]
    [F     jfloatArray    float[]
    [B     jbyteArray    byte[]
    [C    jcharArray    char[]
    [S    jshortArray   short[]
    [D    jdoubleArray double[]
    [J     jlongArray     long[]
    [Z    jbooleanArray boolean[]

    上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

    Ljava/lang/String; String jstring
    Ljava/net/Socket; Socket jobject

    如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

    例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"

    展开全文
  • 1 前言 前面两篇文章中,已经对JNI有了一些介绍。现在我们来回顾一下,它主要是通过使用javac -h命令来生成了一个.h的...事实上,JNI有两种关联Native方法的途径,分别是静态注册和动态注册。 2 注册方式 2....

    1 前言

    前面两篇文章中,已经对JNI有了一些介绍。现在我们来回顾一下,它主要是通过使用javac -h命令来生成了一个.h的头文件,来产生Java和Native两边方法函的注册关联。这样当Java代码中去执行Native方法的时候,就会通过两边的关联的映射关系来找到这些Native真正实现的地方。事实上,JNI有两种关联Native方法的途径,分别是静态注册和动态注册。

    2 注册方式

    2.1 静态注册

    前面文章所列举的Demo中使用命令生成.h头文件的方式就是使用了静态注册的方式。可以发现通过这种方式生成的.h头文件中,函数的名称是有规律的,如:Java_com_zyx_jnidemo_JNIUtils_getInfo,规律就是:Java前缀 + Native方法所在类的全称(用_替换.) + Native方法名。

    静态注册的好处就是全自动生成操作方便,但缺点也是相当明显,就是函名过长和不够灵活。

    2.2 动态注册

    动态注册是通过RegisterNatives函数来将Java层和Native层的方法和函数动态关联起来,而且无需遵循特定的方法命名格式,使得代码可以更加的灵活。

    当我们在Java代码中使用System.loadLibarary()方法来加载so文件时,Java虚拟机就会去调用JNI_OnLoad函数,该函数的作用是告诉虚机机应该使用哪个版本的JNI,如果我们没有指定,它默认是使用JNI1.1版本。JNI_OnLoad函数中还可以做一些初始化的事件,它对应反加载函数是JNI_OnUnload。所以我们要想进行Native函数的动态注册,最佳的时机就是在JNI_OnLoad函数中去执行。

    4 继续修改Hello World

    继续使用前面文章中的Demo,这次我们不通过命令生成.h头文件,而且自己创建一个新的.h头文件JNIUtils.h,内容如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_zyx_jnidemo_JNIUtils */
    
    #ifndef _Included_com_zyx_jnidemo_JNIUtils
    #define _Included_com_zyx_jnidemo_JNIUtils
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /*
     * Class:     com_zyx_jnidemo_JNIUtils
     * Method:    getInfo
     * Signature: ()Ljava/lang/String;
     */
    //JNIEXPORT jstring JNICALL Java_com_zyx_jnidemo_JNIUtils_getInfo
    //  (JNIEnv *, jclass);
    
    
    /*
     * Native层对应Java层中的方法
     */
    static jstring getInfoToNative(JNIEnv * env, jclass thiz);
    
    /*
     * Java中所定义的Native方法映射表
     */
    static JNINativeMethod gJniNativeMethods[] = {
            {"getInfo", "()Ljava/lang/String;", (void*)getInfoToNative},
    };
    
    /*
     * 初始化
     */
    jint JNI_OnLoad(JavaVM* vm, void* reserved);
    
    
    #ifdef __cplusplus
    }
    #endif
    #endif

    修改JNIUtils.cpp文件:

    #include "JNIUtils.h"
    #include <stdio.h>
    #include <android/log.h>
    
    //JNIEXPORT jstring JNICALL Java_com_zyx_jnidemo_JNIUtils_getInfo(JNIEnv * env, jclass thiz) {
    //    __android_log_print(ANDROID_LOG_DEBUG, "zyx", "Hello world from JNI !");
    //    //return env->NewStringUTF("Hello world from JNI !");
    //
    //    jclass clazz = env->FindClass("com/zyx/jnidemo/JNIUtils");
    //    if (clazz == NULL) {
    //        return env->NewStringUTF("find class error");
    //    }
    //    jmethodID methodId = env->GetStaticMethodID(clazz, "getJavaInfo", "(Ljava/lang/String;I)Ljava/lang/String;");
    //    if(methodId == NULL) {
    //        return env->NewStringUTF("find method error");
    //    }
    //    jstring info = env->NewStringUTF("Hello world from JNI !");
    //    jint index = 2;
    //    return (jstring)env->CallStaticObjectMethod(clazz, methodId, info, index);
    //}
    
    static jstring getInfoToNative(JNIEnv * env, jclass thiz) {
        return env->NewStringUTF("Hello world from JNI !3");
    }
    
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    
        JNIEnv* jniEnv = NULL;
    
        // 从虚拟机中获得JNIEnv,同时指定jni版本
        if (vm->GetEnv((void**) &jniEnv, JNI_VERSION_1_6) != JNI_OK || jniEnv == NULL) {
            return JNI_ERR;
        }
    
        // 获得Java代码中Natives方法所在类
        jclass clazz = (jniEnv)->FindClass("com/zyx/jnidemo/JNIUtils");
        if (clazz == NULL) {
            return JNI_ERR;
        }
    
        // 执行注册
        jint nMethods = sizeof(gJniNativeMethods) / sizeof(JNINativeMethod);
        if ((jniEnv)->RegisterNatives(clazz, gJniNativeMethods, nMethods) < 0) {
            return JNI_ERR;
        }
    
        return JNI_VERSION_1_6;
    }

    也是非常简单的修改,然后再重新编译程序,运行可见:

     

    说明:

    JNIUtils.h中定义了两个函数和一个数组:

    getInfoToNative                               Native层对应Java层中要注册关联的函数

    JNI_OnLoad                                     进行初始化和指定JNI版本

    gJniNativeMethods                          是一个JNINativeMethod类型的数组,它是一个函数是映射表,用于关联Java层中定义的Native方法和Native层的相应函数,其中第一个参数是Java中的方法名,第二个参数是方法的签名,第三个参数是Native中函数的指针

    JNIUtils.cpp中对两个定义的函数进行了实现,可见在JNI_OnLoad函数内部使用了RegisterNatives函数传入Java中的类和gJniNativeMethods数组来执行动态注册关联两边的方法函数。

     

    点击下载示例

     

    展开全文
  • 这一篇我们讲述如何动态注册native方法介绍首先,之前写的文章中通过一个简单的例子来使用了一下NDK,实现了从Native中调用Java方法。下面,我们要介绍的是实现动态绑定native方法来破除命名限制。问题在静态注册的...
  • JNI动态注册native方法及JNI数据使用

    千次阅读 2016-12-07 22:41:14
    前言 或许你知道了jni的简单调用,其实不算什么百度谷歌一大把,虽然这些jni绝大多数情况下都不会让我们安卓工程师来弄,毕竟还是有点难,但是我们还是得打破砂锅知道为什么这样干吧,至少也让我们知道调用流程和...
  • So反调试之动态注册native函数

    千次阅读 2018-01-15 14:10:06
    So反调试之动态注册native函数 简介及说明 通常我们native开发的步骤是: 1. java层声明native函数 2. javac生class文件以及javah生成native的头文件 3. native中会生成java的class类package路径的函数名 ...
  • 关于NDK有两种方法注册:静态注册和动态注册。 静态注册: 就是直接在Java文件里写个native方法 然后再c/c++文件中实现这个方法就行了; 动态注册: 就是为了不要写很长的方法名,用JNI_OnLoad方法实现预注册,即当...
  • Android native方法的动态注册

    千次阅读 2018-06-24 20:32:40
    Android native方法的动态注册 目录 Android native方法的动态注册 目录 Android JNI简介 Android JNI的一般注册方法 Android JNI的动态注册 JNINativeMethod结构体 Java类映射的方法签名 基本类型签名 数组的...
  • 1、爆结果照片 2、介绍JNI注册方式 ... 2) 、调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中(动态注册) 静态注册的实现可以参考我的这篇博客 http://blog.csdn.net/u011068702/a
  • 网上关于多个类的动态注册以及管理案例太少啦……静态注册其实不用多言,Android Studio默认的工程就是静态注册的。静态注册    一般在写C++代码都会有JNIEXPORT和JNICALL,这两个关键字是两个宏定义,它主要的...
  • NDK 开发中引入第三方静态库和动态库 NDK 开发中 Native 与 Java 交互 NDK POSIX 多线程编程 NDK Android OpenSL ES 音频采集与播放 NDK FFmpeg 编译 NDK FFmpeg 音视频解码 NDK 直播流媒体服务器搭建 NDK 直播推流...
  • 前言或许你知道了jni的简单调用,其实不算什么百度谷歌一大把,虽然这些jni绝大多数情况下都不会让我们安卓工程师来弄,毕竟还是有点难,但是我们还是得打破砂锅知道为什么这样干吧,至少也让我们知道调用流程和数据...
  • 1、Android应用层代码: import android.app.Activity; import android.os.Bundle; import android.widget.TextView; import android.util.Log;...public class NdkLoad extends Activity { public static final ...
  • 今天来介绍下动态注册
  • Android Studio jni动态注册步骤

    千次阅读 2017-10-26 15:33:52
    本文主要记录jni动态注册的步骤,这里使用Android studio 的ndk-build,关于使用cmake的,有时间再写1、在src/main/java/下新建一个java类,类似如下:public class testUtils { static { System.loadLibrary(...
  • 这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来。 实现 这里通过JNIEnv的Resisternatives方法完成方法的注册。相关方法介绍: //方法映射描述结构体 typedef struct { const...
  • #JNI动态注册过程中要注意的坑 还记得之前在这篇文章中进行过JNI的配置以及简单的Demo的运行,实际上在该篇文章中他的配置都是基于静态配置的。如果还有对静态配置不是很清楚的可以去看看那篇文章。同时,在这里对那...
  • 【JNI】Native动态注册演练

    千次阅读 2018-12-07 15:08:37
    文章目录前言:一、JNI Native注册介绍二、动态注册的具体事例三、jni对应参数映射表以上就是JNI动态注册的正常使用步骤,当然在项目中还可以更具体的封装,掌握动态注册是进行大型项目开发的必修课~ 前言: 前几...
1 2 3 4 5 ... 20
收藏数 26,821
精华内容 10,728
关键字:

动态注册native