一、Java对象的api
1 Java.available
一个布尔值,指定当前进程是否已加载Java VM,即Dalvik或ART。Java 除非是这种情况,否则请勿调用任何其他属性或方法。
1 | function frida_Java(){ |
2 Java.androidVersion
返回安卓的版本号
1 | [Nexus 5X::com.android.settings]-> Java.androidVersion |
3 枚举加载所有的类
3.1 Java.enumerateLoadedClasses(callbacks)
枚举加载的所有的类,其中callbacks
的对象为:
onMatch(name, handle)
:为每个加载的类调用name
,方法可能会传递给,use()
以获取JavaScript包装器。还可以Java.cast()
在handle
来java.lang.Class
。onComplete()
:在枚举所有类后调用。
注意:与Java.enumerateClassLoaders(callbacks)区分
1 | function frida_Java(){ |
执行结果:
1 | org.chromium.android_webview.AwContentsLifecycleNotifier |
3.2 Java.enumerateLoadedClassesSync()
和3.1一样,枚举加载的所有的类,但返回的是一个数组。
1 | function frida_Java(){ |
4 Java.enumerateClassLoaders(callbacks)
枚举中存在的类加载器,其中callbacks
的对象为:
onMatch(loader)
:匹配类加载器。onComplete()
:在枚举所有类加载器时调用。
注意:与Java.enumerateLoadedClasses(callbacks)区分
与枚举所有的类用法基本一致。
1 | function frida_Java(){ |
执行结果:
1 | dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/mobi.acpm.inspeckage-1/base.apk"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64]]] |
5 Java.enumerateMethods(query):
模糊搜索方法,用法 Java.enumerateMethods("*com*!*rSa*/i/u")
,可以带有/
和一个或多个修饰符后缀:
i
:不区分大小写的匹配。s
:包括方法签名。u
:仅用户定义的类,忽略系统类。
1 | function frida_Java(){ |
执行结果:
1 | [ |
6 Java.perform
确保当前线程已连接到VM并调用fn方法,基本每个脚本都会用。
7 Java.use
动态获取一个 类名,可以通过调用$new()
来调用构造函数以实例化对象。当想要回收类时可以调用$Dispose()
方法显式释放(或等待JavaScript对象进行垃圾回收,或等待脚本卸载)。
1 | function frida_Java(){ |
8 Java.choose
通过扫描Java堆来枚举该类的实例 ,其中callbacks
一个对象指定:
onMatch(instance)
:使用找到的每个可用实例进行调用,instance
就好像可以使用Java.cast()
该特定实例的原始句柄进行调用一样 。此函数可能返回字符串stop
以提早取消枚举。onComplete()
:在列举所有实例后调用
1 | function frida_Java(){ |
执行结果如下:
1 | android.view.View{d6a0de6 V.ED..... ......ID 0,0-1080,63 #102002f android:id/statusBarBackground} |
9 Java.cast
类型转换器,将指定变量或者数据强制转换成你所有需要的类型;创建一个 JavaScript 包装器,给定从 Java.use() 返回的给定类klass的句柄的现有实例。此类包装器还具有用于获取其类的包装器的类属性,以及用于获取其类名的字符串表示的$className属性,通常在拦截so层时会使用此函数将jstring、jarray等等转换之后查看其值。
官方示例:
1 | const Activity = Java.use('android.app.Activity'); |
10 Java.array
定义java数组的api,格式为Java.array('type',[value1,value2,....]);
,生成的Java数组的行为类似于JS数组,但是可以通过引用传递给Java API,以允许它们修改其内容。
1 | function frida_Java(){ |
11 Java.registerClass(spec)
创建一个新的Java类,其中spec
的对象包含:
name
:字符串,指定类的名称。superClassjava.lang.Object
:(可选)父类。忽略继承。implements
:(可选)由此类实现的接口数组。fields
:(可选)指定要公开的每个字段的名称和类型的对象。methods
:(可选)指定要实现对象的方法。
示例:
1 | function frida_Java(){ |
官方文档的例子是绕过证书校验。
1 | //获取目标进程的SomeBaseClass类 |
12 Java.vm
常用api,情景:想要拿到JNI层的JNIEnv对象,可以使用getEnv()。
perform(fn)
:确保当前线程已连接到VM并调用fn
。(在Java回调中这不是必需的。)getEnv()
:获取当前线程的包装器JNIEnv
。如果当前线程未附加到VM,则引发异常。tryGetEnv()
:尝试获取当前线程的包装器JNIEnv
。如果当前线程没有连接到虚拟机返回null。
1 | function frida_Java() { |
13 Java.isMainThread()
确定调用方是否正在主线程上运行。
二、Interceptor对象的api
1 Interceptor.attach(target, callbacks[, data])
对象中包含两个函数,如下:
onEnter:
函数(args
):回调函数,给定一个参数args
,可用于读取或写入参数作为NativePointer
对象的数组。onLeave:
函数(retval
):回调函数给定一个参数retval
,该参数是包含原始返回值的NativePointer
派生对象。可以调用retval.replace(1337)
以整数1337
替换返回值,或者调用retval.replace(ptr("0x1234"))
以替换为指针。请注意,此对象在OnLeave
调用中回收,因此不要将其存储在回调之外并使用它。如果需要存储包含的值,请制作深副本,例如:ptr(retval.toString())
。
对象包含一些有用的属性:
returnAddress
:返回地址作,类型为指针context
:是 NativePointer对象,其他处理器特定的键也可用,例如eax、rax、r0、x0等。也可以通过分配给这些键来更新寄存器值。errno
:(UNIX)当前errno值(您可以替换它)lastError
:(Windows)当前操作系统错误值(可以替换)threadId
:操作系统线程IDdepth
:相对于其他调用的调用深度
示例:
1 | function frida_Java() { |
执行结果:
1 | 执行createHello函数 |
2 Interceptor.detachAll
取消所有之前附加的回调函数。
3 Interceptor.replace(target, replacement[, data])
替换掉原本的函数。
1 | function frida_Interceptor() { |
三、NativePointer对象的api
1 new NativePointer(s)
声明定义一个NativePointer,以十进制或十六进制(以’0x’为前缀)方式在内存地址中以字符串形式创建一个新的NativePointer。为了简洁起见,可以使用简写形式ptr(s)
。
1 | function frida_NativePointer(){ |
执行结果如下:
1 | ptr1------>0x3e8 |
2 运算符以及指针读写API
总结:
2.1 isNull()
检查是否为空
1 | function frida_NativePointer(){ |
执行结果
1 | readAddr---> |
2.2 readPointer()
从指定内存位置读取NativePointer
1 | function frida_NativePointer(){ |
2.3 writePointer(ptr)
1 | function frida_NativePointer(){ |
执行结果:
1 | baseAddr--->0xe5be0000 |
2.4 readS32()/readU32()等等
从该内存位置读取有符号或无符号8/16/32/等浮点数/双精度值,并将其作为数字返回。此处以32位为例。
1 | function frida_NativePointer(){ |
执行结果:
1 | baseAddr--->0xe5be0000 |
2.5 writeS32()、writeU32()等等
将有符号或无符号8/16/32/等或浮点数/双精度值写入此内存位置。
1 | function frida_NativePointer(){ |
执行结果:
1 | baseAddr--->0xe5be0000 |
2.6 readByteArray(length))、writeByteArray(bytes)
readByteArray(length))连续读取内存length个字节,writeByteArray连续写入内存bytes。
1 | function frida_nativePointer() { |
执行结果:
1 | 写入数据----->108,105,110,103,119,117 |
2.7 readCString([size = -1])
readCString功能是读取指针地址位置的字节字符串。
1 | function frida_nativePointer() { |
执行结果:
1 | 写入数据----->108,105,110,103,119,117 |
2.8 writeUtf8String(str)
写入指针地址位置的字符串处
1 | function frida_nativePointer() { |
执行结果:
1 | 写入数据----->108,105,110,103,119,117 |
四、NativeFunction对象的api
用法:new NativeFunction(address, returnType, argTypes[, abi])
创建新的NativeFunction以调用address处的函数(用NativePointer指定),其中rereturn Type指定返回类型,argTypes数组指定参数类型。如果不是系统默认值,还可以选择指定ABI。对于可变函数,添加一个‘.’固定参数和可变参数之间的argTypes条目
官方文档用例:
1 | //创建friendlyFunctionPtr地址的函数 |
returnType和argTypes[,]分别可以填void、pointer、int、uint、long、ulong、char、uchar、float、double、int8、uint8、int16、uint16、int32、uint32、int64、uint64这些类型,根据函数的所需要的type来定义即可。
注意:
- 在定义的时候必须要将参数类型个数和参数类型以及返回值完全匹配,假设有三个参数都是int,则new NativeFunction(address, returnType, [‘int’, ‘int’, ‘int’]),而返回值是int则new NativeFunction(address, ‘int’, argTypes[, options]),必须要全部匹配
- 第一个参数一定要是函数地址指针。
五、NativeCallback对象的api
用法:new NativeCallback(func,returnType,argTypes[,ABI])
:创建一个由JavaScript函数func实现的新NativeCallback,其中rereturn Type指定返回类型,argTypes数组指定参数类型。
1 | function frida_Java() { |