Frida逆向与利用自动化

Kali

kali里面时间老是不对,其实只是时区不对而已,一个命令就搞定:

dpkg-reconfigure tzdata 然后选择Asia→Shanghai,然后重启即可。

KaliLinux默认不带中文

apt install xfonts-intl-chinese

apt install ttf-wqy-microhei

kali装中文输入法,参考https://blog.csdn.net/qq_42333641/article/details/89325576

apt install htop 动态查看当前活跃占用高的进程。

apt install jnettop 流量查看工具。

wifiadb qtscrcpy wifi群控

安装pyenv https://github.com/pyenv/pyenv-installer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sudo apt-get update; sudo apt-get install make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

curl https://pyenv.run | bash
proxychains curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

mkdir ~/.pyenv
proxychains git clone https://github.com/pyenv/pyenv.git ~/.pyenv
ls .pyenv/
echo $HOME
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
ls .pyenv/bin/
source .bash_profile
pyenv
pyenv --version # 获取pyenv版本号


echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\n fi' >> ~/.bashrc 添加"pyenv init -"到shell
exec "$SHELL"
pyenv install 3.8.0

安装vscode

dpkg -i code_1.45.1-1589445602_amd64.deb

安装node

1
2
apt update
apt install -y nodejs

刷机

1
2
3
4
5
6
7
8
9
10
11
wget https://dl.google.com/dl/android/aosp/sailfish-opm1.171019.011-factory-56d15350.zip
下载https://developer.android.com/studio/releases/platform-tools
unzip platform-tools_r33.0.1-linux.zip
cd platform-tools && vim ~/.bashrc
export PATH="/root/platform-tools:$PATH" 有了fastboot
unzip sailfish-opm1.171019.011-factory-56d15350.zip
关机键+音量减 启动到bootloader
./flash-all.sh
重启进入BootLoader
fastboot boot twrp-3.3.1-0-sailfish.img
安装 magisk

frida

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. git clone https://github.com/oleavr/frida-agent-example.git
2. cd frida-agent-example/
3. npm install
4. 使用VSCode等IDE打开此工程,在agent下编写typescript,会有智能提示。
5. npm run watch会监控代码修改自动编译生成js文件
6. frida -U -f com.example.android --no-pause -l _agent.js

想要使用基于特定frida版本的objection,只需先安装好特定版本的frida和frida-tools(星球里搜“特定版本”有对应关系),再去objection的release里找那个日期之后一点点的版本。比如以frida 12.8.0版本举例:
pip install frida==12.8.0
pip install frida-tools==5.3.0
pip install objection==1.8.4
按照这个顺序,在装objection的时候,就会直接Requirement already satisfied,不会再去下载新的frida来安装了。

function main(){
Java.perform(function(){
console.log("Inside Frida Java Perform !")
})
}
setImmediate(main)

模拟器frida-server012.9.4-android-x86.xz

img

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
android hooking list activities
android intent launch_activity com.android.settings.DisplaySettings 启动activity
android hooking list services
memory list modules 进程中加载的so
memory list exports libssl.so 查看导出函数地址
android heap search instances com.android.setting.DisplaySettings --fresh 查看类是否被实例化
android heap execute 0x2286 getPreferenceScreenResId 主动调用

# android heap evaluate 0x2526 也可以在找到的实例上直接编写js脚本
(The handle at `0x2526` will be available as the `clazz` variable.)

console.log("evaluate result:"+clazz.getPreferenceScreenResId())

JavaScript capture complete. Evaluating...
Handle 0x2526 is to class com.android.settings.DisplaySettings
evaluate result:2132082764

android hooking search classes display 搜索相关类
android hooking search methods display 搜索相关方法
android hooking list class_methods sum.util.locale.LocaleUtils 查看类所有方法
android hooking watch class_method android.bluetooth.BluetoothDevice.equals --dump-args --dump-backtrace --dump-return
android hooking watch class java.io.File.$init

proxychains git clone https://github.com/hluwa/Wallbreaker.git
plugin load /root/Wallbreaker
plugin wallbreaker classdump --fullname android.bluetooth.BluetoothDevice
plugin wallbreaker objectdump --fullname 0x12aq
plugin wallbreaker objectsearch android.view.View$ListenerInfo 搜索实例

