当前位置: 首页 > news >正文

Frida0D - hook JNIEnv 相关函数

NewStringUTF

看一个例子:

    private external fun stringFromJNI(): Stringprivate external fun registerNativeStringFromJNI(): String
JNIEXPORT jstring JNICALL registerNativeStringFromJNI(JNIEnv *env,jobject clazz) {std::string hello = "Hello from C++ registerNativeStringFromJNI";return env->NewStringUTF(hello.c_str());
}extern "C" JNIEXPORT jstring JNICALL
Java_com_aprz_myapplication_MainActivity_stringFromJNI(JNIEnv *env,jobject clazz) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

目标

使用 frida hook NewStringUTF 函数,拿到参数与返回值,并尝试修改参数与返回结果。

查找符号

编译后,查看 so 函数符号表:

 objdump -tT libhookjnienv.so > s.txt------t, --syms               Display the contents of the symbol table(s)-T, --dynamic-syms       Display the contents of the dynamic symbol table

搜索NewStringUTF发现了两个结果,是同一个符号:

000000000001e408  w    F .text 0000000000000034              _ZN7_JNIEnv12NewStringUTFEPKc
000000000001e408  w   DF .text 0000000000000034  Base        _ZN7_JNIEnv12NewStringUTFEPKc

如果发现有多个符号,可以去https://demangler.com/ 网站看看这个符号对应的具体方法是什么,比如上面的符号解出来就是:

_JNIEnv::NewStringUTF(char const*)

namespace 是对的上的,说明是我们需要hook的符号。

编写脚本

export function hook_jni_env() {var hook_jni_env = Process.getModuleByName("libhookjnienv.so");var new_string_utf = hook_jni_env.base.add(0x1e408);Interceptor.attach(new_string_utf, {onEnter: function (args) {console.log("jni env = ", args[0]);console.log("char * arg = ", args[1]);}, onLeave: function (retval) {console.log("jstring = ", retval);}});
}

查看输出

[Pixel::com.aprz.myapplication ]-> jni env =  0x76851126c0
char * arg =  0x7ff3d2c6a1
jstring =  0x71

使用Frida提供的JNIEnv来打印参数和返回结果

我们可以使用 frida提供的 JJNIEnv来获取 jstirng与 char * 类型的值。

Java.vm.getEnv().getStringUtfChars()
export function hook_jni_env() {var hook_jni_env = Process.getModuleByName("libhookjnienv.so");var new_string_utf = hook_jni_env.base.add(0x1e408);Interceptor.attach(new_string_utf, {onEnter: function (args) {console.log("jni env = ", args[0]);console.log("char * arg = ", args[1].readCString());}, onLeave: function (retval) {console.log("jstring = ", Java.vm.getEnv().getStringUtfChars(retval, false).readCString());retval.replace(Java.vm.getEnv().newStringUtf("nihao"));}});
}

查看输出

[Pixel::com.aprz.myapplication ]-> jni env =  0x76851126c0
char * arg =  Hello from C++
jstring =  Hello from C++

注意,我们 hook 了 JNIEnv 的 newStringUTF 函数,一般情况下需要注意死循环,比如inlinehook,但是这里使用 frida 提供的不需要担心这个问题,有点神奇。

打印堆栈

我们可以直接使用frida提供的方法:

export function hook_jni_env() {var hook_jni_env = Process.getModuleByName("libhookjnienv.so");var new_string_utf = hook_jni_env.base.add(0x1e408);Interceptor.attach(new_string_utf, {onEnter: function (args) {console.log("jni env = ", args[0]);console.log("char * arg = ", args[1].readCString());console.log('CCCryptorCreate called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');}, onLeave: function (retval) {console.log("jstring = ", Java.vm.getEnv().getStringUtfChars(retval, false).readCString());retval.replace(Java.vm.getEnv().newStringUtf("nihao"));}});
}

查看输出

可以自己比较一下Backtracer.FUZZY 和 Backtracer.ACCURATE 的区别。

