开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 30672|回复: 2
收起左侧

[android教程] frida的使用总结

[复制链接]
发表于 2021-2-26 09:50:42 | 显示全部楼层 |阅读模式   陕西省西安市
1 对多dex进行适配先hook attach函数,可以解决多dex文件找不到类名的错误。对内存中有完整dex的加壳app也可以hook。










Java.perform(function(){

     var application = Java.use("android.app.Application");
     application.attach.overload('android.content.Context').implementation = function(context) {
  var result = this.attach(context); // 先执行原来的attach方法
  var classloader = context.getClassLoader(); // 获取classloader
  Java.classFactory.loader = classloader;
  var Utils = Java.classFactory.use("com.rytong.emp.tool.Utils");\
                //do something
              return result;

     }
}











2 JAVA层hook2.1 hook构造函数hook构造方法是固定写法:$init,代码如下:










var message = Java.use("com.ydscience.fridaandroidstudydemo.utils.Message");
message.$init.implementation =  function(hash,aes,base){
   console.log("hash "+hash+" aes "+aes+" base "+base);
   this.$init("123","eqweq",base);
  };











如何构造方法有多个重载方法,则使用overload(……)形式即可,参数和参数之间用逗号隔开即可,代码如下;










var message = Java.use("com.ydscience.fridaandroidstudydemo.utils.Message");
  message.$init.overload("java.lang.String","java.lang.String"
,"java.lang.String").implementation = function(hash,aes,base){
   console.log("hash "+hash+" aes "+aes+" base "+base);
   this.$init("123","eqweq",base);
  };











修改构造方法的参数及返回值










//修改参数:
this.$init("123","eqweq","tsts");
//修改返回值
this.$init(hash,aes,base)+"test";











2.2 hook普通方法在Xposed中,对于静态方法和非静态方法使用不同的hook函数,在frida中不做区分,hook函数相同。
代码如下:










var myCrypto = Java.use("com.ydscience.fridaandroidstudydemo.utils.MyCrypto");
myCrypto.md5Base64.implementation = function(){
   console.log("msg2 is "+arguments[0]);
   return this.md5Base64(arguments[0]+"test");
  };











如果有多个重载方法,使用overload(……)形式即可,代码如下:










     var myCrypto = Java.use("com.ydscience.fridaandroidstudydemo.utils.MyCrypto");
  myCrypto.md5Base64.overload("java.lang.String")implementation = function(msg){
   console.log("msg1 is "+msg);
   return this.md5Base64(msg+"test");
  };











修改方法的参数以及返回值和修改构造方法的相同
2.3 构造和修改自定义类型对象和属性在hook的时会遇到参数或返回值不是基本类型而是自定义类型,此时如果想修改他的属性值或者调用他的
一个方法我们会使用反射来进行操作,而在返回值的时候,想构造一个自定义类型的对象也是直接用反射实例化
一个对象进行操作的。
构造返回值自定义对象,代码如下:










//构造一个实例对象固定写法为:$new,也可以使用call形式
var coinmoney = Java.use("com.ydscience.fridaandroidstudydemo.utils.CoinMoney");
  var utils = Java.use("com.ydscience.fridaandroidstudydemo.utils.Utils");
  utils.getCoin.implementation = function(){
   var coinObj = coinmoney.$new(12,"yj");//直接构造
   //使用call构造
   //var coinObj = coinmoney.$new.overload("int","java.lang.String")
            //.call(coinmoney,12,"yiyi");
   coinObj.setMoney(24);
   console.log("money set is "+coinObj.getMoney());
   return coinObj;
};











构造自定义对象后,调用方法直接调用其对应的方法即可,而修改自定义对象的属性,只能通过反射去修改字段值。代码如下:










  var clazz = Java.use("java.lang.Class");
  utils.getCoinMoney.implementation = function(){
   var coin =  arguments[0];
   //调用方法
   var money = coin.getMoney();
   console.log("money is "+money);
   //修改属性
   var moneyFiled = Java.cast(coin.getClass(),clazz).getDeclaredField("money");
   moneyFiled.setAccessible(true);
   console.log("money field is "+moneyFiled.get(coin));
   moneyFiled.setInt(coin,32);
   return coin.getMoney();
  };











静态成员变量的修改(私有变量也同样修改)
代码如下:










var clazz = Java.use("java.lang.Class");
   var myLog = Java.use("com.ydscience.fridaandroidstudydemo.utils.MyLog");
   var intance =  myLog.$new();
   var moneyFiled = Java.cast(intance.getClass(),clazz).getDeclaredField("IS_DEBUG");
      moneyFiled.setAccessible(true);
   console.log("IS_DEBUG modify before is "+moneyFiled.get(intance));
   moneyFiled.setBoolean(intance,true);
   console.log("IS_DEBUG moidify after is "+moneyFiled.get(intance));











更加通用的方式
hook已经加载到内存中的类的一个方法,从中获取this指针,该指针即为当前的对象,调用代码如下:










var main = Java.use("seclover.crackme.MainActivity");
main.getInfo.implementation = function(){
//通过该变量的value去赋值
  this.id.value = 1;
  return this.getInfo();
}











2.4 hook重载函数重载函数常见的几种写法如下:
(1)应用arguments










MyClass.MyFunc.overload("java.util.List").implementation = function() {
    this.MyFunc.overload("java.util.List").apply(this, arguments);
}











(2)argments下标










//参数arguments[0]对应的真实的第一个参数
MyClass.MyFunc.overload("java.util.List").implementation = function () {
this.MyFunc(arguments[0]);
};











(3)具体的参数变量










MyClass.MyFuncs.overload("int", "int").implementation = function (s1, s2) {
    var ret = this.MyFuncs(s1, s2);
}











(4)字符串数组










hook.hookMeArray.overload("[Ljava. .String;").implementation = {}











(5)使用call方法










MyClass.MyFunc.overload("java.lang.String").implementation = {
     this.MyFunc.overload("java.lang.String").call(this, args[1])
     MyClass.MyFunc.overload("java.lang.String").call()
}











2.5 对象实例化









var coinmoney = Java.use("com.ydscience.fridaandroidstudydemo.utils.CoinMoney");
//方式一
var newhg = coinmoney .$new(11,12);
//方式二
var newhg1 = coinmoney .$new.overload(“int","int").call(coinmoney,11,12);











2.6 hook 匿名内部类匿名内部类一般在主类后面以“$ 1”数字表现。










var noNameInnerClazz = Java.use("com.ydscience.fridaandroidstudydemo.utils$1");
noNameInnerClazz.getName.overload("java.lang.String").implementation = function(s)
{
  console.log("param is "+s);
  return this.getName(s);
}











2.7 hook 内部类内部类在主类后表现形式为“$ 内部类名”










   //hook内部类构造函数
var innerClazz = Java.use("com.ydscience.fridaandroidstudydemo.utils.Message$InnerClass");
nnerClazz.$init.overload("java.lang.String").implementation = function(s){
  console.log("init  param is "+s);
  return this.$init(s);
}
//hook内部类的方法
innerClazz.setNumber.implementation =  function(num){
                console.log("num is "+num);
  //修改属性
  var number = innerClazz.class.getDeclaredField("number");
  number.setAccessible(true);
  //查看值
  console.log("value  is "+number.get(this));
  //修改方式一
  this.number.value = 122;
  //修改方式二
  number.setInt(123);
  return this.setNumber(num);
}











2.8 打印堆栈java层打印










Java.perform(function() {
        console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
    });











native层打印










var openFile = new File("/sdcard/openFile.txt","a+");
function hookOpen(){
    var openPtr = Module.findExportByName("libc.so", "open");
    var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
    Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) {
    var path = Memory.readUtf8String(pathPtr);

    var fd = open(pathPtr, flags);
    // console.log("Got fd: " + fd);

    var trace = Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n");
    if (trace.indexOf("libcrypt-lib.so") > 0){
        console.log("trace:"+trace);
        console.log("Opening '" + path + "'");

        openFile.write(path+"\n");
        openFile.flush();
    }

    return fd;
}, 'int', ['pointer', 'int']));
}











