在Android Native层中创建Java虚拟机实例
前言
Android应用中JNI代码,是作为本地方法运行的。而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数。例如,你需要用虚拟机实例来创建jstring和其他的Java对象、查找类或成员变量等。大部分情况下,在你用JNI接口从Java层调用Native层中的代码时,你并不需要在native代码中自己初始化一个Dalvik虚拟机实例。但是,如果你在搞逆向或者写exp,你总是需要钻研各种非常规的情况。
最近,我在逆向时需要在native代码中手动创建虚拟机实例用于在JNI接口函数中传递Java对象。在本文中,我将分享我是如何实现这种方法的。
标准方法
在JNI中创建JVM虚拟机实例的官方文档在地址How to Create a JVM Instance in JNI。但是,不幸的是这种方法在Android上面是不能正常运行的,因为jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)函数不是导出函数,无法直接调用。假如你不熟悉这个方法的话,可以根据它的名字在jni.h文件中查找一下,确认是否是导出函数。在我这里,jni.h文件位于android-sdk/ ndk-bundle/platforms/android-9/arch-x86/usr/include/jni.h。相关代码如下:
如果你尝试编译调用上述截图中函数的代码,你可能会得到下面的错误:
官方文档中介绍的如何创建JVM的方法在这里可以用来理解上述的API函数和它们的选项和参数的用途。如果你想在Android中使用这些方法,你必须显示从so库中调用这些方法。
官方文档中介绍的如何初始化虚拟机的类路径,在此处是非常有用的。其内容如下:
上面的配置,设置当前的类路径为当前目录(.)。如果你想要虚拟机访问系统或者app的类,这是必须设置的。实验表明,将该值设置为一个目录并不会起作用。我尝试将其设置为/data/local/tmp,同时在该目录下放置了一个dex文件、含有dex文件的jar包和apk文件。只有在设置jar包、dex文件或apk文件的全路径时,上述选项才起作用。奇怪的是,当类路径中没有一个合法的文件时,系统类(例如java.lang.String)都不能访问。换句话说,除非类路径中至少有一个文件,否则语句(*env)->FindClass(env, "java.lang.String")返回0,甚至java.lang.String这样定义在框架中的类都无法访问。
为了测试,下面将一个apk文件push到模拟器或真机设备中。
JavaVMOption的使用如下:
你现在可以使用FindClass函数来加载系统或者app的类。此外,如果你需要加载本地库到你的虚拟机中,例如在静态初始化器中加载一个库文件,你可以使用optionString = "-Djava.library.path=/data/local/tmp"这样的设置。这有个样例代码。
UniccUnlock方法
从文件UniccUnlock.cpp中,展示了另外一种创建虚拟机的类似技巧。我不敢说我完全理解了它在做什么,但是其中吸引我的是get_transaction_code部分。下面是它的做的事:
1
|
<ol class="linenums"><li class="L0"><span class="pln">creates a </span><span class="typ">Java</span><span class="pln"> VM</span></li><li class="L1"><span class="kwd">use</span><span class="pln"> the VM to </span><span class="kwd">get</span><span class="pln"> reference to com</span><span class="pun">.</span><span class="pln">android</span><span class="pun">.</span><span class="kwd">internal</span><span class="pun">.</span><span class="pln">telephony</span><span class="pun">.</span><span class="typ">ITelephony$Stub</span><span class="pln"> </span><span class="kwd">class</span></li><li class="L2"><span class="kwd">get</span><span class="pln"> the TRANSACTION_sendOemRilRequestRaw field value</span></li><li class="L3"><span class="pln">destroy the VM</span></li><li class="L4"><span class="kwd">return</span><span class="pln"> field value</span></li></ol> |
代码看起来像是根据成员值判断当前设备是否已经解锁或者是解锁方法是否成功。反正我是不很确定,不过我也就想抽取其中创建虚拟机的代码而已。
该方法是通过在库文件libnativehelper.so或者libdvm.so中加载创建虚拟机相关的方法。但是,下面几行代码看起来很奇怪:
任何地方都无法找到这几个方法的文档说明。不过,发现这些方法调用的人相当聪明。如果不调用这些方法,你就会得到下面奇怪的错误信息:
除了这几个奇怪的方法,这种方式创建虚拟机对我很好使。但是,我想知道_ZN13JniInvocationC1Ev方法都做了什么,在不同版本间的Android系统中是否可移植。我的直觉告诉我,硬编码的方法名可能会导致在不同的设备或者Android版本间的不兼容性。
Surfaceflinger 方法
最终,我在谷歌的Surfaceflinger服务的源码中找到了:DdmConnection.cpp。
它默认查找了在libdvm.so中的函数JNI_CreateJavaVM。它没有调用方法_ZN13JniInvocation,而是调用了库libandroid_runtime.so中的Java_com_android_internal_util_WithFramework_registerNatives方法。registerNatives方法的内容在此描述了。
同时,感兴趣的是创建虚拟机的选项:
这些选项在这篇文档中详细描述了。根据文档,它仅仅用于调试JVM时使用。
同时,我注意到它JNI的版本是1_4,但是我设置为1_6了,因为谷歌的样例代码中就是这样设置的。下面就是jni.h中支持的版本号:
最后,我使用上面的方式来创建虚拟机,因为它来自谷歌,具有很好的健壮性和兼容性。
最终代码
下面就是最终的创建虚拟机的代码:
下面是其使用方法:
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://calebfenton.github.io/2017/04/05/creating_java_vm_from_android_native_code/
在Android Native层中创建Java虚拟机实例的更多相关文章
- Android Native层异步消息处理框架
*本文系作者工作学习总结,尚有不完善及理解不恰当之处,欢迎批评指正* 一.前言 在NuPlayer中,可以发现许多类似于下面的代码: //============================== ...
- Android native层动态库注射
1.简单介绍 本文解说在Android native层.root权限下.注射动态库到目标进程,从而hook目标进程中动态库的函数的实现方式. 文中的源代码所有来源于网络.我仅仅是略微加以整理. 环境: ...
- Android内核sysfs中switch类使用实例
Android内核sysfs中switch类使用实例 最终在这个周末,能够干点自己想要干的事了. 由我这个二流的内核驱动开发人员来解析一下sysfs中的switch类.先猜測一下来历,在普通的嵌入式L ...
- 在Android源码中查找Java代码中native函数对应的C++实现
Android源码中很多关键代码都是C++实现的,java通过jni来调用,经常会看到java中这样的代码: static native Thread currentThread(); 如何根据方法名 ...
- 本地C代码中创建Java对象
作者:唐老师,华清远见嵌入式学院讲师. 创建Java域的对象就是创建Java类的实例,再调用Java类的构造方法. 以Bitmap的构建为例,Bitmap中并没有Java对象创建的代码及外部能访问的构 ...
- [Android]Android SDk Manager中创建模拟器无法选择CPU问题解析
方法一.正常下载所需sdk包 启动 Android SDK Manager ,打开主界面,依次选择「Tools」.「Options...」,弹出『Android SDK Manager - Setti ...
- Java中JNI的使用详解第四篇:C/C++中创建Java对象和String字符串对象及对字符串的操作方法
首先来看一下C/C++中怎么创建Java对象:在JNIEnv中有两种方法是用来创建Java对象的: 第一种方法: jobject NewObject(jclass clazz , jmethodI ...
- 如何使用 Packer 在 Azure 中创建 Windows 虚拟机映像
Azure 中的每个虚拟机 (VM) 都是基于定义 Windows 分发和操作系统版本的映像创建的. 映像可以包括预安装的应用程序和配置. Azure 应用商店为最常见的操作系统和应用程序环境提供了许 ...
- 面试中关于Java虚拟机(jvm)的问题看这篇就够了
最近看书的过程中整理了一些面试题,面试题以及答案都在我的文章中有所提到,希望你能在以问题为导向的过程中掌握虚拟机的核心知识.面试毕竟是面试,核心知识我们还是要掌握的,加油~~~ 下面是按jvm虚拟机知 ...
随机推荐
- Math.log()对数的妙用
###对数log 的妙用... formatBytes(bytes) { if (bytes === '0' || isNaN(bytes)) return ''; var s = ['Bytes', ...
- Deep Learning (中文版&英文版)
Bengio Yoshua,Ian J. Goodfellow 和 Aaron Courville共同撰写的<深度学习>(Deep Learning)是一本为了帮助学生及从业者入门机器学习 ...
- Android从零开始
Android开发环境的安装 1 IDE Android可以使用开发的IDE有Eclipse 或者 Android Studio.Android Studio还处于v 0.1.x版本,是early a ...
- Redis学习系列五Set(集合)
一.简介 Redis中的Set(集合)相当于C#中的HashSet,它内部的键值对时无序的.唯一的.用过Dictionary的都知道,Dictionary都知道,里面的每个键值对肯定是唯一的,因为键不 ...
- tensorflow进阶篇-4(损失函数3)
Softmax交叉熵损失函数(Softmax cross-entropy loss)是作用于非归一化的输出结果只针对单个目标分类的计算损失.通过softmax函数将输出结果转化成概率分布,然后计算真值 ...
- JavaScript -- Window-Blur
-----030-Window-Blur.html----- <!DOCTYPE html> <html> <head> <meta http-equiv=& ...
- Jenkins-pipeline的实现步骤
jenkins实现持续集成 搭建jenkins环境,安装插件 建立pipeline公用类库,文件夹vars,默认的 添加.groovy文件,可以由以下几个类库组成 dockerImageBuild 负 ...
- linux比较文件夹的差异命令
可以使用 diff -ruNa s1 s2 或者使用 diff -uN c1 c2 结果如下: sandbox$ tree . |-- dir1 | |-- a.txt | `-- b.txt `-- ...
- 从nsq中学习如何优雅的退出go 网络程序
退出运行中的程序,可以粗暴的kill -9 $PID,但这样会破坏业务的完整性,有可能一个正在在执行的逻辑半途而费,从而产生不正常的垃圾数据. 本文总结在go语言中,如何能优雅的退出网络应用,涉及的知 ...
- 【一个小功能】从js判断ie版本,浅谈navigator对象的appName属性
判断IE版本主要的是获取两个属性,a.当前浏览器名称,b.当前浏览器版本,为此不得不了解navigator对象. 先贴代码 window.onload = function() { var brows ...