Compare commits
8 Commits
8fc63ad9c0
...
19333bc43a
| Author | SHA1 | Date |
|---|---|---|
|
|
19333bc43a | |
|
|
e74d6554e9 | |
|
|
528b0d8885 | |
|
|
23640b607a | |
|
|
649dd5ac89 | |
|
|
27631b814a | |
|
|
c8b014e457 | |
|
|
b46444dea2 |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -0,0 +1,59 @@
|
|||
# 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
|
||||
```
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
Reference in New Issue