2.9 动态调用(1)静态方法
     静态方法的调用和Xposed一样,直接使用类名调用方法,代码如下:










var hexStr =  myCrypto.sha1Hex("ueyeueu");
console.log("hex is "+hexStr);












(2)非静态方法
        对于非静态方法而言,如果想调用方法,则必须找到该方法依赖于调用的类对象,即在内存中找到该方法
依赖类已经创建的实例对象,获取该实例对象后,就可以任意调用该类中的方法。案例如下:
message类代码










public class Message {
    private String hash;
    private String aes;
    private String base;

    public Message(String hash, String aes, String base) {
        this.hash = hash;
        this.aes = aes;
        this.base = base;
    }

    public String getHash() {
        return hash;
    }

    public void setHash(String hash) {
        this.hash = hash;
    }

    public String getAes() {
        return aes;
    }

    public void setAes(String aes) {
        this.aes = aes;
    }

    public String getBase() {
        return base;
    }

    public void setBase(String base) {
        this.base = base;
    }
}











在加载调用时代码如下:










Message message = new Message(MyCrypto.md5Base64(msg), MyCrypto.sha1Base64(msg)
            ,MyCrypto.Base64Encode(msg.getBytes()));
        message.setHash("ddweqwerd");











通过调用方法可以看出,可以通过构造函数和setHash方法得到Message类对象,代码如下:










