Repository: r0ysue/r0capture Branch: main Commit: 3e4e9f9765c4 Files: 5 Total size: 55.7 KB Directory structure: gitextract_zqdvuyha/ ├── LICENSE ├── README.md ├── myhexdump.py ├── r0capture.py └── script.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ # r0capture 安卓应用层抓包通杀脚本 ## 简介 - 仅限安卓平台,测试安卓7、8、9、10、11、12、13、14 、15、16可用 ; - 无视所有证书校验或绑定,不用考虑任何证书的事情; - 通杀TCP/IP四层模型中的应用层中的全部协议; - 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本; - 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等; - 无视加固,不管是整体壳还是二代壳或VMP,不用考虑加固的事情; - 如果有抓不到的情况欢迎提issue,或者直接加vx:r0ysue,进行反馈~ ### March.2026更新frida17支持 推荐搭配:frida17/安卓16 frida16.5.2/安卓14 frida15.2.2/安卓12 ### June.18th 2023 update:测试Pixel4/安卓13/KernelSU/Frida16 功能工作正常 正常抓包 导出证书 ### January.14th 2021 update:增加几个辅助功能 - 增加App收发包函数定位功能 - 增加App客户端证书导出功能 - 新增host连接方式“-H”,用于Frida-server监听在非标准端口时的连接 ## 用法 - 推荐环境:[https://github.com/r0ysue/AndroidSecurityStudy/blob/master/FRIDA/A01/README.md](https://github.com/r0ysue/AndroidSecurityStudy/blob/master/FRIDA/A01/README.md) 切记仅限安卓平台7、8、9、10、11 可用 ,禁止使用模拟器。 - Spawn 模式: `$ python3 r0capture.py -U -f com.coolapk.market -v` - Attach 模式,抓包内容保存成pcap文件供后续分析: `$ python3 r0capture.py -U 酷安 -v -p iqiyi.pcap` 建议使用`Attach`模式,从感兴趣的地方开始抓包,并且保存成`pcap`文件,供后续使用Wireshark进行分析。 > 老版本Frida使用包名,新版本Frida使用APP名。APP名必须是点开app后,frida-ps -U显示的那个app名字。 ![](pic/Sample.PNG) - 收发包函数定位:`Spawn`和`attach`模式均默认开启; > 可以使用`python r0capture.py -U -f cn.soulapp.android -v >> soul3.txt`这样的命令将输出重定向至txt文件中稍后过滤内容 ![](pic/locator.png) - 客户端证书导出功能:默认开启;必须以Spawm模式运行; > 运行脚本之前必须手动给App加上存储卡读写权限; > 并不是所有App都部署了服务器验证客户端的机制,只有配置了的才会在Apk中包含客户端证书 > 导出后的证书位于/sdcard/Download/包名xxx.p12路径,导出多次,每一份均可用,密码默认为:r0ysue,推荐使用[keystore-explorer](http://keystore-explorer.org/)打开查看证书。 ![](pic/clientcer.png) - 新增host连接方式“-H”,用于Frida-server监听在非标准端口时的连接。有些App会检测Frida标准端口,因此frida-server开在非标准端口可以绕过检测。 ![](pic/difport.png) ## 感谢[爱吃菠菜](https://bbs.pediy.com/user-760871.htm)巨巨总结的本项目知识点 ![](pic/summary1.jpg) ![](pic/summary2.jpg) PS: > 这个项目基于[frida_ssl_logger](https://github.com/BigFaceCat2017/frida_ssl_logger),之所以换个名字,只是侧重点不同。 原项目的侧重点在于抓ssl和跨平台,本项目的侧重点是抓到所有的包。 > 局限:部分开发实力过强的大厂或框架,采用的是自身的SSL框架,比如WebView、小程序或Flutter,这部分目前暂未支持。部分融合App本质上已经不属于安卓App,没有使用安卓系统的框架,无法支持。当然这部分App也是少数。暂不支持HTTP/2、或HTTP/3,该部分API在安卓系统上暂未普及或布署,为App自带,无法进行通用hook。各种模拟器架构、实现、环境较为复杂,建议珍爱生命、使用真机。暂未添加多进程支持,比如:service或:push等子进程,可以使用Frida的Child-gating来支持一下。支持多进程之后要考虑pcap文件的写入锁问题,可以用frida-tool的Reactor线程锁来支持一下。 ## 以下是原项目的简介: [https://github.com/BigFaceCat2017/frida_ssl_logger](https://github.com/BigFaceCat2017/frida_ssl_logger) ### frida_ssl_logger ssl_logger based on frida for from https://github.com/google/ssl_logger ### 修改内容 1. 优化了frida的JS脚本,修复了在新版frida上的语法错误; 2. 调整JS脚本,使其适配iOS和macOS,同时也兼容了Android; 3. 增加了更多的选项,使其能在多种情况下使用; ### 安装依赖 ``` Python版本>=3.6 pip install loguru pip install click ``` ### Usage ```shell python3 ./ssl_logger.py -U -f com.bfc.mm python3 ./ssl_logger.py -v -p test.pcap 6666 ```` ================================================ FILE: myhexdump.py ================================================ # -*- 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 ' __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 ================================================ FILE: r0capture.py ================================================ # 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_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="", 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="", help="Process whose SSL calls to log") args.add_argument("-ssl", default="", metavar="", 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="", 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 ) ================================================ FILE: script.js ================================================ /** * 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.enumerateMatches("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) { // Multiple matches found - prefer com.android.conscrypt library var selectedMatch = null; for (var k = 0; k < matches.length; k++) { if (matches[k].name.indexOf("com.android.conscrypt") !== -1) { selectedMatch = matches[k]; break; } } // If no conscrypt match, use the first one if (selectedMatch === null) { selectedMatch = matches[0]; } addresses[name] = selectedMatch.address; console.log("[*] Selected " + name + " from: " + selectedMatch.name); } else { 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 = len.readU32(); 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" + p.add(i).readU8().toString(16).toUpperCase()).slice(-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, this.buf.readByteArray(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, args[1].readByteArray(parseInt(args[2]))); }, onLeave: function (retval) { } }); if (typeof Java !== 'undefined' && 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) ptr.add(i).writeU8(bytearry[offset + i] & 0xFF); send(message, ptr.readByteArray(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) ptr.add(i).writeU8(bytearry[offset + i] & 0xFF); send(message, ptr.readByteArray(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; } } } ) }