文章部分摘自看雪高妍 及 凡墙总是门大佬的文章

attach spawn

# spawn
frida -U -f com.xx.xx -l xx.js

# attach
frida -U com.xx.xx -l xx.js

常用命令

# jar转dex, 或者用np管理器转
# 可以看这里https://blog.devilwst.top/xiaosheng/2425.html
dx --dex --output=ddex.dex ddex.jar
chmod 777 ddex.dex

frida改端口

app server

./frida14-2 -l 192.168.1.103:6677

# 也可以使用默认端口使用
./frida14-2 -l 192.168.1.103

client

# 使用自定义端口
frida -H 192.168.1.103:6677 com.xx.xx -l xx.js

# 使用默认端口
frida -H com.xx.xx -l xx.js

hook重载

function main(){
    Java.perform(function(){
        var UtilsClass = Java.use("com.xx.Utils");

        // 重载无参方法
        UtilsClass.test.overload().implementation = function () {
            console.log("hook overload no args");
            return this.test();
        }

        // 重载有参方法 - 基础数据类型
        UtilsClass.test.overload('int').implementation = function(num){
            console.log("hook overload int args");
            var myNum = 9999;
            var oriResult = this.test(num);
            console.log("oriResult is :" + oriResult);
            return this.test(myNum);
        }

        // 重载有参方法 - 引用数据类型Money
        UtilsClass.test.overload('com.xx.Money').implementation = function(money){
            console.log("hook Money args");
            return this.test(money);
        }

        // hook 指定test方法的所有重载
        var ClassName = Java.use("com.xx.Utils");
        var overloadsLength = ClassName.test.overloads.length;
        for (var i = 0; i < overloadsLength; i++){
            ClassName.test.overloads[i].implementation = function () {
                // 遍历打印 arguments 
                for (var a = 0; a < arguments.length; a++){
                    console.log(a + " : " + arguments[a]);
                }
                // 调用原方法
                return this.test.apply(this,arguments);
            }
        }
    })
}

setImmediate(main);

hook构造方法 & 主动调用

var mone = Java.use("com.xx.Money");
mone.init.overload().implementation = function(){
}

// 有参构造
var strClass = Java.use("java.lang.String")
mone.init.overload("java.lang.String",'int').implementation = function(x,y) {
    // 主动调用
    var myx = strClass.new("hi");    var myy = 9;
    this.init(myx,myy);
}

hook匿名类

        // hook 匿名类
        // 匿名类在 smail中以 1,2 等方式存在, 需要通过 java 行号去 smail 找到准确的匿名类名称 
        // new __接口__{} 可以理解成 new 了一个实现接口的匿名类, 在匿名类的内部(花括号内),实现了这个接口
        var NiMingClass = Java.use("com.xxxx.MainActivity$1");
        NiMingClass.getInfo.implementation = function (){
            return "hook匿名类";
        }

调用静态函数

private static void setstaticBoolVar(){
       FridaActivity2.staticBoolVar = true;
    }

hook代码

    var FridaActivity2 = Java.use(
      "com.xxxxxx.FridaActivity2");
    console.log("FridaActivity2.staticBoolVar:", FridaActivity2.staticBoolVar.value);
    FridaActivity2.setstaticBoolVar();  //调用静态函数
    console.log("FridaActivity2.staticBoolVar:", FridaActivity2.staticBoolVar.value);

调用非静态函数

    private void setboolVar(){
       this.boolVar = true;
    }

hook代码

    //调用非静态函数
    Java.choose("com.xxxxxy.FridaActivity2", {
      onMatch : function(instance) {
        console.log("FridaActivity2.boolVar:", instance.boolVar.value);
        instance.setboolVar();
        console.log("FridaActivity2.boolVar:", instance.boolVar.value);
      }, onComplete : function() {
      }
    })

方法和参数同名情况

    private boolean sameNameBoolVar;
    private boolean boolVar;

    private void sameNameBoolVar(){
       Log.d("Frida", FridaActivity3.staticBoolVar+" "+this.boolVar+" "+this.sameNameBoolVar);
    }

hook代码

    Java.choose("com.xxxxx.Activity.FridaActivity3", {
      onMatch : function(instance) {
        console.log("FridaActivity3.bool_var:", instance.boolVar.value);
        instance.bool_var.value = true;
        console.log("FridaActivity3.bool_var:", instance.boolVar.value);

        console.log("FridaActivity3._sameNameBoolVar:", instance._sameNameBoolVar.value);
        instance._sameNameBoolVar.value = true;
        console.log("FridaActivity3._sameNameBoolVar:", instance._sameNameBoolVar.value);
      }, onComplete : function() {
      }
    })