[Pixel::com.aprz.myapplication ]-> jni env =  0x76851126c0
char * arg =  Hello from C++
CCCryptorCreate called from:
0x7591e3a330 libhookjnienv.so!Java_com_aprz_myapplication_MainActivity_stringFromJNI+0x50
0x75ffd52354 libart.so!art_quick_generic_jni_trampoline+0x94
0x75ffd49338 libart.so!art_quick_invoke_stub+0x228
0x75ffd58068 libart.so!_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+0xf8
0x75ffef6bc4 libart.so!_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_11ShadowFrameEtPNS_6JValueE+0x184
0x75ffef1abc libart.so!_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+0x3a4
0x76001b7620 libart.so!MterpInvokeDirect+0x194
0x75ffd43918 libart.so!mterp_op_invoke_direct+0x18
0x76001b8158 libart.so!MterpInvokeStatic+0x48c
0x75ffd43998 libart.so!mterp_op_invoke_static+0x18
0x76001b8158 libart.so!MterpInvokeStatic+0x48c
0x75ffd43998 libart.so!mterp_op_invoke_static+0x18
0x76001b6d88 libart.so!MterpInvokeInterface+0x6e8
0x75ffd43a18 libart.so!mterp_op_invoke_interface+0x18
0x76001b5568 libart.so!MterpInvokeVirtual+0x5b4
0x75ffd43818 libart.so!mterp_op_invoke_virtual+0x18jstring =  Hello from C++

可以看到还是比较准确的。

想到 frida 可以注入 so,所以也可以考虑编译一个打印堆栈的 so,然后调用方法。

RegisterNatives

对这个函数的 hook,前一篇文章已经贴了一段代码了,这里简单分析一下就ok。

  function getNativeAddress(idx) {return env.handle.readPointer().add(idx * pSize).readPointer();}

上面,我们采用的是查询符号的地址,然后hook的方式。但是这里更加巧妙,它利用了 JNIENV 的源码相关知识。

JNIEnv 是一个结构体,整个结构体看成一个表,第 215 项对应的就是  RegisterNatives ,所以我们可以直接用指针计算偏移。

getNativeAddress(215)

因为,JNIEnv 结构是不会变化的,所以这个相当实用。

同样的,在处理参数的时候,RegisterNatives 接收一个JNINativeMethod 结构体指针,我们也可以利用指针来获取整个结构体的字段信息。

console.log("[RegisterNatives]method counts :", args[3]);
var env = args[0];
var jclass = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(jclass);
var methods_ptr = ptr(args[2]);
var method_count = parseInt(args[3]);
for (var i = 0; i < method_count; i++) {var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));var name = Memory.readCString(name_ptr);var sig = Memory.readCString(sig_ptr);var find_module = Process.findModuleByAddress(fnPtr_ptr);console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • gradle 学习备忘
  • 基于EPS32C3电脑远程开机模块设计
  • springboot健康管理系统-计算机毕业设计源码25248
  • vue3项目,本地页面正常显示,打包后页面空白
  • 计算机视觉中,什么是上下文信息(contextual information)?
  • Ubuntu系统修改静态IP
  • 软件测试报告示例模板
  • 高效录屏攻略:电脑视频录制时声音同步捕获技巧,轻松搞定高清音质
  • 【C++二分查找】911. 在线选举
  • vue3定义响应式数据(ref,reactive)
  • 《中文Python穿云箭量化平台二次开发技术10》基于Tkinter的可视化股票池量化平台开发技术
  • 【Kubernetes知识点问答题】资源配额 / 访问控制
  • 摩托车加装车载手机充电usb方案/雅马哈USB充电方案开发
  • OpenCV结构分析与形状描述符(19)查找二维点集的最小面积外接旋转矩形函数minAreaRect()的使用
  • 数据库系统概论笔记(持续更新)
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • Angular 2 DI - IoC DI - 1
  • angular组件开发
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • JWT究竟是什么呢?
  • mysql innodb 索引使用指南
  • python docx文档转html页面
  • React-redux的原理以及使用
  • Terraform入门 - 3. 变更基础设施
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 看域名解析域名安全对SEO的影响
  • 坑!为什么View.startAnimation不起作用?
  • 思考 CSS 架构
  • 怎样选择前端框架
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • ######## golang各章节终篇索引 ########
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $.ajax中的eval及dataType
  • (1)Android开发优化---------UI优化
  • (C#)一个最简单的链表类
  • (c语言)strcpy函数用法
  • (libusb) usb口自动刷新
  • (第二周)效能测试
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (循环依赖问题)学习spring的第九天
  • (一)springboot2.7.6集成activit5.23.0之集成引擎
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (杂交版)植物大战僵尸
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Core中的去虚
  • .NET Project Open Day(2011.11.13)
  • .net生成的类,跨工程调用显示注释
  • //usr/lib/libgdal.so.20:对‘sqlite3_column_table_name’未定义的引用
  • :not(:first-child)和:not(:last-child)的用法
  • :如何用SQL脚本保存存储过程返回的结果集