frida初探3:frida hook java

文章最后更新时间为:2023年07月20日 15:46:16

本篇文章主要记录frida hook java的常见用法,内容多参考自《frida 协议分析》

1. 枚举所有的类

hook第一步是需要找到需要hook的类,可以通过以下方式枚举所有加载到内存中的类:

function main() {
  Java.perform(function () {
      Java.enumerateLoadedClasses({
          onMatch: function (name, handle){
             console.log("name:" + name + " handle:" + handle)
          },
          onComplete: function () {
              console.log("end")
          }
      })
  })
}
setImmediate(main)

一般情况下需要通过关键字来过滤一下类:

function main() {
  Java.perform(function () {
      Java.enumerateLoadedClasses({
          onMatch: function (name, handle){
            if (name.split(".")[1] == "bluetooth"){
              console.log("name:" + name + " handle:" + handle)
            }
            if (name.indexOf("android.bluetooth.BluetoothMap") != -1) {
              console.log("name:" + name + " handle:" + handle)
          }
            //  console.log("name:" + name + " handle:" + handle)
          },
          onComplete: function () {
              console.log("end")
          }
      })
  })
}
setImmediate(main)

2. 枚举类中的方法

function main() {
  var a = enumMethods("android.bluetooth.BluetoothDevice")
  a.forEach(function(s) {
      console.log(s);
  });
}

function enumMethods(targetClass)
{
    var hook = Java.use(targetClass);
    var ownMethods = hook.class.getDeclaredMethods();
    hook.$dispose;

    return ownMethods;
}

setImmediate(main)

3. 获取和修改类的字段

静态字段只要拿到类就可以访问,如果是实例字段则需要使用Java.choice先获取到类。如果字段和方法同名,则使用下划线访问字段

// 静态
var money=Java.use("com.xxx.hook.Money");
console.log(money.flag.value);
money.flag.value="修改后的结果";
console.log(money.flag.value);


// 实例

Java.choose("com.xxx.hook.Money",{
    onMatch:function(obj){
        console.log("Java onMatch:",obj.currency.value);
    },
    onComplete:function(){
    }
})

4. hook内部类和匿名类

java中如果一个类定义在另一个类或一个方法里面,这样的类称为内部类,访问时需要在类和内部类名之间加上$字符。

var Wallet$InnerSturcture=Java.use("com.xxx.hook.Wallet$InnerStructure");
    console.log(Wallet$InnerSturcture);

    Java.choose("com.xxx.hook.Wallet$InnerStructure",{
        onMatch:function(obj){
            console.log("Java Wallet$InnerSturcture:",obj.bankCardsList.value);
        },
        onComplete:function(){
        }
    })

匿名类时一个没有名字的类,是内部类的简化方法,而匿名类可以借助smali的语法进行类的定位,通常如下面的这种形式:

var money=Java.use("com.xxx.app.MainActivity$1");
money.getInfo.implementation=function(){
    var result=this.getInfo();
    console.log(result);
    return result;
}

5. 对象参数的构造

在有些方法中,需要传入对象作为参数,比如deposit方法传入Money类作为参数,如果需要构造一个Money类,则需要java.use先定位到Money类,在调用$new方法构造一个类对象,其中初始参数也需要依次传输进去,比如