proxychains git clone https://github.com/hluwa/FRIDA-DEXDump.git
plugin load /root/FRIDA-Dexdump
plugin dexdump dump 脱壳

objection无法hook native
objection -g packageName explore --startup-command 'android hooking watch xxx' 如果找不到进程就去spawn启动,看来是自带spawn

实用FRIDA进阶:内存漫游、hook anywhere、抓包

jadx-gui-1.3.5-no-jre-win.exe 反编译软件

java -jar abe-all.jar unpack 1.ab 1.tar 其中abe.jar 安卓备份文件

1
2
wget https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2021.1.1.23/android-studio-2021.1.1.23-linux.tar.gz
./bin/studio.sh

frida -U com.example.lesson -l lesson.js

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
48
49
50
51
52
53
54
55
56
function main(){
Java.perform(function(){
/*
// 重载
Java.use("com.example.lesson4one.MainActivity").fun.overload('int', 'int').implementation = function(arg1,arg2){
var result = this.fun(arg1,arg2);
// 打印调用栈
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("arg1,arg2,result",arg1,arg2,result)
return 800;
}
Java.use("com.example.lesson4one.MainActivity").fun.overload('java.lang.String').implementation = function (arg1 ){

var result = this.fun(Java.use('java.lang.String').$new("NIHAOJAVA"));
console.log("arg1,result",arg1,result)
return Java.use('java.lang.String').$new("NIHAOJS");
}
// 主动调用动态函数
Java.choose("com.example.lesson4one.MainActivity",{
onMatch:function(instance){
console.log("found instance :",instance)
console.log("found instance :",instance.secret())
},onComplete:function(){}
})
// 主动调用静态函数
var result = Java.use("com.example.lesson4one.MainActivity").secret2();
console.log(result);
*/

Java.use("net.sqlcipher.database.SQLiteOpenHelper").getWritableDatabase.overload('java.lang.String').implementation = function (x){
var result = this.getWritableDatabase(x);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("xString,result",x,result);
return result;
}
Java.use("net.sqlcipher.database.SQLiteOpenHelper").getWritableDatabase.overload('[C').implementation = function (x){
var result = this.getWritableDatabase(x);
console.log("xCharSe,result",x,result);
return result;
}
})
}
setImmediate(main)

function (){
Java.perform(function(){
Java.choose("com.example.yaphetshan.tencentwelcome.MainActivity",{
onMatch:function(instance){
console.log("found insttance ",instance);
console.log("invoke instance.a ",instance.a());
},onComplete:function(){console.log("search completed !")}
})
})
}

setTimeout(invoke,2000)

Frida

npm i -g @types/frida-gum 安装vs的frida自动提示

frida_example_1.0

frida -U -f com.frida.example -l hook.js –no-pause

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
function main() {
Java.perform(function () {

Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
//console.log(gson.$new().toJson(xxx));


/*
// 字符串
Java.use("java.lang.Character").toString.overload('char').implementation = function(x){
var result = this.toString(x);
console.log("x,result",x,result);
return "对象数组";
}
// 字符数组
Java.use("java.util.Arrays").toString.overload('[C').implementation = function (x){
var charArray = Java.array('char', [ '一','去','二','三','里' ]);
var result = this.toString(charArray);
// console.log("x,result",JSON.stringify(x),result);
console.log("x,result",gson.$new().toJson(charArray),result);
return Java.use('java.lang.String').$new(Java.array('char',['烟','村','四','五','家']));
}
// 字节数组
Java.use("java.util.Arrays").toString.overload('[B').implementation = function (x){
var result = this.toString(x);
//console.log("x,result",gson.$new().toJson(x),result);
console.log("x,result",x,result);
return result;
}


// 类型转换 父->子
var Waterhandle = null;
Java.choose("com.r0ysue.a0526printout.Water",{
onMatch:function(instance){
console.log("found instance:",instance);
console.log("water instance call still:",instance.still(instance));
Waterhandle = instance;
},onComplete:function(){console.log("search completed!")}
})
var JuiceHandle = Java.cast(Waterhandle,Java.use("com.r0ysue.a0526printout.Juice"));
console.log("Juice fillEnergy method :",JuiceHandle.fillEnergy());


// 类型转换 子->父
var JuiceHandle = null ;
Java.choose("com.r0ysue.a0526printout.Juice",{
onMatch:function(instance){
console.log("found instance :",instance);
console.log("filling energy,",instance.fillEnergy());
JuiceHandle= instance;
},onComplete:function(){"Search Completed!"}
})
var WaterHandle = Java.cast(JuiceHandle ,Java.use("com.r0ysue.a0526printout.Water"));
console.log("Water invoke still ", WaterHandle.still(WaterHandle));


// 注册一个接口类
var beer = Java.registerClass({
name: 'com.r0ysue.a0526printout.beer',
implements: [Java.use('com.r0ysue.a0526printout.liquid')],
methods: {
flow: function () {
console.log("look I`m beer!");
return "taste good!";
}
}
});
console.log("beer.flow:",beer.$new().flow());
*/

// 主动调用枚举实例方法
Java.choose("com.r0ysue.a0526printout.Signal",{
onMatch:function(instance){
console.log("found instance:",instance)
console.log("invoke getDeclaringClass",instance.getDeclaringClass())
},onComplete:function(){console.log("Search Completed!")}
})
})
}
//setImmediate(main,2000)



