frida使用与常见api(二)

一、Java对象的api

1 Java.available

一个布尔值,指定当前进程是否已加载Java VM,即Dalvik或ART。Java 除非是这种情况,否则请勿调用任何其他属性或方法。

1
2
3
4
5
6
7
8
9
10
11
function frida_Java(){
Java.perform(function(){
if (Java.available){
//注入的逻辑代码
console.log("已加载jvm")
}else{
console.log("未加载jvm")
}
});
}
setImmediate(frida_Java,0)

2 Java.androidVersion

返回安卓的版本号

1
2
[Nexus 5X::com.android.settings]-> Java.androidVersion
"7.1.2"

3 枚举加载所有的类

3.1 Java.enumerateLoadedClasses(callbacks)

枚举加载的所有的类,其中callbacks的对象为:

  • onMatch(name, handle):为每个加载的类调用name,方法可能会传递给,use()以获取JavaScript包装器。还可以Java.cast()handlejava.lang.Class
  • onComplete():在枚举所有类后调用。

注意:与Java.enumerateClassLoaders(callbacks)区分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function frida_Java(){
Java.perform(function(){
//枚举当前加载的所有类
Java.enumerateLoadedClasses({
//每一次回调此函数时其参数className就是类的信息
onMatch:function(className){
console.log(className)
},
onComplete:function(){
console.log("输出完毕")
}
});
});
}
setImmediate(frida_Java,0);

执行结果:

1
2
3
4
5
6
org.chromium.android_webview.AwContentsLifecycleNotifier
org.chromium.content.browser.input.CursorAnchorInfoController$ViewDelegate
org.chromium.android_webview.AwSettings$EventHandler$1
org.chromium.content.browser.accessibility.captioning.KitKatCaptioningBridge$KitKatCaptioningChangeListener
org.chromium.content.browser.accessibility.captioning.CaptioningChangeDelegate$ClosedCaptionFont
输出完毕

3.2 Java.enumerateLoadedClassesSync()

和3.1一样,枚举加载的所有的类,但返回的是一个数组。

1
2
3
4
5
6
7
8
9
10
function frida_Java(){
Java.perform(function(){
//枚举当前加载的所有类
var className=Java.enumerateLoadedClassesSync()
for (var i=0;i<className.length;i++){
console.log(className[i])
}
});
}
setImmediate(frida_Java,0);

4 Java.enumerateClassLoaders(callbacks)

枚举中存在的类加载器,其中callbacks的对象为:

  • onMatch(loader):匹配类加载器。
  • onComplete():在枚举所有类加载器时调用。

注意:与Java.enumerateLoadedClasses(callbacks)区分

与枚举所有的类用法基本一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function frida_Java(){
Java.perform(function(){
//枚举所有的类加载器
var className=Java.enumerateClassLoaders({
onMatch:function(classLoaders){
console.log(classLoaders)
},
onComplete:function(){
console.log("枚举完毕")
}
})

});
}
setImmediate(frida_Java,0);

执行结果:

1
2
3
4
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/mobi.acpm.inspeckage-1/base.apk"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64]]]
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.tangxy.myapplication-1/base.apk"],nativeLibraryDirectories=[/data/app/com.tangxy.myapplication-1/lib/arm64, /system/lib64, /vendor/lib64]]]
dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/Chrome/Chrome.apk"],nativeLibraryDirectories=[/system/app/Chrome/lib/arm64, /system/app/Chrome/Chrome.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]]
枚举完毕

5 Java.enumerateMethods(query):

模糊搜索方法,用法 Java.enumerateMethods("*com*!*rSa*/i/u"),可以带有/和一个或多个修饰符后缀:

  • i:不区分大小写的匹配。
  • s:包括方法签名。
  • u:仅用户定义的类,忽略系统类。