hook内部类

public class FridaActivity4{

    public static class InnerClasses    // class@00073d from classes.dex{
        public void InnerClasses(){
           super();
        }
        public static boolean check1(){
           return false;
        }
        public static boolean check2(){
           return false;
        }
        public static boolean check3(){
           return false;
        }
        public static boolean check4(){
           return false;
        }
        public static boolean check5(){
           return false;
        }
        public static boolean check6(){
           return false;
        }
    }

}

hook代码, $, 不同反编译器结果不一样,Jadx更偏向于开发时候写的内部类,GDA编译的内部类class FridaActivity4$InnerClasses 单独写在外部

var InnerClasses = Java.use(
      "com.xxxxx.FridaActivity4$InnerClasses")
   InnerClasses.check1.implementation = function() {
     console.log("InnerClasses.check1:");
     return true;
   }
或者hook一个类内部所有的方法


hook一个类内部所有的方法及重载

Java.perform(function(){
        // hook md5 class in app
        // 1. iterate classes
        var classList = Java.enumerateLoadedClassesSync();
        for (var i = 0; i < classList.length; i++){
            // 筛选过滤 只遍历 MD5 下面的方法
            if (classList[i].indexOf("com.xx.app.MD5") != -1){
                var className = classList[i];
                console.log("class name is :", className);

                // 2. get methods of the class
                // 返回一个 Methods对象的数组
                var methodsList = Java.use(className).class.getDeclaredMethods();
                for (var k=0; k<methodsList.length; k++){                    
                    // console.log("method is :",methodsList[k],typeof(methodsList[k]));

                    // 3. Method object.getName() --> methodName and class[methodName] to hook method
                    var methodName = methodsList[k].getName(); // 

                    // console.log('methodName',methodName);

                    // 4. use apply and arguments to implementation
                    var hookClass = Java.use(className);
                    // 5. 重载
                    for (var o = 0; o< hookClass[methodName].overloads.length; o++){
                        hookClass[methodName].overloads[o].implementation = function(){
                            for (var a=0; a<arguments.length; a++){
                                console.log('argument ',a,arguments[a]);
                            }
                            // return this[methodName].apply(this,arguments);
                            return "fucking the md5"
                        }
                    }
                }
            }
        }
    })

hook动态加载dex的类

private void loaddex(){
       DexClassLoader e2;
       File cacheDir = this.getCacheDir();
       if (!cacheDir.exists()) {
          cacheDir.mkdir();
       }
       String str = "DynamicPlugin.dex";
       String str1 = cacheDir.getAbsolutePath()+File.separator+str;
       try{
          File uFile = new File(str1);
          if (!uFile.exists()) {
             uFile.createNewFile();
             FridaActivity5.copyFiles(this, str, uFile);
          }
       }catch(java.io.IOException e2){
          e2.printStackTrace();
       }
       String absolutePath = cacheDir.getAbsolutePath();
       ClassLoader classLoader = this.getClassLoader();
       try{
          e2 = new DexClassLoader(str1, absolutePath, null, classLoader);
          this.DynamicDexCheck = e2.loadClass("com.xxxx.Dynamic.DynamicCheck").newInstance();
          if (this.DynamicDexCheck == null) {
             Toast.makeText(this, "loaddex Failed!", 1).show();
          }
       }catch(java.lang.Exception e0){
          e0.printStackTrace();
       }
       return;
    }
    public CheckInterface getDynamicDexCheck(){
       if (this.DynamicDexCheck == null) {
          this.loaddex();
       }
       return this.DynamicDexCheck;
    }

public interface abstract CheckInterface    // class@000745 from classes.dex
{
    boolean check();
}

hook代码

这个直接hook com.xxxx.Dynamic.DynamicCheck是hook不到的,要先找到加载该类的classLoader

  Java.perform(function() {
    Java.enumerateClassLoaders({
      onMatch : function(loader) {
        try {
          // 找到classLoader   loadClass 或者 findClass
          if (loader.findClass("com.xxxx.Dynamic.DynamicCheck")) {
            // 设置classLoader
            Java.classFactory.loader = loader;
            console.log(loader);
          }
        } catch (error) {

        }
      }, onComplete : function() {
      }
    });
    // 设置完classLoader就能hook到了 
    var DynamicCheck = Java.use("com.xxxxx.Dynamic.DynamicCheck");
    DynamicCheck.check.implementation = function() {
      var result = this.check();
      console.log("DynamicCheck.check:", result);
      return true;
    }
  })

