[Unidbg 的基本使用(五)](https://www.yuque.com/lilac-2hqvv/xdwlsg/hiiaukxvr2zuz5mb?# 《Unidbg 的基本使用(五)》)学!
Unidbg 类型封装
根据 Unidbg 类型封装过程的分析,类型封装情况可以分为以下四类:
1. 基本类型直接传递
对于 Java 的基本数据类型,调用时可以直接传递,无需额外封装。这些类型包括:
int
,long
,boolean
,double
Unidbg 会自动将这些基本类型直接映射为 JNI 方法的入参,并传递给底层逻辑。
2. 常见对象类型的直接传递
常见的 Java 对象类型,可以直接作为参数传递,Unidbg 会自动封装为相应的 JNI 类型(如 StringObject
、ByteArray
等):
String
(字符串)基本类型的数组:
byte[]
,short[]
,int[]
,float[]
,double[]
Enum
(枚举类型)
如果需要手动封装这些对象类型,也可以通过相关构造器创建对应的封装对象,例如使用 StringObject
或 ByteArray
等。
3. JDK 标准类库的封装
对于 JDK 中包含的类库(如二维数组、字符串数组、HashMap
等),可以通过 ProxyDvmObject.createObject()
进行封装。
该方法会自动将类库对象转换为 ProxyDvmObject
,以便在 JNI 调用中正确传递。此方法支持封装 JDK 标准类库对象以及常见的第三方类库(如 OkHttp 等)。
支持封装的类型:
二维数组,字符串数组,集合类(如 HashMap
等)
4. 非 JDK 标准类库的封装
对于无法在 JDK 中包含的类库(如 Android Framework 的组件类或样本自定义的类库),需要通过 resolveClass
动态解析类名 并调用 .newObject
来手动构造封装对象。此类封装方法通常用于处理 Android 系统组件(如 Context
)或特定样本中自定义的复杂类。
如果要发起对目标函数的调用,那么查看代码的反编译情况,可以看到清晰的参数类型
1 | static native SignedQuery s(SortedMap<String, String> sortedMap); |
如果样本通过 JNI 访问 JAVA 方法,那么根据报错确定返回值类型。
例一
1 | java.lang.UnsupportedOperationException: com/izuiyou/common/base/BaseApplication->getAppContext()Landroid/content/Context; |
方法签名中,返回值是Landroid/content/Context;
JAVA 类型 | 签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
class | Lclass; |
[ type | type[] |
根据对应关系,其类型就是android/content/Context
。
类型一
1. 基本类型的直接传递
基本类型包括:
- 整数类型:
byte
、short
、int
、long
- 浮点类型:
float
、double
- 布尔类型:
boolean
- 字符类型:
char
这些类型可以直接传递给 JNI 方法,无需额外的封装操作。Unidbg 会自动识别并处理这些基本类型的参数。
例如,在 AbstractJNI
类中,重写的 getStaticIntField
方法直接返回了一个整型值 0x40
:
1 |
|
注意 Long 类型的显式声明
在参数准备和函数调用时,如果涉及到 long
类型的参数,必须显式声明整数为 Long 类型。这是因为 Unidbg 无法识别未声明的 long
类型参数,并且可能会将其误识别为 int
类型。
正确的传递方式: 在整数值后面添加 L
来显式标记为 Long 类型,例如:
1 | callStaticJniMethod(emulator, "nativeFunction(J)V", 1234567890L); |
类型二
基本对象的封装与处理
在 Unidbg 环境中,基本对象的封装包括字符串(String
)、基本类型数组(如 byte[]
、int[]
)、对象数组(如 String[]
)、以及集合(如 List
)。它们都需要通过特定的方法封装为 DVM 对象,供虚拟机正确识别和调用。
String(字符串)
在 Unidbg 中,字符串会被封装为 StringObject
。
1 | java.lang.UnsupportedOperationException: cn/xiaochuankeji/tieba/AppController->getPackageName()Ljava/lang/String; |
方法一:通过 StringObject
构造
直接通过 StringObject
构造字符串对象:
1 | String packageName = vm.getPackageName(); |
方法二:通过 ProxyDvmObject.createObject
使用 ProxyDvmObject.createObject
,它会自动检测类型并封装为 StringObject
:
1 | String packageName = vm.getPackageName(); |
两者对比:
- **
StringObject
**:直观且代码明确,适合直接封装字符串。 - **
ProxyDvmObject.createObject
**:适合动态场景,可以传入任意类型。
Integer(基本类型的包装类)
1 | java.lang.UnsupportedOperationException: java/lang/Integer-><init>(I)V |
基本类型(如 int
)的包装类(如 Integer
)在 Unidbg 中通过 valueOf
方法进行封装。
1 | case "java/lang/Integer-><init>(I)V": { |
Unidbg 对常用包装类(如 Integer
、Long
、Float
等)进行了支持,这些包装类位于 com.github.unidbg.linux.android.dvm.wrapper
包下。
基本类型数组
1 | java.lang.UnsupportedOperationException: java/net/NetworkInterface->getHardwareAddress()[B |
[B
即 byte[],在 Unidbg 中对应于ByteArray
方法一:通过 ByteArray
类
对于基本类型数组(如 byte[]
),Unidbg 提供了相应的类(如 ByteArray
、IntArray
)进行封装。例如:
1 | byte[] byteArray = new byte[]{1, 2, 3}; |
方法二:通过 ProxyDvmObject.createObject
使用 ProxyDvmObject.createObject
,它会自动检测并封装为对应的数组对象:
1 | byte[] byteArray = new byte[]{1, 2, 3}; |
在com.github.unidbg.linux.android.dvm.array
包下有对各种基本类型数组的表示和处理。
对象数组
1 | java.lang.UnsupportedOperationException: android/os/Build->SUPPORTED_ABIS:[Ljava/lang/String; |
类型是[Ljava/lang/String;
方法一:通过 ArrayObject
类
Unidbg 提供了 ArrayObject
来表示对象数组。如果是字符串数组,还提供了方便的静态方法 newStringArray
进行构造:
1 | String[] abis = new String[]{"arm64-v8a", "armeabi-v7a", "armeabi"}; |
方法二:通过 ProxyDvmObject.createObject
ProxyDvmObject.createObject
可以将 Java 的对象数组自动转换为 ArrayObject
:
1 | String[] abis = new String[]{"arm64-v8a", "armeabi-v7a", "armeabi"}; |
内部的实现会将数组元素逐一封装为 DvmObject
,再组合成 ArrayObject
:
1 | Class<?> clazz = value.getClass(); |
对于对象数组类型,用ProxyDvmObject.createObject
更省事。
集合
1 | java.lang.UnsupportedOperationException: android/hardware/SensorManager->getSensorList(I)Ljava/util/List; |
封装为ArrayListObject
对于集合类型(如 List
),可以使用 ArrayListObject
封装。
示例:返回传感器列表 以下代码演示了如何封装 List
对象为 ArrayListObject
:
1 | case "android/hardware/SensorManager->getSensorList(I)Ljava/util/List;": { |
类型三
在 Unidbg 的模拟环境中,部分 JDK 类的行为无法直接支持或无法解析,例如:
- 无法直接通过
newObject
创建实例。 - 无法直接返回 Dalvik 层的复杂类型。
- 无法确定返回值类型(如
Object
类型)。
这种情况下,我们需要使用 ProxyDvmObject.createObject 来手动创建代理对象,以桥接 Dalvik 层和 Java 层之间的调用。
无法直接创建对象(HashMap 示例)
1 | java.lang.UnsupportedOperationException: java/util/HashMap-><init>()V |
使用 ProxyDvmObject.createObject
手动创建 HashMap
实例,并返回代理对象。
1 | case "java/util/HashMap-><init>()V": { |
返回复杂对象(InputStream 示例)
1 | java.lang.UnsupportedOperationException: java/util/zip/ZipFile->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream; |
手动调用 Java 方法,并将结果包装成 ProxyDvmObject 返回。
1 | case "java/util/zip/ZipFile->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;": { |
返回不确定类型的对象(Map 示例)
1 | java.lang.UnsupportedOperationException: java/util/Map->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; |
这里只知道是 Object,而不确定具体是字符串还是其他什么类型
手动处理 Map.put()
方法的逻辑,并将返回值包装为 ProxyDvmObject。
1 | case "java/util/Map->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;": { |
类型四
对于 Android FrameWork 类库中的对象和类,使用resolveClass
创建对应的DvmClass
和DvmObject
比较好。
静态方法调用(FtTelephonyAdapter
示例)
1 | java.lang.UnsupportedOperationException: android/telephony/FtTelephonyAdapter->getFtTelephony(Landroid/content/Context;)Landroid/telephony/FtTelephony; |
这是因为 FtTelephonyAdapter
是 Android Framework 层的类,Unidbg 无法直接处理。
使用 resolveClass
创建占位类,并返回占位对象。
占位类(Placeholder Class)是一种编程中的设计概念,用于在特定上下文中临时替代实际类,以满足程序的运行需求。它的作用是为未实现的、缺失的或者特定场景中的类提供一个占位符,从而避免程序运行时发生错误或异常。
1 | case "android/telephony/FtTelephonyAdapter->getFtTelephony(Landroid/content/Context;)Landroid/telephony/FtTelephony;": { |
静态方法调用(Class.forName
示例)
1 | java.lang.UnsupportedOperationException: java/lang/Class->forName(Ljava/lang/String;)Ljava/lang/Class; |
Class.forName(className)
用于加载类,我们不确定所加载的类是样本自定义的类、Android 框架层类库,还是 JDK 中的标准类库,这种情况里使用resolveClass
是好办法。
通过 resolveClass
动态解析类名并返回占位对象。
1 | case "java/lang/Class->forName(Ljava/lang/String;)Ljava/lang/Class;": { |
构造方法调用(IntentFilter
示例)
1 | java.lang.UnsupportedOperationException: android/content/IntentFilter-><init>(Ljava/lang/String;)V |
IntentFilter
是 Android Framework 层的类,通常用于广播接收器的过滤条件。
通过 resolveClass
创建占位类,并传递参数用于初始化。
1 | case "android/content/IntentFilter-><init>(Ljava/lang/String;)V": { |
传递参数(intent
)是因为样本可能初始化了多个 IntentFilter
,需要区分不同实例。后续方法调用时,可以通过 intent
参数还原初始化状态。
resolveClass
与 ProxyDvmObject
的区别
特性 | resolveClass | ProxyDvmObject |
---|---|---|
用途 | 用于解析 Android Framework 层类并创建占位类。 | 用于包装 Java 对象,桥接 Dalvik 和 Java 层。 |
适用场景 | Android Framework 类(如 IntentFilter )。 |
JDK 类(如 HashMap 、InputStream )。 |
返回类型 | DvmClass 和 DvmObject 。 |
ProxyDvmObject (包装的代理对象)。 |
使用复杂度 | 较低,直接调用 resolveClass 方法即可。 |
较高,需要自定义方法逻辑并包装对象。 |