function hashmap888(){
Java.perform(function(){
/*
// 主动调用打印map
Java.choose("java.util.HashMap",{
onMatch:function(instance){
if(instance.toString().indexOf("ISBN")!=-1){
console.log("found HashMap",instance);
console.log("HashMap toString:",instance.toString());
}

},onComplete:function(){console.log("Search Completed!")}
})
*/
// hook map方法
Java.use("java.util.HashMap").put.implementation = function(x,y){
var result = this.put(x,y);
console.log("x,y,result",x,y,result)
return result;
}
})
}

setImmediate(hashmap888);

Non-ASCII有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int ֏(int x) {
return x + 100;
}

Java.perform(
function x() {
var targetClass = "com.example.hooktest.MainActivity";
var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();
for (var i in methods) {
console.log(methods[i].toString());
console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")));
}
hookCls[decodeURIComponent("%D6%8F")]
.implementation = function (x) {
console.log("original call: fun(" + x + ")");
var result = this[decodeURIComponent("%D6%8F")](900);
return result;
}
}
)

六层锁机案例

objection -g com.example.androiddemo explore

image-20220503192559469

image-20220503192835365

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function main(){
Java.perform(function(){
// 拿到result放入密码框即可 hook LoginActivity.a
// hook函数 打印入参及调用原返回值
// input text "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=" 自动输入文本
Java.use("com.example.androiddemo.Activity.LoginActivity").a.overload('java.lang.String', 'java.lang.String').implementation = function(x,y){
var result = this.a(x,y);
console.log("x,y,result",x,y,result);
return result;
}
// hook 修改返回值,没有调用原来的函数,直接返回值
Java.use("com.example.androiddemo.Activity.FridaActivity1").a.implementation = function (x){
console.log("first challenge 1")
return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=" ;
}
})
}

image-20220503193009705

1
2
3
4
5
6
7
8
9
10
11
12
13
function second(){
Java.perform(function(){
// 静态主动调用
Java.use("com.example.androiddemo.Activity.FridaActivity2").setStatic_bool_var();
// 非静态主动调用
Java.choose("com.example.androiddemo.Activity.FridaActivity2",{
onMatch:function(instance){
console.log("found instance :",instance);
instance.setBool_var()
},onComplete:function(){}
})
})
}

image-20220503200717670

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function third(){
Java.perform(function(){
// hook设置成员变量的值
Java.use("com.example.androiddemo.Activity.FridaActivity3").static_bool_var.value = true ;
Java.choose("com.example.androiddemo.Activity.FridaActivity3",{
onMatch:function(instance){
console.log("found instance :",instance);
//设置非静态成员变量的值
instance.bool_var.value = true ;
//设置有相同函数名的成员变量的值 same_name_bool_var
instance._same_name_bool_var.value = true ;
},onComplete:function(){}
})
})
}