但是在加壳的app中可能没法找到正常加载app类的classLoader,可以直接loadClass尝试来加载

function hook() {
    Java.perform(function () {
        Java.enumerateClassLoadersSync().forEach(function (classloader) {
            try {
                console.log("classloader", classloader);
                classloader.loadClass("com.xxxx.MainActivity");
                Java.classFactory.loader = classloader;
                var mainActivityClass = Java.use("com.kanxue.encrypt01.MainActivity");
                console.log("mainActivityClass", mainActivityClass);
            } catch (error) {
                console.log("error", error);
            }
        });
    })
}

强转Java.cast

var temp = null;  //用来存储找到的对象
var toCast = Java.use("comx.xx.xx.xx")
Java.choose("com.xx.xx", {
    onMatch:function(instance) {
        temp = instance;
        var toCastInstance = Java.cast(temp, toCast);
    },
    onComplete:function() {

    }
})

hook打印类 实现的接口

Java.enumerateLoadedClasses({
            onComplete: function(){},
            onMatch: function(name,handle){
                if (name.indexOf("com.xx.xx") > -1) { // 使用包名进行过滤
                    console.log("find class");
                    var targetClass = Java.use(name);
                    var interfaceList = targetClass.class.getInterfaces(); // 使用反射获取类实现的接口数组
                    if (interfaceList.length > 0) {
                        console.log(name) // 打印类名
                        for (var i in interfaceList) {
                            console.log("\t", interfaceList[i].toString()); // 直接打印接口名称
                        }
                    }
                }
            }
        })

hook枚举类

Java.choose("com.xxxx.OptEnum",{
            onComplete: function(){},
            onMatch: function(instance){
                console.log('find enum :',instance);
                console.log(instance.class.getName());
            }
        })

hook获取applicationContext

Java.perform(function(){
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        console.log(currentApplication);
        var context = currentApplication.getApplicationContext();
        console.log(context);
        var packageName = context.getPackageName();
        console.log(packageName);
        console.log(currentApplication.getPackageName());
    })

hook打印char

        var CharClass = Java.use("java.lang.Character");
        CharClass.toString.overload("char").implementation = function(inputChar){
            var result = this.toString(inputChar);
            console.log("inputChar, result: ", inputChar, result);
            return result;
        }

hook打印char数组

        var ArrayClass = Java.use("java.util.Arrays");
        ArrayClass.toString.overload('[C').implementation = function(charArray){
            // 1. java.util.Arrays.toString()
            var result = this.toString(charArray);
            // 2. javascript JSON.stringify()
            var result1 = JSON.stringify(charArray);
            console.log('charArray, result : ', charArray, result);
            console.log('charArray, result :', charArray, result1);
        }

打印调用栈printStackTraces

function printstack(){
    send(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()))
}

打印一些常用东西

dexTool封装自

    var StringClass = Java.use("java.lang.String");
    var StringClass = Java.use("java.lang.String");
    var byteArray = StringClass.new("Hello World").getBytes();

    Java.openClassFile("/data/local/tmp/xiaosheng-dex-tool.dex").load();
    var js = Java.use("com.xiaosheng.tool.json.Gson");
    var gson = js.new();

    console.log(gson.toJson(byteArray));

hook打印 non-ascii 和特殊字符

可以先通过encodeURIComponent编码再decodeURIComponent解码的方式进行 hook

int ֏(int x) {
        return x + 100;
    }
var targetClass = "com.xxxx.MainActivity";

var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();

for (var i in methods) {
    console.log(methods[i].toString());
    console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?/, "1")));
}

// hook解码后的字符
hookCls[decodeURIComponent("%D6%8F")]
                .implementation = function (x) {
                    console.log("original call: fun(" + x + ")");
                    var result = this[decodeURIComponent("%D6%8F")](900);
                    return result;
                }

hook onClick()

var jclazz = null;
var jobj = null;

function getObjClassName(obj) {
    if (!jclazz) {
        var jclazz = Java.use("java.lang.Class");
    }
    if (!jobj) {
        var jobj = Java.use("java.lang.Object");
    }
    return jclazz.getName.call(jobj.getClass.call(obj));
}

