【一】创建项目
【1】新建utils.c
- 在cpp文件夹下新建utils.c 的源文件,后缀名是c,因为怎么用c写(.cpp 是c++,不要用这个)
【2】新建java类
- 新建一个java类,类中写函数
package com.dream.demo11;
public class Utils {
// 定义一个方法,实现C中的某个方法相对应
public static native int v1(int a,int b);
}
【3】引入静态文件
在java类中,引入静态文件
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
【4】在CMakeLists.txt中注册
- 在CMakeLists.txt中加入编写的c文件
# 注册自己写的c文件
add_library( # Sets the name of the library.
utils # 最终这个utils.c会编译成so文件,编译成的名字叫 lib + utils
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
utils.c) # 指定需要编译的c文件
target_link_libraries( # Specifies the target library.
demo11 utils # 注册C文件,用空格分隔
# Links the target library to the log library
# included in the NDK.
${log-lib})
【5】在utils中写代码
#include <jni.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jint // 函数返回的类型是int类型 ---> jint类型
JNICALL
// 函数名必须叫 Java_包名(com_dream_demo11)_类名_方法名
// JNIEnv *env, jclass 参数固定(结构体)
// jint a, jint b 这两个参数和Java中Utils下的v1方法对应,接收两个 int 类型的参数
Java_com_dream_demo11_Utils_v1(JNIEnv *env, jclass clazz, jint a, jint b) {
// TODO: implement v1()
return a + b;
}
【6】在java代码中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'demo11' library on application startup.
static {
System.loadLibrary("demo11");
}
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()); // 函数是调用的C代码执行的结果
tv.setText(String.valueOf(Utils.v1(3, 5))); // 调用C文件中的代码
}
/**
* A native method that is implemented by the 'demo11' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
【二】逆向和反编译
【1】反编译 apk
- 把咱们自己写的apk,拖动到jadx中即可
-
反编译成java代码 ----> jadx
- 已经看不到具体的操作了 ---> 反编译c代码
-
反编译它的so文件的c代码 ---> IDA
- 拿到apk---》后缀名改成zip----》使用压缩软件解压
- 拿到咱们写的so文件,使用IDA反编译
- 把so文件拖动到ida软件中
- 点中exports
【2】反编译so文件
-
使用压缩工具把 apk解压
-
进入lib的arm64-v8a目录,看到so文件
-
把so文件拖动到IDA中
-
选择exports导出
-
双击函数名,看到汇编
-
按F5,把混编进行反编译
-
找到
F:\桌面\app-debug - 副本\lib\arm64-v8a\libutils.so
- C文件前面会自动加 lib
- 用IDA反编译
- ida.exe 解32位so文件
- ida64.exe 解64位so文件
- 编译文件
- 点击Exports
- 双击会显示汇编页面
- 但是我们没有学过汇编,所以还要再编译成c
- 按F5
- 反编译出来和原来的代码相差不大
【三】优化代码运行程序
-
当我们不使用自带的C++文件时,可以将文件删除
-
删除
native-lib.cpp
文件 -
删除注册
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.22.1) # Declares and names the project. project("demo11") # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) # 注册自己写的c文件 add_library( # Sets the name of the library. utils # 最终这个utils.c会编译成so文件,编译成的名字叫 lib + utils # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). utils.c) # 指定需要编译的c文件 # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. utils # 注册C文件,用空格分隔 # Links the target library to the log library # included in the NDK. ${log-lib})
-
主文件删除相关信息
package com.dream.demo11; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; import com.dream.demo11.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { 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()); // 函数是调用的C代码执行的结果 tv.setText(String.valueOf(Utils.v1(3, 5))); // 调用C文件中的代码 } }
-
【四】Java调用C案例之指针修改字符串
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v2(String s);
}
【2】在 utils.c 中注册
#include <jni.h>
#include <syslog.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jstring JNICALL // jstring : JNI的签名
Java_com_dream_demo11_Utils_v2(JNIEnv *env, jclass clazz, jstring s) {
// TODO: implement v2()
// 传入字符串 dream 将 字符串第一个位置和第三个位置的字符串都改为 p,然后返回
// char info[] = {'d','r','e','a','m'};
// 通过env指针,取到GetStringUTFChars结构体,初始化得到char指针
char *info = (*env)->GetStringUTFChars(env, s, 0);
syslog(LOG_ERR, "%s", info);
// C语言,通过字符数据指针修改字符串
info += 1;
*info = 'p'; // 修改第一个位置字符为p
info += 3;
*info = 'p';// 修改第三个位置字符为p
info -= 4; //指针回退
syslog(LOG_ERR, "%s", info);
return (*env)->NewStringUTF(env, info); // 把char类型指针,再转成字符串类型返回
}
【3】在主文件中调用该方法
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(Utils.v2("dream"));
}
}
- 运行APP,发现字符被替换成功
【五】Java调用C案例之数组修改字符串
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v3(String s);
}
【2】在utils.c中注册
#include <jni.h>
#include <syslog.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v3(JNIEnv *env, jclass clazz, jstring s) {
// TODO: implement v3()
// 字符串数组
char *info = (*env)->GetStringUTFChars(env, s, 0);
info[0] = 'p';
info[1] = 'p';
return (*env)->NewStringUTF(env, info);
}
【3】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(Utils.v3("dream"));
}
}
- 运行APP
- 发现字符串被修改成功
【六】Java调用C案例之字符串拼接
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v4(String name, String role);
}
【2】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v4(JNIEnv *env, jclass clazz, jstring name, jstring role) {
// TODO: implement v4()
// 拿到字符串
char *nameString = (*env)->GetStringUTFChars(env, name, 0);
char *roleString = (*env)->GetStringUTFChars(env, role, 0);
// 对字符串进行拼接
// 定义指针,分配内容
// char *result = malloc(GetStringLen(nameString) + GetStringLen(roleString) + 1);
char *result = malloc(strlen(nameString) + strlen(roleString) + 1);
// 把名字copy到新定义的指针中
strcpy(result, nameString);
// 把角色拼接到后面
strcat(result, roleString);
syslog(LOG_ERR, "%s", result);
// 返回字符串
return (*env)->NewStringUTF(env, result);
}
【3】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(Utils.v4("dream","admin"));
}
}
- 运行APP
- 可以看到字符串被拼接成功
【七】Java调用C案例之字符串处理
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v5(String data);
}
【2】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
//
// Created by Administrator on 2023/8/2.
//
// 定义下面函数调用的方法 --- 统计字符串的长度
int GetStringLen(char *dataString) {
int count = 0;
for (int i = 0; dataString[i] != '\0'; i++) {
count += 1;
}
return count;
}
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v5(JNIEnv *env, jclass clazz, jstring data) {
// TODO: implement v5()
// "name=dream&age=19"
char *urlParams = (*env)->GetStringUTFChars(env, data, 0);
int size = GetStringLen(urlParams);
// 统计字符串长度也可以使用
// int size = strlen(urlParams);
// v34 = {1,2,,,,,,,,,,,}
char v34[size * 2]; // 定义字符数组,长度是传进来的字符串的两倍
// 指向指针
char *v28 = v34;
for (int i = 0; urlParams[i] != '\0'; i++) {
//syslog(LOG_ERR, "%02x", urlParams[i]);
// 转成 16 进制 返回数据
sprintf(v28, "%02x", urlParams[i]);
v28 += 2;
}
return (*env)->NewStringUTF(env, v34);
}
【3】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(Utils.v5("name=dream&age=19")); // 函数是调用的C代码执行的结果
}
}
- 运行APP
- 可以看到返回了一串二进制字符
【八】Java调用C案例之字节处理一
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v6(byte[] data);
}
【2】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v6(JNIEnv *env, jclass clazz, jbyteArray data) {
// TODO: implement v6()
// jbyte *byteArray = (*env)->GetByteArrayElements(env, data, 0);
char *byteArray = (*env)->GetByteArrayElements(env, data, 0);
int size = (*env)->GetArrayLength(env, data);
char v34[size * 2];
char *v28 = v34;
for (int i = 0; byteArray[i] != '\0'; i++) {
syslog(LOG_ERR, "%02x", byteArray[i]);
sprintf(v28, "%02x", byteArray[i]);
v28 += 2;
}
return (*env)->NewStringUTF(env, v34);
}
【3】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(String.valueOf(Utils.v6("name=dream&age=19".getBytes()))); // 函数是调用的C代码执行的结果
}
}
- 运行APP
- 可以看到屏幕上返回了一串二进制数字
【九】Java调用C案例之字节处理二
【1】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
public static native String v7(byte[] data);
}
【2】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
//
// Created by Administrator on 2023/8/2.
//
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v7(JNIEnv *env, jclass clazz, jbyteArray data) {
// TODO: implement v7()
char *byteArray = (*env)->GetByteArrayElements(env, data, 0);
int size = (*env)->GetArrayLength(env, data);
char v34[size * 2];
char *v28 = v34;
int v29 = 0;
do {
sprintf(v28, "%02x", byteArray[v29++]);
v28 += 2;
} while (v29 != size);
return (*env)->NewStringUTF(env, v34);
}
【3】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(String.valueOf(Utils.v7("name=dream&age=19".getBytes()))); // 函数是调用的C代码执行的结果
}
}
- 运行APP
- 可以看到在屏幕上显示了一串二进制数字
【十】C调用Java案例
-
Java调用C语言
- 某些APP,加密函数如果使用Java开发,被反编译了后能直接被看到
- 高级一些的操作,加密函数,不使用Java实现,而是JN开发,C实现
-
某些公司可能
- Java调用C
- C写加密麻烦,有些人不愿意写
- 于是C中又调用了Java的假面,完成加密,返回数据
【十一】C调用Java案例之使用static修饰
【1】静态方法创建
- 在包下创建新文件
demo11\app\src\main\java\com\dream\demo11\Func.java
package com.dream.demo11;
public class Func {
// 定义一个静态方法
public static String getSign() {
return "Sign001";
}
}
【2】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// ****** C 调用 Java 案例
// 触发C代码中的代码 Java_com_dream_demo11_Utils_v8 方法执行 ---> 内部再调用 Func的 getSign 静态方法
public static native String v8();
}
【3】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
// v8 方法 ----> 内部调用Java的Func类下的 getSign 静态方法
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v8(JNIEnv *env, jclass clazz) {
// TODO: implement v8()
// (1) 找到类
jclass cls = (*env)->FindClass(env,"com/dream/demo11/Func");
// (2)找到类中的静态方法
// "()Ljava/lang/String;" :JNI签名
// () 表示没参数
// Ljava/lang/String; 表示返回的类型是字符串类型
jmethodID method1 = (*env)->GetStaticMethodID(env, cls, "getSign", "()Ljava/lang/String;");
//(3)执行静态方法 getSign
jstring res1 = (*env)->CallStaticObjectMethod(env, cls, method1);
// 由于是 jstring 的字符串 ,直接返回即可
return res1;
// return (*env)->NewStringUTF(env, result);
}
【4】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(Utils.v8()); // 函数是调用的C代码执行的结果
}
}
【十二】C调用Java案例之静态方法
【1】静态方法创建
- 在包下创建新文件
demo11\app\src\main\java\com\dream\demo11\Func.java
package com.dream.demo11;
public class Func {
// 定义一个静态方法
public static String getSign() {
return "Sign001";
}
// 重载 : 函数名相同 参数不同
public static String getSign(int a, int b) {
return "sign002";
}
public static String getSign(String s) {
return "sign003";
}
public static int getSign(String prev, int v1) {
return 100;
}
}
【2】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// 定义一个方法,实现C中的某个方法相对应
// ****** C 调用 Java 案例
// 触发C代码中的代码 Java_com_dream_demo11_Utils_v8 方法执行 ---> 内部再调用 Func的 getSign 静态方法
public static native String v8();
}
【3】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
//
// Created by Administrator on 2023/8/2.
//
// v8 方法 ----> 内部调用Java的Func类下的 getSign 静态方法
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v8(JNIEnv *env, jclass clazz) {
// TODO: implement v8()
// (1) 找到类
jclass cls = (*env)->FindClass(env, "com/dream/demo11/Func");
// ******第一组开始*******
// (2)找到类中的静态方法
// "()Ljava/lang/String;" :JNI签名
// () 表示没参数
// Ljava/lang/String; 表示返回的类型是字符串类型
// jmethodID method1 = (*env)->GetStaticMethodID(env, cls, "getSign", "()Ljava/lang/String;");
//(3)执行静态方法 getSign
// jstring res1 = (*env)->CallStaticObjectMethod(env, cls, method1);
// ******第一组结束*******
// ******第二组开始*******
// (2.1) 找到类中的静态方法
//jmethodID method2 = (*env)->GetStaticMethodID(env, cls, "getSign", "(II)Ljava/lang/String;");
// jstring res1 = (*env)->CallStaticObjectMethod(env, cls, method2, 88, 99);
// ******第二组结束*******
// ******第三组开始*******
jmethodID method3 = (*env)->GetStaticMethodID(env, cls, "getSign","(Ljava/lang/String;)Ljava/lang/String;");
jstring res1 = (*env)->CallStaticObjectMethod(env, cls, method3, (*env)->NewStringUTF(env,"dream"));
// ******第三组结束*******
// ******第四组开始*******
// 因为 这个res1是数字类型,但是 return 回去的是字符串类型,所以直接运行会报错,这里只是演示代码
//jmethodID method4 = (*env)->GetStaticMethodID(env, cls, "getSign", "(Ljava/lang/String;I)I");
//jint res1 = (*env)->CallStaticIntMethod(env, cls, method4,(*env)->NewStringUTF(env, "dream"), 999);
// 可以拿到数字对数字再进行转字符串操作然后返回
// ******第四组结束*******
// 可以继续对字符串/数字进行操作
// 由于是 jstring 的字符串 ,直接返回即可
return res1;
// return (*env)->NewStringUTF(env, result);
}
【4】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(String.valueOf(Utils.v8())); // 函数是调用的C代码执行的结果
}
}
【十三】C调用Java案例之成员方法
【1】静态方法创建
- 在包下创建新文件
demo11\app\src\main\java\com\dream\demo11\Func.java
package com.dream.demo11;
public class Func {
// 定义一个变量 --- 属性
public String name;
// 构造函数
// 初始化赋值
public Func(String name) {
this.name = name;
}
// 成员方法 --- 调用属性
public String ShowName() {
return this.name;
}
}
【2】在Utils.java中写方法
package com.dream.demo11;
public class Utils {
// Used to load the 'demo11' library on application startup.
static {
// 对应c文件的文件名
System.loadLibrary("utils");
}
// ****** C 调用 Java 案例
public static native String v9();
}
【3】在utils.c中注册
#include <jni.h>
#include <syslog.h>
#include <malloc.h>
JNIEXPORT jstring JNICALL
Java_com_dream_demo11_Utils_v9(JNIEnv *env, jclass clazz) {
// TODO: implement v9()
// 调用 Func的成员方法
// (1) 找到类
jclass cls = (*env)->FindClass(env, "com/dream/demo11/Func");
// (2)找到构造方法
jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
// (3)执行构造方法得到对象
jobject cls_obj = (*env)->NewObject(env, cls, init, (*env)->NewStringUTF(env, "dream"));
// (4)找到成员方法
jmethodID method1 = (*env)->GetMethodID(env, cls, "ShowName", "()Ljava/lang/String;");
// (5)使用对象调用成员方法
jstring res1 = (*env)->CallObjectMethod(env, cls_obj, method1);
return res1;
}
【4】在主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(String.valueOf(Utils.v9())); // 函数是调用的C代码执行的结果
}
}
【补充】静态注册和动态注册
【1】静态注册
- 上述编写的C语言的函数和Java的对应关系,在函数名上就可以体现,例如:
Java_com_dream_demo11_Utils_v9
Java_com_dream_demo11_Utils_v8
- 这种称为静态注册,如果是静态注册,那么在逆向时,是比较方便的,直接可以找到函数在C中的实现。例如:车智赢。
【2】动态注册
- 有的APP为了增加逆向难度,会使用动态注册
- 看到Java中的方法后,不知道它对应的C是谁
(1)文件创建
-
demo11\app\src\main\cpp\dynamic.c
-
demo11\app\src\main\java\com\dream\demo11\Dynamic.java
package com.dream.demo11;
public class Dynamic {
static {
System.loadLibrary("dynamic");
}
public static native int vv1(int a1, int a2);
public static native int vv2(String s);
}
(2)注册app
CMakeLists.txt
# 注册自己写的c文件
add_library( # Sets the name of the library.
dynamic # 最终这个dynamic.c会编译成so文件,编译成的名字叫 lib + dynamic
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
dynamic.c) # 指定需要编译的c文件
target_link_libraries( # Specifies the target library.
utils dynamic # 注册C文件,用空格分隔
# Links the target library to the log library
# included in the NDK.
${log-lib})
(3)在 dynamic.c 中注册
#include <jni.h>
//
// Created by Administrator on 2023/8/2.
//
// 静态注册 有 对应关系
//JNIEXPORT jint JNICALL
//Java_com_dream_demo11_Dynamic_vv1(JNIEnv *env, jclass clazz, jint a1, jint a2) {
// // TODO: implement vv1()
//
//}
// 动态注册 --- 固定写一个方法 --- JNI_OnLoad
jint plus1(JNIEnv *env, jobject obj, jint v1, jint v2) {
return v1 + v2;
}
jint plus2(JNIEnv *env, jobject obj, jstring s1) {
return 100;
}
static JNINativeMethod gMethods[] = {
{"vv1", "(II)I", (void *) plus1},
{"vv2", "(Ljava/lang/String;)I", (void *) plus2},
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
// 在java虚拟机中获取env
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// ----上面都是固定的----
// 找到Java中的类
jclass clazz = (*env)->FindClass(env, "com/dream/demo11/Dynamic");
// 将类中的方法注册到JNI中 (RegisterNatives)
int res = (*env)->RegisterNatives(env, clazz, gMethods, 1);
// ----下面都是固定的----
if (res < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
(4)主文件中调用
package com.dream.demo11;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.dream.demo11.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
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(String.valueOf(Dynamic.vv1(11, 22))); // 函数是调用的C代码执行的结果 ---- 动态注册
}
}
- 运行app
- 页面显示 33
【十四】静态注册和动态注册逆向
【1】静态注册逆向
-
使用IDA打开文件后,如果是静态注册,函数跟明显
-
上述编写的C语言的函数和Java的对应关系,在函数名上就可以体现,例如:
Java_com_dream_demo11_Utils_v9
Java_com_dream_demo11_Utils_v8
-
这种称为静态注册,如果是静态注册,那么在逆向时,是比较方便的,直接可以找到函数在C中的实现。例如:车智赢。
-
jadx反编译
- 逆向so文件
- 搜索关键字 java
- 找到位置
【2】动态注册逆向
- 使用IDA打开文件后,如果是动态注册,需要去找jni_onload
- 这是我们写的函数
- 找到我们写的c加密
【补充】JNI签名对应的关系
【1】JNI和JNI签名是什么
- JNI (Java Native Interface) 是一种编程接口,它允许Java应用程序与本地代码(如C、C++)进行交互。
- JNI签名指的是用于表示JNI方法的描述符,它告知Java虚拟机如何在本地库中找到对应的函数。
【2】JNI签名由以下几部分组成:
- 返回类型:
- 表示JNI方法的返回类型
- 例如"V"表示void,"I"表示int,"Ljava/lang/String;"表示返回String类型等。
- 参数类型列表:
- 表示JNI方法的参数类型,按照参数顺序排列。
- 参数类型使用单个字符来表示,例如"Ljava/lang/String;"表示参数为String类型。
- 方法名称:
- 表示该JNI方法的名称。
【3】简写的字符来表示常见的参数类型
- "Z"表示boolean类型
- "B"表示byte类型
- "C"表示char类型
- "S"表示short类型
- "I"表示int类型
- "J"表示long类型
- "F"表示float类型
- "D"表示double类型
- "Ljava/lang/Object;"表示Object类型
- "[I"表示int数组类型
【4】示例JNI签名及其对应的Java方法:
- Java方法:
- public static native void doSomething();
- JNI签名:()V
- Java方法:
- public static native int add(int a, int b);
- JNI签名:(II)I
- Java方法:
- public static native String getString(int[] array);
- JNI签名:([I)Ljava/lang/String;