image-20220503200858241

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 forth(){
//hook内部类 private static class InnerClasses
Java.perform(function(){
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check1.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check2.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check3.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check4.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check5.implementation = function(){return true};
Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check6.implementation = function(){return true};
})
}
function forth2(){
// 反射获取所有内部类的方法hook设置返回值 hook 类的多个函数
Java.perform(function(){
var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses" ;
var InnerClass = Java.use(class_name);
var all_methods = InnerClass.class.getDeclaredMethods();
//console.log(all_methods);
for(var i = 0;i<all_methods.length;i++){
var method = all_methods[i];
//console.log(method.toString());
var substring = method.toString().substr(method.toString().indexOf(class_name)+class_name.length+1);
var finalMethodString = substring.substr(0,substring.indexOf("("));
console.log(finalMethodString);
InnerClass[finalMethodString].implementation = function(){return true};
}
})
}

image-20220503202727892

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
function fifth(){
// hook 动态dex中的方法
Java.perform(function(){
Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
onMatch:function(instance){
// 拿到getDynamicDexCheck返回的类的className是DynamicCheck
console.log("found instance getDynamicDexCheck :",instance.getDynamicDexCheck().$className);
},onComplete:function(){console.log("search complete!")}
})
// 枚举所有classloader
Java.enumerateClassLoaders({
onMatch:function(loader){
try {
// 切换classloader
if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
console.log("Succefully found loader!",loader);
Java.classFactory.loader = loader;
}
} catch (error) {
console.log("found error "+error)

}
},onComplete:function(){"enum completed!"}
})
Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function(){return true};
})
}

image-20220503202932282

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function sixth (){
// 主动调用静态方法
Java.perform(function(){
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 };
})
}
function sixth2(){
Java.perform(function(){
// 枚举所有类 主动调用静态方法
Java.enumerateLoadedClasses({
onMatch:function(name,handle){
if(name.toString().indexOf("com.example.androiddemo.Activity.Frida6.Frida6")>=0){
console.log("name",name)
Java.use(name).check.implementation = function(){return true}
}
},onComplete(){}
})
})
}
setImmediate(sixth2);

