主动调用java及native层

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

example

frida-server-linux

frida-python修改examples中rpc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
import time
session = frida.attach("mousepad")
script = session.create_script("""\
rpc.exports = {
hello: function () {
return 'Hello';
},
failPlease: function () {
return 'oops';
}
};
""")
script.load()
api = script.exports
print("api.hello() =>", api.hello())
print("api.fail_please() =>",api.fail_please())

7z x frida-server-14.2.12-linux-x86_64.xz && chmod 777 frida-server-14.2.12-linux-x86_64 && ./frida-server-14.2.12-linux-x86_64 启动linux的frida server

通过netstat -tuulp可以查询被frida-server listen的27042端口

echo 123>1.txt && mousepad 1.txt 记事本打开1.txt,ps aux|grep -i mousepad查看该进程

python rpc.py 查看rpc attach mousepad引用打印结果

python-binding里连接frida-server的非标准端口:

./fs1428arm64 -l 0.0.0.0:6666
import frida
device = frida.get_device_manager().add_remote_device(‘192.168.1.101:6666’)
print(device.name,devie.type,device.id)


frida-python修改examples中bytecode.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
system_session = frida.attach(0)
bytecode = system_session.compile_script(name="bytecode-example", source="""\
rpc.exports = {
listThreads: function () {
return Process.enumerateThreadsSync();
}
};
""")

session = frida.attach("mousepad")
script = session.create_script_from_bytes(bytecode)
script.load()
api = script.exports
print("api.list_threads() =>", api.list_threads())

python bytecode.py 查看rpc 编译生成的thread列表


frida-server-android