1
2
3
4
5
6
7
8
9
function frida_Java(){
Java.perform(function(){
//枚举所有匹配的方法
var className=Java.enumerateMethods("*com*!*rSa*/i/u")
//转化为json值,并格式化输出。
console.log(JSON.stringify(className,null,2));
});
}
setImmediate(frida_Java,0);

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[
{
"loader": null,
"classes": [
{
"name": "com.android.org.conscrypt.OpenSSLProvider",
"methods": [
"putRAWRSASignatureImplClass",
"putRSACipherImplClass"
]
},
{
"name": "com.android.org.conscrypt.CryptoUpcalls",
"methods": [
"rsaDecryptWithPrivateKey"
]
},
{
"name": "com.android.org.conscrypt.NativeCrypto",
"methods": [
"EVP_PKEY_CTX_set_rsa_mgf1_md",
"EVP_PKEY_CTX_set_rsa_padding",
"EVP_PKEY_CTX_set_rsa_pss_saltlen",
"EVP_PKEY_new_RSA",
"RSA_generate_key_ex",
"RSA_private_decrypt",
"RSA_private_encrypt",
"RSA_public_decrypt",
"RSA_public_encrypt",
"RSA_size",
"getRSAPrivateKeyWrapper",
"get_RSA_private_params",
"get_RSA_public_params",
"i2d_RSAPrivateKey",
"i2d_RSAPublicKey"
]
}
]
}
]

6 Java.perform

确保当前线程已连接到VM并调用fn方法,基本每个脚本都会用。

7 Java.use

动态获取一个 类名,可以通过调用$new()来调用构造函数以实例化对象。当想要回收类时可以调用$Dispose()方法显式释放(或等待JavaScript对象进行垃圾回收,或等待脚本卸载)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function frida_Java(){
Java.perform(function(){
//获取类
var className=Java.use('cn.com.test.EncryptUtil');
//拦截className类的encrypt_aes方法
className.encrypt_aes.implementation=function(str1,str2){
var data=this.encrypt_aes(str1,str2)
send('str1--->'+str1);
send('str2--->'+str2);
return data;
}
});
}
setImmediate(frida_Java,0);

8 Java.choose

通过扫描Java堆来枚举该类的实例 ,其中callbacks一个对象指定:

  • onMatch(instance):使用找到的每个可用实例进行调用,instance就好像可以使用Java.cast()该特定实例的原始句柄进行调用一样 。此函数可能返回字符串stop以提早取消枚举。
  • onComplete():在列举所有实例后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function frida_Java(){
Java.perform(function(){
//查找目标类在堆上的实例化对象
Java.choose('android.view.View',{
onMatch:function(instance){
console.log(instance)
},
onComplete:function(){
console.log("枚举完毕")
}
});
});
}
setImmediate(frida_Java,0);

执行结果如下:

1
2
3
4
android.view.View{d6a0de6 V.ED..... ......ID 0,0-1080,63 #102002f android:id/statusBarBackground}
android.view.View{c482f27 V.ED..... ......ID 0,1794-1080,1920 #1020030 android:id/navigationBarBackground}
android.view.View{84425d4 V.ED..... ......ID 0,0-1080,63 #102002f android:id/statusBarBackground}
枚举完毕

9 Java.cast

类型转换器,将指定变量或者数据强制转换成你所有需要的类型;创建一个 JavaScript 包装器,给定从 Java.use() 返回的给定类klass的句柄的现有实例。此类包装器还具有用于获取其类的包装器的类属性,以及用于获取其类名的字符串表示的$className属性,通常在拦截so层时会使用此函数将jstring、jarray等等转换之后查看其值。

官方示例:

1
2
const Activity = Java.use('android.app.Activity');
const activity = Java.cast(ptr('0x1234'), Activity);

10 Java.array

定义java数组的api,格式为Java.array('type',[value1,value2,....]);,生成的Java数组的行为类似于JS数组,但是可以通过引用传递给Java API,以允许它们修改其内容。

1
2
3
4
5
6
7
8
9
10
11
function frida_Java(){
Java.perform(function(){
//定义一个int数组
var intArr=Java.array('int',[1,2,3,4,5]);
console.log("-----intArr-------")
for(var j=0;j<intArr.length;j++){
console.log(intArr[j])
}
});
}
setImmediate(frida_Java,0);

支持的array类型

11 Java.registerClass(spec)

创建一个新的Java类,其中spec的对象包含:

  • name:字符串,指定类的名称。
  • superClassjava.lang.Object:(可选)父类。忽略继承。
  • implements:(可选)由此类实现的接口数组。
  • fields:(可选)指定要公开的每个字段的名称和类型的对象。
  • methods:(可选)指定要实现对象的方法。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function frida_Java(){
Java.perform(function(){
var registerClass=Java.registerClass({
//注册一个目标进程中的类,返回的是一个类对象
name:'cn.com.lingwu.test',
methods:{
add:function(){
console.log('add方法');
}
}
});
console.log(registerClass.$className);
});
}
setImmediate(frida_Java,0);

官方文档的例子是绕过证书校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//获取目标进程的SomeBaseClass类
var SomeBaseClass = Java.use('com.example.SomeBaseClass');
//获取目标进程的X509TrustManager类
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');

//注册一个新的java类
var MyWeirdTrustManager = Java.registerClass({
//注册一个MyWeirdTrustManager类
name: 'com.example.MyWeirdTrustManager',
//父类是SomeBaseClass类
superClass: SomeBaseClass,
//实现了MyWeirdTrustManager接口类
implements: [X509TrustManager],
//类中的属性
fields: {
description: 'java.lang.String',
limit: 'int',
},
//定义的方法
methods: {
//类的构造函数
$init: function () {
console.log('Constructor called');
},
//X509TrustManager接口中方法之一,该方法作用是检查客户端的证书
checkClientTrusted: function (chain, authType) {
console.log('checkClientTrusted');
},
//该方法检查服务器的证书,不信任时。在这里通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
checkServerTrusted: [{
//返回值类型
returnType: 'void',
//参数列表
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String'],
//实现方法
implementation: function (chain, authType) {
//输出
console.log('checkServerTrusted A');
}
}, {
returnType: 'java.util.List',
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'],
implementation: function (chain, authType, host) {
console.log('checkServerTrusted B');
//返回null会信任所有证书
return null;
}
}],
// 返回受信任的X509证书数组。
getAcceptedIssuers: function () {
console.log('getAcceptedIssuers');
return [];
},
}
});

12 Java.vm

常用api,情景:想要拿到JNI层的JNIEnv对象,可以使用getEnv()。

  • perform(fn):确保当前线程已连接到VM并调用fn。(在Java回调中这不是必需的。)
  • getEnv():获取当前线程的包装器JNIEnv。如果当前线程未附加到VM,则引发异常。
  • tryGetEnv():尝试获取当前线程的包装器JNIEnv。如果当前线程没有连接到虚拟机返回null。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function frida_Java() {
Java.perform(function () {
//直接获取libxposed_art.so中的导出函数的地址,对目标函数进行附加拦截
var ExportName = Module.findExportByName('libxposed_art.so', '_ZN6xposed36methodXposedBridgeHandleHookedMethodE')
console.log(ExportName)
Interceptor.attach(ExportName, {
onEnter: function (args) {
console.log("getStr");
},
onLeave: function (retval) {
//它的返回值的是retval 在jni层getStr的返回值的jstring
//先获取一个Env对象
var env = Java.vm.tryGetEnv();
var jstr = env.newStringUtf("lingwu"); //主动调用jni函数 cstr转jstr
retval.replace(jstr);
var cstr = env.getStringUtfChars(jstr); //主动调用 jstr转cstr
console.log(cstr.readCString());
console.log(hexdump(cstr));
}
});
});
}
setImmediate(frida_Java, 0);

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:操作系统线程ID
  • depth:相对于其他调用的调用深度

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function frida_Java() {
Java.perform(function () {
//拦截createHello函数
//使用Module对象getExportByNameAPI直接获取libhello.so中的导出函数createHello的地址,对createHello函数进行附加拦截
var ExportName = Module.getExportByName('libhello.so', 'createHello')
console.log("createHello函数的基址--->"+ExportName)
Interceptor.attach(ExportName, {
//每次执行createhello函数的时候,都会执行onEnter回调函数
onEnter: function (args) {
console.log("执行createHello函数");
//输出
console.log('Context information:');
//输出上下文因其是一个Objection对象,需要它进行接送、转换才能正常看到值
console.log('Context : ' + JSON.stringify(this.context));
//输出返回地址
console.log('Return : ' + this.returnAddress);
//输出线程id
console.log('ThreadId : ' + this.threadId);
console.log('Depth : ' + this.depth);
console.log('Errornr : ' + this.err);
},
//creageHello函数执行完成之后会执行onLeave回调函数
onLeave: function (retval) {
console.log("执行createHello函数完成")
}
});
});
}
setImmediate(frida_Java, 0);

执行结果:

1
2
3
4
5
6
7
8
执行createHello函数
Context information:
Context : {"pc":"0xe59def1c","sp":"0xffc6f528","r0":"0xc","r1":"0x0","r2":"0x4f891297","r3":"0x3","r4":"0xd7f7c17c","r5":"0xffc6f8b0","r6":"0xffc6f660","r7":"0xffc6f558","r8":"0xffc6f8b0","r9":"0xe6505400","r10":"0xe650548c","r11":"0xffc6f5ec","r12":"0xe59e1f94","lr":"0xe59def91"}
Return : 0xe59def91
ThreadId : 17280
Depth : 0
Errornr : undefined
执行createHello函数完成

2 Interceptor.detachAll

取消所有之前附加的回调函数。

3 Interceptor.replace(target, replacement[, data])

替换掉原本的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function frida_Interceptor() {
Java.perform(function () {
//这个c_getSum方法有两个int参数、返回结果为两个参数相加
//这里用NativeFunction函数自己定义了一个getSum函数
var add_method = new NativeFunction(Module.findExportByName('libhello.so', 'getSum'),
'int',['int','int']);
//输出结果: 3
console.log("result:",add_method(1,2));
//这里对原函数的功能进行替换实现
Interceptor.replace(add_method, new NativeCallback(function (a, b) {
//不论传入什么参数,都返回123
return 123;
}, 'int', ['int', 'int']));
//再次调用 则返回123
console.log("result:",add_method(1,2));
});
}

三、NativePointer对象的api

1 new NativePointer(s)

声明定义一个NativePointer,以十进制或十六进制(以’0x’为前缀)方式在内存地址中以字符串形式创建一个新的NativePointer。为了简洁起见,可以使用简写形式ptr(s)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function frida_NativePointer(){
Java.perform(function(){
//第一种方式传入十进制,得到0x3e8
//此处的十进制字符型和整形都可以
var ptr1=new NativePointer('1000');
console.log("ptr1------>"+ptr1);
var ptr2=new NativePointer(1000);
console.log("ptr2------>"+ptr2);
//第二种方式传入十六进制,同样得到0x3e8
var ptr3=new NativePointer('0x3e8');
console.log("ptr3------>"+ptr3);
//简写形式
var ptr4=ptr('1000');
console.log("ptr4------>"+ptr4);
});
}
setImmediate(frida_NativePointer,0)

执行结果如下:

1
2
3
ptr1------>0x3e8
ptr2------>0x3e8
ptr3------>0x3e8

2 运算符以及指针读写API

总结:

2.1 isNull()

检查是否为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function frida_NativePointer(){
Java.perform(function(){
//获取libhello.so的基址
var pointerAddr=Process.findModuleByName("libhello.so").base;
console.log("baseAddr--->"+pointerAddr)
//读取从pointerAddr地址开始的0x20个字节
var readAddr=pointerAddr.readByteArray(0x20);
console.log("readAddr--->");
console.log(hexdump(readAddr));

// 索引9.isNull函数
var isNull=pointerAddr.isNull();
console.log("isNull函数---->"+isNull);
});
}
setImmediate(frida_NativePointer,0)

执行结果

1
2
3
4
5
readAddr--->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4...
isNull函数---->false

2.2 readPointer()

从指定内存位置读取NativePointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function frida_NativePointer(){
Java.perform(function(){
//获取libhello.so的基址
var pointerAddr=Process.findModuleByName("libhello.so").base;
console.log("baseAddr--->"+pointerAddr)
//读取从pointerAddr地址开始的0x20个字节
var readAddr=pointerAddr.readByteArray(0x20);
console.log("readAddr--->");
console.log(hexdump(readAddr));

// 索引11.readPointer函数
var readPointer=pointerAddr.readPointer();
console.log("readPointer函数----->"+readPointer);

});
}
setImmediate(frida_NativePointer,0)

2.3 writePointer(ptr)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function frida_NativePointer(){
Java.perform(function(){
//获取libhello.so的基址
var pointerAddr=Process.findModuleByName("libhello.so").base;
console.log("baseAddr--->"+pointerAddr)
//读取从pointerAddr地址开始的0x20个字节
var readAddr=pointerAddr.readByteArray(0x20);
console.log("readAddr--->");
console.log(readAddr);

// 索引12.writePointer函数
var addr= Memory.alloc(4);
addr.writePointer(pointerAddr);
var buffer=addr.readByteArray(0x20);
console.log("writePointer函数--->");
console.log(buffer);
});
}
setImmediate(frida_NativePointer,0)

执行结果:

1
2
3
4
5
6
7
8
9
baseAddr--->0xe5be0000
readAddr--->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4...
writePointer函数--->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 00 00 be e5 40 00 ed de 10 00 00 00 3b 00 00 00 ....@.......;...
00000010 02 00 00 00 00 01 3d 00 18 cb 7a c7 78 1e 10 c7 ......=...z.x...

2.4 readS32()/readU32()等等

从该内存位置读取有符号或无符号8/16/32/等浮点数/双精度值,并将其作为数字返回。此处以32位为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function frida_NativePointer(){
Java.perform(function(){
//获取libhello.so的基址
var pointerAddr=Process.findModuleByName("libhello.so").base;
console.log("baseAddr--->"+pointerAddr)
//读取从pointerAddr地址开始的0x20个字节
var readAddr=pointerAddr.readByteArray(0x20);
console.log("readAddr--->");
console.log(readAddr);

// 索引13.readS32函数
var readS32=pointerAddr.readS32();
console.log("readS32函数--->"+readS32);
// 索引13.readU32函数
var readU32=pointerAddr.readU32();
console.log("readU32函数--->"+readU32);
});
}
setImmediate(frida_NativePointer,0)

执行结果:

1
2
3
4
5
6
7
baseAddr--->0xe5be0000
readAddr--->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4...
readS32函数--->1179403647
readU32函数--->1179403647

2.5 writeS32()、writeU32()等等

将有符号或无符号8/16/32/等或浮点数/双精度值写入此内存位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function frida_NativePointer(){
Java.perform(function(){
//获取libhello.so的基址
var pointerAddr=Process.findModuleByName("libhello.so").base;
console.log("baseAddr--->"+pointerAddr)
//读取从pointerAddr地址开始的0x20个字节
var readAddr=pointerAddr.readByteArray(0x20);
console.log("readAddr--->");
console.log(readAddr);

var r1= Memory.alloc(4);
// 索引14.writeS32函数
//将0x12345678写入r1地址中
var writeS32=r1.writeS32(0x12345678);
console.log("writeS32函数--->"+writeS32);

var r2= Memory.alloc(4);
// 索引14.writeU32函数
//将0x12345678写入r2地址中
var writeU32=r2.writeU32(0x12345678);
console.log("writeU32函数--->"+writeU32);
});
}
setImmediate(frida_NativePointer,0)

执行结果:

1
2
3
4
5
6
7
baseAddr--->0xe5be0000
readAddr--->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4...
writeS32函数--->0xc77bb8a8
writeU32函数--->0xc77bba50

2.6 readByteArray(length))、writeByteArray(bytes)

readByteArray(length))连续读取内存length个字节,writeByteArray连续写入内存bytes。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function frida_nativePointer() {     
Java.perform(function () {

// 索引18.writeByteArray函数
//定义需要写入的字节数组
var arr = [ 0x6c, 0x69, 0x6e, 0x67, 0x77, 0x75];
//申请一个新的内存空间 返回指针 大小是arr.length
const r = Memory.alloc(arr.length);
console.log("写入数据----->"+ arr)
//将arr数组写入R地址中
var data =Memory.writeByteArray(r,arr);

//索引17 读取从pointerAddr地址开始的0x20个字节
var buffer = Memory.readByteArray(r, arr.length);
console.log("读取数据----->");
console.log(hexdump(buffer, {
offset: 0,
length: arr.length,
header: true,
ansi: false
}));
});
}
setImmediate(frida_nativePointer,0);

执行结果:

1
2
3
4
写入数据----->108,105,110,103,119,117
读取数据----->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 6c 69 6e 67 77 75 lingwu

2.7 readCString([size = -1])

readCString功能是读取指针地址位置的字节字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function frida_nativePointer() {     
Java.perform(function () {

// 索引18.writeByteArray函数
//定义需要写入的字节数组
var arr = [ 0x6c, 0x69, 0x6e, 0x67, 0x77, 0x75];
//申请一个新的内存空间 返回指针 大小是arr.length
const r = Memory.alloc(arr.length);
console.log("写入数据----->"+ arr)
//将arr数组写入R地址中
var data =Memory.writeByteArray(r,arr);

//索引19 readCString函数。
//在这里直接使用readCString读取会把上面的'lingwu'字符串读取出来
console.log(r.readCString());
});
}
setImmediate(frida_nativePointer,0);

执行结果:

1
2
3
4
5
写入数据----->108,105,110,103,119,117
读取数据----->
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 6c 69 6e 67 77 75 lingwu
lingwuz�i

2.8 writeUtf8String(str)

写入指针地址位置的字符串处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function frida_nativePointer() {     
Java.perform(function () {

// 索引18.writeByteArray函数
//定义需要写入的字节数组
var arr = [ 0x6c, 0x69, 0x6e, 0x67, 0x77, 0x75];
//申请一个新的内存空间 返回指针 大小是arr.length
const r = Memory.alloc(arr.length);
console.log("写入数据----->"+ arr)
//将arr数组写入R地址中
var data =Memory.writeByteArray(r,arr);

//索引19 readCString函数。
//在这里直接使用readCString读取会把上面的'lingwu'字符串读取出来
console.log("readCString函数---->"+r.readCString());

//索引20 这里是写入字符串 也就是在字符串"lingwu"起始位置开始被替换为目标字符串'wuling'
var newStr=r.writeUtf8String("wuling");
console.log("writeUtf8String函数---->"+newStr.readCString());
});
}
setImmediate(frida_nativePointer,0);

执行结果:

1
2
3
写入数据----->108,105,110,103,119,117
readCString函数---->lingwuz�i
writeUtf8String函数---->wuling

四、NativeFunction对象的api

用法:new NativeFunction(address, returnType, argTypes[, abi])

创建新的NativeFunction以调用address处的函数(用NativePointer指定),其中rereturn Type指定返回类型,argTypes数组指定参数类型。如果不是系统默认值,还可以选择指定ABI。对于可变函数,添加一个‘.’固定参数和可变参数之间的argTypes条目

官方文档用例:

1
2
3
4
5
6
7
//创建friendlyFunctionPtr地址的函数
var friendlyFunctionName = new NativeFunction(friendlyFunctionPtr,
'void', ['pointer', 'pointer']);
//申请内存空间
var returnValue = Memory.alloc(sizeOfLargeObject);
//调用friendlyFunctionName函数
friendlyFunctionName(returnValue, thisPtr);

returnType和argTypes[,]分别可以填void、pointer、int、uint、long、ulong、char、uchar、float、double、int8、uint8、int16、uint16、int32、uint32、int64、uint64这些类型,根据函数的所需要的type来定义即可。

注意:

  1. 在定义的时候必须要将参数类型个数和参数类型以及返回值完全匹配,假设有三个参数都是int,则new NativeFunction(address, returnType, [‘int’, ‘int’, ‘int’]),而返回值是int则new NativeFunction(address, ‘int’, argTypes[, options]),必须要全部匹配
  2. 第一个参数一定要是函数地址指针。

五、NativeCallback对象的api

用法:new NativeCallback(func,returnType,argTypes[,ABI]):创建一个由JavaScript函数func实现的新NativeCallback,其中rereturn Type指定返回类型,argTypes数组指定参数类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function frida_Java() {
Java.perform(function () {
var ExportName = Module.findExportByName('libhello.so', 'createHello')
console.log("createHello函数的基址--->"+ExportName)
var newMethod=new NativeFunction(ExportName,'char',[]);
console.log("结果1---->"+newMethod());

//在这里new一个新的函数,但是参数的个数和返回值必须对应
Interceptor.replace(newMethod,new NativeCallback(function(){
console.log("newMethod已被替换");
return "111111111";
},'char',[]));
console.log("结果2---->"+newMethod());
});
}
setImmediate(frida_Java, 0);