//setHash方法得到类Message对象实现非静态方法的任意调用
var MainActivity =  Java.use("com.ydscience.fridaandroidstudydemo.MainActivity");
var message = Java.use("com.ydscience.fridaandroidstudydemo.utils.Message");
   message.setHash.implementation = function(){
    var hash = this.getHash();
    var aes = this.getAes();
    console.log("hash is "+hash+" aes is "+aes);
                            //通过创建MainActivity对象调用MD5方法
    var intance = MainActivity.$new();
    var md5 =  intance.md5(hash);
    console.log("md5 is "+md5);
    return this.setHash(arguments[0]);
}
//构造方法得到类Message对象实现非静态方法的任意调用
message.$init.implementation =function(){
   //todo
}











(3)Java.choose模式
使用Java.choose方法时,调用的类必须已经加载到内存,使用代码如下










setImmediate(function() {
    Java.perform(function () {
        Java.choose("com.ydscience.fridaandroidstudydemo.MainActivity", {
             "onMatch":function(instance){
                  console.log("Instance found");
                  var str = Java.use("java.lang.String");
                  var re = instance.md5(str.$new("erwer"));
                  console.log("call result:" + re);
             },
             "onComplete":function() {
                  console.log(" Finished heap search");
             }
        });
    });
});











在某些机型手机上使用Java.choose可能会崩溃,选择合适的手机进行调试使用。(4)静态变量的调用与赋值










var clazz = Java.use("com.ydscience.fridaandroidstudydemo.utils.Utils");
var CoinMoney = clazz.sCoinMoney.value;
console.log("CoinMoney "+CoinMoney)
var money = CoinMoney.getMoney();
console.log("money is "+money);
   clazz.line.value = 1100;











2.10 java.registerClass的使用以注册一个线程为例,代码如下:










var Thread = Java.use("java.lang.Thread");
var Runnable = Java.use("java.lang.Runnable");
var MyRunnable = Java.registerClass({
  name: 'com.example.mythread',
  implements  : [Runnable],
  methods: {
     run : function () {
                //todo
                }
         }
    });
var runnable = MyRunnable.$new();
var postThread = Thread.$new(runnable);
postThread.start();











2.11 实现和Xposed中XC_MethodReplace取代方法内部执行逻辑









var hook = Java.use("seclover.crackme.MainActivity$2");
hook.run.implementation = function(){
  console.log("unhook detected");
        //不调用原来方法,不执行return this.run();在此处只会打印一个log
}











2.12 UI thread 注入









Java.perform(function() {
  var Toast = Java.use('android.widget.Toast');
  var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
  var context = currentApplication.getApplicationContext();

  Java.scheduleOnMainThread(function() {
    Toast.makeText(context, "Hello World", Toast.LENGTH_LONG.value).show();
  })
})











2.13 获取对象实例









