Compare commits
82 Commits
a193b1b5b5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 13d89b020d | |||
| 34735c3e0e | |||
| 866701b499 | |||
| f47e9cb306 | |||
| 8c4cd61081 | |||
| cfefd337b9 | |||
| af1a9e1859 | |||
| 6c7278a064 | |||
| 58c81f32e5 | |||
| 67623951ce | |||
| 8960406388 | |||
| 51bf1e5a6f | |||
| 0e75201906 | |||
| 8aa5351ccc | |||
| 1e80a8067b | |||
| df7b021ea0 | |||
| 86f1a339d0 | |||
| b8e653e57d | |||
| 9ec56ced34 | |||
| e949fdc3e5 | |||
| 7167780f0f | |||
| dd6a0f4018 | |||
| 64e5f9c6d9 | |||
| f16dd8cae1 | |||
| e358abc840 | |||
| 054d36b04d | |||
| 424ff95d2b | |||
| 8405b06e9a | |||
| 115626622b | |||
| 7a34565a64 | |||
| 9de27d332d | |||
| deb6e8fb4c | |||
| fc56cd8c54 | |||
| 7dc4eb24fd | |||
| 358c51a054 | |||
| db7b6cb54a | |||
| b1b381b7a6 | |||
| 7dd5c0d661 | |||
| 4b174c9f3e | |||
| 0892550341 | |||
| 1613f15825 | |||
| 0370063b48 | |||
| 239f2b1de4 | |||
| df79d971f4 | |||
| b1164af03a | |||
| c0a5ef2486 | |||
| 75d28434ee | |||
| aec778ff41 | |||
| 21aa528f08 | |||
| 29fc054c02 | |||
| 9e8761d7a4 | |||
| dd3e023690 | |||
| 64d25ac27d | |||
| 4cb52cd56e | |||
| 0226485c31 | |||
| 19333bc43a | |||
| e74d6554e9 | |||
| 528b0d8885 | |||
| 23640b607a | |||
| 649dd5ac89 | |||
| 27631b814a | |||
| c8b014e457 | |||
| b46444dea2 | |||
| 8fc63ad9c0 | |||
| 26622767e6 | |||
| 2311f5ad29 | |||
| 1c199de379 | |||
| c8e27de06b | |||
| 132bc23c8c | |||
| 0059733ffd | |||
| d8e7976afc | |||
| cf74259b5e | |||
| e0f14d46ea | |||
| 1d88394e2b | |||
| c0f2b7110c | |||
| 017ef1ef56 | |||
| e9c1de8fe5 | |||
| d251f3e86c | |||
| 7e54e08c90 | |||
| ff00478296 | |||
| 9d2464ee9e | |||
| ff05268d01 |
2
.idea/hook.iml
generated
2
.idea/hook.iml
generated
@@ -6,7 +6,7 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv 3.11" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv 3.9" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (hook-frida)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (hook) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (hook-frida)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (hook) (2)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,6 +0,0 @@
|
||||
Java.perform(function() {
|
||||
var AppLog = Java.use("com.ss.android.common.applog.AppLog");
|
||||
console.log("BASE_HEADER_KEYS value: " + AppLog.BASE_HEADER_KEYS.value);
|
||||
return AppLog.BASE_HEADER_KEYS.value;
|
||||
});
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//AppLogReaperServiceImpl类的LIZ方法
|
||||
Java.perform(function() {
|
||||
var AppLogReaperServiceImpl = Java.use("com.ss.android.ugc.aweme.lancet.AppLogReaperServiceImpl");
|
||||
|
||||
AppLogReaperServiceImpl.LIZ.overload('org.json.JSONObject').implementation = function(jsonObject) {
|
||||
console.log("Logged JSON: " + jsonObject.toString());
|
||||
|
||||
// Call the original function
|
||||
return this.LIZ(jsonObject);
|
||||
};
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
//RegionMockConfig类
|
||||
Java.perform(function() {
|
||||
var AppLog = Java.use("com.ss.android.common.applog.AppLog");
|
||||
console.log("BASE_HEADER_KEYS value: " + AppLog.BASE_HEADER_KEYS.value);
|
||||
});
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
|
||||
Java.perform(function() {
|
||||
var SettingServiceImpl = Java.use("com.p314ss.android.ugc.aweme.setting.services.SettingServiceImpl");
|
||||
|
||||
var originalLJIILL = SettingServiceImpl.LJIILL.implementation;
|
||||
|
||||
SettingServiceImpl.LJIILL.implementation = function() {
|
||||
// 打印日志,表示方法被hook
|
||||
console.log("LJIILL method is called!");
|
||||
|
||||
// 尝试获取 accountRegion 字段的值
|
||||
if (this.hasOwnProperty('accountRegion')) {
|
||||
console.log("accountRegion: " + this.accountRegion);
|
||||
} else {
|
||||
console.log("accountRegion field not found in SettingServiceImpl.");
|
||||
}
|
||||
|
||||
// 调用原始方法
|
||||
originalLJIILL.call(this);
|
||||
}
|
||||
});
|
||||
65
frida抓包.md
Normal file
65
frida抓包.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# frida抓包
|
||||
|
||||
## 环境和工具准备:
|
||||
|
||||
- python 3.x
|
||||
- r0capture
|
||||
- frida / frida-tools
|
||||
- apkshell
|
||||
|
||||
## r0capture
|
||||
|
||||
```shell
|
||||
git clone https://github.com/r0ysue/r0capture
|
||||
```
|
||||
|
||||
## frida
|
||||
|
||||
frida-server下载地址
|
||||
|
||||
```
|
||||
https://github.com/frida/frida/releases
|
||||
```
|
||||
|
||||
虚拟环境安装frida和frida- tools
|
||||
|
||||
```shell
|
||||
pip install frida
|
||||
```
|
||||
|
||||
```shell
|
||||
pip install frida-tools
|
||||
```
|
||||
|
||||
使用Frida查看包进程
|
||||
|
||||
```shell
|
||||
frida-ps -U | grep "包名"
|
||||
```
|
||||
|
||||
## 运行 r0capture.py
|
||||
|
||||
```bash
|
||||
python r0capture.py -U 前面记录的目标应用包名 -p xxx.pcap
|
||||
```
|
||||
|
||||
```
|
||||
python3 r0capture/r0capture.py -U com.vmall.client -p com.vmall.client.pcap
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中 -p 参数用来保存抓包结果,.pcap 是数据报存储格式,包括 Wireshark 在内的很多主流抓包软件都可以生成或者导入 pcap 数据包并分析
|
||||
|
||||
如果中途提示 hexdump 名称错误,pip 安装一下即可
|
||||
|
||||
```bash
|
||||
pip install hexdump
|
||||
```
|
||||
|
||||
# hook appsflyer conversions
|
||||
|
||||
```
|
||||
frida -U -l <jshook代码> -f <packageName>
|
||||
```
|
||||
|
||||
76
main.py
Normal file
76
main.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import frida
|
||||
import modules.command
|
||||
import modules.files_utils
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
FIRST_WRITE = True # 全局变量,用于跟踪是否是首次写入
|
||||
|
||||
def on_message(message, data):
|
||||
print(message)
|
||||
# modules.files_utils.write_log(message)
|
||||
# print(message)
|
||||
# if message['type'] == 'send':
|
||||
# print(message['payload'])
|
||||
|
||||
def attach_method(is_spawn):
|
||||
if type(is_spawn) == bool:
|
||||
if is_spawn:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
print(f"is_spawn type error,please check is_spawn type.")
|
||||
|
||||
|
||||
def main(package_name,script_path,is_spawn):
|
||||
# 从Python发送数据到Frida的JavaScript脚本
|
||||
print(f"script_path: {script_path}")
|
||||
js_code = modules.files_utils.read_javascript(script_path)
|
||||
# print(js_code)
|
||||
modules.command.start_frida()
|
||||
# modules.command.clearCache(package_name)
|
||||
# 连接到USB设备
|
||||
device = frida.get_usb_device()
|
||||
print(device)
|
||||
if attach_method(is_spawn):
|
||||
pid = device.spawn(package_name)
|
||||
print(f"进程pid: {pid}")
|
||||
process = device.attach(pid)
|
||||
script = process.create_script(js_code)
|
||||
# script.on("message", on_message)
|
||||
script.load()
|
||||
# data_to_send = {'data': 'Hello from Python!'}
|
||||
# script.post({'type': 'input_data', 'payload': data_to_send})
|
||||
device.resume(pid) # 加载完脚本后, 恢复进程运行
|
||||
sys.stdin.read()
|
||||
else:
|
||||
# 列出设备上的所有进程
|
||||
pid = None
|
||||
processes = device.enumerate_processes()
|
||||
for process in processes:
|
||||
if process.name == attach_process_name:
|
||||
pid = process.pid
|
||||
print(f"pid: {process.pid},App Name: {process.name}")
|
||||
# 如果你想附加到一个特定的进程并注入一个脚本:
|
||||
if pid is not None:
|
||||
session = device.attach(pid)
|
||||
script = session.create_script(js_code)
|
||||
script.on('message', on_message)
|
||||
script.load()
|
||||
else:
|
||||
print(f"get process error")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 是否以spawn模式运行
|
||||
is_spawn = True
|
||||
# 目标进程名
|
||||
attach_process_name = "百度网盘"
|
||||
# 目标包名
|
||||
package_name = "com.baidu.netdisk"
|
||||
# 注入的脚本路径
|
||||
# script_path = "scripts/hook_conversions.js"
|
||||
script_path = "scripts/baidunetdisk.js"
|
||||
main(package_name,script_path, is_spawn)
|
||||
98
modules/command.py
Normal file
98
modules/command.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import subprocess
|
||||
import logging
|
||||
import time
|
||||
import json
|
||||
from modules.files_utils import get_path
|
||||
|
||||
|
||||
def run_adb_command(command_list):
|
||||
process = subprocess.Popen(command_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = process.communicate()
|
||||
output = stdout.decode().strip()
|
||||
status_code = process.returncode
|
||||
error = stderr.decode().strip()
|
||||
return output, status_code, error
|
||||
def start_frida():
|
||||
if is_frida_running():
|
||||
return
|
||||
else:
|
||||
output, status_code, error = run_adb_command(['adb', 'shell', 'su', '-c', '/data/local/tmp/frica'])
|
||||
if status_code == 0:
|
||||
print(output)
|
||||
logging.info(f"start frida output: {output}")
|
||||
return True
|
||||
else:
|
||||
print(error)
|
||||
logging.error(f"start error,error:{error}")
|
||||
return False
|
||||
|
||||
def is_frida_running():
|
||||
output, status_code, error = run_adb_command(['adb', 'shell', 'ps', '|', 'grep', 'frica'])
|
||||
print(output)
|
||||
return 'frica' in output
|
||||
|
||||
|
||||
def get_main_activity_for_package(package_name):
|
||||
output, status_code, error = run_adb_command(['adb', 'shell', 'dumpsys', 'package', package_name])
|
||||
if status_code != 0:
|
||||
print(f"Error getting main activity: {error}")
|
||||
return None
|
||||
else:
|
||||
# print(f"output: {output},output type: {type(output)}")
|
||||
return find_mainActivity(output,package_name)
|
||||
|
||||
|
||||
def find_mainActivity(output,package_name):
|
||||
activity = []
|
||||
start_append = False
|
||||
lines = [line.strip() for line in output.split("\n")]
|
||||
for line in lines:
|
||||
if "Activity Resolver Table:" in line:
|
||||
activity.append(line)
|
||||
if "Non-Data Actions:" in line:
|
||||
activity.append(line)
|
||||
start_append = True
|
||||
elif "android.intent.category.LAUNCHER" in line:
|
||||
activity.append(line)
|
||||
break
|
||||
elif start_append == True:
|
||||
activity.append(line)
|
||||
for main_ac in activity:
|
||||
if package_name in main_ac:
|
||||
tmp = main_ac.split()
|
||||
print(tmp[1])
|
||||
return tmp[1]
|
||||
# lines = output.split('\n')
|
||||
# print(f"lines: \n{lines},lines type: {type(lines)}")
|
||||
# last_line_indent = 0
|
||||
# for line in lines:
|
||||
# stripped = line.lstrip()
|
||||
# indent = len(line) - len(stripped)
|
||||
# print(indent)
|
||||
|
||||
|
||||
|
||||
def clearCache(package_name):
|
||||
if stopApp(package_name):
|
||||
output, status_code, error = run_adb_command(['adb', 'shell', 'pm', 'clear', package_name])
|
||||
if status_code == 0:
|
||||
print(f"clear cache status_code: {status_code}\noutput: {output}")
|
||||
return True
|
||||
else:
|
||||
print(error)
|
||||
print(f"clear cache error: {error}")
|
||||
return False
|
||||
else:
|
||||
print("stopApp error")
|
||||
|
||||
def stopApp(package_name):
|
||||
print(f"强行停止{package_name}")
|
||||
output, status_code, error = run_adb_command(['adb', 'shell', 'am', 'force-stop', package_name])
|
||||
if status_code == 0:
|
||||
print(f"status_code: {status_code}\noutput: {output}")
|
||||
return True
|
||||
else:
|
||||
print(error)
|
||||
logging.error(f"stop APP error: {error}")
|
||||
return False
|
||||
|
||||
23
modules/files_utils.py
Normal file
23
modules/files_utils.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
|
||||
# 获取当前Python脚本的绝对路径
|
||||
def get_path(script_name):
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_directory = os.path.dirname(script_directory)
|
||||
# 使用os.path.join构建hook_conversions.js的完整路径
|
||||
script_path = os.path.join(parent_directory,script_name)
|
||||
print(script_path)
|
||||
return script_path
|
||||
|
||||
def read_javascript(script_path):
|
||||
with open(script_path, "r") as file:
|
||||
script_code = file.read()
|
||||
return script_code
|
||||
def write_log(messages):
|
||||
global FIRST_WRITE
|
||||
print(f"FIRST_WRITE: {FIRST_WRITE}")
|
||||
with open("frida_log.log", "a") as log_file:
|
||||
if FIRST_WRITE: # 如果是首次写入
|
||||
log_file.write("\n\n\n") # 空出三行
|
||||
FIRST_WRITE = False # 更新状态,表明已经写过了
|
||||
log_file.write(str(messages) + "\n")
|
||||
479
r0capture/myhexdump.py
Normal file
479
r0capture/myhexdump.py
Normal file
@@ -0,0 +1,479 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# !/usr/bin/env python
|
||||
# -*- coding: latin-1 -*-
|
||||
|
||||
# <-- removing this magic comment breaks Python 3.4 on Windows
|
||||
"""
|
||||
1. Dump binary data to the following text format:
|
||||
|
||||
00000000: 00 00 00 5B 68 65 78 64 75 6D 70 5D 00 00 00 00 ...[hexdump]....
|
||||
00000010: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
|
||||
|
||||
It is similar to the one used by:
|
||||
Scapy
|
||||
00 00 00 5B 68 65 78 64 75 6D 70 5D 00 00 00 00 ...[hexdump]....
|
||||
00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
|
||||
|
||||
Far Manager
|
||||
000000000: 00 00 00 5B 68 65 78 64 ¦ 75 6D 70 5D 00 00 00 00 [hexdump]
|
||||
000000010: 00 11 22 33 44 55 66 77 ¦ 88 99 AA BB CC DD EE FF ?"3DUfwª»ÌÝîÿ
|
||||
|
||||
|
||||
2. Restore binary data from the formats above as well
|
||||
as from less exotic strings of raw hex
|
||||
|
||||
"""
|
||||
|
||||
__version__ = '3.3'
|
||||
__author__ = 'anatoly techtonik <techtonik@gmail.com>'
|
||||
__license__ = 'Public Domain'
|
||||
|
||||
__history__ = \
|
||||
"""
|
||||
3.3 (2015-01-22)
|
||||
* accept input from sys.stdin if "-" is specified
|
||||
for both dump and restore (issue #1)
|
||||
* new normalize_py() helper to set sys.stdout to
|
||||
binary mode on Windows
|
||||
|
||||
3.2 (2015-07-02)
|
||||
* hexdump is now packaged as .zip on all platforms
|
||||
(on Linux created archive was tar.gz)
|
||||
* .zip is executable! try `python hexdump-3.2.zip`
|
||||
* dump() now accepts configurable separator, patch
|
||||
by Ian Land (PR #3)
|
||||
|
||||
3.1 (2014-10-20)
|
||||
* implemented workaround against mysterious coding
|
||||
issue with Python 3 (see revision 51302cf)
|
||||
* fix Python 3 installs for systems where UTF-8 is
|
||||
not default (Windows), thanks to George Schizas
|
||||
(the problem was caused by reading of README.txt)
|
||||
|
||||
3.0 (2014-09-07)
|
||||
* remove unused int2byte() helper
|
||||
* add dehex(text) helper to convert hex string
|
||||
to binary data
|
||||
* add 'size' argument to dump() helper to specify
|
||||
length of chunks
|
||||
|
||||
2.0 (2014-02-02)
|
||||
* add --restore option to command line mode to get
|
||||
binary data back from hex dump
|
||||
* support saving test output with `--test logfile`
|
||||
* restore() from hex strings without spaces
|
||||
* restore() now raises TypeError if input data is
|
||||
not string
|
||||
* hexdump() and dumpgen() now don't return unicode
|
||||
strings in Python 2.x when generator is requested
|
||||
|
||||
1.0 (2013-12-30)
|
||||
* length of address is reduced from 10 to 8
|
||||
* hexdump() got new 'result' keyword argument, it
|
||||
can be either 'print', 'generator' or 'return'
|
||||
* actual dumping logic is now in new dumpgen()
|
||||
generator function
|
||||
* new dump(binary) function that takes binary data
|
||||
and returns string like "66 6F 72 6D 61 74"
|
||||
* new genchunks(mixed, size) function that chunks
|
||||
both sequences and file like objects
|
||||
|
||||
0.5 (2013-06-10)
|
||||
* hexdump is now also a command line utility (no
|
||||
restore yet)
|
||||
|
||||
0.4 (2013-06-09)
|
||||
* fix installation with Python 3 for non English
|
||||
versions of Windows, thanks to George Schizas
|
||||
|
||||
0.3 (2013-04-29)
|
||||
* fully Python 3 compatible
|
||||
|
||||
0.2 (2013-04-28)
|
||||
* restore() to recover binary data from a hex dump in
|
||||
native, Far Manager and Scapy text formats (others
|
||||
might work as well)
|
||||
* restore() is Python 3 compatible
|
||||
|
||||
0.1 (2013-04-28)
|
||||
* working hexdump() function for Python 2
|
||||
"""
|
||||
|
||||
import binascii # binascii is required for Python 3
|
||||
import sys
|
||||
|
||||
# --- constants
|
||||
PY3K = sys.version_info >= (3, 0)
|
||||
|
||||
|
||||
# --- workaround against Python consistency issues
|
||||
def normalize_py():
|
||||
''' Problem 001 - sys.stdout in Python is by default opened in
|
||||
text mode, and writes to this stdout produce corrupted binary
|
||||
data on Windows
|
||||
|
||||
python -c "import sys; sys.stdout.write('_\n_')" > file
|
||||
python -c "print(repr(open('file', 'rb').read()))"
|
||||
'''
|
||||
if sys.platform == "win32":
|
||||
# set sys.stdout to binary mode on Windows
|
||||
import os, msvcrt
|
||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||
|
||||
|
||||
# --- - chunking helpers
|
||||
def chunks(seq, size):
|
||||
'''Generator that cuts sequence (bytes, memoryview, etc.)
|
||||
into chunks of given size. If `seq` length is not multiply
|
||||
of `size`, the lengh of the last chunk returned will be
|
||||
less than requested.
|
||||
|
||||
>>> list( chunks([1,2,3,4,5,6,7], 3) )
|
||||
[[1, 2, 3], [4, 5, 6], [7]]
|
||||
'''
|
||||
d, m = divmod(len(seq), size)
|
||||
for i in range(d):
|
||||
yield seq[i * size:(i + 1) * size]
|
||||
if m:
|
||||
yield seq[d * size:]
|
||||
|
||||
|
||||
def chunkread(f, size):
|
||||
'''Generator that reads from file like object. May return less
|
||||
data than requested on the last read.'''
|
||||
c = f.read(size)
|
||||
while len(c):
|
||||
yield c
|
||||
c = f.read(size)
|
||||
|
||||
|
||||
def genchunks(mixed, size):
|
||||
'''Generator to chunk binary sequences or file like objects.
|
||||
The size of the last chunk returned may be less than
|
||||
requested.'''
|
||||
if hasattr(mixed, 'read'):
|
||||
return chunkread(mixed, size)
|
||||
else:
|
||||
return chunks(mixed, size)
|
||||
|
||||
|
||||
# --- - /chunking helpers
|
||||
|
||||
|
||||
def dehex(hextext):
|
||||
"""
|
||||
Convert from hex string to binary data stripping
|
||||
whitespaces from `hextext` if necessary.
|
||||
"""
|
||||
if PY3K:
|
||||
return bytes.fromhex(hextext)
|
||||
else:
|
||||
hextext = "".join(hextext.split())
|
||||
return hextext.decode('hex')
|
||||
|
||||
|
||||
def dump(binary, size=2, sep=' '):
|
||||
'''
|
||||
Convert binary data (bytes in Python 3 and str in
|
||||
Python 2) to hex string like '00 DE AD BE EF'.
|
||||
`size` argument specifies length of text chunks
|
||||
and `sep` sets chunk separator.
|
||||
'''
|
||||
hexstr = binascii.hexlify(binary)
|
||||
if PY3K:
|
||||
hexstr = hexstr.decode('ascii')
|
||||
return sep.join(chunks(hexstr.upper(), size))
|
||||
|
||||
|
||||
def dumpgen(data, only_str):
|
||||
'''
|
||||
Generator that produces strings:
|
||||
|
||||
'00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................'
|
||||
'''
|
||||
generator = genchunks(data, 16)
|
||||
for addr, d in enumerate(generator):
|
||||
line = ""
|
||||
if not only_str:
|
||||
# 00000000:
|
||||
line = '%08X: ' % (addr * 16)
|
||||
# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
dumpstr = dump(d)
|
||||
line += dumpstr[:8 * 3]
|
||||
if len(d) > 8: # insert separator if needed
|
||||
line += ' ' + dumpstr[8 * 3:]
|
||||
# ................
|
||||
# calculate indentation, which may be different for the last line
|
||||
pad = 2
|
||||
if len(d) < 16:
|
||||
pad += 3 * (16 - len(d))
|
||||
if len(d) <= 8:
|
||||
pad += 1
|
||||
line += ' ' * pad
|
||||
|
||||
for byte in d:
|
||||
# printable ASCII range 0x20 to 0x7E
|
||||
if not PY3K:
|
||||
byte = ord(byte)
|
||||
if 0x20 <= byte <= 0x7E:
|
||||
line += chr(byte)
|
||||
else:
|
||||
line += '.'
|
||||
yield line
|
||||
|
||||
|
||||
def hexdump(data, result='print', only_str=False):
|
||||
'''
|
||||
Transform binary data to the hex dump text format:
|
||||
|
||||
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
|
||||
[x] data argument as a binary string
|
||||
[x] data argument as a file like object
|
||||
|
||||
Returns result depending on the `result` argument:
|
||||
'print' - prints line by line
|
||||
'return' - returns single string
|
||||
'generator' - returns generator that produces lines
|
||||
'''
|
||||
if PY3K and type(data) == str:
|
||||
raise TypeError('Abstract unicode data (expected bytes sequence)')
|
||||
|
||||
gen = dumpgen(data, only_str=only_str)
|
||||
if result == 'generator':
|
||||
return gen
|
||||
elif result == 'return':
|
||||
return '\n'.join(gen)
|
||||
elif result == 'print':
|
||||
for line in gen:
|
||||
print(line)
|
||||
else:
|
||||
raise ValueError('Unknown value of `result` argument')
|
||||
|
||||
|
||||
def restore(dump):
|
||||
'''
|
||||
Restore binary data from a hex dump.
|
||||
[x] dump argument as a string
|
||||
[ ] dump argument as a line iterator
|
||||
|
||||
Supported formats:
|
||||
[x] hexdump.hexdump
|
||||
[x] Scapy
|
||||
[x] Far Manager
|
||||
'''
|
||||
minhexwidth = 2 * 16 # minimal width of the hex part - 00000... style
|
||||
bytehexwidth = 3 * 16 - 1 # min width for a bytewise dump - 00 00 ... style
|
||||
|
||||
result = bytes() if PY3K else ''
|
||||
if type(dump) != str:
|
||||
raise TypeError('Invalid data for restore')
|
||||
|
||||
text = dump.strip() # ignore surrounding empty lines
|
||||
for line in text.split('\n'):
|
||||
# strip address part
|
||||
addrend = line.find(':')
|
||||
if 0 < addrend < minhexwidth: # : is not in ascii part
|
||||
line = line[addrend + 1:]
|
||||
line = line.lstrip()
|
||||
# check dump type
|
||||
if line[2] == ' ': # 00 00 00 ... type of dump
|
||||
# check separator
|
||||
sepstart = (2 + 1) * 7 + 2 # ('00'+' ')*7+'00'
|
||||
sep = line[sepstart:sepstart + 3]
|
||||
if sep[:2] == ' ' and sep[2:] != ' ': # ...00 00 00 00...
|
||||
hexdata = line[:bytehexwidth + 1]
|
||||
elif sep[2:] == ' ': # ...00 00 | 00 00... - Far Manager
|
||||
hexdata = line[:sepstart] + line[sepstart + 3:bytehexwidth + 2]
|
||||
else: # ...00 00 00 00... - Scapy, no separator
|
||||
hexdata = line[:bytehexwidth]
|
||||
line = hexdata
|
||||
result += dehex(line)
|
||||
return result
|
||||
|
||||
|
||||
def runtest(logfile=None):
|
||||
'''Run hexdump tests. Requires hexfile.bin to be in the same
|
||||
directory as hexdump.py itself'''
|
||||
|
||||
class TeeOutput(object):
|
||||
def __init__(self, stream1, stream2):
|
||||
self.outputs = [stream1, stream2]
|
||||
|
||||
# -- methods from sys.stdout / sys.stderr
|
||||
def write(self, data):
|
||||
for stream in self.outputs:
|
||||
if PY3K:
|
||||
if 'b' in stream.mode:
|
||||
data = data.encode('utf-8')
|
||||
stream.write(data)
|
||||
stream.flush()
|
||||
|
||||
def tell(self):
|
||||
raise IOError
|
||||
|
||||
def flush(self):
|
||||
for stream in self.outputs:
|
||||
stream.flush()
|
||||
# --/ sys.stdout
|
||||
|
||||
if logfile:
|
||||
openlog = open(logfile, 'wb')
|
||||
# copy stdout and stderr streams to log file
|
||||
savedstd = sys.stderr, sys.stdout
|
||||
sys.stderr = TeeOutput(sys.stderr, openlog)
|
||||
sys.stdout = TeeOutput(sys.stdout, openlog)
|
||||
|
||||
def echo(msg, linefeed=True):
|
||||
sys.stdout.write(msg)
|
||||
if linefeed:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
expected = '''\
|
||||
00000000: 00 00 00 5B 68 65 78 64 75 6D 70 5D 00 00 00 00 ...[hexdump]....
|
||||
00000010: 00 11 22 33 44 55 66 77 88 99 0A BB CC DD EE FF .."3DUfw........\
|
||||
'''
|
||||
|
||||
# get path to hexfile.bin
|
||||
# this doesn't work from .zip
|
||||
# import os.path as osp
|
||||
# hexfile = osp.dirname(osp.abspath(__file__)) + '/hexfile.bin'
|
||||
# this doesn't work either
|
||||
# hexfile = osp.dirname(sys.modules[__name__].__file__) + '/hexfile.bin'
|
||||
# this works
|
||||
import pkgutil
|
||||
bin = pkgutil.get_data('hexdump', 'data/hexfile.bin')
|
||||
|
||||
# varios length of input data
|
||||
hexdump(b'zzzz' * 12)
|
||||
hexdump(b'o' * 17)
|
||||
hexdump(b'p' * 24)
|
||||
hexdump(b'q' * 26)
|
||||
# allowable character set filter
|
||||
hexdump(b'line\nfeed\r\ntest')
|
||||
hexdump(b'\x00\x00\x00\x5B\x68\x65\x78\x64\x75\x6D\x70\x5D\x00\x00\x00\x00'
|
||||
b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\x0A\xBB\xCC\xDD\xEE\xFF')
|
||||
print('---')
|
||||
# dumping file-like binary object to screen (default behavior)
|
||||
hexdump(bin)
|
||||
print('return output')
|
||||
hexout = hexdump(bin, result='return')
|
||||
assert hexout == expected, 'returned hex didn\'t match'
|
||||
print('return generator')
|
||||
hexgen = hexdump(bin, result='generator')
|
||||
assert next(hexgen) == expected.split('\n')[0], 'hex generator 1 didn\'t match'
|
||||
assert next(hexgen) == expected.split('\n')[1], 'hex generator 2 didn\'t match'
|
||||
|
||||
# binary restore test
|
||||
bindata = restore(
|
||||
'''
|
||||
00000000: 00 00 00 5B 68 65 78 64 75 6D 70 5D 00 00 00 00 ...[hexdump]....
|
||||
00000010: 00 11 22 33 44 55 66 77 88 99 0A BB CC DD EE FF .."3DUfw........
|
||||
''')
|
||||
echo('restore check ', linefeed=False)
|
||||
assert bin == bindata, 'restore check failed'
|
||||
echo('passed')
|
||||
|
||||
far = \
|
||||
'''
|
||||
000000000: 00 00 00 5B 68 65 78 64 ¦ 75 6D 70 5D 00 00 00 00 [hexdump]
|
||||
000000010: 00 11 22 33 44 55 66 77 ¦ 88 99 0A BB CC DD EE FF ?"3DUfwª»ÌÝîÿ
|
||||
'''
|
||||
echo('restore far format ', linefeed=False)
|
||||
assert bin == restore(far), 'far format check failed'
|
||||
echo('passed')
|
||||
|
||||
scapy = '''\
|
||||
00 00 00 5B 68 65 78 64 75 6D 70 5D 00 00 00 00 ...[hexdump]....
|
||||
00 11 22 33 44 55 66 77 88 99 0A BB CC DD EE FF .."3DUfw........
|
||||
'''
|
||||
echo('restore scapy format ', linefeed=False)
|
||||
assert bin == restore(scapy), 'scapy format check failed'
|
||||
echo('passed')
|
||||
|
||||
if not PY3K:
|
||||
assert restore('5B68657864756D705D') == '[hexdump]', 'no space check failed'
|
||||
assert dump('\\\xa1\xab\x1e', sep='').lower() == '5ca1ab1e'
|
||||
else:
|
||||
assert restore('5B68657864756D705D') == b'[hexdump]', 'no space check failed'
|
||||
assert dump(b'\\\xa1\xab\x1e', sep='').lower() == '5ca1ab1e'
|
||||
|
||||
print('---[test file hexdumping]---')
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
hexfile = tempfile.NamedTemporaryFile(delete=False)
|
||||
try:
|
||||
hexfile.write(bin)
|
||||
hexfile.close()
|
||||
hexdump(open(hexfile.name, 'rb'))
|
||||
finally:
|
||||
os.remove(hexfile.name)
|
||||
if logfile:
|
||||
sys.stderr, sys.stdout = savedstd
|
||||
openlog.close()
|
||||
|
||||
|
||||
def main():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser(usage='''
|
||||
%prog [binfile|-]
|
||||
%prog -r hexfile
|
||||
%prog --test [logfile]''', version=__version__)
|
||||
parser.add_option('-r', '--restore', action='store_true',
|
||||
help='restore binary from hex dump')
|
||||
parser.add_option('--test', action='store_true', help='run hexdump sanity checks')
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if options.test:
|
||||
if args:
|
||||
runtest(logfile=args[0])
|
||||
else:
|
||||
runtest()
|
||||
elif not args or len(args) > 1:
|
||||
parser.print_help()
|
||||
sys.exit(-1)
|
||||
else:
|
||||
## dump file
|
||||
if not options.restore:
|
||||
# [x] memory effective dump
|
||||
if args[0] == '-':
|
||||
if not PY3K:
|
||||
hexdump(sys.stdin)
|
||||
else:
|
||||
hexdump(sys.stdin.buffer)
|
||||
else:
|
||||
hexdump(open(args[0], 'rb'))
|
||||
|
||||
## restore file
|
||||
else:
|
||||
# prepare input stream
|
||||
if args[0] == '-':
|
||||
instream = sys.stdin
|
||||
else:
|
||||
if PY3K:
|
||||
instream = open(args[0])
|
||||
else:
|
||||
instream = open(args[0], 'rb')
|
||||
|
||||
# output stream
|
||||
# [ ] memory efficient restore
|
||||
if PY3K:
|
||||
sys.stdout.buffer.write(restore(instream.read()))
|
||||
else:
|
||||
# Windows - binary mode for sys.stdout to prevent data corruption
|
||||
normalize_py()
|
||||
sys.stdout.write(restore(instream.read()))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
# [x] file restore from command line utility
|
||||
# [ ] write dump with LF on Windows for consistency
|
||||
# [ ] encoding param for hexdump()ing Python 3 str if anybody requests that
|
||||
|
||||
# [ ] document chunking API
|
||||
# [ ] document hexdump API
|
||||
# [ ] blog about sys.stdout text mode problem on Windows
|
||||
374
r0capture/r0capture.py
Normal file
374
r0capture/r0capture.py
Normal file
@@ -0,0 +1,374 @@
|
||||
# Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Decrypts and logs a process's SSL traffic.
|
||||
Hooks the functions SSL_read() and SSL_write() in a given process and logs the
|
||||
decrypted data to the console and/or to a pcap file.
|
||||
Typical usage example:
|
||||
ssl_log("wget", "log.pcap", True)
|
||||
Dependencies:
|
||||
frida (https://www.frida.re/):
|
||||
sudo pip install frida
|
||||
hexdump (https://bitbucket.org/techtonik/hexdump/) if using verbose output:
|
||||
sudo pip install hexdump
|
||||
"""
|
||||
|
||||
__author__ = "geffner@google.com (Jason Geffner)"
|
||||
__version__ = "2.0"
|
||||
|
||||
"""
|
||||
# r0capture
|
||||
|
||||
ID: r0ysue
|
||||
|
||||
安卓应用层抓包通杀脚本
|
||||
|
||||
https://github.com/r0ysue/r0capture
|
||||
|
||||
## 简介
|
||||
|
||||
- 仅限安卓平台,测试安卓7、8、9、10 可用 ;
|
||||
- 无视所有证书校验或绑定,无视任何证书;
|
||||
- 通杀TCP/IP四层模型中的应用层中的全部协议;
|
||||
- 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本;
|
||||
- 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等;
|
||||
"""
|
||||
|
||||
# Windows版本需要安装库:
|
||||
# pip install 'win_inet_pton'
|
||||
# pip install hexdump
|
||||
import argparse
|
||||
import os
|
||||
import pprint
|
||||
import random
|
||||
import signal
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
from loguru import logger
|
||||
|
||||
try:
|
||||
if os.name == 'nt':
|
||||
import win_inet_pton
|
||||
except ImportError:
|
||||
# win_inet_pton import error
|
||||
pass
|
||||
|
||||
try:
|
||||
import myhexdump as hexdump # pylint: disable=g-import-not-at-top
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from shutil import get_terminal_size as get_terminal_size
|
||||
except:
|
||||
try:
|
||||
from backports.shutil_get_terminal_size import get_terminal_size as get_terminal_size
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
import click
|
||||
except:
|
||||
class click:
|
||||
@staticmethod
|
||||
def secho(message=None, **kwargs):
|
||||
print(message)
|
||||
|
||||
@staticmethod
|
||||
def style(**kwargs):
|
||||
raise Exception("unsupported style")
|
||||
banner = """
|
||||
--------------------------------------------------------------------------------------------
|
||||
.oooo. .
|
||||
d8P'`Y8b .o8
|
||||
oooo d8b 888 888 .ooooo. .oooo. oo.ooooo. .o888oo oooo oooo oooo d8b .ooooo.
|
||||
`888""8P 888 888 d88' `"Y8 `P )88b 888' `88b 888 `888 `888 `888""8P d88' `88b
|
||||
888 888 888 888 .oP"888 888 888 888 888 888 888 888ooo888
|
||||
888 `88b d88' 888 .o8 d8( 888 888 888 888 . 888 888 888 888 .o
|
||||
d888b `Y8bd8P' `Y8bod8P' `Y888""8o 888bod8P' "888" `V88V"V8P' d888b `Y8bod8P'
|
||||
888
|
||||
o888o
|
||||
https://github.com/r0ysue/r0capture
|
||||
--------------------------------------------------------------------------------------------\n
|
||||
"""
|
||||
|
||||
|
||||
def show_banner():
|
||||
colors = ['bright_red', 'bright_green', 'bright_blue', 'cyan', 'magenta']
|
||||
try:
|
||||
click.style('color test', fg='bright_red')
|
||||
except:
|
||||
colors = ['red', 'green', 'blue', 'cyan', 'magenta']
|
||||
try:
|
||||
columns = get_terminal_size().columns
|
||||
if columns >= len(banner.splitlines()[1]):
|
||||
for line in banner.splitlines():
|
||||
click.secho(line, fg=random.choice(colors))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# ssl_session[<SSL_SESSION id>] = (<bytes sent by client>,
|
||||
# <bytes sent by server>)
|
||||
ssl_sessions = {}
|
||||
|
||||
|
||||
def ssl_log(process, pcap=None, host=False, verbose=False, isUsb=False, ssllib="", isSpawn=True, wait=0):
|
||||
"""Decrypts and logs a process's SSL traffic.
|
||||
Hooks the functions SSL_read() and SSL_write() in a given process and logs
|
||||
the decrypted data to the console and/or to a pcap file.
|
||||
Args:
|
||||
process: The target process's name (as a string) or process ID (as an int).
|
||||
pcap: The file path to which the pcap file should be written.
|
||||
verbose: If True, log the decrypted traffic to the console.
|
||||
Raises:
|
||||
NotImplementedError: Not running on a Linux or macOS system.
|
||||
"""
|
||||
|
||||
# if platform.system() not in ("Darwin", "Linux"):
|
||||
# raise NotImplementedError("This function is only implemented for Linux and "
|
||||
# "macOS systems.")
|
||||
|
||||
def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port,
|
||||
dst_addr, dst_port, data):
|
||||
"""Writes the captured data to a pcap file.
|
||||
Args:
|
||||
pcap_file: The opened pcap file.
|
||||
ssl_session_id: The SSL session ID for the communication.
|
||||
function: The function that was intercepted ("SSL_read" or "SSL_write").
|
||||
src_addr: The source address of the logged packet.
|
||||
src_port: The source port of the logged packet.
|
||||
dst_addr: The destination address of the logged packet.
|
||||
dst_port: The destination port of the logged packet.
|
||||
data: The decrypted packet data.
|
||||
"""
|
||||
t = time.time()
|
||||
|
||||
if ssl_session_id not in ssl_sessions:
|
||||
ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF),
|
||||
random.randint(0, 0xFFFFFFFF))
|
||||
client_sent, server_sent = ssl_sessions[ssl_session_id]
|
||||
|
||||
if function == "SSL_read":
|
||||
seq, ack = (server_sent, client_sent)
|
||||
else:
|
||||
seq, ack = (client_sent, server_sent)
|
||||
|
||||
for writes in (
|
||||
# PCAP record (packet) header
|
||||
("=I", int(t)), # Timestamp seconds
|
||||
("=I", int((t * 1000000) % 1000000)), # Timestamp microseconds
|
||||
("=I", 40 + len(data)), # Number of octets saved
|
||||
("=i", 40 + len(data)), # Actual length of packet
|
||||
# IPv4 header
|
||||
(">B", 0x45), # Version and Header Length
|
||||
(">B", 0), # Type of Service
|
||||
(">H", 40 + len(data)), # Total Length
|
||||
(">H", 0), # Identification
|
||||
(">H", 0x4000), # Flags and Fragment Offset
|
||||
(">B", 0xFF), # Time to Live
|
||||
(">B", 6), # Protocol
|
||||
(">H", 0), # Header Checksum
|
||||
(">I", src_addr), # Source Address
|
||||
(">I", dst_addr), # Destination Address
|
||||
# TCP header
|
||||
(">H", src_port), # Source Port
|
||||
(">H", dst_port), # Destination Port
|
||||
(">I", seq), # Sequence Number
|
||||
(">I", ack), # Acknowledgment Number
|
||||
(">H", 0x5018), # Header Length and Flags
|
||||
(">H", 0xFFFF), # Window Size
|
||||
(">H", 0), # Checksum
|
||||
(">H", 0)): # Urgent Pointer
|
||||
pcap_file.write(struct.pack(writes[0], writes[1]))
|
||||
pcap_file.write(data)
|
||||
|
||||
if function == "SSL_read":
|
||||
server_sent += len(data)
|
||||
else:
|
||||
client_sent += len(data)
|
||||
ssl_sessions[ssl_session_id] = (client_sent, server_sent)
|
||||
|
||||
def on_message(message, data):
|
||||
"""Callback for errors and messages sent from Frida-injected JavaScript.
|
||||
Logs captured packet data received from JavaScript to the console and/or a
|
||||
pcap file. See https://www.frida.re/docs/messages/ for more detail on
|
||||
Frida's messages.
|
||||
Args:
|
||||
message: A dictionary containing the message "type" and other fields
|
||||
dependent on message type.
|
||||
data: The string of captured decrypted data.
|
||||
"""
|
||||
if message["type"] == "error":
|
||||
logger.info(f"{message}")
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
return
|
||||
if len(data) == 1:
|
||||
logger.info(f'{message["payload"]["function"]}')
|
||||
logger.info(f'{message["payload"]["stack"]}')
|
||||
return
|
||||
p = message["payload"]
|
||||
if verbose:
|
||||
src_addr = socket.inet_ntop(socket.AF_INET,
|
||||
struct.pack(">I", p["src_addr"]))
|
||||
dst_addr = socket.inet_ntop(socket.AF_INET,
|
||||
struct.pack(">I", p["dst_addr"]))
|
||||
session_id = p['ssl_session_id']
|
||||
logger.info(f"SSL Session: {session_id}")
|
||||
logger.info("[%s] %s:%d --> %s:%d" % (
|
||||
p["function"],
|
||||
src_addr,
|
||||
p["src_port"],
|
||||
dst_addr,
|
||||
p["dst_port"]))
|
||||
gen = hexdump.hexdump(data, result="generator",only_str=True)
|
||||
str_gen = ''.join(gen)
|
||||
logger.info(f"{str_gen}")
|
||||
logger.info(f"{p['stack']}")
|
||||
if pcap:
|
||||
log_pcap(pcap_file, p["ssl_session_id"], p["function"], p["src_addr"],
|
||||
p["src_port"], p["dst_addr"], p["dst_port"], data)
|
||||
|
||||
if isUsb:
|
||||
try:
|
||||
device = frida.get_usb_device()
|
||||
except:
|
||||
device = frida.get_remote_device()
|
||||
else:
|
||||
if host:
|
||||
manager = frida.get_device_manager()
|
||||
device = manager.add_remote_device(host)
|
||||
else:
|
||||
device = frida.get_local_device()
|
||||
|
||||
if isSpawn:
|
||||
pid = device.spawn([process])
|
||||
time.sleep(1)
|
||||
session = device.attach(pid)
|
||||
time.sleep(1)
|
||||
device.resume(pid)
|
||||
else:
|
||||
print("attach")
|
||||
session = device.attach(process)
|
||||
if wait > 0:
|
||||
print(f"wait for {wait} seconds")
|
||||
time.sleep(wait)
|
||||
|
||||
# session = frida.attach(process)
|
||||
|
||||
# pid = device.spawn([process])
|
||||
# pid = process
|
||||
# session = device.attach(pid)
|
||||
# device.resume(pid)
|
||||
if pcap:
|
||||
pcap_file = open(pcap, "wb", 0)
|
||||
for writes in (
|
||||
("=I", 0xa1b2c3d4), # Magic number
|
||||
("=H", 2), # Major version number
|
||||
("=H", 4), # Minor version number
|
||||
("=i", time.timezone), # GMT to local correction
|
||||
("=I", 0), # Accuracy of timestamps
|
||||
("=I", 65535), # Max length of captured packets
|
||||
("=I", 228)): # Data link type (LINKTYPE_IPV4)
|
||||
pcap_file.write(struct.pack(writes[0], writes[1]))
|
||||
|
||||
with open(Path(__file__).resolve().parent.joinpath("./script.js"), encoding="utf-8") as f:
|
||||
_FRIDA_SCRIPT = f.read()
|
||||
# _FRIDA_SCRIPT = session.create_script(content)
|
||||
# print(_FRIDA_SCRIPT)
|
||||
script = session.create_script(_FRIDA_SCRIPT)
|
||||
script.on("message", on_message)
|
||||
script.load()
|
||||
|
||||
if ssllib != "":
|
||||
script.exports.setssllib(ssllib)
|
||||
|
||||
print("Press Ctrl+C to stop logging.")
|
||||
|
||||
def stoplog(signum, frame):
|
||||
print('You have stoped logging.')
|
||||
session.detach()
|
||||
if pcap:
|
||||
pcap_file.flush()
|
||||
pcap_file.close()
|
||||
exit()
|
||||
|
||||
signal.signal(signal.SIGINT, stoplog)
|
||||
signal.signal(signal.SIGTERM, stoplog)
|
||||
sys.stdin.read()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
show_banner()
|
||||
|
||||
|
||||
class ArgParser(argparse.ArgumentParser):
|
||||
|
||||
def error(self, message):
|
||||
print("ssl_logger v" + __version__)
|
||||
print("by " + __author__)
|
||||
print("Modified by BigFaceCat")
|
||||
print("Error: " + message)
|
||||
print()
|
||||
print(self.format_help().replace("usage:", "Usage:"))
|
||||
self.exit(0)
|
||||
|
||||
|
||||
parser = ArgParser(
|
||||
add_help=False,
|
||||
description="Decrypts and logs a process's SSL traffic.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=r"""
|
||||
Examples:
|
||||
%(prog)s -pcap ssl.pcap openssl
|
||||
%(prog)s -verbose 31337
|
||||
%(prog)s -pcap log.pcap -verbose wget
|
||||
%(prog)s -pcap log.pcap -ssl "*libssl.so*" com.bigfacecat.testdemo
|
||||
""")
|
||||
|
||||
args = parser.add_argument_group("Arguments")
|
||||
args.add_argument("-pcap", '-p', metavar="<path>", required=False,
|
||||
help="Name of PCAP file to write")
|
||||
args.add_argument("-host", '-H', metavar="<192.168.1.1:27042>", required=False,
|
||||
help="connect to remote frida-server on HOST")
|
||||
args.add_argument("-verbose", "-v", required=False, action="store_const", default=True,
|
||||
const=True, help="Show verbose output")
|
||||
args.add_argument("process", metavar="<process name | process id>",
|
||||
help="Process whose SSL calls to log")
|
||||
args.add_argument("-ssl", default="", metavar="<lib>",
|
||||
help="SSL library to hook")
|
||||
args.add_argument("--isUsb", "-U", default=False, action="store_true",
|
||||
help="connect to USB device")
|
||||
args.add_argument("--isSpawn", "-f", default=False, action="store_true",
|
||||
help="if spawned app")
|
||||
args.add_argument("-wait", "-w", type=int, metavar="<seconds>", default=0,
|
||||
help="Time to wait for the process")
|
||||
|
||||
parsed = parser.parse_args()
|
||||
logger.add(f"{parsed.process.replace('.','_')}-{int(time.time())}.log", rotation="500MB", encoding="utf-8", enqueue=True, retention="10 days")
|
||||
|
||||
ssl_log(
|
||||
int(parsed.process) if parsed.process.isdigit() else parsed.process,
|
||||
parsed.pcap,
|
||||
parsed.host,
|
||||
parsed.verbose,
|
||||
isUsb=parsed.isUsb,
|
||||
isSpawn=parsed.isSpawn,
|
||||
ssllib=parsed.ssl,
|
||||
wait=parsed.wait
|
||||
)
|
||||
339
r0capture/script.js
Normal file
339
r0capture/script.js
Normal file
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* Initializes 'addresses' dictionary and NativeFunctions.
|
||||
*/
|
||||
"use strict";
|
||||
rpc.exports = {
|
||||
setssllib: function (name) {
|
||||
console.log("setSSLLib => " + name);
|
||||
libname = name;
|
||||
initializeGlobals();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
var addresses = {};
|
||||
var SSL_get_fd = null;
|
||||
var SSL_get_session = null;
|
||||
var SSL_SESSION_get_id = null;
|
||||
var getpeername = null;
|
||||
var getsockname = null;
|
||||
var ntohs = null;
|
||||
var ntohl = null;
|
||||
var SSLstackwrite = null;
|
||||
var SSLstackread = null;
|
||||
|
||||
var libname = "*libssl*";
|
||||
|
||||
function uuid(len, radix) {
|
||||
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
|
||||
var uuid = [], i;
|
||||
radix = radix || chars.length;
|
||||
|
||||
if (len) {
|
||||
// Compact form
|
||||
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
|
||||
} else {
|
||||
// rfc4122, version 4 form
|
||||
var r;
|
||||
|
||||
// rfc4122 requires these characters
|
||||
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
||||
uuid[14] = '4';
|
||||
|
||||
// Fill in random data. At i==19 set the high bits of clock sequence as
|
||||
// per rfc4122, sec. 4.1.5
|
||||
for (i = 0; i < 36; i++) {
|
||||
if (!uuid[i]) {
|
||||
r = 0 | Math.random() * 16;
|
||||
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uuid.join('');
|
||||
}
|
||||
function return_zero(args) {
|
||||
return 0;
|
||||
}
|
||||
function initializeGlobals() {
|
||||
var resolver = new ApiResolver("module");
|
||||
var exps = [
|
||||
[Process.platform == "darwin" ? "*libboringssl*" : "*libssl*", ["SSL_read", "SSL_write", "SSL_get_fd", "SSL_get_session", "SSL_SESSION_get_id"]], // for ios and Android
|
||||
[Process.platform == "darwin" ? "*libsystem*" : "*libc*", ["getpeername", "getsockname", "ntohs", "ntohl"]]
|
||||
];
|
||||
// console.log(exps)
|
||||
for (var i = 0; i < exps.length; i++) {
|
||||
var lib = exps[i][0];
|
||||
var names = exps[i][1];
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
var name = names[j];
|
||||
// console.log("exports:" + lib + "!" + name)
|
||||
var matches = resolver.enumerateMatchesSync("exports:" + lib + "!" + name);
|
||||
if (matches.length == 0) {
|
||||
if (name == "SSL_get_fd") {
|
||||
addresses["SSL_get_fd"] = 0;
|
||||
continue;
|
||||
}
|
||||
throw "Could not find " + lib + "!" + name;
|
||||
}
|
||||
else if (matches.length != 1) {
|
||||
// Sometimes Frida returns duplicates.
|
||||
var address = 0;
|
||||
var s = "";
|
||||
var duplicates_only = true;
|
||||
for (var k = 0; k < matches.length; k++) {
|
||||
if (s.length != 0) {
|
||||
s += ", ";
|
||||
}
|
||||
s += matches[k].name + "@" + matches[k].address;
|
||||
if (address == 0) {
|
||||
address = matches[k].address;
|
||||
}
|
||||
else if (!address.equals(matches[k].address)) {
|
||||
duplicates_only = false;
|
||||
}
|
||||
}
|
||||
if (!duplicates_only) {
|
||||
throw "More than one match found for " + lib + "!" + name + ": " + s;
|
||||
}
|
||||
}
|
||||
addresses[name] = matches[0].address;
|
||||
}
|
||||
}
|
||||
if (addresses["SSL_get_fd"] == 0) {
|
||||
SSL_get_fd = return_zero;
|
||||
} else {
|
||||
SSL_get_fd = new NativeFunction(addresses["SSL_get_fd"], "int", ["pointer"]);
|
||||
}
|
||||
SSL_get_session = new NativeFunction(addresses["SSL_get_session"], "pointer", ["pointer"]);
|
||||
SSL_SESSION_get_id = new NativeFunction(addresses["SSL_SESSION_get_id"], "pointer", ["pointer", "pointer"]);
|
||||
getpeername = new NativeFunction(addresses["getpeername"], "int", ["int", "pointer", "pointer"]);
|
||||
getsockname = new NativeFunction(addresses["getsockname"], "int", ["int", "pointer", "pointer"]);
|
||||
ntohs = new NativeFunction(addresses["ntohs"], "uint16", ["uint16"]);
|
||||
ntohl = new NativeFunction(addresses["ntohl"], "uint32", ["uint32"]);
|
||||
}
|
||||
initializeGlobals();
|
||||
|
||||
function ipToNumber(ip) {
|
||||
var num = 0;
|
||||
if (ip == "") {
|
||||
return num;
|
||||
}
|
||||
var aNum = ip.split(".");
|
||||
if (aNum.length != 4) {
|
||||
return num;
|
||||
}
|
||||
num += parseInt(aNum[0]) << 0;
|
||||
num += parseInt(aNum[1]) << 8;
|
||||
num += parseInt(aNum[2]) << 16;
|
||||
num += parseInt(aNum[3]) << 24;
|
||||
num = num >>> 0;//这个很关键,不然可能会出现负数的情况
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dictionary of a sockfd's "src_addr", "src_port", "dst_addr", and
|
||||
* "dst_port".
|
||||
* @param {int} sockfd The file descriptor of the socket to inspect.
|
||||
* @param {boolean} isRead If true, the context is an SSL_read call. If
|
||||
* false, the context is an SSL_write call.
|
||||
* @return {dict} Dictionary of sockfd's "src_addr", "src_port", "dst_addr",
|
||||
* and "dst_port".
|
||||
*/
|
||||
function getPortsAndAddresses(sockfd, isRead) {
|
||||
var message = {};
|
||||
var src_dst = ["src", "dst"];
|
||||
for (var i = 0; i < src_dst.length; i++) {
|
||||
if ((src_dst[i] == "src") ^ isRead) {
|
||||
var sockAddr = Socket.localAddress(sockfd)
|
||||
}
|
||||
else {
|
||||
var sockAddr = Socket.peerAddress(sockfd)
|
||||
}
|
||||
if (sockAddr == null) {
|
||||
// 网络超时or其他原因可能导致socket被关闭
|
||||
message[src_dst[i] + "_port"] = 0
|
||||
message[src_dst[i] + "_addr"] = 0
|
||||
} else {
|
||||
message[src_dst[i] + "_port"] = (sockAddr.port & 0xFFFF)
|
||||
message[src_dst[i] + "_addr"] = ntohl(ipToNumber(sockAddr.ip.split(":").pop()))
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Get the session_id of SSL object and return it as a hex string.
|
||||
* @param {!NativePointer} ssl A pointer to an SSL object.
|
||||
* @return {dict} A string representing the session_id of the SSL object's
|
||||
* SSL_SESSION. For example,
|
||||
* "59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336".
|
||||
*/
|
||||
function getSslSessionId(ssl) {
|
||||
var session = SSL_get_session(ssl);
|
||||
if (session == 0) {
|
||||
return 0;
|
||||
}
|
||||
var len = Memory.alloc(4);
|
||||
var p = SSL_SESSION_get_id(session, len);
|
||||
len = Memory.readU32(len);
|
||||
var session_id = "";
|
||||
for (var i = 0; i < len; i++) {
|
||||
// Read a byte, convert it to a hex string (0xAB ==> "AB"), and append
|
||||
// it to session_id.
|
||||
session_id +=
|
||||
("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2);
|
||||
}
|
||||
return session_id;
|
||||
}
|
||||
|
||||
Interceptor.attach(addresses["SSL_read"],
|
||||
{
|
||||
onEnter: function (args) {
|
||||
var message = getPortsAndAddresses(SSL_get_fd(args[0]), true);
|
||||
message["ssl_session_id"] = getSslSessionId(args[0]);
|
||||
message["function"] = "SSL_read";
|
||||
message["stack"] = SSLstackread;
|
||||
this.message = message;
|
||||
this.buf = args[1];
|
||||
},
|
||||
onLeave: function (retval) {
|
||||
retval |= 0; // Cast retval to 32-bit integer.
|
||||
if (retval <= 0) {
|
||||
return;
|
||||
}
|
||||
send(this.message, Memory.readByteArray(this.buf, retval));
|
||||
}
|
||||
});
|
||||
|
||||
Interceptor.attach(addresses["SSL_write"],
|
||||
{
|
||||
onEnter: function (args) {
|
||||
var message = getPortsAndAddresses(SSL_get_fd(args[0]), false);
|
||||
message["ssl_session_id"] = getSslSessionId(args[0]);
|
||||
message["function"] = "SSL_write";
|
||||
message["stack"] = SSLstackwrite;
|
||||
send(message, Memory.readByteArray(args[1], parseInt(args[2])));
|
||||
},
|
||||
onLeave: function (retval) {
|
||||
}
|
||||
});
|
||||
|
||||
if (Java.available) {
|
||||
Java.perform(function () {
|
||||
function storeP12(pri, p7, p12Path, p12Password) {
|
||||
var X509Certificate = Java.use("java.security.cert.X509Certificate")
|
||||
var p7X509 = Java.cast(p7, X509Certificate);
|
||||
var chain = Java.array("java.security.cert.X509Certificate", [p7X509])
|
||||
var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC");
|
||||
ks.load(null, null);
|
||||
ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);
|
||||
try {
|
||||
var out = Java.use("java.io.FileOutputStream").$new(p12Path);
|
||||
ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())
|
||||
} catch (exp) {
|
||||
console.log(exp)
|
||||
}
|
||||
}
|
||||
//在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue
|
||||
Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () {
|
||||
var result = this.getPrivateKey()
|
||||
var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
|
||||
storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');
|
||||
var message = {};
|
||||
message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue';
|
||||
message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
|
||||
var data = Memory.alloc(1);
|
||||
send(message, Memory.readByteArray(data, 1))
|
||||
return result;
|
||||
}
|
||||
Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () {
|
||||
var result = this.getCertificateChain()
|
||||
var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
|
||||
storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');
|
||||
var message = {};
|
||||
message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue';
|
||||
message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
|
||||
var data = Memory.alloc(1);
|
||||
send(message, Memory.readByteArray(data, 1))
|
||||
return result;
|
||||
}
|
||||
|
||||
//SSLpinning helper 帮助定位证书绑定的关键代码a
|
||||
Java.use("java.io.File").$init.overload('java.io.File', 'java.lang.String').implementation = function (file, cert) {
|
||||
var result = this.$init(file, cert)
|
||||
var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
|
||||
if (file.getPath().indexOf("cacert") >= 0 && stack.indexOf("X509TrustManagerExtensions.checkServerTrusted") >= 0) {
|
||||
var message = {};
|
||||
message["function"] = "SSLpinning position locator => " + file.getPath() + " " + cert;
|
||||
message["stack"] = stack;
|
||||
var data = Memory.alloc(1);
|
||||
send(message, Memory.readByteArray(data, 1))
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) {
|
||||
var result = this.socketWrite0(fd, bytearry, offset, byteCount);
|
||||
var message = {};
|
||||
message["function"] = "HTTP_send";
|
||||
message["ssl_session_id"] = "";
|
||||
message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop()));
|
||||
message["src_port"] = parseInt(this.socket.value.getLocalPort().toString());
|
||||
message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop()));
|
||||
message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop());
|
||||
message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
var ptr = Memory.alloc(byteCount);
|
||||
for (var i = 0; i < byteCount; ++i)
|
||||
Memory.writeS8(ptr.add(i), bytearry[offset + i]);
|
||||
send(message, Memory.readByteArray(ptr, byteCount))
|
||||
return result;
|
||||
}
|
||||
Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) {
|
||||
var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout);
|
||||
var message = {};
|
||||
message["function"] = "HTTP_recv";
|
||||
message["ssl_session_id"] = "";
|
||||
message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop()));
|
||||
message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop());
|
||||
message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop()));
|
||||
message["dst_port"] = parseInt(this.socket.value.getLocalPort());
|
||||
message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
if (result > 0) {
|
||||
var ptr = Memory.alloc(result);
|
||||
for (var i = 0; i < result; ++i)
|
||||
Memory.writeS8(ptr.add(i), bytearry[offset + i]);
|
||||
send(message, Memory.readByteArray(ptr, result))
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (parseFloat(Java.androidVersion) > 8) {
|
||||
Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {
|
||||
var result = this.write(bytearry, int1, int2);
|
||||
SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
return result;
|
||||
}
|
||||
Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {
|
||||
var result = this.read(bytearry, int1, int2);
|
||||
SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {
|
||||
var result = this.write(bytearry, int1, int2);
|
||||
SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
return result;
|
||||
}
|
||||
Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {
|
||||
var result = this.read(bytearry, int1, int2);
|
||||
SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
colorama==0.4.6
|
||||
frida==16.0.19
|
||||
frida-tools==12.2.1
|
||||
loguru==0.7.2
|
||||
prompt-toolkit==3.0.38
|
||||
Pygments==2.15.1
|
||||
typing_extensions==4.6.3
|
||||
wcwidth==0.2.6
|
||||
21
scripts/baidunetdisk.js
Normal file
21
scripts/baidunetdisk.js
Normal file
@@ -0,0 +1,21 @@
|
||||
console.log("Script loaded successfully");
|
||||
|
||||
Java.perform(function () {
|
||||
var Request = Java.use("okhttp3.Request");
|
||||
|
||||
// Hook Request的toString方法
|
||||
Request.toString.implementation = function () {
|
||||
// 调用原始的toString方法并保存结果
|
||||
var result = this.toString();
|
||||
|
||||
// 打印结果
|
||||
console.log("Request.toString result: " + result);
|
||||
|
||||
// 返回原始方法调用的结果
|
||||
return result;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
197
scripts/hook_conversions.js
Normal file
197
scripts/hook_conversions.js
Normal file
@@ -0,0 +1,197 @@
|
||||
log_info("Script loaded successfully");
|
||||
// hook_okhttp_client()
|
||||
if (Java.available) {
|
||||
hook_json()
|
||||
let class_name = "okhttp3.OkHttpClient"
|
||||
if (check_class(class_name)){
|
||||
hook_okhttp_client()
|
||||
}
|
||||
Java.perform(function () {
|
||||
log_info("start hook java.net.URL");
|
||||
var URL = Java.use('java.net.URL');
|
||||
URL.$init.overload('java.lang.String').implementation = function (spec) {
|
||||
// console.log("URL request:" + spec)
|
||||
log_info("URL request: " + spec)
|
||||
if (spec.includes("appsflyer")) {
|
||||
// console.log("URL request: " + spec);
|
||||
if (spec.includes("conversions")) {
|
||||
var stackTrace = Java.use('java.lang.Exception').$new().getStackTrace().toString();
|
||||
// console.log(stackTrace);
|
||||
}
|
||||
}
|
||||
return this.$init(spec);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function printMethods(className) {
|
||||
log_info("start print methods.")
|
||||
var jclass = Java.use(className);
|
||||
var methods = jclass.class.getDeclaredMethods();
|
||||
console.log("Printing methods of " + className + ":\n");
|
||||
methods.forEach(function (method) {
|
||||
// console.log(method);
|
||||
log_info("The methods under the class" + className + " are: " + method);
|
||||
});
|
||||
}
|
||||
|
||||
function hook_okhttp_client() {
|
||||
if (Java.available) {
|
||||
Java.perform(function () {
|
||||
log_info("start hook_okhttp_client.")
|
||||
try {
|
||||
var OkHttpClient = Java.use("okhttp3.OkHttpClient");
|
||||
OkHttpClient.newCall.overload('okhttp3.Request').implementation = function (request) {
|
||||
var requestUrl = request.url();
|
||||
if (requestUrl) {
|
||||
console.log("OkHttp Request URL: " + requestUrl.toString());
|
||||
} else {
|
||||
console.log("OkHttp Request URL is not available");
|
||||
}
|
||||
console.log("OkHttp Request Headers: " + request.headers().toString());
|
||||
|
||||
if (request.method() == "POST") {
|
||||
console.log("OkHttp Request Body: " + request.body().contentType().toString());
|
||||
}
|
||||
var call = this.newCall(request);
|
||||
var response = call.execute();
|
||||
console.log("OkHttp Response: " + response.body().string());
|
||||
return call;
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
console.log("Error hooking OkHttp: " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hook_HttpURLConnection_stream() {
|
||||
console.log("start hook_HttpURLConnection_stream")
|
||||
if (Java.available) {
|
||||
Java.perform(function () {
|
||||
var HttpURLConnection = Java.use("java.net.HttpURLConnection");
|
||||
|
||||
HttpURLConnection.getOutputStream.implementation = function () {
|
||||
var outputStream = this.getOutputStream();
|
||||
var OutputStreamWrapper = Java.use("java.io.OutputStream");
|
||||
|
||||
var newOutputStream = Java.registerClass({
|
||||
name: "CustomOutputStream",
|
||||
superClass: OutputStreamWrapper,
|
||||
methods: {
|
||||
write: function (buffer, byteOffset, byteCount) {
|
||||
var data = Array.prototype.slice.call(buffer.slice(byteOffset, byteOffset + byteCount));
|
||||
console.log("Request data: " + String.fromCharCode.apply(null, data));
|
||||
outputStream.write(buffer, byteOffset, byteCount);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return newOutputStream.$new(outputStream);
|
||||
};
|
||||
|
||||
HttpURLConnection.getInputStream.implementation = function () {
|
||||
var inputStream = this.getInputStream();
|
||||
var InputStreamWrapper = Java.use("java.io.InputStream");
|
||||
|
||||
var newInputStream = Java.registerClass({
|
||||
name: "CustomInputStream",
|
||||
superClass: InputStreamWrapper,
|
||||
methods: {
|
||||
read: function (buffer, byteOffset, byteCount) {
|
||||
var bytesRead = inputStream.read(buffer, byteOffset, byteCount);
|
||||
if (bytesRead != -1) {
|
||||
var data = Array.prototype.slice.call(buffer.slice(byteOffset, byteOffset + bytesRead));
|
||||
console.log("Response data: " + String.fromCharCode.apply(null, data));
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return newInputStream.$new(inputStream);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function hook_retrofit() {
|
||||
Java.perform(function () {
|
||||
var retrofitBuilder = Java.use("retrofit2.Retrofit$Builder");
|
||||
retrofitBuilder.build.implementation = function () {
|
||||
var retrofit = this.build();
|
||||
var httpClient = retrofit.callFactory().clone();
|
||||
httpClient.interceptors().add(new Java.use('okhttp3.Interceptor')({
|
||||
intercept: function (chain) {
|
||||
console.log("HTTP Request -> " + chain.request().toString())
|
||||
var response = chain.proceed(chain.request());
|
||||
console.log("HTTP Response -> " + response.toString());
|
||||
return response;
|
||||
}
|
||||
}))
|
||||
return retrofit.newBuilder()
|
||||
.callFactory(httpClient)
|
||||
.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function hook_json() {
|
||||
Java.perform(function () {
|
||||
var JSONObject = Java.use('org.json.JSONObject');
|
||||
JSONObject.toString.overload().implementation = function () {
|
||||
var result = this.toString.call(this);
|
||||
// get_conversions(result)
|
||||
// log_info("Serialized JSONObject: " + result)
|
||||
return result;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function log_info(messages) {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
|
||||
|
||||
const timestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}:${milliseconds}`;
|
||||
|
||||
console.log(`${timestamp} - ${messages}`);
|
||||
send(`${timestamp} - ${messages}`);
|
||||
}
|
||||
|
||||
function check_class(class_name) {
|
||||
var classFound = false; // 默认为未找到
|
||||
Java.enumerateLoadedClasses({
|
||||
onMatch: function(currentClassName) {
|
||||
if (currentClassName === class_name) {
|
||||
classFound = true; // 如果找到了类,则设置为true
|
||||
}
|
||||
},
|
||||
onComplete: function() {
|
||||
if (classFound) {
|
||||
log_info(class_name + " has been loaded!");
|
||||
} else {
|
||||
log_info(class_name + " has not been loaded yet.");
|
||||
}
|
||||
}
|
||||
});
|
||||
return classFound;
|
||||
}
|
||||
|
||||
recv('input_data', function(payload) {
|
||||
console.log(typeof(payload))
|
||||
|
||||
console.log(JSON.stringify(payload))
|
||||
console.log('Received data from Python: ' + payload.payload.data);
|
||||
});
|
||||
|
||||
40
scripts/hook_qqmusic.js
Normal file
40
scripts/hook_qqmusic.js
Normal file
@@ -0,0 +1,40 @@
|
||||
log_info("Script loaded successfully");
|
||||
if (Java.available) {
|
||||
hook_json()
|
||||
Java.perform(function () {
|
||||
log_info("start hook java.net.URL");
|
||||
var URL = Java.use('java.net.URL');
|
||||
URL.$init.overload('java.lang.String').implementation = function (spec) {
|
||||
log_info("URL request: " + spec)
|
||||
return this.$init(spec);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function log_info(messages) {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
|
||||
|
||||
const timestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}:${milliseconds}`;
|
||||
|
||||
console.log(`${timestamp} - ${messages}`);
|
||||
send(`${timestamp} - ${messages}`);
|
||||
}
|
||||
|
||||
function hook_json() {
|
||||
Java.perform(function () {
|
||||
var JSONObject = Java.use('org.json.JSONObject');
|
||||
JSONObject.toString.overload().implementation = function () {
|
||||
var result = this.toString.call(this);
|
||||
// get_conversions(result)
|
||||
log_info("Serialized JSONObject: " + result)
|
||||
return result;
|
||||
};
|
||||
});
|
||||
}
|
||||
362
scripts/hookssl.js
Normal file
362
scripts/hookssl.js
Normal file
@@ -0,0 +1,362 @@
|
||||
console.log("Script loaded successfully");
|
||||
Java.perform(function() {
|
||||
|
||||
/*
|
||||
hook list:
|
||||
1.SSLcontext
|
||||
2.okhttp
|
||||
3.webview
|
||||
4.XUtils
|
||||
5.httpclientandroidlib
|
||||
6.JSSE
|
||||
7.network\_security\_config (android 7.0+)
|
||||
8.Apache Http client (support partly)
|
||||
9.OpenSSLSocketImpl
|
||||
10.TrustKit
|
||||
11.Cronet
|
||||
*/
|
||||
|
||||
// Attempts to bypass SSL pinning implementations in a number of
|
||||
// ways. These include implementing a new TrustManager that will
|
||||
// accept any SSL certificate, overriding OkHTTP v3 check()
|
||||
// method etc.
|
||||
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
|
||||
var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
|
||||
var SSLContext = Java.use('javax.net.ssl.SSLContext');
|
||||
var quiet_output = false;
|
||||
|
||||
// Helper method to honor the quiet flag.
|
||||
|
||||
function quiet_send(data) {
|
||||
|
||||
if (quiet_output) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
send(data)
|
||||
}
|
||||
|
||||
|
||||
// Implement a new TrustManager
|
||||
// ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
|
||||
// Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.
|
||||
/*
|
||||
06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
|
||||
06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err: at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)
|
||||
at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
|
||||
06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err: at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)
|
||||
at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)
|
||||
at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
|
||||
*/
|
||||
var X509Certificate = Java.use("java.security.cert.X509Certificate");
|
||||
var TrustManager;
|
||||
try {
|
||||
TrustManager = Java.registerClass({
|
||||
name: 'org.wooyun.TrustManager',
|
||||
implements: [X509TrustManager],
|
||||
methods: {
|
||||
checkClientTrusted: function(chain, authType) {},
|
||||
checkServerTrusted: function(chain, authType) {},
|
||||
getAcceptedIssuers: function() {
|
||||
// var certs = [X509Certificate.$new()];
|
||||
// return certs;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);
|
||||
}
|
||||
// Prepare the TrustManagers array to pass to SSLContext.init()
|
||||
var TrustManagers = [TrustManager.$new()];
|
||||
|
||||
try {
|
||||
// Prepare a Empty SSLFactory
|
||||
var TLS_SSLContext = SSLContext.getInstance("TLS");
|
||||
TLS_SSLContext.init(null, TrustManagers, null);
|
||||
var EmptySSLFactory = TLS_SSLContext.getSocketFactory();
|
||||
} catch (e) {
|
||||
quiet_send(e.message);
|
||||
}
|
||||
|
||||
send('Custom, Empty TrustManager ready');
|
||||
|
||||
// Get a handle on the init() on the SSLContext class
|
||||
var SSLContext_init = SSLContext.init.overload(
|
||||
'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
|
||||
|
||||
// Override the init method, specifying our new TrustManager
|
||||
SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
|
||||
|
||||
quiet_send('Overriding SSLContext.init() with the custom TrustManager');
|
||||
|
||||
SSLContext_init.call(this, null, TrustManagers, null);
|
||||
};
|
||||
|
||||
/*** okhttp3.x unpinning ***/
|
||||
|
||||
|
||||
// Wrap the logic in a try/catch as not all applications will have
|
||||
// okhttp as part of the app.
|
||||
try {
|
||||
|
||||
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
|
||||
|
||||
quiet_send('OkHTTP 3.x Found');
|
||||
|
||||
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
|
||||
|
||||
quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
// If we dont have a ClassNotFoundException exception, raise the
|
||||
// problem encountered.
|
||||
if (err.message.indexOf('ClassNotFoundException') === 0) {
|
||||
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Appcelerator Titanium PinningTrustManager
|
||||
|
||||
// Wrap the logic in a try/catch as not all applications will have
|
||||
// appcelerator as part of the app.
|
||||
try {
|
||||
|
||||
var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
|
||||
|
||||
send('Appcelerator Titanium Found');
|
||||
|
||||
PinningTrustManager.checkServerTrusted.implementation = function() {
|
||||
|
||||
quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
// If we dont have a ClassNotFoundException exception, raise the
|
||||
// problem encountered.
|
||||
if (err.message.indexOf('ClassNotFoundException') === 0) {
|
||||
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/*** okhttp unpinning ***/
|
||||
|
||||
|
||||
try {
|
||||
var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
|
||||
OkHttpClient.setCertificatePinner.implementation = function(certificatePinner) {
|
||||
// do nothing
|
||||
quiet_send("OkHttpClient.setCertificatePinner Called!");
|
||||
return this;
|
||||
};
|
||||
|
||||
// Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
|
||||
var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
|
||||
CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1) {
|
||||
// do nothing
|
||||
quiet_send("okhttp Called! [Certificate]");
|
||||
return;
|
||||
};
|
||||
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1) {
|
||||
// do nothing
|
||||
quiet_send("okhttp Called! [List]");
|
||||
return;
|
||||
};
|
||||
} catch (e) {
|
||||
quiet_send("com.squareup.okhttp not found");
|
||||
}
|
||||
|
||||
/*** WebView Hooks ***/
|
||||
|
||||
/* frameworks/base/core/java/android/webkit/WebViewClient.java */
|
||||
/* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
|
||||
var WebViewClient = Java.use("android.webkit.WebViewClient");
|
||||
|
||||
WebViewClient.onReceivedSslError.implementation = function(webView, sslErrorHandler, sslError) {
|
||||
quiet_send("WebViewClient onReceivedSslError invoke");
|
||||
//执行proceed方法
|
||||
sslErrorHandler.proceed();
|
||||
return;
|
||||
};
|
||||
|
||||
WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d) {
|
||||
quiet_send("WebViewClient onReceivedError invoked");
|
||||
return;
|
||||
};
|
||||
|
||||
WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function() {
|
||||
quiet_send("WebViewClient onReceivedError invoked");
|
||||
return;
|
||||
};
|
||||
|
||||
/*** JSSE Hooks ***/
|
||||
|
||||
/* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
|
||||
/* public final TrustManager[] getTrustManager() */
|
||||
/* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error */
|
||||
// var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
|
||||
// TrustManagerFactory.getTrustManagers.implementation = function(){
|
||||
// quiet_send("TrustManagerFactory getTrustManagers invoked");
|
||||
// return TrustManagers;
|
||||
// }
|
||||
|
||||
var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
|
||||
/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
|
||||
/* public void setDefaultHostnameVerifier(HostnameVerifier) */
|
||||
HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) {
|
||||
quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");
|
||||
return null;
|
||||
};
|
||||
/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
|
||||
/* public void setSSLSocketFactory(SSLSocketFactory) */
|
||||
HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory) {
|
||||
quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");
|
||||
return null;
|
||||
};
|
||||
/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
|
||||
/* public void setHostnameVerifier(HostnameVerifier) */
|
||||
HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) {
|
||||
quiet_send("HttpsURLConnection.setHostnameVerifier invoked");
|
||||
return null;
|
||||
};
|
||||
|
||||
/*** Xutils3.x hooks ***/
|
||||
//Implement a new HostnameVerifier
|
||||
var TrustHostnameVerifier;
|
||||
try {
|
||||
TrustHostnameVerifier = Java.registerClass({
|
||||
name: 'org.wooyun.TrustHostnameVerifier',
|
||||
implements: [HostnameVerifier],
|
||||
method: {
|
||||
verify: function(hostname, session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
//java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"
|
||||
quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
var RequestParams = Java.use('org.xutils.http.RequestParams');
|
||||
RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {
|
||||
sslSocketFactory = EmptySSLFactory;
|
||||
return null;
|
||||
}
|
||||
|
||||
RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {
|
||||
hostnameVerifier = TrustHostnameVerifier.$new();
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
quiet_send("Xutils hooks not Found");
|
||||
}
|
||||
|
||||
/*** httpclientandroidlib Hooks ***/
|
||||
try {
|
||||
var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
|
||||
AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {
|
||||
quiet_send("httpclientandroidlib Hooks");
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
quiet_send("httpclientandroidlib Hooks not found");
|
||||
}
|
||||
|
||||
/***
|
||||
android 7.0+ network_security_config TrustManagerImpl hook
|
||||
apache httpclient partly
|
||||
***/
|
||||
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
|
||||
// try {
|
||||
// var Arrays = Java.use("java.util.Arrays");
|
||||
// //apache http client pinning maybe baypass
|
||||
// //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471
|
||||
// TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {
|
||||
// quiet_send("TrustManagerImpl checkTrusted called");
|
||||
// //Generics currently result in java.lang.Object
|
||||
// return Arrays.asList(chain);
|
||||
// }
|
||||
//
|
||||
// } catch (e) {
|
||||
// quiet_send("TrustManagerImpl checkTrusted nout found");
|
||||
// }
|
||||
|
||||
try {
|
||||
// Android 7+ TrustManagerImpl
|
||||
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
|
||||
quiet_send("TrustManagerImpl verifyChain called");
|
||||
// Skip all the logic and just return the chain again :P
|
||||
//https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/
|
||||
// https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
|
||||
return untrustedChain;
|
||||
}
|
||||
} catch (e) {
|
||||
quiet_send("TrustManagerImpl verifyChain nout found below 7.0");
|
||||
}
|
||||
// OpenSSLSocketImpl
|
||||
try {
|
||||
var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
|
||||
OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {
|
||||
quiet_send('OpenSSLSocketImpl.verifyCertificateChain');
|
||||
}
|
||||
|
||||
quiet_send('OpenSSLSocketImpl pinning')
|
||||
} catch (err) {
|
||||
quiet_send('OpenSSLSocketImpl pinner not found');
|
||||
}
|
||||
// Trustkit
|
||||
try {
|
||||
var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
|
||||
Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {
|
||||
quiet_send('Trustkit.verify1: ' + str);
|
||||
return true;
|
||||
};
|
||||
Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {
|
||||
quiet_send('Trustkit.verify2: ' + str);
|
||||
return true;
|
||||
};
|
||||
|
||||
quiet_send('Trustkit pinning')
|
||||
} catch (err) {
|
||||
quiet_send('Trustkit pinner not found')
|
||||
}
|
||||
|
||||
try {
|
||||
//cronet pinner hook
|
||||
//weibo don't invoke
|
||||
|
||||
var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");
|
||||
|
||||
//https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
|
||||
netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation = function(arg) {
|
||||
|
||||
//weibo not invoke
|
||||
console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);
|
||||
|
||||
//true to enable the bypass, false to disable.
|
||||
var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
|
||||
return ret;
|
||||
};
|
||||
|
||||
netBuilder.addPublicKeyPins.implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {
|
||||
console.log("cronet addPublicKeyPins hostName = " + hostName);
|
||||
|
||||
//var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);
|
||||
//this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder
|
||||
return this;
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
console.log('[-] Cronet pinner not found')
|
||||
}
|
||||
});
|
||||
32
scripts/monitor_request.js
Normal file
32
scripts/monitor_request.js
Normal file
@@ -0,0 +1,32 @@
|
||||
log_info("Script loaded successfully");
|
||||
Java.perform(function () {
|
||||
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
|
||||
var Request = Java.use('okhttp3.Request');
|
||||
|
||||
OkHttpClient.newCall.implementation = function (request) {
|
||||
var url = request.url().toString();
|
||||
var method = request.method();
|
||||
var body = request.body();
|
||||
var size = body != null ? body.contentLength() / 1024 : 0;
|
||||
console.log("Method: " + method + "\nURL: " + url + "\nSize: " + size + " kb");
|
||||
|
||||
return this.newCall(request);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
function log_info(messages) {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
|
||||
|
||||
const timestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}:${milliseconds}`;
|
||||
|
||||
console.log(`${timestamp} - ${messages}`);
|
||||
send(`${timestamp} - ${messages}`);
|
||||
}
|
||||
20
scripts/test.js
Normal file
20
scripts/test.js
Normal file
@@ -0,0 +1,20 @@
|
||||
Java.perform(function() {
|
||||
var ByteArrayOutputStream = Java.use('java.io.ByteArrayOutputStream');
|
||||
var HttpsURLConnection = Java.use('javax.net.ssl.HttpsURLConnection');
|
||||
|
||||
HttpsURLConnection.getOutputStream.implementation = function() {
|
||||
var outputStream = this.getOutputStream();
|
||||
|
||||
// 创建一个新的 ByteArrayOutputStream 实例,以便于我们读取数据。
|
||||
var byteArrayOutputStream = ByteArrayOutputStream.$new();
|
||||
|
||||
outputStream.write.overload('[B').implementation = function(buffer) {
|
||||
byteArrayOutputStream.write(buffer);
|
||||
console.log("[*] Data written to URL:", this.getURL().toString());
|
||||
console.log("[*] Data:", byteArrayOutputStream.toString('UTF-8'));
|
||||
return outputStream.write(buffer);
|
||||
};
|
||||
|
||||
return outputStream;
|
||||
};
|
||||
});
|
||||
82
scripts/url_request.js
Normal file
82
scripts/url_request.js
Normal file
@@ -0,0 +1,82 @@
|
||||
Java.perform(function() {
|
||||
var URL = Java.use("java.net.URL");
|
||||
var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
|
||||
var HttpURLConnection = Java.use("java.net.HttpURLConnection");
|
||||
var OutputStreamWriter = Java.use("java.io.OutputStreamWriter");
|
||||
var BufferedReader = Java.use("java.io.BufferedReader");
|
||||
var InputStreamReader = Java.use("java.io.InputStreamReader");
|
||||
var OutputStream = Java.use('java.io.OutputStream');
|
||||
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
|
||||
|
||||
URL.$init.overload('java.lang.String').implementation = function(url) {
|
||||
console.log("[*] URL init:", url.toString());
|
||||
return this.$init(url);
|
||||
};
|
||||
|
||||
HttpsURLConnection.setDoOutput.implementation = function(value) {
|
||||
console.log("[*]HttpsURLConnection setDoOutput:", value);
|
||||
return this.setDoOutput(value);
|
||||
};
|
||||
|
||||
HttpsURLConnection.setRequestProperty.implementation = function(key, value) {
|
||||
console.log("[*] setRequestProperty:", key, value);
|
||||
return this.setRequestProperty(key, value);
|
||||
};
|
||||
|
||||
|
||||
HttpsURLConnection.getOutputStream.implementation = function() {
|
||||
console.log("[*] getOutputStream");
|
||||
return this.getOutputStream();
|
||||
};
|
||||
|
||||
|
||||
|
||||
HttpURLConnection.setRequestProperty.implementation = function(key, value) {
|
||||
console.log("[*] setRequestProperty:", key, value);
|
||||
return this.setRequestProperty(key, value);
|
||||
};
|
||||
|
||||
HttpURLConnection.setDoOutput.implementation = function(value) {
|
||||
console.log("[*]HttpURLConnection setDoOutput:", value);
|
||||
return this.setDoOutput(value);
|
||||
};
|
||||
|
||||
// HttpURLConnection.getOutputStream.implementation = function() {
|
||||
// console.log("[*] getOutputStream");
|
||||
// var outputStream = this.getOutputStream();
|
||||
//
|
||||
// outputStream.write.overload('[B').implementation = function(buffer) {
|
||||
// console.log("[*] Data written:", Java.array('byte', buffer).toString());
|
||||
// return this.write(buffer);
|
||||
// };
|
||||
// return outputStream;
|
||||
// };
|
||||
|
||||
// OutputStream.write.overload('[B').implementation = function(buffer) {
|
||||
// console.log("[*] Data written:", Java.array('byte', buffer).toString());
|
||||
// return this.write(buffer);
|
||||
// };
|
||||
|
||||
BufferedReader.readLine.overload().implementation = function() {
|
||||
var line = this.readLine();
|
||||
// console.log("[*] BufferedReader.readLine:", line);
|
||||
return line;
|
||||
};
|
||||
|
||||
InputStreamReader.$init.overload('java.io.InputStream').implementation = function(stream) {
|
||||
console.log("[*] InputStreamReader.init:", stream);
|
||||
return this.$init(stream);
|
||||
};
|
||||
|
||||
OkHttpClient.newCall.overload('okhttp3.Request').implementation = function(request) {
|
||||
console.log("[*] Request URL:", request.url().toString());
|
||||
console.log("[*] Request Headers:", request.headers().toString());
|
||||
|
||||
if (request.method() == "POST") {
|
||||
console.log("[*] Request Body:", request.body().toString());
|
||||
}
|
||||
|
||||
return this.newCall(request);
|
||||
};
|
||||
});
|
||||
|
||||
31
test.py
31
test.py
@@ -1,22 +1,11 @@
|
||||
import frida
|
||||
|
||||
def list_devices():
|
||||
devices = frida.enumerate_devices()
|
||||
for device in devices:
|
||||
print(device)
|
||||
|
||||
# list_devices()
|
||||
|
||||
def connect_to_device(device_id):
|
||||
device = frida.get_device(id=device_id, timeout=10)
|
||||
print(device)
|
||||
|
||||
# Connect to the Pixel 3 device
|
||||
connect_to_device("8C1X1H1YG")
|
||||
|
||||
def on_message(message, data):
|
||||
print(message)
|
||||
|
||||
|
||||
|
||||
import frida,sys
|
||||
import modules.files_utils
|
||||
|
||||
js_code = modules.files_utils.read_javascript("scripts/hook_conversions.js")
|
||||
device = frida.get_usb_device()
|
||||
pid = device.spawn(["com.naviapp"]) # 以挂起方式创建进程
|
||||
process = device.attach(pid)
|
||||
script = process.create_script(js_code)
|
||||
script.load()
|
||||
device.resume(pid) # 加载完脚本, 恢复进程运行
|
||||
sys.stdin.read()
|
||||
|
||||
Reference in New Issue
Block a user