var walletils=Java.use("com.XXX.hook.Wallet");
var Money=Java.use("com.XXX.hook.Money");
walletils.deposit.implementaton=function(a){
console.log("money.$init param:",a.getInfo());
return this.deposit(Money.$new("美元",1000));

6. hook方法

6.1 hook静态方法和实例方法

function test(){
    Java.perform(function(){
        var money=Java.use("com.xxx.hook.Money");
        money.setFlag.implementation=function(a){
            console.log("money.setFlag param:",a);
            return this.setFlag(a);
        };
        money.getInfo.implementation=function(){
            var result=this.getInfo();
            console.log("money.getInfo:",result);
            return result;
        };
    })
};
test();

6.2 hook构造方法

通过clazz.$init.implementation来构造函数。

function main() {
  Java.perform(function () {
    Java.use("com.tlamb96.kgbmessenger.b.a").$init.implementation = function (i, str1, str2, z) {
      this.$init(i, str1, str2, z)
      console.log(i, str1, str2, z)
  }
  })
}

setImmediate(main)

6.3 hook重载方法

对方法有重载时,直接hook该方法会报错。

void fun(int x , int y ){
    Log.d("ROYSUE.Sum" , String.valueOf(x+y));
}

String fun(String x){
    total +=x;
    return x.toLowerCase();
}

所以我们需要指定具体hook的是哪个重载的方法,用法如下:

my_class.fun.overload("int" , "int").implementation = function(x,y){
...
my_class.fun.overload("java.lang.String").implementation = function(x){

当我们要hook某个方法的所有重载时,可以这么做:

//目标类
var hook = Java.use(targetClass);
//重载次数
var overloadCount = hook[targetMethod].overloads.length;
//打印日志:追踪的方法有多少个重载
console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
//每个重载都进入一次
for (var i = 0; i < overloadCount; i++) {
//hook每一个重载
    hook[targetMethod].overloads[i].implementation = function() {
        console.warn("n*** entered " + targetClassMethod);
        // 打印参数
        if (arguments.length) console.log();
        for (var j = 0; j < arguments.length; j++) {
            console.log("arg[" + j + "]: " + arguments[j]);
        }

        //打印返回值
        var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
        console.log("nretval: " + retval);
        console.warn("n*** exiting " + targetClassMethod);
        return retval;
    }
}

6.4 hook类的所有方法

可以使用objection完成,也可以使用下面的代码,先枚举类的方法,再枚举方法的重载,再进行hook

function test(){
    Java.perform(function(){
        var Utils=Java.use("com.xxx.hook.Utils");
        var methods=Utils.class.getDeclaredMethods();
        for(let k=0;k<methods.length;k++){
            var methodName=methods[k].getName();
            var overloadArr=Utils[methods[k].getName()].overloads;
            console.log("fun:",methodName);
            for(var i=0;i<overloadArr.length;i++){
                overloadArr[i].implementation=function(){
                    var params="";
                    for(var j=0;j<arguments.length;j++){
                        params+=arguments[j]+" ";
                    };
                    console.log("utils."+methodName+" is called! params is:",params);
                    return this[methodName].apply(this,arguments);
                }
            }
        };
});
};
test();

7. 主动调用

  • 静态函数直接use class然后调用方法,非静态函数需要先choose实例然后调用。
  • 设置成员变量的值,写法是xx.value = yy,其他方面和函数一样。如果有一个成员变量和成员函数的名字相同,则在其前面加一个_,如_xx.value = yy
function main() {
  Java.perform(function () {
      //hook静态函数直接调用
      var demoClass = Java.use("com.example.demo.DemoClass")
      demoClass.setAge()

      //hook动态函数,找到instance实例,从实例调用函数方法
      Java.choose("com.example.demo.DemoClass", {
          onMatch: function (instance) {
              instance.setName()
          },
          onComplete: function () {
              console.log("end")
          }
      })
  })
}
setImmediate(main)

8. 打印栈回溯

function main() {
  Java.perform(function () {
    var demoClass = Java.use("com.example.demo.DemoClass")

    demoClass.isHook.implementation = function () {
        showStacks()
        printStack("com.example.demo.DemoClass.IsHook")
        var result = this.isHook()
        return false
        
    }})
}

function printStack(name) {
  Java.perform(function () {
      var Exception = Java.use("java.lang.Exception");
      var ins = Exception.$new("Exception");
      var straces = ins.getStackTrace();
      if (straces != undefined && straces != null) {
          var strace = straces.toString();
          var replaceStr = strace.replace(/,/g, "\n");
          console.log("=============================" + name + " Stack strat=======================");
          console.log(replaceStr);
          console.log("=============================" + name + " Stack end=======================\r\n");
          Exception.$dispose();
      }
  });
}
setImmediate(main)

9. 利用hook快速定位

在java代码中直接搜索关键词可能难以搜索到,所以可以使用下列方式对常见的类进行hook,从而定位到相关代码。

9.1 hook hashMap 定位散列表

var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {
console.log("HashMap.put: ", a, b);
return this.put(a, b);
}

9.2 hook ArrayList 定位动态数组

var arrayList = Java.use("java.util.ArrayList");
arrayList.add.overload('java.lang.Object').implementation = function (a) {
    console.log("ArrayList.add: ", a);
    return this.add(a);
}
arrayList.add.overload('int', 'java.lang.Object').implementation = function (a, b) {
    console.log("ArrayList.add: ", a, b);
    return this.add(a, b);
}

9.3 hook Toast 定位提示

var toast = Java.use("android.widget.Toast");
toast.show.implementation = function () {
    showStacks();
    console.log("Toast.show");
    return this.show();
}

9.4 hook findViewById 定位组件

在实际开发中,经常需要用到findViewById来获取组件,再进行事件的绑定或数据的获取,所以我们可以使用类似uiautomatorviewer的软件来获取组件id,然后使用frida来获取id对应的数值,下面是hook appCompatActivity的findViewById方法,当传入的值和btn_login_id相等时,获取函数栈,即可指定为登录按钮组件的代码位置。

Java.perform(function () {
    var btn_login_id = Java.use("com.dodonew.online.R$id").btn_login.value;
    console.log("btn_login_id", btn_login_id);
    var appCompatActivity = Java.use("android.support.v7.app.AppCompatActivity");
    appCompatActivity.findViewById.implementation = function (a) {
        if(a == btn_login_id){
            showStacks();
            console.log("appCompatActivity.findViewById: ", a);
        }
    return this.findViewById(a);
}
});

9.5 hook setOnClickListener 定位按钮点击事件

同9.4差不多

Java.perform(function () {
    var btn_login_id = Java.use("com.dodonew.online.R$id").btn_login.value;
    console.log("btn_login_id", btn_login_id);
    var view = Java.use("android.view.View");
    view.setOnClickListener.implementation = function (a) {
        if(this.getId() == btn_login_id){
            showStacks();
            console.log("view.id: " + this.getId());
            console.log("view.setOnClickListener is called");
        }
        return this.setOnClickListener(a);
    }
});

9.6 hook JSONObject 定位JSON数据

JSONObject 类可能不是很常用,所以可能hook不到。

var jSONObject = Java.use("org.json.JSONObject");
jSONObject.put.overload('java.lang.String', 'java.lang.Object').implementation = function (a, b) {
    showStacks();
    console.log("JSONObject.put: ", a, b);
    return this.put(a, b);
}
jSONObject.getString.implementation = function (a) {
    showStacks();
    var result = this.getString(a);
    console.log("JSONObject.getString: ", a, result);
    return result;
}

9.7 hook Collections 定位排序算法

一般app再进行数据签名时,会先对数据进行排序,因为摘要结果不可逆,所以需要保证服务端和客户端的数据顺序一致。比较常用的排序方法有Collections的sort方法,Array的sort方法,或者自己实现。下面是hook Collections的sort方法:

var collections = Java.use("java.util.Collections");
collections.sort.overload('java.util.List').implementation = function (a) {
    showStacks();
    console.log("Collections.sort List: ", a.toString());
    return this.sort(a);
}
collections.sort.overload('java.util.List', 'java.util.Comparator').implementation = function (a, b) {
    showStacks();
    // 直接打印a.strings()会显示object,可以使用Java.cast转为java.util.ArrayList再打印
    var result = Java.cast(a, Java.use("java.util.ArrayList"));
    console.log("Collections.sort List Comparator: ", result.toString());
    return this.sort(a, b);
    return this.sort(a, b);
}

9.8 hook String 定位字符转换

通常在加密前,需要将string转为byte,需要用到String类的getBytes方法

var str = Java.use("java.lang.String");
str.getBytes.overload().implementation = function () {
    showStacks();
    var result = this.getBytes();
    var newStr = str.$new(result);
    console.log("str.getBytes: ", newStr);
    return result;
}
str.getBytes.overload('java.lang.String').implementation = function (a) {
    showStacks();
    var result = this.getBytes(a);
    var newStr = str.$new(result, a);
    console.log("str.getBytes: ", newStr);
    return result;
}

9.9 hook StringBuilder 定位字符串操作

为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象:

StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
    sb.append(',');
    sb.append(i);
}
String s = sb.toString();
Java.perform(function(){
    var stringBuilder = Java.use("java.lang.StringBuilder");
    stringBuilder.toString.implementation = function () {
        var result = this.toString.apply(this, arguments);
        if(result == "username=13866668888"){
            showStacks();
            console.log("stringBuilder.toString is called!", result);
        }
        return result;
    }
});

10. hook常见加解密算法

包括base64、md5、aes、des等,比较常用

var N_ENCRYPT_MODE = 1
var N_DECRYPT_MODE = 2

function showStacks() {
    var Exception = Java.use("java.lang.Exception");
    var ins = Exception.$new("Exception");
    var straces = ins.getStackTrace();

    if (undefined == straces || null == straces) {
        return;
    }

    console.log("============================= Stack strat=======================");
    console.log("");

    for (var i = 0; i < straces.length; i++) {
        var str = "   " + straces[i].toString();
        console.log(str);
    }

    console.log("");
    console.log("============================= Stack end=======================\r\n");
    Exception.$dispose();
}

//工具相关函数 
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));

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
}
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
}
function hexToBase64(str) {
    return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
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
}
//stringToBase64 stringToHex stringToBytes
//base64ToString base64ToHex base64ToBytes
//               hexToBase64  hexToBytes    
// bytesToBase64 bytesToHex bytesToString