获取枚举所有实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Java.perform(function(){
Java.enumerateLoadedClasses({
"onMatch" : function(classname){
if(classname.indexOf("com.csair.mbp") < 0){
return;
}
// 实现类 implements
try{
var hookCls = Java.use(classname)
var interFaces = hookCls.class.getInterfaces();
if (interFaces.length > 0) {
console.log(classname)
for (var i in interFaces) {
// 接口类 interFaces
console.log("\t", interFaces[i].toString())
}
}
}catch(e){
console.log(e)
}
},
"onComplete" : function(){}
})

kgb-messenger

image-20221011174718043

R.string.User在资源文件的resources.arsc/res/valus/strings.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function hook_java() { 

Java.perform(function () {
var System = Java.use("java.lang.System");
console.log(System);
//frida -U --no-pause -f com.tlamb96.spetsnazmessenger -l hook.js
//hook System,
System.getProperty.overload('java.lang.String').implementation = function (key) {
var result = this.getProperty(key);
result = "Russia";
console.log("System.getProperty:", key, result);
return result;
};

System.getenv.overload('java.lang.String').implementation = function (key) {
var result = this.getProperty(key);
result = "RkxBR3s1N0VSTDFOR180UkNIM1J9Cg==";
console.log("System.getenv:", key, result);
return result;
};

});
}

image-20221012080840299

R.string.username在资源文件的resources.arsc/res/valus/strings.xml

image-20221012081407315

image-20221012081924781

image-20221012082145842

image-20221012082555162

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
function hook_java() {
//var ddex = Java.openClassFile("/data/local/tmp/ddex.dex");
//frida动态加载了dex
/*
jar -cvf ddex.jar com/example/androiddemo/DecodeUtils.class
/Users/yang/Library/Android/sdk/build-tools/28.0.3/dx --dex --output=ddex.dex ddex.jar 生成dex的类DecodeUtils用来实现a算法逆向
*/
var ddex2 = Java.openClassFile("/data/local/tmp/ddex2.dex");

Java.perform(function () {
//frida动态加载了dex
ddex2.load();
var DecodeUtils = Java.use("com.example.androiddemo.DecodeUtils");
console.log("DecodeUtils.decode_p:", DecodeUtils.decode_p());
console.log("r to hex", DecodeUtils.r_to_hex());


var a = Java.use("com.tlamb96.kgbmessenger.b.a");
//hook 构造函数
a.$init.implementation = function (i, str, str2, z) {
this.$init(i, str, str2, z);
console.log("a.$init:", i, str, str2, z);
print_stack(); //打印了调用栈找到com.tlamb96.kgbmessenger.MessengerActivity.a
};

var MessengerActivity = Java.use("com.tlamb96.kgbmessenger.MessengerActivity");
MessengerActivity.a.implementation = function (str) {
console.log("MessengerActivity.a:", str);
var result = this.a(str);
console.log("MessengerActivity.a:", str, result);
return result;
};
});
}

function print_stack() {
Java.perform(function () {
var Exception = Java.use("java.lang.Exception");
var instance = Exception.$new("print_stack");
var stack = instance.getStackTrace();
console.log(stack);
instance.$dispose();
});
}

image-20221012083108436

image-20221012083120793

先解密r为text字符串,需要用到z3的库

image-20221012083317771

1
2
jar -cvf ddex.jar com/example/androiddemo/DecodeUtils.class
/Users/yang/Library/Android/sdk/build-tools/28.0.3/dx --dex --output=ddex.dex ddex.jar 生成dex的类DecodeUtils用来实现r的16进制字符串为0064736c707d6f510020646b73247c4d0068202b4159516700502a214d24675100
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
48
49
50
51
52
53
54
55
56
57
58
from z3 import *
// pip install z3-solver
from binascii import b2a_hex, a2b_hex

def main():
s = Solver()

r = "0064736c707d6f510020646b73247c4d0068202b4159516700502a214d24675100"
// 转成byte数组
r_result = bytearray(a2b_hex(r))
print(r_result)
// 字符串翻转
for i in range(int(len(r_result) / 2)):
c = r_result[i]
r_result[i] = r_result[len(r_result) - i - 1]
r_result[len(r_result) - i - 1] = c

print(b2a_hex(r_result))

// 将反转字符串转成字节数组
x = [BitVec("x%s" % i, 32) for i in range(len(r_result))]
for i in range(len(r_result)):
c = r_result[i]
print(i, hex(c))
s.add(((x[i] >> (i % 8)) ^ x[i]) == r_result[i]) #z3
// Solver转为字符串
if (s.check() == sat):
model = (s.model())
print(model)
flag = ""
for i in range(len(r_result)):
if (model[x[i]] != None):
flag += chr(model[x[i]].as_long().real)
else:
flag += " "
print('"' + flag + '"')
print(len(flag), len(r_result))


if __name__ == "__main__":
main()


'''
private String b(String str) {
char[] charArray = str.toCharArray();
for (int i = 0; i < charArray.length; i++) {
charArray[i] = (char) ((charArray[i] >> (i % 8)) ^ charArray[i]);
}
// 字符串翻转
for (int i2 = 0; i2 < charArray.length / 2; i2++) {
char c = charArray[i2];
charArray[i2] = charArray[(charArray.length - i2) - 1];
charArray[(charArray.length - i2) - 1] = c;
}
return new String(charArray);
}
'''

xman

image-20221012224252879

image-20221012224231686

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function hook_java() {
Java.perform(function () {
var MyApp = Java.use("com.gdufs.xman.MyApp");
// hook java 层的jni函数
MyApp.saveSN.implementation = function (str) {
console.log("MyApp.saveSN:", str);
this.saveSN(str);
};

var Process = Java.use("android.os.Process");
// 干掉killProcess
Process.killProcess.implementation = function (pid) {
//this.killProcess(pid);
console.log("Process.killProcess:", pid);
};

console.log("hook_java");
});
}

由于搜索导出函数没有saveSN,查看JNI_OnLoad和.init_array(ctrl+s没有函数),当registerNative不好找时**
frida_hook_libart
**

image-20221012225420693

off_5004是函数数组,双击进入

image-20221012225750559

saveSN在n2中,双击进入

image-20221012225852454

进入text View模式,设置opcode字节为4,函数开头2字节,中间4字节,是thumb格式函数,基地址+1才能正确hook

image-20221012230511023

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function hook_native() {
//获取模块的基址
var base_myjni = Module.findBaseAddress("libmyjni.so");
if (base_myjni) {
//获取模块的导出函数
var n2 = Module.findExportByName("libmyjni.so", "n2");
//thumb的函数,0x000011F8, 实际地址0xdba461f9
console.log("base_myjni:", base_myjni, "n2:", n2);
//hook模块的导出函数
Interceptor.attach(n2, {
onEnter: function (args) {
console.log("n2 onEnter:", args[0], args[1], args[2]);
}, onLeave: function (retval) {

}
});
}
}

image-20221012231056873

image-20221012232514658

MyApp中m=0,需要找到哪里对m设值,在initSN中n1函数

image-20221012232630088

image-20221012232638460

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
function hook_libart() {
var module_libart = Process.findModuleByName("libart.so");
var symbols = module_libart.enumerateSymbols(); //枚举模块的符号

var addr_GetStringUTFChars = null;
var addr_FindClass = null;
var addr_GetStaticFieldID = null;
var addr_SetStaticIntField = null;

for (var i = 0; i < symbols.length; i++) {
var name = symbols[i].name;
if (name.indexOf("art") >= 0) {
if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) {
if (name.indexOf("GetStringUTFChars") >= 0) {
console.log(name);
addr_GetStringUTFChars = symbols[i].address;
} else if (name.indexOf("FindClass") >= 0) {
console.log(name);
addr_FindClass = symbols[i].address;
} else if (name.indexOf("GetStaticFieldID") >= 0) {
console.log(name);
addr_GetStaticFieldID = symbols[i].address;
} else if (name.indexOf("SetStaticIntField") >= 0) {
console.log(name);
addr_SetStaticIntField = symbols[i].address;
}
}
}
}

//hook jni的一些函数
if (addr_GetStringUTFChars) {
Interceptor.attach(addr_GetStringUTFChars, {
onEnter: function (args) {
//打印调用栈
// console.log('addr_GetStringUTFChars onEnter called from:\n' +
// Thread.backtrace(this.context, Backtracer.FUZZY)
// .map(DebugSymbol.fromAddress).join('\n') + '\n');
}, onLeave: function (retval) {
// retval const char* 打印char*
console.log("addr_GetStringUTFChars onLeave:", ptr(retval).readCString(), "\r\n");
}
});
}

if (addr_FindClass) {
Interceptor.attach(addr_FindClass, {
onEnter: function (args) {
console.log("addr_FindClass:", ptr(args[1]).readCString());
}, onLeave: function (retval) {

}
});
}
if (addr_GetStaticFieldID) {
Interceptor.attach(addr_GetStaticFieldID, {
onEnter: function (args) {
console.log("addr_GetStaticFieldID:", ptr(args[2]).readCString(), ptr(args[3]).readCString());
}, onLeave: function (retval) {

}
});
}
if (addr_SetStaticIntField) {
Interceptor.attach(addr_SetStaticIntField, {
onEnter: function (args) {
console.log("addr_SetStaticIntField:", args[3]);
}, onLeave: function (retval) {

}
});
}
}

需要在n1函数中strcmp返回true时将v9设置为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function hook_libc() {
//hook libc的函数
var strcmp = Module.findExportByName("libc.so", "strcmp");
console.log("strcmp:", strcmp);
Interceptor.attach(strcmp, {
onEnter: function (args) {
var str_2 = ptr(args[1]).readCString();
if (str_2 == "EoPAoY62@ElRD") {
console.log("strcmp:", ptr(args[0]).readCString(),
ptr(args[1]).readCString());
}
}, onLeave: function (retval) {
}
});

}

将/sdcard/reg.dat中内容改为EoPAoY62@ElRD即可

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
function write_reg_dat() {

//frida 的api来写文件
var file = new File("/sdcard/reg.dat", "w");
file.write("EoPAoY62@ElRD");
file.flush();
file.close();
}
function write_reg_dat2() {

//把C函数定义为NativeFunction来写文件
var addr_fopen = Module.findExportByName("libc.so", "fopen");
var addr_fputs = Module.findExportByName("libc.so", "fputs");
var addr_fclose = Module.findExportByName("libc.so", "fclose");

console.log("addr_fopen:", addr_fopen, "addr_fputs:", addr_fputs, "addr_fclose:", addr_fclose);
var fopen = new NativeFunction(addr_fopen, "poin ter", ["pointer", "pointer"]);
var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);
var fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);

