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']
          }
      }
  • 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);
          }
      }
  • 6、扩展

    • 如果so中方法调用过程中用到其它包中的类,则需要补上相关类和包
      • 可配合flask API实现更便捷的调用
posted @ 2023-10-28 03:05  eliwang  阅读(458)  评论(0编辑  收藏  举报