语雀
该文章整理有点乱,有兴趣可以直接看我的雀语
这个东西很重要,so 和 java 交互已司空见惯了
调用动态注册的函数
java 中 MainActivity
中的 stringFromJNI
函数
native
函数
步骤:
- 先完成对 JNI_OnLoad 的调用
- 对动态注册后的 stringFromJNI 的调用
完成 JNI_OnLoad 的调用
首先我们要知道的事:
JNI_OnLoad 3 个参数
- JavaVM ==> emulator.java_vm.address_ptr
- 动态注册函数个数
- JNINativeMethod 地址 (数组)
我们在 emu 中的调用方法为: emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0, )
但是我们只是这样写的话会报错的。因为要调用 java 中的 函数,所以要手动注册一下这个 java 的函数
完成 java 函数复写
被调用的java类和函数需要模拟实现,否则调用就会报错.Emu 的示例代码在:example_jiagu.py 中,这里模仿实现如下:
class MainActivity(metaclass=JavaClassDef, jvm_name='com/example/unicorncourse05/MainActivity'): # jvm_name 就是路径
def __init__(self):
pass
# 有 3 个参数
@java_method_def(name='stringFromJNI', signature='(Ljava/lang/String;)Ljava/lang/String;', native=True)
def stringFromJNI(self, mu, arg0):
pass
这是 stringFromJNI 的例子, 并且必须在调用前注册!emulator.java_classloader.add_class(MainActivity)
代码如下
"""
so 是动态注册的
目标函数 stringFromJNI
操作步骤:
1. 先完成对 `JNI_OnLoad` 的调用
2. 对动态注册后的 `stringFromJNI` 的调用
"""
import logging
import sys
import os
from unicorn import UC_HOOK_CODE
from debug_utils import hook_code
from androidemu.java.java_class_def import JavaClassDef
from androidemu.java.java_method_def import java_method_def
from androidemu.emulator import Emulator
sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
logging.basicConfig(
stream=sys.stdout,
level=logging.DEBUG,
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger = logging.getLogger(__name__)
emulator = Emulator(vfp_inst_set=True) # 初始化模拟器
emulator.load_library("example_binaries/libc.so", do_init=False) # 加载 so libc
lib_module = emulator.load_library("test/unicorn05.so", do_init=False)
logger.info("Loaded modules:")
for module in emulator.modules:
logger.info("[0x%x] %s" % (module.base, module.filename))
"""
被调用的java类和函数需要模拟实现,否则调用就会报错
Emu 的示例代码在:example_jiagu.py 中
这里模仿实现如下
并且要在调用前注册!emulator.java_classloader.add_class(MainActivity)
"""
class MainActivity(metaclass=JavaClassDef, jvm_name='com/example/unicorncourse05/MainActivity'): # jvm_name 就是路径
def __init__(self):
pass
# 有 3 个参数
@java_method_def(name='stringFromJNI', signature='(Ljava/lang/String;)Ljava/lang/String;', native=True)
def stringFromJNI(self, mu, arg0):
pass
# emulator.mu.hook_add(UC_HOOK_CODE, hook_code) # 自己修改的回调函数
"""
JNI_OnLoad 3 个参数
1. JavaVM ==> emulator.java_vm.address_ptr
2. 动态注册函数个数
3. JNINativeMethod 地址 (数组)
"""
# 因为要调用 java 中的 函数,所以要手动注册一下这个 java 的函数
emulator.java_classloader.add_class(MainActivity)
emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0, )
在 so 中调用 java 函数
方法1
"""
上面的工作都做完后,就可以调用 java 函数了
通过地址调用
"""
activity = MainActivity()
activity.stringFromJNI(emulator, 'hello zok')
方法2
# 上面的工作都做完后,就可以调用 java 函数了
"""
方法1: 定点调用
"""
activity = MainActivity()
result = activity.stringFromJNI(emulator, 'hello zok')
print('[定点调用] 返回值:%s' % result)
"""
方法2: 根据地址调用,先遍历拿到函数地址
"""
for method in MainActivity.jvm_methods.values():
print('javaClassName:' + method.name+' sig:' + method.signature+' addr:' + str(method.native_addr))
if method.name == 'stringFromJNI':
# 该函数有3个参数, 1: jni ptr
result = emulator.call_native(method.native_addr, emulator.java_vm.jni_env.address_ptr, 0, 'hello zok 2') # 前面两个参数要给上
print('[地址调用] 返回值:%s' % result)
jni 函数中 调用了普通 java 类函数
这里有个 Encrypt 类, 下面有个 base64 的函数。
• 需要解决 jni 中 call 的模拟实现
方法,其实和上面说的调用方法类似,但是实力话 class 的时候写法需要改变一下
class Encrypt(metaclass=JavaClassDef, jvm_name='com/example/unicorncourse07/Encrypt'): # jvm_name 就是路径
"""模拟 java 函数"""
def __init__(self):
pass
# 普通java 函数 native=False , args_list 参数类型 jstring
# args_list 只支持:jint jstring jobject jbyteArray
@java_method_def(name='base64', args_list=['jstring'], signature='(Ljava/lang/String;)Ljava/lang/String;', native=False)
def base64(self, *args, **kwargs): # python 实现 base64
content = args[0].value # jstring 需要 .value 取值
encode_str = content.encode('UTF-8')
result = base64.b64encode(encode_str)
return str(result, 'utf-8')