var filename = Memory.allocUtf8String("/sdcard/reg.dat");
var open_mode = Memory.allocUtf8String("w+");
var file = fopen(filename, open_mode);
console.log("fopen file:", file);

var buffer = Memory.allocUtf8String("EoPAoY62@ElRD");
var ret = fputs(buffer, file);
console.log("fputs ret:", ret);

fclose(file);
}

RPC

主动调用

lesson7lesson4.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function invoke(){
Java.perform(function(){
Java.choose("com.example.lesson4one.MainActivity",{
onMatch:function(instance){
console.log("found instance :",instance)
console.log("found instance :",instance.secret())
},onComplete:function(){}
})
})
}
//setTimeout(invoke,2000) 主动调用
rpc.exports = {
invokefunc:invoke
}

lesson7loader.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
import time
import frida

# frida默认27042 ./fs1280arm4 -l 0.0.0.0:9999
def my_message_handler(message,payload):
print(message)
print(payload)

#device = frida.get_usb_device() wifi连接可以实现多主机多手机多端口混连
device = frida.get_device_manager().add_remote_device("192.168.1.102:9999")
#device = frida.get_device_manager().add_remote_device("192.168.1.109:9999")
# 枚举所有进程
print(device.enumerate_processes())
# spawn模式
#pid = device.spawn(["com.example.lesson4one"])
#device.resume
#time.sleep(1)
#session = device.attach(pid)
# attach模式
session = device.attach("com.example.lesson4one")
with open("lesson7lesson4.js") as f :
script = session.create_script(f.read())
script.on("message",my_message_handler)
script.load()

