首页
社区
课程
招聘
[分享]Frida Hook Java 基础
发表于: 2023-7-15 09:18 5438

[分享]Frida Hook Java 基础

2023-7-15 09:18
5438

前言

本文主要介绍frida框架java层应用。对常用的类、方法、静态、动态的主动调用、RPC调用等。

1.启动方式

spawn模式:

1
frida -U -f com.xxx.android -l hook.js  --no-pause

attach模式 :

1
frida -UF -l hook.js

2.frida_server自定义端口

1
2
3
4
5
6
7
8
frida server 默认端口:27042
 
taimen:/ $ su
taimen:/ # cd data/local/tmp/
taimen:/data/local/tmp # ./fs1280 -l 0.0.0.0:6666
 
使用默认端口:
taimen:/data/local/tmp # ./fs1280 -l 0.0.0.0

3.RPC

sign.js 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sign(inputStr){
    var result = null;
    Java.perform(function(){
        Java.choose("com.example.xxxx.MainActivity",{
            onMatch: function(ins){
                result = ins.method02(inputStr);
            },
            onComplete: function(){}
        })
    });
    return result;
}
 
rpc.exports = {
    sign: sign
}

sign.py 模板

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import frida
import json
from flask import Flask, jsonify, request
 
def message(message, data):
    if message['type'] == 'send':
        print(f"[*] {message['payload']}")
    else:
        print(message)
 
# ./fs120800 -l "0.0.0.0:6666"
# adb wifi 10.0.0.123
# 远程 frida-server 路径 adb wifi 的 ip : frida-server 启动的端口
 
'''
模式一:attach模式
'''
session=frida.get_device_manager().add_remote_device('192.168.xxx.91:6666').attach('com.xxxx.live')
 
'''
模式二:spawn模式
'''
device = frida.get_remote_device()
pid = device.spawn(['com.xxxx.demo'])   #包名i,或者说是进程名吧
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
 
with open("sign.js") as f:
    jsCode = f.read()
 
script = session.create_script(jsCode)
script.on("message", message)
script.load()
 
timeStr = str(int(time.time()))
decodeResult = script.exports.sign(timeStr)
 
app = Flask(__name__)
 
@app.route('/sign_dt', methods=['POST']) 
def sign_dt():
    time_sign = str(int(time.time()))
    return time_sign
 
if __name__ == "__main__":
    app.run()

4.RPC 内网穿透,外网可访问

1
2
# 这里用的ssh当然还有很多工具,不过配置比较麻烦。准备好一台服务器,外网端口开放的是8079,你们随意。
ssh -fCNR 8079:localhost:5002  root@124.xxx.xxx.xxx

5.常用的转换函数的封装

打印调用栈

1
2
3
var throwable = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
 
var exception = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());

base64ToString

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
26
27
28
29
30
31
32
33
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
}

stringToBase64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
}

hexToBase64

1
2
3
function hexToBase64(str) {
    return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}

base64ToHex

1
2
3
4
5
6
7
8
9
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("");
}

hexToBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

bytesToHex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

stringToHex

1
2
3
4
5
6
7
8
9
10
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
}

stringToBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}

bytesToString

1
2
3
4
5
6
7
8
9
10
11
function bytesToString(arr) {
    if (typeof arr === 'string') {
        return arr;
    }
    var str = "";
    arr = new Uint8Array(arr);
    for (var i in arr) {
        str += String.fromCharCode(arr[i]);
    }
    return str
}

bytesToBase64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
}

base64ToBytes

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
26
27
28
29
30
31
32
33
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
}

array 转成 string

1
2
3
4
5
6
7
8
function array2string(array) {
    var buffer = Java.array('byte', array);
    var result = '';
    for (var i = 0; i < buffer.length; i++) {
        result += (String.fromCharCode(buffer[i]))
    }
    return result;
}

Uint8Array转字符串

1
2
3
4
5
6
7
8
function Uint8ArrayToString(fileData) {
    var dataString = "";
    for (var i = 0; i < fileData.length; i++) {
        dataString += String.fromCharCode(fileData[i]);
        // console.log(dataString)
    }
    return dataString
}

byte数组转16进制字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
function byteToHexString(uint8arr) {
    if (!uint8arr) {
        return '';
    }
    var hexStr = '';
    for (var i = 0; i < uint8arr.length; i++) {
        var hex = (uint8arr[i] & 0xff).toString(16);
        hex = (hex.length === 1) ? '0' + hex : hex;
        hexStr += hex;
    }
 
    return hexStr.toUpperCase();
}

