If you’re writing native / JNI code for Android, it’s probably as native method of an Android app. These methods are always passed the Dalvik VM instance of the app as the first parameter. You need this to create jstrings and other Java objects, lookup classes and fields, etc. It’s not normal for you to have to instantiate a VM from native code because most of the time, if you’re using the Java Native Interface (JNI), you started in Java land and are only dipping into native code land for them sweet, sweet performance benefits. However, if you’re reverse engineering or writing an exploit, you’re likely always delving int all kinds of unusual trouble which the developers reasonably believed would never happen or at least would only be a theoretical edge case.

I recently needed to create a VM from native code to pass Java object arguments to a JNI function. In this post, I want to share what I came up with and why I finally settled on this particular method.

Standard Method

The official, standard method is documented here: How to Create a JVM Instance in JNI. Unfortunately, this won’t work in Android because jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*) isn’t exported. Even if you’re not familiar with this function, it’s name should be a clue that it’s important. If you want to check if it’s not exported yourself, look at jni.h from your Android NDK directory. In my case, it’s in android-sdk/android-ndk-r13b/platforms/android-9/arch-x86/usr/include/jni.h. The relevant code is:

1
2
3
4
5
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif

If you try to compile the code, you’ll probably get an error like this:

1
2
warning: implicit declaration of function 'JNI_CreateJavaVM' is invalid in C99
[-Wimplicit-function-declaration]

The official documentation is still useful for understanding the API and what all those options and arguments do. But if we want to use this on Android, we’re going to have to explicitly load the necessary methods from some library.

One useful detail this code shows is how to set the VM’s class path. Here’s how it’s done:

1
2
JavaVMOption jvmopt[1];
jvmopt[0].optionString = "-Djava.class.path=" + ".";

This sets the class path to the current directory (.). This is needed if you want your VM to access system or app classes. Some early experiments show that setting to a directory doesn’t seem to work. I tried setting to /data/local/tmp and pushing naked DEX files, as well as a JAR containing DEX files and the app’s APK. The only option that worked was setting the full path to either the JAR, DEX, or the APK. What was odd is that system classes (i.e. java.lang.String) were not accessible without having at least one valid file in the class path. In other words, (*env)->FindClass(env, "java.lang.String") returns 0 unless there’s at least one file on the class path, even though java.lant.String is defined in the framework.

To test yourself, push an APK to the emulator or device:

1
adb push shim_app.apk /data/local/tmp

Use this for your JavaVMOption:

1
2
3
4
5
JavaVMOption opt[2];
opt[0].optionString = "-Djava.class.path=/data/local/tmp/shim_app.apk";
opt[1].optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
// ...
args.nOptions = 2;

You should now be able to use FindClass to load system and app classes.

And if you need to load native libraries in your VM, such as if you load a class which loads a library in it’s static initilzer, you can see that path as well with optionString = "-Djava.library.path=/data/local/tmp". There’s an example here.

UiccUnlock Method

My genius cyber-sleuth skills revealed another possible technique. It’s from UiccUnlock.cpp which looks like a quasi-exploit to do a SIM unlock.

I won’t claim to fully understand what it’s doing, but the interesting part to me is in get_transation_code. Here’s what it does:

  • creates a Java VM
  • use the VM to get reference to com.android.internal.telephony.ITelephony$Stub class
  • get the TRANSACTION_sendOemRilRequestRaw field value
  • destroy the VM
  • return field value

It looks like the field value is used to check if the device is already unlocked or perhaps to check if the unlock method will work. I’m not sure. I just wanted to rip out the code to create the VM.

The approach seemed sound: load the relevant VM creation functions by through libnativehelper.so or libdvm.so as a backup. However, there were a few lines in there that looked weird:

1
2
3
JniInvocation_ctor = (JniInvocation_ctor_t) dlsym(libnativehelper, "_ZN13JniInvocationC1Ev");
JniInvocation_dtor = (JniInvocation_dtor_t) dlsym(libnativehelper, "_ZN13JniInvocationD1Ev");
JniInvocation_Init = (JniInvocation_Init_t) dlsym(libnativehelper, "_ZN13JniInvocation4InitEPKc");