Java.perform(function () {
    var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) {
        showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        console.log("算法名:" + b + "|str**:" + bytesToString(a));
        console.log("算法名:" + b + "|Hex**:" + bytesToHex(a));
        return result;
    }

    var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec');
    DESKeySpec.$init.overload('[B').implementation = function (a) {
        showStacks();
        var result = this.$init(a);
        console.log("======================================");
        var bytes_key_des = this.getKey();
        console.log("des**  |str " + bytesToString(bytes_key_des));
        console.log("des**  |hex " + bytesToHex(bytes_key_des));
        return result;
    }

    DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) {
        showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        var bytes_key_des = this.getKey();
        console.log("des**  |str " + bytesToString(bytes_key_des));
        console.log("des**  |hex " + bytesToHex(bytes_key_des));
        return result;
    }

    var mac = Java.use('javax.crypto.Mac');
    mac.getInstance.overload('java.lang.String').implementation = function (a) {
        showStacks();
        var result = this.getInstance(a);
        console.log("======================================");
        console.log("算法名:" + a);
        return result;
    }
    mac.update.overload('[B').implementation = function (a) {
        //showStacks();
        this.update(a);
        console.log("======================================");
        console.log("update:" + bytesToString(a))
    }
    mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        this.update(a, b, c)
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
    }
    mac.doFinal.overload().implementation = function () {
        //showStacks();
        var result = this.doFinal();
        console.log("======================================");
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }
    mac.doFinal.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.doFinal(a);
        console.log("======================================");
        console.log("doFinal参数: |str  :"     + bytesToString(a));
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }

    var md = Java.use('java.security.MessageDigest');
    md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) {
        //showStacks();
        console.log("======================================");
        console.log("算法名:" + a);
        return this.getInstance(a, b);
    }
    md.getInstance.overload('java.lang.String').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("算法名:" + a);
        return this.getInstance(a);
    }
    md.update.overload('[B').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("update:" + bytesToString(a))
        return this.update(a);
    }
    md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
        return this.update(a, b, c);
    }
    md.digest.overload().implementation = function () {
        //showStacks();
        console.log("======================================");
        var result = this.digest();
        console.log("digest结果:" + bytesToHex(result));
        console.log("digest结果:" + bytesToBase64(result));
        return result;
    }
    md.digest.overload('[B').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("digest参数:" + bytesToString(a));
        var result = this.digest(a);
        console.log("digest结果:" + bytesToHex(result));
        console.log("digest结果:" + bytesToBase64(result));
        return result;
    }

    var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec');
    ivParameterSpec.$init.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.$init(a);
        console.log("======================================");
        console.log("iv向量: |str:" + bytesToString(a));
        console.log("iv向量: |hex:" + bytesToHex(a));
        return result;
    }

    var cipher = Java.use('javax.crypto.Cipher');
    cipher.getInstance.overload('java.lang.String').implementation = function (a) {
        //showStacks();
        var result = this.getInstance(a);
        console.log("======================================");
        console.log("模式填充:" + a);
        return result;
    }
    cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) {
        //showStacks();
        var result = this.init(a, b);
        console.log("======================================");
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) {
        //showStacks();
        var result = this.init(a, b);
        console.log("======================================");
        
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        console.log("======================================");
        
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }
     
        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));

        return result;
    }
    cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

         var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) {
        //showStacks();
        var result = this.init(a, b, c, d);
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) {
        //showStacks();
        var result = this.update(a, b, c, d);
        if (N_ENCRYPT_MODE == a) 
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

         var bytes_key = b.getEncoded();
        console.log("init key:" + "|str**:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex**:" + bytesToHex(bytes_key));
        return result;
    }

    cipher.update.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.update(a);
        console.log("======================================");
        console.log("update:" + bytesToString(a));
        return result;
    }
    cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        var result = this.update(a, b, c);
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
        return result;
    }
    cipher.doFinal.overload().implementation = function () {
        //showStacks();
        var result = this.doFinal();
        console.log("======================================");
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }
    cipher.doFinal.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.doFinal(a);
        console.log("======================================");
        console.log("doFinal参数: |str  :"     + bytesToString(a));
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }

    var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec');
    x509EncodedKeySpec.$init.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.$init(a);
        console.log("======================================");
        console.log("RSA**:" + bytesToBase64(a));
        return result;
    }

    var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec');
    rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) {
        //showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        //console.log("RSA**:" + bytesToBase64(a));
        console.log("RSA**N:" + a.toString(16));
        console.log("RSA**E:" + b.toString(16));
        return result;
    }

    var KeyPairGenerator = Java.use('java.security.KeyPairGenerator');
    KeyPairGenerator.generateKeyPair.implementation = function () 
    {
        //showStacks();
        var result = this.generateKeyPair();
        console.log("======================================");
        
        var str_private = result.getPrivate().getEncoded();
        var str_public = result.getPublic().getEncoded();
        console.log("公钥  |hex" + bytesToHex(str_public));
        console.log("私钥  |hex" + bytesToHex(str_private));

        return result;
    }

    KeyPairGenerator.genKeyPair.implementation = function () 
    {
        //showStacks();
        var result = this.genKeyPair();
        console.log("======================================");

        var str_private = result.getPrivate().getEncoded();
        var str_public = result.getPublic().getEncoded();
        console.log("公钥  |hex" + bytesToHex(str_public));
        console.log("私钥  |hex" + bytesToHex(str_private));

        return result;
    }
});