function watch(obj, mtdName) {
    var listener_name = getObjClassName(obj);
    var target = Java.use(listener_name);
    if (!target || !mtdName in target) {
        return;
    }
    // send("[WatchEvent] hooking " + mtdName + ": " + listener_name);
    target[mtdName].overloads.forEach(function (overload) {
        overload.implementation = function () {
            //send("[WatchEvent] " + mtdName + ": " + getObjClassName(this));
            console.log("[WatchEvent] " + mtdName + ": " + getObjClassName(this))
            return this[mtdName].apply(this, arguments);
        };
    })
}

function OnClickListener() {
    Java.perform(function () {

        //以spawn启动进程的模式来attach的话
        Java.use("android.view.View").setOnClickListener.implementation = function (listener) {
            if (listener != null) {
                watch(listener, 'onClick');
            }
            return this.setOnClickListener(listener);
        };

        //如果frida以attach的模式进行attch的话
        Java.choose("android.view.View$ListenerInfo", {
            onMatch: function (instance) {
                instance = instance.mOnClickListener.value;
                if (instance) {
                    console.log("mOnClickListener name is :" + getObjClassName(instance));
                    watch(instance, 'onClick');
                }
            },
            onComplete: function () {
            }
        })
    })
}
setImmediate(OnClickListener);

hook 用frida实现activity跳转

Java.perform(function () {
        var context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext();
        var intentClazz = Java.use("android.content.Intent");
        var activityClazz = Java.use("ctrip.android.hotel.view.UI.inquire.HotelInquireActivity");
        var intentObj = intentClazz.$new(context, activityClazz.class);
        intentObj.setFlags(0x10000000);
        context.startActivity(intentObj);
        console.log("startActivity");
    })

hook 禁止app退出

Java.perform(function(){
        console.log("[*] Starting hook exit");
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(){
            console.log("[*] System.exit.called");
        }
        console.log("[*] hooking calls to System.exit");
    })

常用打印及转换

//工具相关函数
var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 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, (-1), (-1), (-1), (-1), (-1), (-1), 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, (-1), (-1), (-1), (-1), (-1));

// base64 解码
function stringToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e.charCodeAt(a++),
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}

// base64 编码
function base64ToString(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = ''; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d += String.fromCharCode(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d += String.fromCharCode((3 & c) << 6 | h)
    }
    return d
}

// hex 字符转 base64
function hexToBase64(str) {
    return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x1 ").replace(/ +/, "").split(" ")));
}

// base64 转 hex
function base64ToHex(str) {
    for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
        var tmp = bin.charCodeAt(i).toString(16);
        if (tmp.length === 1)
            tmp = "0" + tmp;
        hex[hex.length] = tmp;
    }
    return hex.join("");
}


function hexToBytes(str) {
    var pos = 0;
    var len = str.length;
    if (len % 2 != 0) {
        return null;
    }
    len /= 2;
    var hexA = new Array();
    for (var i = 0; i < len; i++) {
        var s = str.substr(pos, 2);
        var v = parseInt(s, 16);
        hexA.push(v);
        pos += 2;
    }
    return hexA;
}

function bytesToHex(arr) {
    var str = '';
    var k, j;
    for (var i = 0; i < arr.length; i++) {
        k = arr[i];
        j = k;
        if (k < 0) {
            j = k + 256;
        }
        if (j < 16) {
            str += "0";
        }
        str += j.toString(16);
    }
    return str;
}

function stringToHex(str) {
    var val = "";
    for (var i = 0; i < str.length; i++) {
        if (val == "")
            val = str.charCodeAt(i).toString(16);
        else
            val += str.charCodeAt(i).toString(16);
    }
    return val
}

function stringToBytes(str) {
    var ch, st, re = [];
    for (var i = 0; i < str.length; i++) {
        ch = str.charCodeAt(i);
        st = [];
        do {
            st.push(ch & 0xFF);
            ch = ch >> 8;
        }
        while (ch);
        re = re.concat(st.reverse());
    }
    return re;
}

//将byte[]转成String的方法
function bytesToString(arr) {
    var str = '';
    arr = new Uint8Array(arr);
    for (var i in arr) {
        str += String.fromCharCode(arr[i]);
    }
    return str;
}

function bytesToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e[a++],
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}

function base64ToBytes(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = []; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d.push(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d.push((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d.push((3 & c) << 6 | h)
    }
    return d
}

点击查看备忘录

分类: app逆向

浙公网安备33011302000604

辽ICP备20003309号