Java.choose("android.view.View", {
             "onMatch":function(instance){//This function will be called for every instance found by frida
                  console.log("
  • Instance found");
                 },
                 "onComplete":function() {
                      console.log("
  • Finished heap search")
                 }
            });











    2.14 创建Java数组









    byte数组创建
    var buffer = Java.array('byte', [ 13, 37, 42 ]);











    3 Native层3.1导出函数hook 导出函数时,直接使用导出的函数名。
    打印参数时,如果时Java层调用的native交互式方法,如“JAVA_COM_YDFSCIEN_XXX”,则真实有效的参数从第三个开始,前两个分别为
    JNIENV 和Jobject,其他的则从第一个开始算起打印参数与返回值,格式如下:










    int : toInt32()
    String:
    var String_java = Java.use('java.lang.String');
    var args_4 = Java.cast(args[2], String_java);
    char与指针类型
    Memory.readCString() 和Memory.readUtf8String()
    //以十六进制输出
    function printData(address) {
        var keyAry = Memory.readByteArray(address, 16);
        var byteAry = new Uint8Array(keyAry);
        dataStr = "";
        for(var i=0; i<byteAry.length; i++){
            dataStr += ("0x" + byteAry.toString(16) + ",");
        }
        return dataStr;
    }











    示例代码如下:










    Interceptor.attach(Module.findExportByName("libnative-lib.so" , "jstringToChar"), {
        onEnter: function(args) {
         var String_java = Java.use('java.lang.String');
         var args_4 = Java.cast(args[1], String_java);
         console.log("open called! args[0]:"+args_4);
        },
        onLeave:function(retval){
         //Memory.readCString也可以
         console.log("leave ret is "+Memory.readUtf8String(retval));
        }
       });











    3.2 hook未导出函数在hook未导出函数时,需要计算地址。地址为:基地址+相对地址。
    (1)手动计算。










    查看基地址
    查看进程pid
    ps | grep com.kuaiduizuoye.scan
    查看基地址
    cat /proc/2275/maps |grep libanti_spam.so
       相对地址直接用IDA打开so文件就可以查看。











    (2)自动计算。










    Module.findBaseAddress(“libanti_spam.so”).add(相对地址)











    无论哪种方式最后得到的地址,对地址最后一位需要进行奇偶性判断,1是thumb,0是arm。
    参考地址:http://www.520monkey.com/archives/1256,操作代码如下:










    var nativePointer = new NativePointer(0x8949479);
    Interceptor.attach(nativePointer ,
            onEnter: function(args) {
              //todo
        },
        onLeave:function(retval){
                 //todo
             }
    )











    3.3加载so中导出函数









    var exports = Module.enumerateExportsSync("libnative-lib.so");
    for(var i=0; i<exports.length; i++){  
        console.log("name is  "+ exports.name +" address "+ exports.address);
    }











    3.4 查找so的基址









    //遍历模块找基址
            Process.enumerateModules({
                onMatch: function (exp) {
                    if (exp.name == 'libnative-lib.so') {
                        send('enumerateModules find');
                        send(exp.name + "|" + exp.base + "|" + exp.size + "|" + exp.path);
                        send(exp);
                        return 'stop';
                    }
                },
                onComplete: function () {
                    send('enumerateModules stop');
                }
            });

            //通过模块名直接查找基址
            var soAddr = Module.findBaseAddress("libnative-lib.so");
            send("soAddr:" + soAddr);












    3.5 修改参数及返回值代码如下:










    //int类型
    Interceptor.attach(faddptr, {
                onEnter: function (args) {
                    send("onEnter add()");
                    x = args[0];
                    y = args[1];
                    args[0] = ptr(x * 2);
                    args[1] = ptr(y * 2);
                    send("hook add()修改参数为原来的两倍 args[0]:" + args[0].toInt32() + "  args[1]:" + args[1].toInt32());
                },
                onLeave: function (retval) {
                    send("onLeave  add()");
                    retval.replace(678);
                    send("add()修改返回值为:"+retval.toInt32())
                }
            });
    //String类型
    Interceptor.attach(fsayptr, {
                onEnter: function (args) {
                    send("onEnter say()");
                },
                onLeave: function (retval) {
                    send("onLeave say()");
                    var s = Java.cast(retval, str);
                    send("say() 原返回值:" + s);
                    var env = Java.vm.getEnv();
                    var jstring = env.newStringUtf("frida hook native");
                    retval.replace(ptr(jstring));
                    send("修改say()返回值:" + Java.cast(jstring, str));
                }
            });
    详细见myfrida.js











    4 RPC的使用rpc模板代码如下:










    rpc.exports= {
        add:function(a,b){
            return a+b;
        },
        sha1:function(msg){
            var ret;
            Java.perform(function() {
                 var myCrypto = Java.use("com.ydscience.fridaandroidstudydemo.utils.MyCrypto");
                 ret =  myCrypto.sha1Hex(msg);
            });
            return ret;
        },
    }











    使用方法一:










      (1)下载注入程序https://github.com/frida/frida/releases,名称为frida-inject-*
      (2)下载rpc模板,从动态调用列表的功能按钮中选择导出并保存
      (3)下载资源文件web.html vue.min.js 放入 /data/local/tmp
      (4)将注入程序和脚本上传至远端,执行frida-inject -n com.seclover.dhook -s inject.js
       (5)打开http://remoteip:3000/即可调用rpc











    使用方法二:
    配合dhook使用,打开dhook后,在动态调用页面创建脚本,填写需要调用的类和方法,完成调用即可。

  • 发表于 2022-2-2 15:53:34 高大上手机用户 | 显示全部楼层   四川省成都市
    666666666666666666
    回复 支持 反对

    使用道具 举报

    结帖率:75% (3/4)
    发表于 2021-2-26 10:05:18 | 显示全部楼层   广东省东莞市
    一看这排版好像是复制粘贴的了 看着难受
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则 致发广告者

    发布主题 收藏帖子 返回列表

    sitemap| 易语言源码| 易语言教程| 易语言论坛| 诚聘英才| 易语言模块| 手机版| 广告投放| 精易论坛
    拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
    论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
    防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 800073686,邮箱:800073686@b.qq.com
    Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

    快速回复 返回顶部 返回列表