11. 定位接口的实现类

Java.perform(function () {
    var classes = Java.enumerateLoadedClassesSync();
    for (const index in classes) {
        var className = classes[index];
        if(className.indexOf("com.xxx") === -1) continue;
        var clazz = Java.use(className);
        var resultArr = clazz.class.getInterfaces();
        if(resultArr.length === 0) continue;
        for (let i = 0; i < resultArr.length; i++) {
            if(resultArr[i].toString().indexOf("com.xxx.app.TestRegisterClass") !== -1){
                console.log(className, resultArr);
            }
        }
    }
});

12. 定位抽象类的实现类

Java.perform(function () {
    var classes = Java.enumerateLoadedClassesSync();
    for (const index in classes) {
        var className = classes[index];
        if(className.indexOf("com.xxx") === -1) continue;
        var clazz = Java.use(className);
        var resultClass = clazz.class.getSuperclass();
        if(resultClass == null) continue;
        if(resultClass.toString().indexOf("com.xxx.app.TestAbstract") !== -1){
            console.log(className, resultClass);
        }
    }
});

13. byte转字符串

// byte[]转string
// 完美解决中文乱码的问题
// 网上的常规思路是将数组变为16进制字符串,然后再每两位转化成字符,这样会带来中文乱码的问题


function utf8ByteToUnicodeStr(utf8Bytes) {
    var unicodeStr = "";
    for (var pos = 0; pos < utf8Bytes.length;) {
        var flag = utf8Bytes[pos];
        var unicode = 0;
        if ((flag >>> 7) === 0) {
            unicodeStr += String.fromCharCode(utf8Bytes[pos]);
            pos += 1;

        } else if ((flag & 0xFC) === 0xFC) {
            unicode = (utf8Bytes[pos] & 0x3) << 30;
            unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24;
            unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos + 5] & 0x3F);
            unicodeStr += String.fromCharCode(unicode);
            pos += 6;

        } else if ((flag & 0xF8) === 0xF8) {
            unicode = (utf8Bytes[pos] & 0x7) << 24;
            unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos + 4] & 0x3F);
            unicodeStr += String.fromCharCode(unicode);
            pos += 5;

        } else if ((flag & 0xF0) === 0xF0) {
            unicode = (utf8Bytes[pos] & 0xF) << 18;
            unicode |= (utf8Bytes[pos + 1] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos + 2] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos + 3] & 0x3F);
            unicodeStr += String.fromCharCode(unicode);
            pos += 4;

        } else if ((flag & 0xE0) === 0xE0) {
            unicode = (utf8Bytes[pos] & 0x1F) << 12;;
            unicode |= (utf8Bytes[pos + 1] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos + 2] & 0x3F);
            unicodeStr += String.fromCharCode(unicode);
            pos += 3;

        } else if ((flag & 0xC0) === 0xC0) { //110
            unicode = (utf8Bytes[pos] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos + 1] & 0x3F);
            unicodeStr += String.fromCharCode(unicode);
            pos += 2;

        } else {
            unicodeStr += String.fromCharCode(utf8Bytes[pos]);
            pos += 1;
        }
    }
    return unicodeStr;
}

14 trace方法调用

一键hook所有类和方法,见https://github.com/r0ysue/r0tracer

1 + 9 =
快来做第一个评论的人吧~