-
-
[分享]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 匿名类" ; } }) } |
赞赏记录
参与人
雪币
留言
时间
你瞒我瞒
为你点赞~
2023-7-17 09:14
Russohan
为你点赞~
2023-7-16 10:01
西贝巴巴
为你点赞~
2023-7-15 16:26
赞赏
他的文章
看原图
赞赏
雪币:
留言: