开发环境为 Visual Studio Code
-
新建目录 frida-demo
-
在目录中创建 package.json,内容如下,依赖不指定版本,均为最新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{
"name": "frida-demo",
"version": "1.0.0",
"description": "Example Frida agent written in TypeScript",
"private": true,
"main": "index.ts",
"scripts": {
"prepare": "npm run build",
"build": "frida-compile.cmd index.ts -o _agent.js -c",
"watch": "frida-compile.cmd index.ts -o _agent.js -w"
},
"devDependencies": {
"@types/frida-gum": "*",
"@types/node": "*",
"frida-compile": "*"
}
}
|
-
在目录中创建 tsconfig.json,内容如下
1
2
3
4
5
6
7
8
9
10
|
{
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"allowJs": true,
"noEmit": true,
"strict": true,
"esModuleInterop": true
}
}
|
-
新建一个 TypeScript 文件,例如 index.ts
-
初始化项目
-
启动 watch 功能监控文件更改,自动编译
在 vs code 中打开 package.json, 点击”调试“,选择 watch 即可。这样每次修改保存 index.ts 后,都会自动进行编译。
启动 app,执行命令获取到当前登录页面的 Activity 名
1
2
|
# adb shell dumpsys window | grep mCurrentFocus
mCurrentFocus=Window{1f38bfa u0 com.example.androiddemo/com.example.androiddemo.Activity.LoginActivity}
|
使用 jadx 打开 apk,找到 LoginActivity,可以看到登录逻辑如下
我们 hook 这里的函数 a,修改返回值为 1,这样我们用户名任意输入,密码输入 1 即可正常登录。
1
2
3
4
5
6
7
8
9
10
|
function hookSign() {
Java.perform(function () {
var LoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity");
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str1: any, str2: any) {
var result = this.a(str1, str2);
console.log("str1, str2, result is =>", str1, str2, result);
return "1";
}
});
}
|
在 jadx 中拿到字符串,hook 函数,修改返回值即可。
1
2
3
4
5
6
7
8
9
|
function frida1() {
Java.perform(function () {
var FridaActivity1 = Java.use("com.example.androiddemo.Activity.FridaActivity1");
FridaActivity1.a.implementation = function (x: any) {
console.log("com.example.androiddemo.Activity.FridaActivity1.a");
return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
}
})
}
|
直接修改变量的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function frida2_1() {
Java.perform(function () {
var FridaActivity2 = Java.use("com.example.androiddemo.Activity.FridaActivity2");
FridaActivity2.static_bool_var.value = true;
Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
onMatch: function (ins) {
console.log("ins found =>", ins);
ins.bool_var.value = true;
}, onComplete: function () {
console.log("Search completed!");
}
})
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function frida2_2() {
Java.perform(function () {
Java.use("com.example.androiddemo.Activity.FridaActivity2").setStatic_bool_var();
Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
onMatch: function (ins) {
console.log("ins found =>", ins);
ins.setBool_var();
}, onComplete: function () {
console.log("Search completed!");
}
})
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
function frida2_3() {
Java.perform(function () {
Java.use("com.example.androiddemo.Activity.FridaActivity2").$init.implementation = function () {
console.log("Frida2 hook init function");
this.$init();
// this.setBool_var();
// this.setStatic_bool_var();
this.bool_var.value = true;
this.static_bool_var.value = true;
}
})
}
|
1
2
3
4
5
6
7
8
9
10
11
|
function frida3() {
Java.perform(function () {
Java.use("com.example.androiddemo.Activity.FridaActivity3").$init.implementation = function () {
console.log("Frida3 hook init function");
this.$init();
this.static_bool_var.value = true;
this.bool_var.value = true;
this._same_name_bool_var.value = true;
}
})
}
|
内部类使用 $ 连接
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
|
function frida4_1() {
Java.perform(function () {
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check1.implementation = function () {
console.log("InnerClasses.check1 called!");
return true;
}
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check2.implementation = function () {
console.log("InnerClasses.check2 called!");
return true;
}
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check3.implementation = function () {
console.log("InnerClasses.check3 called!");
return true;
}
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check4.implementation = function () {
console.log("InnerClasses.check4 called!");
return true;
}
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check5.implementation = function () {
console.log("InnerClasses.check5 called!");
return true;
}
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check6.implementation = function () {
console.log("InnerClasses.check6 called!");
return true;
}
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function frida4_2() {
Java.perform(function () {
var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses";
var all_methods = Java.use(class_name).class.getDeclaredMethods();
for (var i = 0; i < all_methods.length; i++) {
var method = all_methods[i];
var methodStr = method.toString();
var substring = methodStr.substr(methodStr.indexOf(class_name) + class_name.length + 1);
var funcname = substring.substr(0, substring.indexOf("("));
Java.use(class_name)[funcname].implementation = function () {
return true;
}
}
})
}
|
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
|
function frida5() {
// Java.perform(function () {
// var groups = Java.enumerateMethods("*!check");
// console.log(JSON.stringify(groups, null, 2));
// })
var getDynamicDexCheckClassName = "";
Java.choose("com.example.androiddemo.Activity.FridaActivity5", {
onMatch: function (instance) {
getDynamicDexCheckClassName = instance.getDynamicDexCheck().$className;
}, onComplete: function () {
}
});
console.log("className is =>", getDynamicDexCheckClassName);
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass(getDynamicDexCheckClassName)) {
console.log("Successfully found loader");
//Java.classFactory.loader 属性为只读,需要进入源码,删除只读属性
Java.classFactory.loader = loader;
console.log("Switch Classloader Successfully ! ")
}
} catch (error) {
console.warn("continuing =>", error)
}
}, onComplete: function () {
console.log("EnumerateClassloader END")
}
})
Java.use(getDynamicDexCheckClassName).check.implementation = function () {
return true;
}
}
|
1
2
3
4
5
6
7
|
function frida6_1() {
Java.perform(() => {
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class0").check.implementation = function () { return true; };
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class1").check.implementation = function () { return true; };
Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class2").check.implementation = function () { return true; };
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function frida6_2() {
Java.perform(() => {
Java.enumerateLoadedClasses({
onMatch: function (classname) {
if (classname.toString().indexOf("com.example.androiddemo.Activity.Frida6.Frida6Class") >= 0) {
console.log("Found Class => ", classname);
Java.use(classname).check.implementation = function () { return true; }
}
}, onComplete: function () {
console.log("Search Class Completed!");
}
})
})
}
|