frida-RPC
一、前提
- 手机连接电脑
- 手机端启动frida-server
- 手机打开app
- 电脑上进行端口转发+运行脚本
二、脚本示例
-
普通脚本
import frida rdev = frida.get_remote_device() session = rdev.attach("app名") scr = """ rpc.exports = { xx:function(j2,str,j3){ var res; Java.perform(function () { // 包.类 var Crypt = Java.use("com.yoloho.libcore.util.Crypt"); // 类中的方法 res = Crypt.encrypt_data(j2,str,j3); }); return res; } } """ script = session.create_script(scr) script.load() # 调用 sign = script.exports.xx(0, "3806f26c10f2109a6b368470c4b52389a8a45024user/login18630088888GGjp1eHdaK4e22QpCp0kfg==", 85) print(sign)
-
flask服务 + frida-RPC
import frida from flask import Flask, request app = Flask(__name__) rdev = frida.get_remote_device() session = rdev.attach("app名") scr = """ rpc.exports = { encrypt:function(j2,str,j3){ var res; Java.perform(function () { var Crypt = Java.use("com.yoloho.libcore.util.Crypt"); res = Crypt.encrypt_data(j2,str,j3); }); return res; } } """ script = session.create_script(scr) script.load() @app.route('/getSign', methods=['GET', 'POST']) def getSign(): arg1 = int(request.values['arg1']) arg2 = request.values['arg2'] arg3 = int(request.values['arg3']) result = script.exports.encrypt(arg1, arg2, arg3) return result if __name__ == '__main__': app.run()
三、参数类型转换及传参
-
基础概念
- python和Javascript之间:通过json进行通信
- Javascript和java之间:通过frida Api
-
可直接传递的参数类型
- 字符串
- 整型
- 浮点型
- 列表
- 字典
import frida rdev = frida.get_remote_device() session = rdev.attach("app名") scr = """ rpc.exports = { encrypt:function(v1,v2,v3,v4,v5){ // 1、字符串 console.log(v1,typeof v1); // 2、整型 console.log(v2,typeof v2); // 3、浮点型 console.log(typeof v3, v3, parseInt(v3)); // 4、数组 console.log(typeof v4, v4[0], v4[1]); // 5、对象 console.log(typeof v5, v5.name, v5.age); } } """ script = session.create_script(scr) script.load() # 调用 script.exports.encrypt('-1', 100, 19.2, [11,22,33], {"name":"张三", "age":30})
-
可间接传递的类型
- 字节:
- 需要由python先转换为列表,由frida api将其转成字节数组
- 某个类对象:
- java中加密函数,参数是某个java自己实现的对象
- 可以通过参数的形式进行传递,然后在Javascript中通过frida api来构造对象
import frida rdev = frida.get_remote_device() session = rdev.attach("大姨妈") # com.yoloho.dayima scr = """ rpc.exports = { encrypt:function(v1, v2, v3, v4){ // 1、数组转换为java的字节数组 var bArr = Java.array('byte',v1); console.log(JSON.stringify(bArr)) // 2、根据参数,构造StringBuilder对象 const StringBuilder = Java.use('java.lang.StringBuilder');; var obj = StringBuilder.$new(); // 实例化对象,java中:obj = new StringBuilder() obj.append(v2);//obj.append(v2) obj.append(v3);//obj.append(v3) obj.append(v4);//obj.append(v4) var res; Java.perform(function () { var Crypt = Java.use("com.yoloho.libcore.util.Crypt"); res = Crypt.encrypt_data(0 ,obj, 85); console.log(res) }); } } """ script = session.create_script(scr) script.load() # 调用 # 1、字节转列表 arg_bytes = 'hello world'.encode('utf-8') arry = list(arg_bytes) # [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100] script.exports.encrypt(arry, '张三', '男', '工程师')
- 字节:
四、开发安卓应用供frida-RPC调用
-
1、创建空项目
- 选择Empty Activity或者Empyt Views Activity,语言一定要是java
-
2、拷贝so到指定目录,比如app/libs
- 将app的apk改名.zip然后解压,将lib目录下的armeabi-v7a和arm64-v8a文件夹,内部均删除其它so文件,只剩下一个so文件:libCrypt.so
- 然后将armeabi-v7a和arm64-v8a文件夹复制到项目app/libs目录下
-
3、编写项目中build.gradle配置文件
- 在android节点内部结尾,添上以下配置
sourceSets { main { jniLibs.srcDirs = ['libs'] } }
- 在android节点内部结尾,添上以下配置
-
4、项目中新建native类(copy原有类),比如
package com.yoloho.libcore.util; public class Crypt { static { System.loadLibrary("Crypt"); } public static native String encrypt_data(long j2, String str, long j3); }
在src-main-java目录下新建类:com.yoloho.libcore.util.Crypt
-
5、app调用
- 在MainActivity类中进行调用(实际上,import相关类即可,目的是加载相关类到内存中,供后续直接调用)
package com.example.dym; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import com.yoloho.libcore.util.Crypt; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 调用 String sign = Crypt.encrypt_data(0, "64e6176e45397c5989504e76f98ecf2e63b2679euser/login15131255555A4rE0CKaCsUMlKpHjtL0AQ==", 85); Log.e("sign=", sign); } }
- 在MainActivity类中进行调用(实际上,import相关类即可,目的是加载相关类到内存中,供后续直接调用)
-
6、扩展
- 如果so中方法调用过程中用到其它包中的类,则需要补上相关类和包
- 可配合flask API实现更便捷的调用
- 如果so中方法调用过程中用到其它包中的类,则需要补上相关类和包