frida-python修改examples中rpc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
device = frida.get_usb_device()
#device = frida.get_device_manager.add_remote_device("192.168.0.2:8888") # 基于远程ip的集群
pid = device.spawn(["com.android.settings"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
script = session.create_script("""\
rpc.exports = {
hello: function () {
return 'Hello';
},
failPlease: function () {
return 'oops';
}
};
""")
script.load()
api = script.exports
print("api.hello() =>", api.hello())
print("api.fail_please() =>",api.fail_please())

./fs1428arm64

pyenv local 3.8.5 && python rpc.py 自启动进程查看rpc attach pid引用打印结果


frida-python修改examples中detached.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def on_detached():
print("on_detached")

def on_detached_with_reason(reason):
print("on_detached_with_reason:", reason)

def on_detached_with_varargs(*args):
print("on_detached_with_varargs:", args)

device = frida.get_usb_device()
#device = frida.get_device_manager.add_remote_device("192.168.0.2:8888") # 基于远程ip的集群
pid = device.spawn(["com.android.settings"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
print("attached")
session.on('detached', on_detached)
session.on('detached', on_detached_with_reason)
session.on('detached', on_detached_with_varargs)
sys.stdin.read()

./fs1428arm64

pyenv local 3.8.5 && python detached.py 自启动进程detach进程后捕获进程退出打印log


frida-python修改examples中crash_reporting.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def on_process_crashed(crash):
print("on_process_crashed")
print("\tcrash:", crash)

def on_detached(reason, crash):
print("on_detached()")
print("\treason:", reason)
print("\tcrash:", crash)

device = frida.get_usb_device()
device.on('process-crashed', on_process_crashed)
session = device.attach("com.android.settings")
session.on('detached', on_detached)
print("[*] Ready")
sys.stdin.read()

./fs1428arm64

pyenv local 3.8.5 && python crash_reporting.py 进程运行中detach进程后捕获进程退出打印log


当需要hook一个app的子进程时,调用child_gating.py在子进程创建时创建脚本打印内容。

argv = [“/bin/sh”, “-c”, “cat /etc/hosts”] # 在bullhead中/system/bin/sh


frida-python修改examples中bytecode.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
device = frida.get_usb_device()
#device = frida.get_device_manager.add_remote_device("192.168.0.2:8888") # 基于远程ip的集群
pid = device.spawn(["com.hd.zhibo"])
device.resume(pid)
time.sleep(1)

system_session = device.attach(0)
bytecode = system_session.compile_script(name="bytecode-example", source="""\
rpc.exports = {
listThreads: function () {
return Process.enumerateThreadsSync();
}
};
""")

session = device.attach(pid)
script = session.create_script_from_bytes(bytecode)
script.load()
api = script.exports
print("api.list_threads() =>", api.list_threads())

python bytecode.py 查看rpc 编译生成的thread列表


DEXDump:js暴露3个export api(memorydump,switchmode,scandex),在py层调用三个api进行交互

dexdump暴露api

Zentracer:使用替换js中字符串(MATCHREGEX,BLACKREGEX)将黑白名单传输给js文件,将js文件直接加载通过send将信息传输给py端

Zentracer替换

java层主动调用

pyenv 升级

1
2
3
4
5
6
7
8
9
pyenv versions
git clone https://github.com/pyenv/pyenv-update.git $(pyenv root)/plugins/pyenv-update
pyenv update
pyenv install 3.8.6
pip install objection 安装最新版objection和frida
frida -UF
Frida 查看版本
Frida.heapSize
Script 查看运行环境使用的是QJS

项目Httpsocket

libssl

库的抓包导出表与符号表

1
2
3
4
5
6
7
objection -g com.roysue.httpsocket.explore
memory list modules 搜索libssl.so
memory list exports libssl.so --json /root/Desktop/libssl.so.json 通过json文件查看导出函数表
memory list exports libcrypto.so --json /root/Desktop/libcrypto.so.json
frida-trace -UF -I libssl.so 指定模块trace自动化导出表查看经过了哪里
frida-trace -U -f com.roysue.Httpsocket -I libssl.so 容易崩
frida-trace -U -f com.roysue.Httpsocket -I libcrypto.so 自动生成的hook在当前目录的__handlers__下

尝试通过frida实现hook所有so的导出表和符号表frida -U -f com.roysue.httpsocket -l traceNative.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 writeSomething(path, contents) {
var fopen_addr = Module.findExportByName("libc.so", "fopen");
var fputs_addr = Module.findExportByName("libc.so", "fputs");
var fclose_addr = Module.findExportByName("libc.so", "fclose");

//console.log("fopen=>",fopen_addr," fputs=>",fputs_addr," fclose=>",fclose_addr);

var fopen = new NativeFunction(fopen_addr, "pointer", ["pointer", "pointer"])
var fputs = new NativeFunction(fputs_addr, "int", ["pointer", "pointer"])
var fclose = new NativeFunction(fclose_addr, "int", ["pointer"])

//console.log(path,contents)

var fileName = Memory.allocUtf8String(path);
var mode = Memory.allocUtf8String("a+");

var fp = fopen(fileName, mode);

var contentHello = Memory.allocUtf8String(contents);
var ret = fputs(contentHello, fp)

fclose(fp);
}
// 枚举出所有导出函数和内部符号,找到关键函数存在的so,动态注册无效,名称和native可以完全不一样
function traceNativeExport(){

var modules = Process.enumerateModules();
for(var i = 0;i<modules.length;i++){
var module = modules[i];

if(module.name.indexOf("libssl.so")<0){
continue;
}
// 枚举so所有导出表
var exports = module.enumerateExports();
for(var j = 0;j<exports.length;j++){
//console.log("module name is =>",module.name," symbol name is =>",exports[j].name)
//var path = "/sdcard/Download/so/"+module.name+".txt"
// 通过objection -g com.roysue.httpsocket explore 中env拿到的filesDirectory地址
//var path = "/data/data/com.roysue.d0so2/cache/"+module.name+".txt"
//writeSomething(path,"type: "+exports[j].type+" function name :"+exports[j].name+" address : "+exports[j].address+" offset => 0x"+(exports[j].address - module[i].address)+"\n")
// 把所有包含SSL_write的函数全部hook上
if(exports[j].name.indexOf("SSL_write")>=0){
attach(exports[j].name,exports[j].address);
}
// if(exports[j].name.indexOf("set")>=0){
// attach(exports[j].name,exports[j].address);
// }
// if(exports[j].name.indexOf("send")>=0){
// attach(exports[j].name,exports[j].address);
// }
// if(exports[j].name.indexOf("recv")>=0){
// attach(exports[j].name,exports[j].address);
// }

}
}
}



function attach(name,address){
console.log("attaching ",name);
Interceptor.attach(address,{
onEnter:function(args){
console.log("Entering => " ,name)
console.log("args[0] => ",hexdump(args[0]) )
// console.log("args[1] => ",args[1].readCString())
// console.log("args[2] => ",args[2])

},onLeave:function(retval){
//console.log("retval is => ",retval)
}
})

}

// 枚举符号表
function traceNativeSymbol(){
var modules = Process.enumerateModules();
for(var i = 0;i<modules.length;i++){
var module = modules[i];
// console.log(JSON.stringify(modules))
if(module.name.indexOf("linker64")<0){
continue;
}

var exports = module.enumerateSymbols();
console.log(JSON.stringify(exports))
for(var j = 0;j<exports.length;j++){
if(exports[j] == null){
continue;
}
console.log("module name is =>",module.name," symbol name is =>",exports[j].name)
var path = "/data/data/com.roysue.d0so2/cache/"+module.name+"Symbol.txt"
writeSomething(path,"type: "+exports[j].type+" function name :"+exports[j].name+" address : "+exports[j].address+" offset => 0x"+(exports[j].address - module[i].address)+"\n")
}
}
}



function main(){
console.log("Entering main")
traceNativeExport();
//traceNativeSymbol();

}
setImmediate(main)

git clone https://github.com/BigFaceCat2017/frida_ssl_logger.git

python ssl_logger.py -U -f com.roysue.httpsocket -p capture.pcap 抓包ssl保存到pcap中使用wireshark查看

libc

去除httpSocket.js中traceNativeExport对libssl.so的过滤,frida -UF -l traceNative.js,trace后的结果到/data/data/com.roysue.d0so2/cache中cat libs.so.txt|grep open

frida -UF -l traceNativelibc.js -o trace.txt

frida -U -f com.ss.android.auto -l traceNativelibc.js -o trace.txt

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
function writeSomething(path, contents) {
var fopen_addr = Module.findExportByName("libc.so", "fopen");
var fputs_addr = Module.findExportByName("libc.so", "fputs");
var fclose_addr = Module.findExportByName("libc.so", "fclose");

//console.log("fopen=>",fopen_addr," fputs=>",fputs_addr," fclose=>",fclose_addr);

var fopen = new NativeFunction(fopen_addr, "pointer", ["pointer", "pointer"])
var fputs = new NativeFunction(fputs_addr, "int", ["pointer", "pointer"])
var fclose = new NativeFunction(fclose_addr, "int", ["pointer"])

//console.log(path,contents)

var fileName = Memory.allocUtf8String(path);
var mode = Memory.allocUtf8String("a+");

var fp = fopen(fileName, mode);

var contentHello = Memory.allocUtf8String(contents);
var ret = fputs(contentHello, fp)

fclose(fp);
}

function traceNativeExport(){

var modules = Process.enumerateModules();
for(var i = 0;i<modules.length;i++){
var module = modules[i];

if(module.name.indexOf("libc.so")<0){
continue;
}

var exports = module.enumerateExports();
for(var j = 0;j<exports.length;j++){
//console.log("module name is =>",module.name," symbol name is =>",exports[j].name)
//var path = "/sdcard/Download/so/"+module.name+".txt"
// var path = "/data/data/com.roysue.d0so2/cache/"+module.name+".txt"
// writeSomething(path,"type: "+exports[j].type+" function name :"+exports[j].name+" address : "+exports[j].address+" offset => 0x"+ ( exports[j].address.sub(modules[i].base) )+"\n")
// if(exports[j].name.indexOf("strto")>=0)continue;
// if(exports[j].name.indexOf("strco")>=0)continue;
// if(exports[j].name.indexOf("_l")>=0)continue;
// if(exports[j].name.indexOf("pthread")>=0)continue;


// if(exports[j].name.indexOf("socket")>=0){ // fopen fputs fputc fclose
// attach(exports[j].name,exports[j].address);
// }
// 过反调试,一般反调试都有单独创建线程
if(exports[j].name.indexOf("pthread_create")>=0){
attach(exports[j].name,exports[j].address);
}
// if(exports[j].name.indexOf("read")>=0){ // man read
// attach(exports[j].name,exports[j].address);
// }
// if(exports[j].name.indexOf("write")>=0){
// attach(exports[j].name,exports[j].address);
// }
// if(exports[j].name.indexOf("send")>=0){
// attach(exports[j].name,exports[j].address);
// }
// if(exports[j].name.indexOf("recv")>=0){
// attach(exports[j].name,exports[j].address);
// }

}
}
}

function attach(name,address){
console.log("attaching ",name);
Interceptor.attach(address,{
onEnter:function(args){
console.log("Entering => " ,name)
// console.log("args[0] => ",args[0].readCString() )
// console.log("args[1] => ",args[1].readCString())
// console.log( hexdump(args[1]))

console.log("args[2] => ",args[2])
// 打印调用栈
// console.log('R0YSUE called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');

},onLeave:function(retval){
console.log("exit => ",name)
// console.log("retval is => ",retval.readCString())
}
})

}

function traceNativeSymbol(){
var modules = Process.enumerateModules();

// console.log(JSON.stringify(modules))

for(var i = 0;i<modules.length;i++){
var module = modules[i];
// console.log(JSON.stringify(modules))

// if(module.name.indexOf("linker64")<0){
// continue;
// }

var exports = module.enumerateSymbols();
// console.log(JSON.stringify(exports))
for(var j = 0;j<exports.length;j++){

// console.log("module name is =>",module.name," symbol name is =>",exports[j].name)
var path = "/data/data/com.roysue.d0so2/cache/"+module.name+"Symbol.txt"
writeSomething(path,"type: "+exports[j].type+" function name :"+exports[j].name+" address : "+exports[j].address+" offset => 0x"+ ( exports[j].address.sub(modules[i].base) )+"\n")


}
}
}



function main(){
console.log("Entering main")
traceNativeExport();
// traceNativeSymbol();

}
setImmediate(main)

Unicorn 在 Android 的应用

项目d0so2

1
2
3
objection -g com.roysue.d0so2 explore -s "android hooking watch class_method com.roysue.d0so2.MainActivity.sI3 --dump-args --dump-backtrace --dump-return"
memory list modules 查找native-lib为libnative-lib.so
memory list exports libnative-lib.so 只能找到静态注册的stringFromJNI1,stringFromJNI1,但是找不到动态注册的sI3

git clone https://github.com/lasting-yang/frida_hook_libart.githttps://github.com/lasting-yang/frida_hook_libart.git

image-20210626200659411

1
frida -U --no-pause -f com.roysue.d0so2 -l hook_RegisterNatives.js  找到动态注册的类和原名

image-20210626200829573

其中0x7612a10454绝对地址减去0x7612a01000模块地址,得到0xf454函数地址

1
memory list exports libnative-lib.so  搜索0x7612a10454

image-20210626201140766

将函数名复制到http://demangler.com/ 进行解码得到原函数名

image-20210626201219371

1
android hooking watch class_method com.rosyue.d0so2.MainActivity.sI3() --dump-args --dump-backtrace --dump-return

frida实现hook动态注册函数,frida -UF -l hookNative.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
function hook_native(){
var libnative_addr = Module.findBaseAddress('libnative-lib.so');
console.log("libnative_addr is => ",libnative_addr)
var stringfromJNI3 = libnative_addr.add(0xf454);
console.log("stringfromJNI3 address is =>",stringfromJNI3);

var stringfromJNI3_2 = Module.findExportByName('libnative-lib.so', "_Z14stringFromJNI3P7_JNIEnvP7_jclassP8_jstring")
console.log("stringfromJNI3_2 address is =>",stringfromJNI3_2);

Interceptor.attach(stringfromJNI3_2,{
onEnter:function(args){

console.log("jnienv pointer =>",args[0])
console.log("jobj pointer =>",args[1])
console.log("jstring pointer=>",Java.vm.getEnv().getStringUtfChars(args[2], null).readCString() )

},onLeave:function(retval){
console.log("retval is =>",Java.vm.getEnv().getStringUtfChars(retval, null).readCString())
console.log("=================")

}
})

}
function main(){
hook_native()
}
setImmediate(main)

项目 demoso1

Android JNI(一)——NDK与JNI基础

Android逆向新手答疑解惑篇——JNI与动态注册

pyenv 3.8.0

1
2
3
4
5
6
objection -g com.example.demoso1 explore
android hooking search classes com.example.demoso1 打印包名下所有的类
android hooking list class_methods com.example.demoso1.MainActivity 打印类下所有方法
android hooking watch class com.example.demoso1.MainActivity
android hooking watch class_method com.example.demoso1.MainActivity.method02 --dump-args --dump-backtrace --dump-return
objection -N -h 192.168.0.105 -p 8888 -g com.example.demoso1 explore

image-20210408082733062

method01在native层的静态方法,method02是native层的动态方法

image-20210408113746342

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
var MainActivityHandle = null;  // 实例调用的性能优化(将instance缓存起来反复调用)
Java.perform(function(){
var javaString = Java.use("java.lang.String")
Java.choose("com.example.demoso1.MainActivity",{
onMatch:function(instance){
MainActivityHandle = instance;
},onComplete(){}
})
console.log("MainActivityHandle is => ",MainActivityHandle)
})
function fridamethod01(plaintext){
var result;
// method01 是 静态方法,使用Java.use
Java.perform(function(){
var MainActivity = Java.use("com.example.demoso1.MainActivity");
var javaString = Java.use("java.lang.String")
result = MainActivity.method01(javaString.$new(plaintext))
})
return result;
}


function fridamethod02(ciphertext){
var result;
Java.perform(function(){
// method02 是 动态方法,使用Java.choose
var javaString = Java.use("java.lang.String")
// Java.choose("com.example.demoso1.MainActivity",{
// onMatch:function(instance){
// MainActivityHandle = instance.method02(javaString.$new(ciphertext));
// },onComplete(){}
// })
result = MainActivityHandle.method02(javaString.$new(ciphertext))
})
return result;
}

image-20210408085409310

hook

主机中 ./fs128arm64 -l 0.0.0.0:8888
frida -H 192.168.0.105:8888 -F -l hookandinvoke.js 手机ip
objection -N -h 192.168.0.105 -p 8888 -g com.example.demoso1 explore 远程objection连接

1
2
3
4
5
6
HTTP 每次都是新连接 速度慢
RPC 是 TCP流,速度快
rpc.exports={
fridamethod01:fridamethod01,
fridamethod02:fridamethod02,
}

rpc调用

curl -s -X POST “http://127.0.0.1:5000/encrypt" -H “Content-Type: application/json” -d ‘{“data”: “roysue”}’

curl -s -X POST “http://127.0.0.1:5000/decrypt" -H “Content-Type: application/json” -d ‘{“data”: “47fcda3822cd10a8e2f667fa49da783f”}’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
device = frida.get_device_manager().add_remote_device("192.168.0.105:8888")

# 启动`demo01`这个app
pid = device.spawn(["com.example.demoso1"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载脚本
with open("hookandinvoke.js") as f:
script = session.create_script(f.read())
script.on("message" , my_message_handler) #调用错误处理
script.load()

print(script.exports.fridamethod01("roysue"))
print(script.exports.fridamethod02("47fcda3822cd10a8e2f667fa49da783f"))

python hookandinvoke.py

压测http并发性能

https://github.com/JoeDog/siege 压测

apt install siege https://www.jianshu.com/p/74c465ff136f

1
siege -c5 -r10 "http://127.0.0.1:5000/encrypt POST <./iloveroysue.json "

nps 内网穿透

客户端:linux_arm64_client.tar.gz 因为n5x是kali nethunter系统,chmod 777 * && ./npc,启动npc

image-20210408115046329

服务端:https://ehang-io.github.io/nps/#/run 添加隧道,端口映射

image-20210408115216335

add_remote_device 修改远程地址,将手机ip和kali主机ip暴露公网调用,通过siege压测

device = frida.get_device_manager().add_remote_device(“192.168.0.105:8888”) 手机ip

linux_arm64_client.tar.gz

device = frida.get_device_manager().add_remote_device(“118.126.66.193:58888”) npc和frida都在手机上,新增客户端,启动npc,手机ip公网映射添加隧道58888目标8888

siege -c5 -r10 “http://127.0.0.1:5000/encrypt POST <./iloveroysue.json “ 启动py脚本

linux_amd64_client.tar.gz

device = frida.get_device_manager().add_remote_device(“118.126.66.193:48888”) npc在kali上,新增客户端,启动npc,配置隧道服务端口55000目标5000端口,手机ip公网映射48888目标192.168.0.105:8888

siege -c5 -r10 “http://118.126.66.193:55000/encrypt POST <./iloveroysue.json “ 启动py脚本

Native层主动调用

demoso1基于https://www.jianshu.com/p/0390f598c34c

1
2
objection -g com.example.demoso1 explore
memory list exports libnative-lib.so 查看导出函数,静态函数直接搜索

image-20210409090128175

_Z8method01P7_JNIEnvP7_jclassP8_jstring 通过搜索http://demangler.com/ 拿到原函数名

image-20210409090231131

或者通过https://github.com/lasting-yang/frida_hook_libart.git

frida -U -f com.example.demoso1 -l hook_RegisterNatives.js –no-pause 查看原始方法名

image-20210410090140648

修改hook_RegisterNatives,添加hookMethod01和invokemethod02

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
在hook_RegisterNatives方法中添加
if(name.indexOf("method01")>=0){
//hookmethod(fnPtr_ptr);
//replacehook(fnPtr_ptr);
method01addr = fnPtr_ptr;
}else if (name.indexOf("method02")>=0){
method02addr = fnPtr_ptr;
method02 = new NativeFunction(method02addr,'pointer',['pointer','pointer','pointer']);
}else{
continue;
}
新增invokemethod01
function invokemethod01(contents){
console.log("ENV=>",ENV)
console.log("JCLZ=>",JCLZ);
console.log("method01_addr is =>",method01addr)
var method01 = new NativeFunction(method01addr,'pointer',['pointer','pointer','pointer']);
var NewStringUTF = new NativeFunction(addrNewStringUTF,'pointer',['pointer','pointer'])
var result = null;
Java.perform(function(){
console.log("Java.vm.getEnv()",Java.vm.getEnv())
var JSTRING = NewStringUTF(Java.vm.getEnv(),Memory.allocUtf8String(contents))
result = method01(Java.vm.getEnv(),JSTRING,JSTRING);
console.log("result is =>",result)
console.log("result is ",Java.vm.getEnv().getStringUtfChars(result, null).readCString())
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}
新增invokemethod02
function invokemethod02(contents){
var result = null;
Java.perform(function(){
var JSTRING = NewStringUTF(Java.vm.getEnv(),Memory.allocUtf8String(contents))
result = method02(Java.vm.getEnv(),JSTRING,JSTRING);
result = Java.vm.getEnv().getStringUtfChars(result, null).readCString();
})
return result;
}

jni 获取类(jclass)的方式

1.需要new 一个对象时需要jclass.
2.需要调用这个对象的方法或者属性时需要jclass.
3.jclass 不需要释放.

1
2
3
4
5
获取对象类
jclass GetObjectClass_(JNIEnv* jni, jobject object) {
jclass c = jni->GetObjectClass(object);
return c;
}
1
2
直接找到类名
jclass long_cls = env->FindClass("java/lang/Long");

image-20210410091647753

rpc

1
2
3
4
rpc.exports = {
invoke1:invokemethod01, // 导出名不可以有大写字母或者下划线
invoke2:invokemethod02
};

init.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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import time
import frida
from flask import Flask, jsonify, request
import json

def my_message_handler(message , payload): #定义错误处理
print(message)
print(payload)


# 连接安卓机上的frida-server
#device = frida.get_device_manager().add_remote_device("192.168.0.3:8888")
#device = frida.get_device_manager().add_remote_device("118.126.66.193:58888")
device = frida.get_device_manager().add_remote_device("192.168.0.105:8888")
#device = frida.get_usb_device()

# 启动`demo01`这个app
pid = device.spawn(["com.example.demoso1"])
session = device.attach(pid)

# 加载脚本
with open("hook_RegisterNatives.js") as f:
script = session.create_script(f.read())
script.on("message" , my_message_handler) #调用错误处理
script.load()

time.sleep(3)
device.resume(pid)

time.sleep(3)


print(script.exports.invoke1("onejane"))
print(script.exports.invoke2("c6138f96658ce0cb845bdab0f9616273"))

# 脚本会持续运行等待输入
#input()


app = Flask(__name__)


@app.route('/encrypt', methods=['POST'])#url加密
def encrypt_class():
data = request.get_data()
json_data = json.loads(data.decode("utf-8"))
postdata = json_data.get("data")
#print(postdata)
res = script.exports.invoke1(postdata)
return res


@app.route('/decrypt', methods=['POST'])#data解密
def decrypt_class():
data = request.get_data()
json_data = json.loads(data.decode("utf-8"))
postdata = json_data.get("data")
res = script.exports.invoke2(postdata)
return res



if __name__ == '__main__':
app.run()

端口usb连接,主机连接adb, ./fs128arm64 -l 0.0.0.0:8888 启动frida server

image-20210410093320312

image-20210410093434378

脱离apk

7z x demoso1/app/build/outputs/apk/debug/app-debug.apk

cp lib/arm64-v8a/libnative-lib.so 到 /data/app/libnative-lib.so 并赋予最高权限

adb push libnative-lib.so /data/app

1
2
3
4
5
6
7
8
9
10
frida -U -f com.android.settings -l hook_RegisterNatives.js --no-pause
objection -g com.android.settings explore
memory list modules 可以看到libnative-lib.so已经被加载
memory list exports libnative-lib.so 找到两个函数名_Z8method01P7_JNIEnvP7_jclassP8_jstring,_Z8method02P7_JNIEnvP8_jobjectP8_jstring

var modulelibnative = Module.load("/data/app/libnative-lib.so") // 加载so
method01addr = modulelibnative.findExportByName("_Z8method01P7_JNIEnvP7_jclassP8_jstring")
method02addr = modulelibnative.findExportByName("_Z8method02P7_JNIEnvP8_jobjectP8_jstring")

修改init.py中pid = device.spawn(["com.android.settings"])

RPC(TCP) 远快于 HTTP,手机暴露frida-server,它是一个TCP,效率很高

自吐算法摘要

adb install HookTestDemo.apk

jadx-gui HookTestDemo.apk

image-20210505213048601

frida -UF -l hookEvent.js 所有点击事件hook,发现所有触发类都是MainActivity

image-20210505213005214

普通方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case R.id.putong /*{ENCODED_INT: 2131165291}*/:
toastPrint(Utils.getCalc(2000, 2000) + BuildConfig.FLAVOR);
toastPrint(new Money.innerClass("资源吧", 666).outPrint());
return;

static class innerClass {
private String name;
private int num;

public innerClass(String name2, int num2) {
this.name = name2;
this.num = num2;
}

public String outPrint() {
return "内部类被调用" + this.name + ": " + this.num;
}
}

objection探索一波,枚举不准的话,延迟加载,每个按钮都点一次触发类

1
2
3
4
5
6
7
objection -g com.xiaojianbang.app explore -P ~/.objection/plugins 
android hooking search classes com.xiaojianbang.app 找不到Money类
android hooking list classes
cat ~/.objection/objection.log | grep -i "com.xiaojianbang.app"
plugin wallbreaker classdump --fullname com.xiaojianbang.app.Money
android heap search instances com.xiaojianbang.app.MainActivity
./fs1426arm64

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
28
29
30
31
32
33
34
var MainActivity = null;

Java.perform(function () {
Java.choose("com.xiaojianbang.app.MainActivity", {
onMatch: function (instanse) {
MainActivity = instanse;
console.log("found instance => ", MainActivity);
}, onComplete: function () {
console.log("search completed!")
}
})
})
function showToast(string) {
Java.perform(function () {
// UI thread 注入
var Toast = Java.use('android.widget.Toast');
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
// 在线程上运行指定的函数
Java.scheduleOnMainThread(function () {
Toast.makeText(context, string, Toast.LENGTH_LONG.value).show();
})
})
}
function invokeNormal() {
Java.perform(function () {

var javaString = Java.use("java.lang.String")
// 静态内部函数
var MoneyInnerClass = Java.use("com.xiaojianbang.app.Money$innerClass").$new(javaString.$new("J"), 666).outPrint();
console.log("result =>", MoneyInnerClass)
showToast(javaString.$new(MoneyInnerClass))
})
}

frida -UF -l hookxjb.js

[Pixel::HookTestDemo]-> result => 内部类被调用J: 666

构造实例方法

1
2
3
4
5
6
7
8
9
10
11
case R.id.gouzao /*{ENCODED_INT: 2131165257}*/:
if (money == null) {
money = new Money("美元", 1000);
}
toastPrint("money: " + money.getInfo());
toastPrint(Utils.getMoney().getInfo());
toastPrint(new Money().name());

public String name() {
return "my name is xiaojianbang";
}

frida -UF -l hookxjb.js

invokeInit()

1
2
3
4
5
6
7
function invokeInit() {
Java.perform(function () {
var javaString = Java.use("java.lang.String")
var MoneyName = Java.use("com.xiaojianbang.app.Money").$new().name();
showToast(javaString.$new(MoneyName))
})
}

重载方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case R.id.chongzai /*{ENCODED_INT: 2131165233}*/:
toastPrint(Utils.test(666));
toastPrint(Utils.test());
toastPrint(Utils.test(new Money("港币", 7000)));
toastPrint(new Money() {
/* class com.xiaojianbang.app.MainActivity.AnonymousClass1 */

@Override // com.xiaojianbang.app.Money
public String getInfo() {
return "匿名类被调用";
}
}.getInfo());
return;

public static String test(int num) {
return "www.zygx8.com " + num;
}

android hooking list class_methods com.xiaojianbang.app.Utils

1
2
3
4
5
6
7
function invokeOverload() {
Java.perform(function () {
// 静态重载方法
var result = Java.use("com.xiaojianbang.app.Utils").test(666);
console.log("invoke overload result is => ", result);
})
}

frida -UF -l hookxjb.js

invokeOverload()

对象参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case R.id.duixiang /*{ENCODED_INT: 2131165247}*/:
toastPrint(Utils.test(new Money("美元", 200)));
toastPrint(new Utils().myPrint(new String[]{"资源共享吧", "官网", ":", "www.zygx8.com"}));
return;

public static String test(Money money) {
return money.getInfo();
}
public String myPrint(String[] strArr) {
StringBuilder sb = new StringBuilder();
for (String str : strArr) {
sb.append(str);
}
return sb.toString();
}

frida -UF -l hookxjb.js

invokeOverload()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function invokeObject() {
Java.perform(function () {
var javaString = Java.use("java.lang.String")
var newMoney = Java.use("com.xiaojianbang.app.Money").$new(javaString.$new("dollar"), 200);
var result = Java.use("com.xiaojianbang.app.Utils").test(newMoney);
showToast(javaString.$new(result))


var StringArray = Java.array("java.lang.String", [javaString.$new("J "), javaString.$new("you are the "), javaString.$new("best ")])
var result = Java.use("com.xiaojianbang.app.Utils").$new().myPrint(StringArray);
showToast(javaString.$new(result))

})
}

Native

1
2
3
4
5
6
7
8
case R.id.helloFromC /*{ENCODED_INT: 2131165260}*/:
toastPrint(NativeHelper.helloFromC());
return;
case R.id.add /*{ENCODED_INT: 2131165218}*/:
toastPrint(NativeHelper.add(5, 6, 7) + BuildConfig.FLAVOR);
return;
public static native String helloFromC();
public static native int add(int i, int i2, int i3);

frida -UF -l hookxjb.js

invokeNative()

1
2
3
4
5
6
7
8
9
10
11
12
function invokeNative() {
Java.perform(function () {
var javaString = Java.use("java.lang.String")
var result = Java.use("com.xiaojianbang.app.NativeHelper").helloFromC();
showToast(javaString.$new(result));

result = Java.use("com.xiaojianbang.app.NativeHelper").add(100, 200, 300);
showToast(javaString.$new(String(result)));


})
}

MD5

1
2
3
4
5
6
7
8
case R.id.JavaMD5 /*{ENCODED_INT: 2131165191}*/:
toastPrint(MD5.md5_1("xiaojianbang"));
return;
public static String md5_1(String args) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5", "BC");
md.update(args.getBytes());
return Utils.byteToHexString(md.digest());
}

hook类java.security.MessageDigest中所有的方法包括md5

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 hookMD5() {
Java.perform(function () {
var targetClassMethod = "java.security.MessageDigest.getInstance"
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;
var targetClass = targetClassMethod.slice(0, delim)
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
var overloadCount = hook[targetMethod].overloads.length;
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
console.warn("\n*** entered " + targetClassMethod);
// print args
if (arguments.length >= 0) {
for (var j = 0; j < arguments.length; j++) {
console.log("arg[" + j + "]: " + arguments[j]);

}
}
var retval = this[targetMethod].apply(this, arguments);
console.log("\nretval: " + retval);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return retval;
}
}
})
}

image-20210505224433551

文章作者: J
文章链接: http://onejane.github.io/2021/02/16/主动调用java及native层/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