- 对本书的赞誉
- 前言
- 基础篇
- 第 1 章 Android 中锁屏密码加密算法分析
- 第 2 章 Android 中 NDK 开发
- 第 3 章 Android 中开发与逆向常用命令总结
- 第 4 章 so 文件格式解析
- 第 5 章 AndroidManifest.xml 文件格式解析
- 第 6 章 resource.arsc 文件格式解析
- 第 7 章 dex 文件格式解析
- 防护篇
- 第 8 章 Android 应用安全防护的基本策略
- 第 9 章 Android 中常用权限分析
- 第 10 章 Android 中的 run-as 命令
- 第 11 章 Android 中的 allowBackup 属性
- 第 12 章 Android 中的签名机制
- 第 13 章 Android 应用加固原理
- 第 14 章 Android 中的 so 加固原理
- 工具篇
- 第 15 章 Android 逆向分析基础
- 第 16 章 反编译神器 apktool 和 Jadx
- 第 17 章 Hook 神器 Xposed
- 第 18 章 脱壳神器 ZjDroid
- 第 19 章 Native 层 Hook 神器 Cydia Substrate
- 操作篇
- 第 20 章 静态方式逆向应用
- 第 21 章 动态调试 smali 源码
- 第 22 章 IDA 工具调试 so 源码
- 第 23 章 逆向加固应用
- 第 24 章 逆向应用经典案例分析
- 第 25 章 Android 中常见漏洞分析
- 第 26 章 文件加密病毒 Wannacry 样本分析
2.5 创建 Java 对象及字符串的操作方法
首先来看一下 C/C++中怎么创建 Java 对象,然后再介绍如何操作 Java 字符串。
2.5.1 native 中创建 Java 对象
在 JNIEnv 中有两种方法创建 Java 对象,下面分别介绍。
第一种方法创建 Java 对象
代码如下:
参数如下:
·clazz:是需要创建的 Java 对象的 Class 对象。
·methodID:是传递一个方法的 ID,想一想 Java 对象在创建的时候,需要执行什么方法呢?对,没错那就是构造方法。
·第三个参数:是构造函数需要传入的参数值(默认的构造方法是不需要传入这个参数的)。所以在创建 Java 对象之前要做的工作就是要获取这个对象的 class 对象,然后再获取该对象的构造方法。想要获取方法的 id,就需要方法的签名,因为构造方法没有返回值,所以认为类的默认构造方法的返回值类型的签名始终是“()V”(因为默认的构造方法是没有参数的),方法的名称始终为“<init>”。
在 C++中构造 Java 中的 Date 对象,并且调用它的 getTime()方法打印当前时间。
Java 中的代码不需要改变,主要是在 C++代码中改写:
编译成.dll 文件,在 Eclipse 中运行结果如图 2-27 所示。
图 2-27 运行结果
第二种方法创建 Java 对象
用 AllocObject 函数创建一个对象,可以根据传入的 jclass 创建一个 Java 对象,但是状态是非初始化的,在这个对象之前绝对要用 CallNonvirtualVoidMethod 来调用该 jclass 的构造函数,这样就可以延迟构造函数的调用。这种方法用得很少,下面只对代码做简单的说明。
Java 中的代码不做任何修改,C++代码修改如下:
2.5.2 native 中操作 Java 字符串
首先来了解一下 Java 和 C/C++中字符串的区别。在 Java 中,使用的字符串 String 对象是 Unicode(UTF-16)码,即每个字符不论是中文还是英文还是符号,一个字符总是占两个字节。Java 通过 JNI 接口可以将 Java 的字符串转换到 C/C++中的宽字符串(wchar_t*),或传回一个 UTF-8 的字符串(char*)到 C/C++;反过来,C/C++可以通过一个宽字符串,或一个 UTF-8 编码的字符串来创建一个 Java 端的 String 对象。
接下来看一下 JNIEnv 中的一些 C++方法。
1)获取字符串的长度:
参数 j_msg 是一个 jstring 对象。
2)将 jstring 对象拷贝到 const jchar*指针字符串:
这是在 Java 1.2 出来的函数,这个函数把 Java 字符串的内容直接拷贝到 C/C++的字符串数组中,在调用这个函数之前必须有一个 C/C++分配出来的字符串(具体看下面的例子),然后传入到这个函数中进行字符串的拷贝。
由于 C/C++中分配内存开销相对小,而且 Java 中的 String 内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会抛出 OutOfMemoryError 异常。
参数 j_msg 是一个 jstring 对象,start 是拷贝字符串的开始位置,len 是拷贝字符串的长度,jstr 是目标指针字符串。
3)生成一个 jstring 对象:
参数:jstr 是字符串指针,size 是字符串长度。
这个方法可以认为是将字符串指针 jstr 转换成字符串对象 jstring。
4)将 jstring 对象转换成 const jchar*字符串指针。有两个方法:GetStringChars 和 GetStringUTFChars 方法。
GetStringChars 方法如下:
返回一个 UTF-16 编码的宽字符串(jchar*)。
参数如下:
·j_msg 是字符串对象。
·copied 是指传入的是一个 jboolean 指针,用来标识是否对 Java 的 String 对象进行了拷贝,如果传入的这个 jboolean 指针不是 NULL,则它会给该指针所指向的内存传入 JNI_TRUE 或 JNI_FALSE 标识是否进行了拷贝,传入 NULL 表示不关心是否拷贝字符串,也就不会给 jboolean*指向的内存赋值。
其对应的释放内存指针的方法:
参数:j_msg 是 jstring 对象,jstr 是字符串指针。
GetStringUTFChars 方法如下:
这个方法是可以取得 UTF-8 编码的字符串(char*)。参数的含义和 GetStringChars 方法是一样的。这个方法也有对应的一个释放内存的方法:
参数的含义和上面的 ReleaseStringChars 方法的参数的含义是一样的。
提示:这两个函数分别都会有两个不同的动作:
·开辟一个新内存,然后在 Java 中的 String 拷贝到这个内存中,然后返回指向这个内存地址的指针。
·直接返回指向 Java 中 String 的内存的指针,这个时候千万不要改变这个内存的内容,这个将会破坏 String 在 Java 中始终是常量的这个原则。
5)将 jstring 对象转化成 const jchar*字符串指针:
参数 j_msg 是字符串对象,copied 同上面的解释,这里就不多说了。
这个方法的作用是为了增加直接传回指向 Java 字符串的指针的可能性(而不是拷贝),JDK 1.2 出来了新的函数 GetStringCritical/ReleaseStringCritical。
在 GetStringCritical/ReleaseStringCritical 之间是一个关键区,在这个关键区域之间不能调用 JNI 的其他函数,否则将造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停,其他的触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器。就是说在关键区域中千万不要出现中断操作,或在 JVM 中分配任何新对象;否则会造成 JVM 死锁。虽然这个函数会增加直接传回指向 Java 字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串。不支持 GetStringUTFCritical,没有这样的函数,由于 Java 字符串用的是 UTF-16,要转成 UTF-8 编码的字符串始终需要进行一次拷贝,所以没有这样的函数。
这个方法和第四个方法是一样的。其对应的释放内存指针的方法如下:
下面来看一下实例:在 Java 中定义一个 String 属性,通过控制台输入值,然后定义一个本地方法 callCppFunction,在 C++中这个方法的实现就是:获取到 Java 中这个字符串属性,将其进行倒序操作,然后再从 Java 中输出。
先来看一下 Java 代码:
再来看一下 C++代码:
这里使用了三种方式实现功能。要注意的是,还有一个方法是将 const jchar*转换成 wstring,因为 reverse 方法接受的参数是 wstring。在 Eclipse 中的运行结果如图 2-28 所示。
图 2-28 Eclipse 中的运行结果
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论