字符转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function dataShow(tag, data) {
    Java.perform(function () {
        var ByteString = Java.use("com.android.okhttp.okio.ByteString");
        //tag为标签,data为数据
        //toBase64 1
        try {
            console.log(tag + " toBase64: ", ByteString.of(data).base64());
        } catch (e) {
        }
        try {
            //toHex 2
            console.log(tag + " toHex: ", ByteString.of(data).hex());
        } catch (e) {
        }
        try {
            //toUtf8 3
            console.log(tag + " toUtf8: ", ByteString.of(data).utf8());
        } catch (e) {
        }
    });
}

6.Hook普通方法

1
2
3
4
5
6
7
8
9
10
11
12
Java.perform(function(){
       var UtilsClass = Java.use("com.xx.app.Utils");
       UtilsClass.getCalc.implementation = function (a,b){
         // 打印信息
         console.log('a:' + a + ' ' + 'b:' + b);
         // 调用原方法获取结果
         var value = this.getCalc(a, b);
         console.log('result:',value);
           // 修改返回值
         return 666666;
       }
   })

7.hook重载方法

1
2
3
4
5
6
7
8
var UtilsClass = Java.use("com.kevin.app.Utils");
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);
    }           

8.Hook指定方法的所有重载

1
2
3
4
5
6
7
8
9
10
11
12
var ClassName = Java.use("com.xiaojianbang.app.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);
}
   

9.Hook 构造方法 $init

1
2
3
4
5
var MoneyClass = Java.use("com.kevin.app.Money");
MoneyClass.$init.overload().implementation = function(){
  console.log("hook Money $init");
  this.$init();
}

10.主动调用

静态变量和方法

1
2
3
4
5
Java.perform(function () {
      var classz = Java.use("com.xxxxxx"); //要hook的类
      classz.abc();    //调用静态方法
      classz.cc.value = true; //静态变量
  });

动态变量

1
2
3
4
5
6
7
8
Java.choose("com.xxxxxx",{  //要hook的类
    onMatch:function(instance){
        instance._same_name_bool_var.value = true; //设置对应变量
    },
    onComplete:function(){
 
    }
});

动态的方法

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
26
27
28
29
30
31
1.通过Java.choose 从内存中找到实例化好的类进行调用
function Proactive_call(data) {
    var ret = null;
    Java.perform(function () {
        Java.choose("com.xxxx.xxxx",{    //要hook的类
            onMatch:function(instance){
                console.log("onMatch ");
                ret=instance.xxxx(data); //要hook的方法
            },
            onComplete:function(){
                console.log("result: " + ret);
            }
        });
    })
    return ret;
}
 
 
2.通过实例化 obj.$new() 对象方法,如果构造函数有参数,那么一定要填参数 $new(参数,…)
 
function Proactive_call(data) {
    var ret = null;
    Java.perform(function () {
        var classname = Java.use("com.xxxxxx"); //类名
        var obj = classname.$new();
        ret = obj.xxxx(data); //xxxx为对应方法名
        console.log("result: " + ret);
        return ret;
 
    })
}

11.hook内部类和匿名类

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
26
27
Java.perfor(function(){
        // hook 内部类
        // 内部类使用$进行分隔 不使用.
        var InnerClass = Java.use("com.xiaojianbang.app.Money$innerClass");
        // 重写内部类的 $init 方法
        InnerClass.$init.overload("java.lang.String","int").implementation = function(x,y){
            console.log("x: ",x);
            console.log("y: ",y);
            this.$init(x,y);
        }
    })
 
 
// 接口, 抽象类, 不可以被new
// 接口, 抽象类 要使用必须要实例化, 实例化不是通过new, 而是通过实现接口方法, 继承抽象类等方式
// new __接口__{} 可以理解成 new 了一个实现接口的匿名类, 在匿名类的内部(花括号内),实现了这个接口
 
function main(){
    Java.perform(function(){
        // hook 匿名类
        // 匿名类在 smail中以 $1, $2 等方式存在, 需要通过 java 行号去 smail 找到准确的匿名类名称
        var NiMingClass = Java.use("com.xiaojianbang.app.MainActivity$1");
        NiMingClass.getInfo.implementation = function (){
            return "kevin change 匿名类";
        }
    })
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//