android java调用参数,如何从命令行调用Android JNI函数并传递Java对象参数

本文介绍了如何在不修改应用的情况下,通过Harness工具隔离和执行Android原生库函数,包括创建Java虚拟机实例、调用JNI_OnLoad以及通过socket服务器接收参数来调用目标函数。示例应用包含加密字符串,通过解密函数服务,可以远程控制解密过程。
摘要由CSDN通过智能技术生成

一、前言

当我们对某个使用原生库(native library)的恶意软件或者应用进行分析或渗透测试时,如果能够对库函数进行隔离和执行是再好不过的事情,这样做我们就可以使用其自身的代码来调试对抗恶意软件。举个例子,如果恶意软件包含加密字符串,并使用原生函数完成解密过程,你可以选择花大量时间逆向分析算法来编写自己的解密函数,也可以选择直接利用这个函数来处理任意输入数据。如果使用后一种方法,即使恶意软件作者完全改变了软件的加密算法,你也可能不需要做任何修改即可完成任务。在这篇文章中,我将向读者介绍如何利用并执行原生库函数,即使调用这些函数时需要传入JVM实例作为参数也没问题。

在之前的一篇文章中,我介绍了如何从Android原生代码中创建一个Java虚拟机,但我没有给出一个具体的例子。因此,我会在本文中给出一个具体的例子来说明这一点。

我们至少可以使用两种方法来调用原生函数。第一种方法是对应用进行修改,使应用接受你的输入数据并传递给原生函数。例如,你可以写一个intent filter,将其转化为Smali语言,将代码添加到目标应用中,修改manifest文件,运行应用,使用adb命令将带有参数的intent发送给目标应用即可。另一种方法更好,你可以添加一个小型socket或web服务器,使用curl向其发送请求,这种方法不需要修改manifest文件。

第二种方法的目标是创建一个通过命令行运行的小型原生可执行工具,用来加载库文件、调用目标函数、传递我们输入的任意参数。这样我们就可以单独运行一个可执行文件,而不需要运行整个应用程序,因此调试起来也就更为方便。

二、目标应用

我创建了一个示例应用,方便读者按照教程学习,应用名为“native-harness-target”。你可以使用以下命令将工程文件复制到本地并完成编译(记得修改其中的“$ANDROID_*”变量)。

git clone https://github.com/CalebFenton/native-harness-target.git

cd native-harness-target

echo 'ndk.dir=$ANDROID_NDK' > local.properties

echo 'sdk.dir=$ANDROID_SDK' >> local.properties

./gradlew build

APK文件最终生成在“app/build/outputs/apk/”目录。这篇文章中,我使用的是一个x86模拟器镜像以及一个名为“app-universal-debug.apk”的应用。

该应用程序包含一个加密字符串,并会在运行时使用原生库对字符串进行解密。以下是在Smail中字符串的解密过程:

const/16 v3, 0x57

new-array v1, v3, [B

fill-array-data v1, :array_2a

.local v1, "encryptedStringBytes":[B

invoke-static {}, Lorg/cf/nativeharness/Cryptor;->getInstance()Lorg/cf/nativeharness/Cryptor;

move-result-object v0

.line 21

.local v0, "c":Lorg/cf/nativeharness/Cryptor;

# v3 contains a String made from encrypted bytes

new-instance v3, Ljava/lang/String;

invoke-direct {v3, v1}, Ljava/lang/String;->([B)V

# Call the decryption method, move result back to v3

invoke-virtual {v0, v3}, Lorg/cf/nativeharness/Cryptor;->decryptString(Ljava/lang/String;)Ljava/lang/String;

move-result-object v3

三、构建Harness工具

我使用的是Tim 'diff' Strazzere开发的一款名为“native-shim”的工具(Tim是RedNaga的一名成员)作为整套利用工具的基础,我将这个工具命名为“Harness”。在Android中,shim就像一个中间垫片,作用是加载一个库,并调用其“JNI_OnLoad”方法。它可以使调试工作更加简单,我们只需要使用调试器启动shim,并将具体路径以参数形式传递给目标库即可。我们可以设置调试器的断点,在库加载时触发断点,这样就能进入“JNI_OnLoad”函数的处理流程。此外,native-shim还可以加载库文件(.so文件)、获取函数的引用并调用函数,这一切对我们来说都非常实用。

首先,我添加了部分代码以初始化一个Java虚拟机实例,并将该实例传递给JNI_OnLoad函数,这样处理可以使JNI的初始化过程更为准确。如果没有真实的虚拟机实例,JNI库的内部状态看起来可能会有些奇怪。不同库文件的JNI_OnLoad的实现可能不尽相同,但这并不重要,重要的是这些实现都会检查JNI版本,如这段代码所示。因此我们需要创建一个虚拟机实例。

printf(" [+] Initializing JavaVM Instance\n");

JavaVM *vm = NULL;

JNIEnv *env = NULL;

int status = init_jvm(&vm, &env);

if (status == 0) {

printf(" [+] Initialization success (vm=%p, env=%p)\n", vm, env);

} else {

printf(" [!] Initialization failure (%i)\n", status);

return -1;

}

printf(" [+] Calling JNI_OnLoad\n");

onLoadFunc(vm, NULL);

我们的最终目标是通过harness工具,开启一个socket服务器,读取socket上传输的参数,使用这些参数来调用函数。这样一来,解密函数就会变成一个服务,我们可以简单使用一个Python脚本与其通信。

四、理解目标函数

在调用函数前,我们需要了解函数的签名(即参数个数和参数类型)及函数的返回类型。我们可以先看一下org.cf.nativeharness.Cryptor类的反编译代码,类中包含decryptString原生方法的声明,如下所示:

public class Cryptor {

private static Cryptor instance = null;

public static Cryptor getInstance() {

if (instance == null) {

instance = new Cryptor();

}

return instance;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值