语雀

该文章整理有点乱,有兴趣可以直接看我的雀语

语雀 《Emu 调用实例》

这个东西很重要,so 和 java 交互已司空见惯了

调用动态注册的函数

java 中 MainActivity 中的 stringFromJNI 函数

native 函数

步骤:

  • 先完成对 JNI_OnLoad 的调用
  • 对动态注册后的 stringFromJNI 的调用

完成 JNI_OnLoad 的调用

首先我们要知道的事:
JNI_OnLoad 3 个参数
  1. JavaVM ==> emulator.java_vm.address_ptr
  2. 动态注册函数个数
  3. 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')
Last modification:May 12, 2021
如果觉得我的文章对你有用,请随意赞赏