Frida Hook Java 層
Frida兩種啟動方式的區別
span
模式:frida 重新打開一個進程
frida -U -f 包名 -l js路徑 --no-pause
attch
模式: 附加在當前打開的進程
frida -U -l js路徑 --no-pause
區別就是一個帶-f
一個不帶
命令行參數詳解
--version 顯示版本號
-h, --help 查看幫助
-D ID, --device=ID 用給定的ID連接到設備
-U, --usb 連接 USB 設備
-R, --remote 連接遠程設備
-H HOST, --host=HOST 連接遠程的設備 地址
-f FILE, --file=FILE spawn FILE 模式
-F, --attach-frontmost
attach to frontmost application 附加到最前端的應用程序
-n NAME, --attach-name=NAME
attach to NAME 附加的名稱
-p PID, --attach-pid=PID
attach to PID 附加的進程ID
--stdio=inherit|pipe stdio behavior when spawning (defaults to “inherit”)
--runtime=duk|v8 script runtime to use (defaults to “duk”) 指定js解釋器 duk 或者 v8
--debug enable the Node.js compatible script debugger
-l SCRIPT, --load=SCRIPT 加載腳本文件
load SCRIPT
-P PARAMETERS_JSON, --parameters=PARAMETERS_JSON
Parameters as JSON, same as Gadget
-C CMODULE, --cmodule=CMODULE
load CMODULE
-c CODESHARE_URI, --codeshare=CODESHARE_URI
load CODESHARE_URI
-e CODE, --eval=CODE evaluate CODE
-q quiet mode (no prompt) and quit after -l and -e
--no-pause automatically start main thread after startup 啟動后自動啟動主線程
-o LOGFILE, --output=LOGFILE 日志輸出文件
output to log file
--exit-on-error exit with code 1 after encountering any exception in
the SCRIPT 異常退出腳本
Hook一個Java層函數
- Java.use
功能:
動態為className生成一個JavaScript包裝器;可以通過調用$new()來實例化對象來調用構造函數
參數: className - implementaion
功能: 方法重新實現的屬性 - overloads
功能: 重載函數指定類型方法
示例:
function hook_java() {
// 必須寫在 Java 虛擬機中
Java.perform(function() {
const login = Java.use('com.example.androiddemo.Activity.LoginActivity');
login.a.overload('java.lang.String', 'java.lang.String').implementation = function(a1, a2) {
let result = this.a(a1, a2);
console.log(a1, a2, result);
return result
}
})
}
修改一個函數返回值或參數
示例:
function hook_java() {
// 必須寫在 Java 虛擬機中
Java.perform(function() {
const login = Java.use('com.example.androiddemo.Activity.LoginActivity');
const string = Java.use('java.lang.String');
login.a.overload('java.lang.String', 'java.lang.String').implementation = function(a1, a2) {
// string int bool 這3種類型 frida 會直接幫我們轉換成 java 類型的
var result = '這是我修改返回的'
// 我們自己進行類型轉換
var result = string.$new("我們自己去轉換的java類型的")
return result
}
})
}
調用靜態函數和非靜態函數
靜態函數和靜態方法可以使用 Java.use
出來的函數直接調用。Java.use
相當於new了一個新的對象
非靜態函數和靜態方法,有兩種使用情況,一種是 Java.use
出來的對象,自己進行實例化 obj.$new()
這種方法需要知道構造參數,比較麻煩。
那么可以使用 Java.choose
該方法是從內存中找到實例化好的類進行調用
示例:
function hook_java() {
// 必須寫在 Java 虛擬機中
Java.perform(function() {
// 調用靜態方法
var frida_activate = Java.use('com.example.Frida');
frida_activate.setStatic_bool)var()
// 調用非靜態方法
Java.choose('com.example.androiddemo.Activity.FridaActivity2', {
// 匹配到的情況下,進行調用
// 注意: 內存中可以有多個實例化好的類?
onMatch: function (instance) {
// 調用方法
instance.setBool_var()
// 設置變量值
instance.static_bool_var.value = true;
},
onComplete: function () {
}
})
})
}
設置成員變量
設置和函數名相同的成員變量
屬性和函數名重名了,需要在屬性前面 加一個 下划線 _
示例:
function hook_java() {
// 必須寫在 Java 虛擬機中
Java.choose('com.example.androiddemo.Activity.FridaActivity3', {
onMatch: function (instance) {
// instance.static_bool_var.value = true;
instance.bool_var.value = true;
// TODO 關注一下這個屬性,他跟函數名重名了,需要加一個 _ 下划線
instance._same_name_bool_var.value = true;
console.log(instance.bool_var.value, instance._same_name_bool_var.value)
},
onComplete: function () {
}
})
}
Hook內部類,枚舉類的函數
示例:
function call_frida4() {
// hook 類的多個函數, 內部類
Java.perform(function () {
// 這是hook類的 內部類
const FridaActivity4 = Java.use('com.example.androiddemo.Activity.FridaActivity4$InnerClasses');
// getDeclaredMethods 獲取當前類的方法 getMethods 獲取當前類以及繼承類的所有方法
const methods = FridaActivity4.class.getDeclaredMethods();
for (let method of methods) {
// console.log(method)
let method_str = method.toString();
let mtdsplit = method_str.split('.')
let meds = mtdsplit[mtdsplit.length - 1].replace('()', '');
console.log(meds)
FridaActivity4[meds].implementation = function () {
return true
}
}
})
}
查找接口,Hook動態加載dex
示例:
function call_frida5() {
// hook 動態加載的dex 類, 以及查看類的類名
Java.perform(function () {
// 1. 嘗試 hook 一下 接口類
// 會提示 Error: java.lang.ClassNotFoundException:
// Java.use('com.example.androiddemo.Dynamic.AbstractC0000CheckInterface');
// 2. 查看動態加載的類名
const FridaActivity5 = Java.use('com.example.androiddemo.Activity.FridaActivity5');
FridaActivity5.getDynamicDexCheck.implementation = function () {
let result = this.getDynamicDexCheck();
// 查看當前返回值的 類名
console.log(result.$className)
return result
}
// 這個時候還是 沒有找到類 需要將 類 loader 進來
// Java.use('com.example.androiddemo.Dynamic.DynamicCheck').check.implementation = function (){
// return true
// }
// 3. hook 動態加載的 dex
// 枚舉類加載
Java.enumerateClassLoaders({
onMatch: function (loader) {
console.log(loader)
try {
if (loader.findClass('com.example.androiddemo.Dynamic.DynamicCheck')) {
console.log(loader);
// 切換classloader
Java.classFactory.loader = loader
}
} catch (e) {
// console.log(e)
}
},
onComplete: function () {
}
})
// 這個時候再來 hook 這個類
Java.use('com.example.androiddemo.Dynamic.DynamicCheck').check.implementation = function () {
return true
}
})
}
枚舉class
示例:
function call_frida6() {
// hook 多個 class
// 發現一個關鍵點 就是 當一個類 沒有執行的時候, frida 枚舉是枚舉不出來的。
Java.perform(function () {
// 遍歷當前 loader 的 所有類
Java.enumerateLoadedClasses({
onMatch: function (name, handle) {
// console.log(name)
if (name.indexOf('com.example.androiddemo.Activity.Frida6') >= 0) {
console.log(name);
Java.use(name).check.implementation = function () {
return true;
}
}
},
onComplete: function () {
}
})
})
}
Hook 構造函數
$init 表示構造函數
示例:
function call_frida6() {
Java.perform(function () {
// $init 表示構造函數
Java.use('com.example.androiddemo.Dynamic.DynamicCheck').$init.implementation = function (a,b) {
return this.$init(a,b)
}
})
}
打印調用棧
示例:
function show_stack() {
Java.perform(function () {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
})
}
加載本地DEX
// 打開 dex 文件
var dex = Java.openClassFile('/data/local/tmp/ddex.dex')
Java.perform(function () {
// 加載 dex
dex.load()
// 進行hook
Java.use('com.tlamb96.kgbmessenger.LoginActivity').j.implementation = function () {
return true
}
})