I couldn’t find these functions documented anywhere. Someone really clever figured these out (kudos!). Without calling these functions, you get some weird errors:

1
2
3
4
W/dalvikvm(5395): No implementation found for native Landroid/os/SystemProperties;.native_get:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
W/dalvikvm(5395): No implementation found for native Landroid/os/SystemProperties;.native_get:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
W/dalvikvm(5395): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Landroid/os/Build;
A/libc(5395): Fatal signal 11 (SIGSEGV) at 0x0000000c (code=1), thread 5395 (create_vm)

Despite these weird functions, this approach totally worked for me. But I wanted to understand what the _ZN13JniInvocationC1Evfunctions did and if they were portable between Android versions. My gut told me that hardcoding that many function names which also included numbers could lead to incompatibilities in some devices or in later Android versions.

Surfaceflinger Method

I eventually found code from Google’s Surfaceflinger service: DdmConnection.cpp.

This defaults to looking for JNI_CreateJavaVM in libdvm.so. Instead of calling _ZN13JniInvocation* methods, it looks like it calls Java_com_android_internal_util_WithFramework_registerNatives from libandroid_runtime.so. The behavior of registerNatives is described well here.

Also interesting are the options used to create the VM:

1
opt.optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";

These options are well documented here and according to the documentation, this just allows for debugging of the JVM. Seems pretty standard.

I noticed the JNI version was 1_4, but I bumped it up to 1_6 because that’s what’s used in the example code here. Here are the supported versions from jni.h:

1
2
3
4
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

I ended up using this approach since I figured it’s the most robust and future-proof because it comes from Google.

Final Code

Here’s the final code to create the VM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <dlfcn.h>
#include <jni.h>
typedef int (*JNI_CreateJavaVM_t)(void *, void *, void *);
typedef jint (*registerNatives_t)(JNIEnv* env, jclass clazz);
static int init_jvm(JavaVM **p_vm, JNIEnv **p_env) {
// https://android.googlesource.com/platform/frameworks/native/+/ce3a0a5/services/surfaceflinger/DdmConnection.cpp
JavaVMOption opt[4];
opt[0].optionString = "-Djava.class.path=/data/local/tmp/shim_app.apk";
opt[1].optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
opt[2].optionString = "-Djava.library.path=/data/local/tmp";
opt[3].optionString = "-verbose:jni"; // may want to remove this, it's noisy
JavaVMInitArgs args;
args.version = JNI_VERSION_1_6;
args.options = opt;
args.nOptions = 4;
args.ignoreUnrecognized = JNI_FALSE;
void *libdvm_dso = dlopen("libdvm.so", RTLD_NOW);
void *libandroid_runtime_dso = dlopen("libandroid_runtime.so", RTLD_NOW);
if (!libdvm_dso || !libandroid_runtime_dso) {
return -1;
}
JNI_CreateJavaVM_t JNI_CreateJavaVM;
JNI_CreateJavaVM = (JNI_CreateJavaVM_t) dlsym(libdvm_dso, "JNI_CreateJavaVM");
if (!JNI_CreateJavaVM) {
return -2;
}
registerNatives_t registerNatives;
registerNatives = (registerNatives_t) dlsym(libandroid_runtime_dso, "Java_com_android_internal_util_WithFramework_registerNatives");
if (!registerNatives) {
return -3;
}
if (JNI_CreateJavaVM(&(*p_vm), &(*p_env), &args)) {
return -4;
}
if (registerNatives(*p_env, 0)) {
return -5;
}
return 0;
}

Here’s how it’s used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdlib.h>
#include <stdio.h>
JavaVM * vm = NULL;
JNIEnv * env = NULL;
int status = init_jvm( & vm, & env);
if (status == 0) {
printf("Initialization success (vm=%p, env=%p)\n", vm, env);
} else {
printf("Initialization failure (%i)\n", status);
return -1;
}
jstring testy = (*env)->NewStringUTF(env, "this should work now!");
const char *str = (*env)->GetStringUTFChars(env, testy, NULL);
printf("testy: %s\n", str);

Conclusion

Now you have everything you need to instantiate a Java VM from native code. You should also have a decent understanding (or at least links to documentation) about some of the options available to you. To see a practical application of this technique, check out Calling JNI Functions with Java Object Arguments from the Command Line.

原文地址: http://calebfenton.github.io/2017/04/05/creating_java_vm_from_android_native_code/

Creating a Java VM from Android Native Code相关推荐

  1. 【Win7下Android native code的编译和调试】

    光为这编译及调试环境就前后折腾了两三天,墙外找了很多教程,bill以为以下教程最为贴切 Using eclipse for android - cc Development Using eclipse ...

  2. 在Android Native层中创建Java虚拟机实例

    前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...

  3. Android framwork service添加(manager 远程调service,service jni调native code)

    在平常android应用开发中,多数只是调framwork中的API进行application layer的coding,而在系统开发中可能会自己添加系统服务: 系统服务如任何添加,服务如何调nati ...

  4. java native code_原生代码(native code)

    原生代码(native code) Native code is the code whose memory is not "managed", as in, memory isn ...

  5. java调用jni接口,Java 中通过jni接口调用native code

    [    Java语言本身是通过Java的虚拟机解释执行的,因此对于Java中调用本地动态链接库的问题便提上了日程,为何会存在这样的需求呢?因为Java本身的机制导致一些要求高效率 在上上篇中已经介绍 ...

  6. Android之jni编译出现no matching function for call to ‘_JNIEnv::GetJava(JNIEnv* , Java VM**)‘解决办法)‘

    1.问题 jni编译出现这个错误 no matching function for call to '_JNIEnv::GetJava(JNIEnv* &, Java VM**) 2.原因 自 ...

  7. Android Native 代码NDK开发学习笔记

    引用:http://www.kunli.info/2011/08/21/android-native-code-study-note/ JNI,全称Java Native Interface,是用于让 ...

  8. 如何调试Android Native Framework

    原文: https://zhuanlan.zhihu.com/p/24867284 如何调试Android Native Framework weishu 7 个月前 半年前写了一篇文章,介绍 如何调 ...

  9. 是否可以将Java 8用于Android开发?

    本文翻译自:Is it possible to use Java 8 for Android development? Searching the web, it is not clear if Ja ...

最新文章

  1. 给研发工程师的代码质量利器 | SOFAChannel#5 直播整理
  2. Android中的ViewDragHelper
  3. ImageView和onTouchListener实现,点击查看图片细节
  4. 移动机器人平台-ROS和GitHub链接
  5. 在mysql-workbench的存储过程中使用循环while,repeat,loop
  6. 90-20-010-源码-调试-Kylin-2.6.0源码调试
  7. CodeForces 173B Chamber of Secrets(最短路)
  8. linux内核类型lagency,使用u盘安装linux(manjaro)时Grub报错
  9. linux office转pdf python_使用python写的PDF转EXCEL工具,已打包exe
  10. 单元在整体坐标系下的刚度矩阵
  11. malloc函数C语言实现
  12. 让TQ2440也用上设备树 (device tree 操作实例3_重要,对于移植很有参考价值_code)
  13. 微信JSAPI之V3版本支付踩坑
  14. 每日一个CSS——模拟键盘
  15. DoraCloud for Proxmox桌面云上启用NVIDIA Tesla P4的vGPU功能
  16. 面向对象——类和对象
  17. Android手机归属地查询工具
  18. 都1202年,我才知道 Tailwindcss
  19. 每一年,每一天,我们都在进步
  20. 基于HOPF振荡器的CPG单元模型matlab实现

热门文章

  1. Java POI 导出EXCEL经典实现 Java导出Excel
  2. 神奇的滚动动画,30个视差滚动网站设计
  3. sql语句添加删除外键
  4. C编程,随机数,排序
  5. 吴恩达 coursera AI 专项三第一课总结+作业答案
  6. Matlab xcorr函数详解
  7. res里面的drawable(ldpi、mdpi、hdpi、xhdpi、xxhdpi)
  8. leetcode_add_two_numbers
  9. USTC English Club Note20171015(5)
  10. ustc小道消息20211227