command = ""
while True :
command = input("Enter Command:")
if command == "1":
break
elif command == "2":
script.exports.invokefunc()

接受回调

image-20220503211944602

lesson7sec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Java.perform(function(){
Java.use("android.widget.TextView").setText.overload('java.lang.CharSequence').implementation = function(x){
var string_to_send_x = x.toString();
var string_to_recv;
// 发送
send(string_to_send_x);
// 回调
recv(function(received_json_objection){
string_to_recv = received_json_objection.my_data
console.log("string_to_recv:"+string_to_recv)
}).wait();
var javaStringToSend=Java.use('java.lang.String').$new(string_to_recv);

var result = this.setText(javaStringToSend);
//console.log("x.toString(),result",x.toString(),result);
return result;
}
})

lesson7secloader.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
import time
import frida
import base64

def my_message_handler(message,payload):
print(message)
print(payload)
if message["type"]=="send":
print(message["payload"])
data = message["payload"].split(":")[1].strip()
print('message:',message)
data = str(base64.b64decode(data))
print('data:',data)
usr,pw = data.split(":")
print('pw:',pw)
// 修改输入用户名改为admin
data = str(base64.b64encode(("admin"+":"+pw).encode()))
print("encode data:",data)
script.post({"my_data":data})
print("Modified data sent !")

device = frida.get_usb_device()

#pid = device.spawn(["com.example.lesson4one"])
#device.resume
#time.sleep(1)
#session = device.attach(pid)
session = device.attach("com.example.lesson7sec")
with open("lesson7sec.js") as f :
script = session.create_script(f.read())
script.on("message",my_message_handler)
script.load()
input()

NPS

内网穿透代理,linux_amd64_server.tar.gz服务端,linux_amd64_client.tar.gz客户端,

1
2
sudo ./nps install
sudo nps start 启动服务端

新增客户端,复制客户端命令在客户端运行即可

image-20220503234945838

客户端启动frida ./fs1280arm64 -l 0.0.0.0:8888 ,新增TCP隧道,将内网端口8888映射到公网58888,其中的目标可以填入很多IP:端口进行映射

image-20220504093204899

Zentrace

批量hook

1
2
3
4
5
6
7
8
# pyenv install 3.8.0
# git clone https://github.com/hluwa/ZenTracer
# cd ZenTracer
# pyenv local 3.8.0
# python -m pip install --upgrade pip
# pip install PyQt5
# pip install frida-tools
# python ZenTracer.py
  1. 点击打开“设置”应用;
  2. 选择ActionMatch RegEx
  3. 输入E:java.io.File,点击add,然后关闭窗口
  4. 点击ActionStart

可以观察到java.io.File类的所有方法都被hook了,,并且像java.io.File.createTempFile方法的所有重载也被hook

  1. 点击ActionStop,再点击ActionClean,本次观察结束。
  2. 也可以使用模糊匹配模式,比如输入M:java.io.File之后,会将诸如java.io.FileOutputStream类的诸多方法也都hook上,不过无法打印调用栈,无法hook构造函数,也就是$init

