├── LICENSE ├── README.md ├── Sample.PNG ├── r0capture.py └── script.js /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # r0capture 2 | 3 | 安卓应用层抓包通杀脚本 4 | 5 | ## 简介 6 | 7 | - 仅限安卓平台,测试安卓7、8、9、10 可用 ; 8 | - 无视所有证书校验或绑定,不用考虑任何证书的事情; 9 | - 通杀TCP/IP四层模型中的应用层中的全部协议; 10 | - 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本; 11 | - 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等; 12 | - 如果有抓不到的情况欢迎提issue,或者直接加vx:r0ysue,进行反馈~ 13 | 14 | ## 用法 15 | 16 | - Spawn 模式: 17 | 18 | `$ python3 r0capture.py -U -f com.qiyi.video` 19 | 20 | - Attach 模式,抓包内容保存成pcap文件供后续分析: 21 | 22 | `$ python3 r0capture.py -U com.qiyi.video -p iqiyi.pcap` 23 | 24 | 建议使用`Attach`模式,从感兴趣的地方开始抓包,并且保存成`pcap`文件,供后续使用Wireshark进行分析。 25 | 26 | ![](Sample.PNG) 27 | 28 | 29 | 30 | PS: 31 | 32 | > 这个项目基于[frida_ssl_logger](https://github.com/BigFaceCat2017/frida_ssl_logger),之所以换个名字,只是侧重点不同。 33 | 34 | > 原项目的侧重点在于抓ssl和跨平台,本项目的侧重点是抓到所有的包。 35 | 36 | ## 以下是原项目的简介: 37 | 38 | [https://github.com/BigFaceCat2017/frida_ssl_logger](https://github.com/BigFaceCat2017/frida_ssl_logger) 39 | 40 | ### frida_ssl_logger 41 | ssl_logger based on frida 42 | for from https://github.com/google/ssl_logger 43 | 44 | ### 修改内容 45 | 1. 优化了frida的JS脚本,修复了在新版frida上的语法错误; 46 | 2. 调整JS脚本,使其适配iOS和macOS,同时也兼容了Android; 47 | 3. 增加了更多的选项,使其能在多种情况下使用; 48 | 49 | ### Usage 50 | ```shell 51 | python3 ./ssl_logger.py -U -f com.bfc.mm 52 | python3 ./ssl_logger.py -v -p test.pcap 6666 53 | ```` 54 | -------------------------------------------------------------------------------- /Sample.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smile1847/r0capture/999f78767f14d9f49e5e61431ff924dcc1a04e9a/Sample.PNG -------------------------------------------------------------------------------- /r0capture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Decrypts and logs a process's SSL traffic. 16 | Hooks the functions SSL_read() and SSL_write() in a given process and logs the 17 | decrypted data to the console and/or to a pcap file. 18 | Typical usage example: 19 | ssl_log("wget", "log.pcap", True) 20 | Dependencies: 21 | frida (https://www.frida.re/): 22 | sudo pip install frida 23 | hexdump (https://bitbucket.org/techtonik/hexdump/) if using verbose output: 24 | sudo pip install hexdump 25 | """ 26 | 27 | __author__ = "geffner@google.com (Jason Geffner)" 28 | __version__ = "2.0" 29 | 30 | """ 31 | # r0cap 32 | 33 | ID: r0ysue 34 | 35 | 安卓应用层抓包通杀脚本 36 | 37 | https://github.com/r0ysue/r0capture 38 | 39 | ## 简介 40 | 41 | - 仅限安卓平台,测试安卓7、8、9、10 可用 ; 42 | - 无视所有证书校验或绑定,无视任何证书; 43 | - 通杀TCP/IP四层模型中的应用层中的全部协议; 44 | - 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本; 45 | - 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等; 46 | """ 47 | 48 | 49 | # Windows版本需要安装库: 50 | # pip install 'win_inet_pton' 51 | # pip install hexdump 52 | import argparse 53 | import os 54 | import platform 55 | import pprint 56 | import random 57 | import signal 58 | import socket 59 | import struct 60 | import time 61 | import sys 62 | 63 | import frida 64 | 65 | try: 66 | if os.name == 'nt': 67 | import win_inet_pton 68 | except ImportError: 69 | # win_inet_pton import error 70 | pass 71 | 72 | try: 73 | import hexdump # pylint: disable=g-import-not-at-top 74 | except ImportError: 75 | pass 76 | 77 | # ssl_session[] = (, 78 | # ) 79 | ssl_sessions = {} 80 | 81 | 82 | def ssl_log(process, pcap=None, verbose=False, isUsb=False, ssllib="", isSpawn=True, wait=0): 83 | """Decrypts and logs a process's SSL traffic. 84 | Hooks the functions SSL_read() and SSL_write() in a given process and logs 85 | the decrypted data to the console and/or to a pcap file. 86 | Args: 87 | process: The target process's name (as a string) or process ID (as an int). 88 | pcap: The file path to which the pcap file should be written. 89 | verbose: If True, log the decrypted traffic to the console. 90 | Raises: 91 | NotImplementedError: Not running on a Linux or macOS system. 92 | """ 93 | 94 | # if platform.system() not in ("Darwin", "Linux"): 95 | # raise NotImplementedError("This function is only implemented for Linux and " 96 | # "macOS systems.") 97 | 98 | def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port, 99 | dst_addr, dst_port, data): 100 | """Writes the captured data to a pcap file. 101 | Args: 102 | pcap_file: The opened pcap file. 103 | ssl_session_id: The SSL session ID for the communication. 104 | function: The function that was intercepted ("SSL_read" or "SSL_write"). 105 | src_addr: The source address of the logged packet. 106 | src_port: The source port of the logged packet. 107 | dst_addr: The destination address of the logged packet. 108 | dst_port: The destination port of the logged packet. 109 | data: The decrypted packet data. 110 | """ 111 | t = time.time() 112 | 113 | if ssl_session_id not in ssl_sessions: 114 | ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF), 115 | random.randint(0, 0xFFFFFFFF)) 116 | client_sent, server_sent = ssl_sessions[ssl_session_id] 117 | 118 | if function == "SSL_read": 119 | seq, ack = (server_sent, client_sent) 120 | else: 121 | seq, ack = (client_sent, server_sent) 122 | 123 | for writes in ( 124 | # PCAP record (packet) header 125 | ("=I", int(t)), # Timestamp seconds 126 | ("=I", int((t * 1000000) % 1000000)), # Timestamp microseconds 127 | ("=I", 40 + len(data)), # Number of octets saved 128 | ("=i", 40 + len(data)), # Actual length of packet 129 | # IPv4 header 130 | (">B", 0x45), # Version and Header Length 131 | (">B", 0), # Type of Service 132 | (">H", 40 + len(data)), # Total Length 133 | (">H", 0), # Identification 134 | (">H", 0x4000), # Flags and Fragment Offset 135 | (">B", 0xFF), # Time to Live 136 | (">B", 6), # Protocol 137 | (">H", 0), # Header Checksum 138 | (">I", src_addr), # Source Address 139 | (">I", dst_addr), # Destination Address 140 | # TCP header 141 | (">H", src_port), # Source Port 142 | (">H", dst_port), # Destination Port 143 | (">I", seq), # Sequence Number 144 | (">I", ack), # Acknowledgment Number 145 | (">H", 0x5018), # Header Length and Flags 146 | (">H", 0xFFFF), # Window Size 147 | (">H", 0), # Checksum 148 | (">H", 0)): # Urgent Pointer 149 | pcap_file.write(struct.pack(writes[0], writes[1])) 150 | pcap_file.write(data) 151 | 152 | if function == "SSL_read": 153 | server_sent += len(data) 154 | else: 155 | client_sent += len(data) 156 | ssl_sessions[ssl_session_id] = (client_sent, server_sent) 157 | 158 | def on_message(message, data): 159 | """Callback for errors and messages sent from Frida-injected JavaScript. 160 | Logs captured packet data received from JavaScript to the console and/or a 161 | pcap file. See https://www.frida.re/docs/messages/ for more detail on 162 | Frida's messages. 163 | Args: 164 | message: A dictionary containing the message "type" and other fields 165 | dependent on message type. 166 | data: The string of captured decrypted data. 167 | """ 168 | if message["type"] == "error": 169 | pprint.pprint(message) 170 | os.kill(os.getpid(), signal.SIGTERM) 171 | return 172 | if len(data) == 0: 173 | return 174 | p = message["payload"] 175 | if verbose: 176 | src_addr = socket.inet_ntop(socket.AF_INET, 177 | struct.pack(">I", p["src_addr"])) 178 | dst_addr = socket.inet_ntop(socket.AF_INET, 179 | struct.pack(">I", p["dst_addr"])) 180 | print("SSL Session: " + p["ssl_session_id"]) 181 | print("[%s] %s:%d --> %s:%d" % ( 182 | p["function"], 183 | src_addr, 184 | p["src_port"], 185 | dst_addr, 186 | p["dst_port"])) 187 | hexdump.hexdump(data) 188 | print() 189 | if pcap: 190 | log_pcap(pcap_file, p["ssl_session_id"], p["function"], p["src_addr"], 191 | p["src_port"], p["dst_addr"], p["dst_port"], data) 192 | 193 | if isUsb: 194 | device = frida.get_usb_device() 195 | # session = device.attach(process) 196 | else: 197 | device = frida.get_local_device() 198 | 199 | if isSpawn: 200 | pid = device.spawn([process]) 201 | time.sleep(1) 202 | session = device.attach(pid) 203 | time.sleep(1) 204 | device.resume(pid) 205 | else: 206 | print("attach") 207 | session = device.attach(process) 208 | if wait > 0: 209 | print("wait for {} seconds".format(wait)) 210 | time.sleep(wait) 211 | 212 | # session = frida.attach(process) 213 | 214 | # pid = device.spawn([process]) 215 | # pid = process 216 | # session = device.attach(pid) 217 | # device.resume(pid) 218 | if pcap: 219 | pcap_file = open(pcap, "wb", 0) 220 | for writes in ( 221 | ("=I", 0xa1b2c3d4), # Magic number 222 | ("=H", 2), # Major version number 223 | ("=H", 4), # Minor version number 224 | ("=i", time.timezone), # GMT to local correction 225 | ("=I", 0), # Accuracy of timestamps 226 | ("=I", 65535), # Max length of captured packets 227 | ("=I", 228)): # Data link type (LINKTYPE_IPV4) 228 | pcap_file.write(struct.pack(writes[0], writes[1])) 229 | 230 | with open("./script.js", encoding="utf-8") as f: 231 | _FRIDA_SCRIPT = f.read() 232 | # _FRIDA_SCRIPT = session.create_script(content) 233 | # print(_FRIDA_SCRIPT) 234 | script = session.create_script(_FRIDA_SCRIPT) 235 | script.on("message", on_message) 236 | script.load() 237 | 238 | if ssllib != "": 239 | script.exports.setssllib(ssllib) 240 | 241 | print("Press Ctrl+C to stop logging.") 242 | 243 | def stoplog(signum, frame): 244 | print('You have stoped logging.') 245 | session.detach() 246 | if pcap: 247 | pcap_file.flush() 248 | pcap_file.close() 249 | exit() 250 | signal.signal(signal.SIGINT, stoplog) 251 | signal.signal(signal.SIGTERM, stoplog) 252 | sys.stdin.read() 253 | 254 | if __name__ == "__main__": 255 | class ArgParser(argparse.ArgumentParser): 256 | 257 | def error(self, message): 258 | print("ssl_logger v" + __version__) 259 | print("by " + __author__) 260 | print("Modified by BigFaceCat") 261 | print("Error: " + message) 262 | print() 263 | print(self.format_help().replace("usage:", "Usage:")) 264 | self.exit(0) 265 | 266 | 267 | parser = ArgParser( 268 | add_help=False, 269 | description="Decrypts and logs a process's SSL traffic.", 270 | formatter_class=argparse.RawDescriptionHelpFormatter, 271 | epilog=r""" 272 | Examples: 273 | %(prog)s -pcap ssl.pcap openssl 274 | %(prog)s -verbose 31337 275 | %(prog)s -pcap log.pcap -verbose wget 276 | %(prog)s -pcap log.pcap -ssl "*libssl.so*" com.bigfacecat.testdemo 277 | """) 278 | 279 | args = parser.add_argument_group("Arguments") 280 | args.add_argument("-pcap", '-p', metavar="", required=False, 281 | help="Name of PCAP file to write") 282 | args.add_argument("-verbose","-v", required=False, action="store_const", default=True, 283 | const=True, help="Show verbose output") 284 | args.add_argument("process", metavar="", 285 | help="Process whose SSL calls to log") 286 | args.add_argument("-ssl", default="", metavar="", 287 | help="SSL library to hook") 288 | args.add_argument("--isUsb", "-U", default=False, action="store_true", 289 | help="connect to USB device") 290 | args.add_argument("--isSpawn", "-f", default=False, action="store_true", 291 | help="if spawned app") 292 | args.add_argument("-wait", "-w", type=int, metavar="", default=0, 293 | help="Time to wait for the process") 294 | 295 | parsed = parser.parse_args() 296 | ssl_log(int(parsed.process) if parsed.process.isdigit() else parsed.process, parsed.pcap, parsed.verbose, isUsb=parsed.isUsb, isSpawn=parsed.isSpawn, ssllib=parsed.ssl, wait=parsed.wait) 297 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initializes 'addresses' dictionary and NativeFunctions. 3 | */ 4 | "use strict"; 5 | rpc.exports = { 6 | setssllib: function (name) { 7 | console.log("setSSLLib => " + name); 8 | libname = name; 9 | initializeGlobals(); 10 | return; 11 | } 12 | }; 13 | 14 | var addresses = {}; 15 | var SSL_get_fd = null; 16 | var SSL_get_session = null; 17 | var SSL_SESSION_get_id = null; 18 | var getpeername = null; 19 | var getsockname = null; 20 | var ntohs = null; 21 | var ntohl = null; 22 | 23 | var libname = "*libssl*"; 24 | 25 | 26 | 27 | function return_zero(args) { 28 | return 0; 29 | } 30 | function initializeGlobals() { 31 | var resolver = new ApiResolver("module"); 32 | var exps = [ 33 | [Process.platform == "darwin" ? "*libboringssl*" : "*libssl*", ["SSL_read", "SSL_write", "SSL_get_fd", "SSL_get_session", "SSL_SESSION_get_id"]], // for ios and Android 34 | [Process.platform == "darwin" ? "*libsystem*" : "*libc*", ["getpeername", "getsockname", "ntohs", "ntohl"]] 35 | ]; 36 | // console.log(exps) 37 | for (var i = 0; i < exps.length; i++) { 38 | var lib = exps[i][0]; 39 | var names = exps[i][1]; 40 | for (var j = 0; j < names.length; j++) { 41 | var name = names[j]; 42 | // console.log("exports:" + lib + "!" + name) 43 | var matches = resolver.enumerateMatchesSync("exports:" + lib + "!" + name); 44 | if (matches.length == 0) { 45 | if (name == "SSL_get_fd") { 46 | addresses["SSL_get_fd"] = 0; 47 | continue; 48 | } 49 | throw "Could not find " + lib + "!" + name; 50 | } 51 | else if (matches.length != 1) { 52 | // Sometimes Frida returns duplicates. 53 | var address = 0; 54 | var s = ""; 55 | var duplicates_only = true; 56 | for (var k = 0; k < matches.length; k++) { 57 | if (s.length != 0) { 58 | s += ", "; 59 | } 60 | s += matches[k].name + "@" + matches[k].address; 61 | if (address == 0) { 62 | address = matches[k].address; 63 | } 64 | else if (!address.equals(matches[k].address)) { 65 | duplicates_only = false; 66 | } 67 | } 68 | if (!duplicates_only) { 69 | throw "More than one match found for " + lib + "!" + name + ": " + s; 70 | } 71 | } 72 | addresses[name] = matches[0].address; 73 | } 74 | } 75 | if (addresses["SSL_get_fd"] == 0) { 76 | SSL_get_fd = return_zero; 77 | } else { 78 | SSL_get_fd = new NativeFunction(addresses["SSL_get_fd"], "int", ["pointer"]); 79 | } 80 | SSL_get_session = new NativeFunction(addresses["SSL_get_session"], "pointer", ["pointer"]); 81 | SSL_SESSION_get_id = new NativeFunction(addresses["SSL_SESSION_get_id"], "pointer", ["pointer", "pointer"]); 82 | getpeername = new NativeFunction(addresses["getpeername"], "int", ["int", "pointer", "pointer"]); 83 | getsockname = new NativeFunction(addresses["getsockname"], "int", ["int", "pointer", "pointer"]); 84 | ntohs = new NativeFunction(addresses["ntohs"], "uint16", ["uint16"]); 85 | ntohl = new NativeFunction(addresses["ntohl"], "uint32", ["uint32"]); 86 | } 87 | initializeGlobals(); 88 | 89 | function ipToNumber(ip) { 90 | var num = 0; 91 | if (ip == "") { 92 | return num; 93 | } 94 | var aNum = ip.split("."); 95 | if (aNum.length != 4) { 96 | return num; 97 | } 98 | num += parseInt(aNum[0]) << 0; 99 | num += parseInt(aNum[1]) << 8; 100 | num += parseInt(aNum[2]) << 16; 101 | num += parseInt(aNum[3]) << 24; 102 | num = num >>> 0;//这个很关键,不然可能会出现负数的情况 103 | return num; 104 | } 105 | 106 | /** 107 | * Returns a dictionary of a sockfd's "src_addr", "src_port", "dst_addr", and 108 | * "dst_port". 109 | * @param {int} sockfd The file descriptor of the socket to inspect. 110 | * @param {boolean} isRead If true, the context is an SSL_read call. If 111 | * false, the context is an SSL_write call. 112 | * @return {dict} Dictionary of sockfd's "src_addr", "src_port", "dst_addr", 113 | * and "dst_port". 114 | */ 115 | function getPortsAndAddresses(sockfd, isRead) { 116 | var message = {}; 117 | var src_dst = ["src", "dst"]; 118 | for (var i = 0; i < src_dst.length; i++) { 119 | if ((src_dst[i] == "src") ^ isRead) { 120 | var sockAddr = Socket.localAddress(sockfd) 121 | } 122 | else { 123 | var sockAddr = Socket.peerAddress(sockfd) 124 | } 125 | if (sockAddr == null) { 126 | // 网络超时or其他原因可能导致socket被关闭 127 | message[src_dst[i] + "_port"] = 0 128 | message[src_dst[i] + "_addr"] = 0 129 | } else { 130 | message[src_dst[i] + "_port"] = (sockAddr.port & 0xFFFF) 131 | message[src_dst[i] + "_addr"] = ntohl(ipToNumber(sockAddr.ip.split(":").pop())) 132 | } 133 | } 134 | return message; 135 | } 136 | /** 137 | * Get the session_id of SSL object and return it as a hex string. 138 | * @param {!NativePointer} ssl A pointer to an SSL object. 139 | * @return {dict} A string representing the session_id of the SSL object's 140 | * SSL_SESSION. For example, 141 | * "59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336". 142 | */ 143 | function getSslSessionId(ssl) { 144 | var session = SSL_get_session(ssl); 145 | if (session == 0) { 146 | return 0; 147 | } 148 | var len = Memory.alloc(4); 149 | var p = SSL_SESSION_get_id(session, len); 150 | len = Memory.readU32(len); 151 | var session_id = ""; 152 | for (var i = 0; i < len; i++) { 153 | // Read a byte, convert it to a hex string (0xAB ==> "AB"), and append 154 | // it to session_id. 155 | session_id += 156 | ("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2); 157 | } 158 | return session_id; 159 | } 160 | 161 | Interceptor.attach(addresses["SSL_read"], 162 | { 163 | onEnter: function (args) { 164 | var message = getPortsAndAddresses(SSL_get_fd(args[0]), true); 165 | message["ssl_session_id"] = getSslSessionId(args[0]); 166 | message["function"] = "SSL_read"; 167 | this.message = message; 168 | this.buf = args[1]; 169 | }, 170 | onLeave: function (retval) { 171 | retval |= 0; // Cast retval to 32-bit integer. 172 | if (retval <= 0) { 173 | return; 174 | } 175 | send(this.message, Memory.readByteArray(this.buf, retval)); 176 | } 177 | }); 178 | 179 | Interceptor.attach(addresses["SSL_write"], 180 | { 181 | onEnter: function (args) { 182 | var message = getPortsAndAddresses(SSL_get_fd(args[0]), false); 183 | message["ssl_session_id"] = getSslSessionId(args[0]); 184 | message["function"] = "SSL_write"; 185 | send(message, Memory.readByteArray(args[1], parseInt(args[2]))); 186 | }, 187 | onLeave: function (retval) { 188 | } 189 | }); 190 | 191 | if (Java.available) { 192 | Java.perform(function () { 193 | Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) { 194 | var result = this.socketWrite0(fd, bytearry, offset, byteCount); 195 | var message = {}; 196 | message["function"] = "HTTP_send"; 197 | message["ssl_session_id"] = ""; 198 | message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); 199 | message["src_port"] = parseInt(this.socket.value.getLocalPort().toString()); 200 | message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); 201 | message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); 202 | var ptr = Memory.alloc(byteCount); 203 | for (var i = 0; i < byteCount; ++i) 204 | Memory.writeS8(ptr.add(i), bytearry[offset + i]); 205 | send(message, Memory.readByteArray(ptr, byteCount)) 206 | return result; 207 | } 208 | Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) { 209 | var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout); 210 | var message = {}; 211 | message["function"] = "HTTP_recv"; 212 | message["ssl_session_id"] = ""; 213 | message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); 214 | message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); 215 | message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); 216 | message["dst_port"] = parseInt(this.socket.value.getLocalPort()); 217 | if (result > 0) { 218 | var ptr = Memory.alloc(result); 219 | for (var i = 0; i < result; ++i) 220 | Memory.writeS8(ptr.add(i), bytearry[offset + i]); 221 | send(message, Memory.readByteArray(ptr, result)) 222 | } 223 | return result; 224 | } 225 | }) 226 | } 227 | --------------------------------------------------------------------------------