案例

过弹窗

1
2
android hooking list activities
android intent launch_activity com.tlamb96.spetsnazmessenger.LoginActivity

image-20220504100541720

image-20220504100812302

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function main(){
Java.perform(function(){
Java.use("java.lang.System").getProperty.overload('java.lang.String').implementation = function(x){
var result = this.getProperty(x);
console.log("x,result:",x,result);
return Java.use("java.lang.String").$new("Russia");
}
Java.use("java.lang.System").getenv.overload('java.lang.String').implementation = function(x){
var result = this.getenv(x);
console.log("x,result:",x,result);
return Java.use("java.lang.String").$new("RkxBR3s1N0VSTDFOR180UkNIM1J9Cg==");
}
})
}
setImmediate(main)

过登录

image-20220504102554204

image-20220504101049048

image-20220504102629422

username:codenameduchess

password:guest

image-20220504103051069

1
2
3
4
5
6
7
8
9
10
11
12
function main(){
Java.perform(function(){
// 发消息
Java.use("com.tlamb96.kgbmessenger.b.a").$init.implementation = function(str0,str1,str2,b){
var result = this.$init(str0,str1,str2,b);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("str0,str1,str2,b:",str0,str1,str2,b)
return result;
}
})
}
setImmediate(main)

image-20220504103555933

image-20220504103541100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function main(){
Java.perform(function(){
Java.use("com.tlamb96.kgbmessenger.MessengerActivity").a.implementation = function(x){
var result = this.a(x);
console.log("a:x,result:",x,result);
if(x=="aaaa"){
result= Java.use("java.lang.String").$new("V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003");
}
return result;
}
Java.use("com.tlamb96.kgbmessenger.MessengerActivity").b.implementation = function(x){
var result = this.b(x);
console.log("b:x,result:",x,result);
if(x=="bbbb"){
result= Java.use("java.lang.String").$new("\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000");
}
return result;
}
})
}
setImmediate(main)

逆向实现

image-20220504113057434

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
public class reverseA {
public static String decode_P(){
String p = "V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003";
String result = a(p);
return result;
}


private static String a(String str) {
char[] charArray = str.toCharArray();
for (int i = 0; i < charArray.length / 2; i++) {
char c = charArray[i];
charArray[i] = (char) (charArray[(charArray.length - i) - 1] ^ 'A');
charArray[(charArray.length - i) - 1] = (char) (c ^ '2');
}
return new String(charArray);
}
public static String r_to_hex(){
String r = "\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000";

byte[] bytes = r.getBytes();
String result = "";
for(int i=0;i<bytes.length;i++){
result += String.format("%02x",bytes[i]);
}
return result;

}
}

将class文件打包成dex,d8 reverseA.class

1
2
3
4
5
6
7
8
9
10
function main(){
Java.perform(function(){
Java.openClassFile("/data/local/tmp/classes.dex").load();
const reverseA = Java.use('com.example.lesson9.reverseA');
console.log("reverseA result:",reverseA.decode_P());
console.log("r_to_hex result:",reverseA.r_to_hex());

})
}
setImmediate(main)

pip install z3-solver

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
from z3 import *
from binascii import b2a_hex,a2b_hex

s =Solver()

r = "0064736c707d6f510020646b73247c4d0068202b4159516700502a214d24675100"


r_result = bytearray(a2b_hex(r))
print(r_result)

for i in range(int(len(r_result)/2)) :
c = r_result[i]
r_result[i] = r_result[len(r_result)-i-1]
r_result[len(r_result)-i-1] = c
print(b2a_hex(r_result))


x = [BitVec("x%s" % i, 32) for i in range(len(r_result))]
for i in range(len(r_result)):
c = r_result[i]
print(i,hex(c))
s.add(((x[i] >> (i % 8)) ^ x[i] ) == r_result[i])
if (s.check() == sat):
model = s.model()
print(model)
flag=""
for i in range(len(r_result)):
if (model[x[i]] != None):
flag += chr(model[x[i]].as_long().real)
else:
flag += " "
print('"' + flag + '"')
print(len(flag), len(r_result))

adb shell

input text “aaa” 输入adb

文章作者: J
文章链接: http://onejane.github.io/2022/05/01/Frida逆向与利用自动化/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