r0capture.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. # -*- coding:utf-8 -*-
  2. # Copyright 2017 Google Inc. All Rights Reserved.
  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. """Decrypts and logs a process's SSL traffic.
  15. Hooks the functions SSL_read() and SSL_write() in a given process and logs the
  16. decrypted data to the console and/or to a pcap file.
  17. Typical usage example:
  18. ssl_log("wget", "log.pcap", True)
  19. Dependencies:
  20. frida (https://www.frida.re/):
  21. sudo pip install frida
  22. hexdump (https://bitbucket.org/techtonik/hexdump/) if using verbose output:
  23. sudo pip install hexdump
  24. """
  25. __author__ = "geffner@google.com (Jason Geffner)"
  26. __version__ = "2.0"
  27. """
  28. # r0capture
  29. ID: r0ysue
  30. 安卓应用层抓包通杀脚本
  31. https://github.com/r0ysue/r0capture
  32. ## 简介
  33. - 仅限安卓平台,测试安卓7、8、9、10 可用 ;
  34. - 无视所有证书校验或绑定,无视任何证书;
  35. - 通杀TCP/IP四层模型中的应用层中的全部协议;
  36. - 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本;
  37. - 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等;
  38. """
  39. # Windows版本需要安装库:
  40. # pip install 'win_inet_pton'
  41. # pip install hexdump
  42. import argparse
  43. import os
  44. import platform
  45. import pprint
  46. import random
  47. import signal
  48. import socket
  49. import struct
  50. import time
  51. import sys
  52. from pathlib import Path
  53. import frida
  54. try:
  55. if os.name == 'nt':
  56. import win_inet_pton
  57. except ImportError:
  58. # win_inet_pton import error
  59. pass
  60. try:
  61. import hexdump # pylint: disable=g-import-not-at-top
  62. except ImportError:
  63. pass
  64. try:
  65. from shutil import get_terminal_size as get_terminal_size
  66. except:
  67. try:
  68. from backports.shutil_get_terminal_size import get_terminal_size as get_terminal_size
  69. except:
  70. pass
  71. try:
  72. import click
  73. except:
  74. class click:
  75. @staticmethod
  76. def secho(message=None, **kwargs):
  77. print(message)
  78. @staticmethod
  79. def style(**kwargs):
  80. raise Exception("unsupported style")
  81. banner = """
  82. --------------------------------------------------------------------------------------------
  83. .oooo. .
  84. d8P'`Y8b .o8
  85. oooo d8b 888 888 .ooooo. .oooo. oo.ooooo. .o888oo oooo oooo oooo d8b .ooooo.
  86. `888""8P 888 888 d88' `"Y8 `P )88b 888' `88b 888 `888 `888 `888""8P d88' `88b
  87. 888 888 888 888 .oP"888 888 888 888 888 888 888 888ooo888
  88. 888 `88b d88' 888 .o8 d8( 888 888 888 888 . 888 888 888 888 .o
  89. d888b `Y8bd8P' `Y8bod8P' `Y888""8o 888bod8P' "888" `V88V"V8P' d888b `Y8bod8P'
  90. 888
  91. o888o
  92. https://github.com/r0ysue/r0capture
  93. --------------------------------------------------------------------------------------------\n
  94. """
  95. def show_banner():
  96. colors = ['bright_red', 'bright_green', 'bright_blue', 'cyan', 'magenta']
  97. try:
  98. click.style('color test', fg='bright_red')
  99. except:
  100. colors = ['red', 'green', 'blue', 'cyan', 'magenta']
  101. try:
  102. columns = get_terminal_size().columns
  103. if columns >= len(banner.splitlines()[1]):
  104. for line in banner.splitlines():
  105. click.secho(line, fg=random.choice(colors))
  106. except:
  107. pass
  108. # ssl_session[<SSL_SESSION id>] = (<bytes sent by client>,
  109. # <bytes sent by server>)
  110. ssl_sessions = {}
  111. def ssl_log(process, pcap=None, host=False, verbose=False, isUsb=False, ssllib="", isSpawn=True, wait=0):
  112. """Decrypts and logs a process's SSL traffic.
  113. Hooks the functions SSL_read() and SSL_write() in a given process and logs
  114. the decrypted data to the console and/or to a pcap file.
  115. Args:
  116. process: The target process's name (as a string) or process ID (as an int).
  117. pcap: The file path to which the pcap file should be written.
  118. verbose: If True, log the decrypted traffic to the console.
  119. Raises:
  120. NotImplementedError: Not running on a Linux or macOS system.
  121. """
  122. # if platform.system() not in ("Darwin", "Linux"):
  123. # raise NotImplementedError("This function is only implemented for Linux and "
  124. # "macOS systems.")
  125. def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port,
  126. dst_addr, dst_port, data):
  127. """Writes the captured data to a pcap file.
  128. Args:
  129. pcap_file: The opened pcap file.
  130. ssl_session_id: The SSL session ID for the communication.
  131. function: The function that was intercepted ("SSL_read" or "SSL_write").
  132. src_addr: The source address of the logged packet.
  133. src_port: The source port of the logged packet.
  134. dst_addr: The destination address of the logged packet.
  135. dst_port: The destination port of the logged packet.
  136. data: The decrypted packet data.
  137. """
  138. t = time.time()
  139. if ssl_session_id not in ssl_sessions:
  140. ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF),
  141. random.randint(0, 0xFFFFFFFF))
  142. client_sent, server_sent = ssl_sessions[ssl_session_id]
  143. if function == "SSL_read":
  144. seq, ack = (server_sent, client_sent)
  145. else:
  146. seq, ack = (client_sent, server_sent)
  147. for writes in (
  148. # PCAP record (packet) header
  149. ("=I", int(t)), # Timestamp seconds
  150. ("=I", int((t * 1000000) % 1000000)), # Timestamp microseconds
  151. ("=I", 40 + len(data)), # Number of octets saved
  152. ("=i", 40 + len(data)), # Actual length of packet
  153. # IPv4 header
  154. (">B", 0x45), # Version and Header Length
  155. (">B", 0), # Type of Service
  156. (">H", 40 + len(data)), # Total Length
  157. (">H", 0), # Identification
  158. (">H", 0x4000), # Flags and Fragment Offset
  159. (">B", 0xFF), # Time to Live
  160. (">B", 6), # Protocol
  161. (">H", 0), # Header Checksum
  162. (">I", src_addr), # Source Address
  163. (">I", dst_addr), # Destination Address
  164. # TCP header
  165. (">H", src_port), # Source Port
  166. (">H", dst_port), # Destination Port
  167. (">I", seq), # Sequence Number
  168. (">I", ack), # Acknowledgment Number
  169. (">H", 0x5018), # Header Length and Flags
  170. (">H", 0xFFFF), # Window Size
  171. (">H", 0), # Checksum
  172. (">H", 0)): # Urgent Pointer
  173. pcap_file.write(struct.pack(writes[0], writes[1]))
  174. pcap_file.write(data)
  175. if function == "SSL_read":
  176. server_sent += len(data)
  177. else:
  178. client_sent += len(data)
  179. ssl_sessions[ssl_session_id] = (client_sent, server_sent)
  180. def on_message(message, data):
  181. """Callback for errors and messages sent from Frida-injected JavaScript.
  182. Logs captured packet data received from JavaScript to the console and/or a
  183. pcap file. See https://www.frida.re/docs/messages/ for more detail on
  184. Frida's messages.
  185. Args:
  186. message: A dictionary containing the message "type" and other fields
  187. dependent on message type.
  188. data: The string of captured decrypted data.
  189. """
  190. if message["type"] == "error":
  191. pprint.pprint(message)
  192. os.kill(os.getpid(), signal.SIGTERM)
  193. return
  194. if len(data) == 1:
  195. print(message["payload"]["function"])
  196. print(message["payload"]["stack"])
  197. return
  198. p = message["payload"]
  199. if verbose:
  200. src_addr = socket.inet_ntop(socket.AF_INET,
  201. struct.pack(">I", p["src_addr"]))
  202. dst_addr = socket.inet_ntop(socket.AF_INET,
  203. struct.pack(">I", p["dst_addr"]))
  204. print("SSL Session: " + p["ssl_session_id"])
  205. print("[%s] %s:%d --> %s:%d" % (
  206. p["function"],
  207. src_addr,
  208. p["src_port"],
  209. dst_addr,
  210. p["dst_port"]))
  211. hexdump.hexdump(data)
  212. print(p["stack"])
  213. if pcap:
  214. log_pcap(pcap_file, p["ssl_session_id"], p["function"], p["src_addr"],
  215. p["src_port"], p["dst_addr"], p["dst_port"], data)
  216. if isUsb:
  217. try:
  218. device = frida.get_usb_device()
  219. except:
  220. device = frida.get_remote_device()
  221. else:
  222. if host:
  223. manager = frida.get_device_manager()
  224. device = manager.add_remote_device(host)
  225. else:
  226. device = frida.get_local_device()
  227. if isSpawn:
  228. pid = device.spawn([process])
  229. time.sleep(1)
  230. session = device.attach(pid)
  231. time.sleep(1)
  232. device.resume(pid)
  233. else:
  234. print("attach")
  235. session = device.attach(process)
  236. if wait > 0:
  237. print("wait for {} seconds".format(wait))
  238. time.sleep(wait)
  239. # session = frida.attach(process)
  240. # pid = device.spawn([process])
  241. # pid = process
  242. # session = device.attach(pid)
  243. # device.resume(pid)
  244. if pcap:
  245. pcap_file = open(pcap, "wb", 0)
  246. for writes in (
  247. ("=I", 0xa1b2c3d4), # Magic number
  248. ("=H", 2), # Major version number
  249. ("=H", 4), # Minor version number
  250. ("=i", time.timezone), # GMT to local correction
  251. ("=I", 0), # Accuracy of timestamps
  252. ("=I", 65535), # Max length of captured packets
  253. ("=I", 228)): # Data link type (LINKTYPE_IPV4)
  254. pcap_file.write(struct.pack(writes[0], writes[1]))
  255. with open(Path(__file__).resolve().parent.joinpath("./script.js"), encoding="utf-8") as f:
  256. _FRIDA_SCRIPT = f.read()
  257. # _FRIDA_SCRIPT = session.create_script(content)
  258. # print(_FRIDA_SCRIPT)
  259. script = session.create_script(_FRIDA_SCRIPT)
  260. script.on("message", on_message)
  261. script.load()
  262. if ssllib != "":
  263. script.exports.setssllib(ssllib)
  264. print("Press Ctrl+C to stop logging.")
  265. def stoplog(signum, frame):
  266. print('You have stoped logging.')
  267. session.detach()
  268. if pcap:
  269. pcap_file.flush()
  270. pcap_file.close()
  271. exit()
  272. signal.signal(signal.SIGINT, stoplog)
  273. signal.signal(signal.SIGTERM, stoplog)
  274. sys.stdin.read()
  275. if __name__ == "__main__":
  276. show_banner()
  277. class ArgParser(argparse.ArgumentParser):
  278. def error(self, message):
  279. print("ssl_logger v" + __version__)
  280. print("by " + __author__)
  281. print("Modified by BigFaceCat")
  282. print("Error: " + message)
  283. print()
  284. print(self.format_help().replace("usage:", "Usage:"))
  285. self.exit(0)
  286. parser = ArgParser(
  287. add_help=False,
  288. description="Decrypts and logs a process's SSL traffic.",
  289. formatter_class=argparse.RawDescriptionHelpFormatter,
  290. epilog=r"""
  291. Examples:
  292. %(prog)s -pcap ssl.pcap openssl
  293. %(prog)s -verbose 31337
  294. %(prog)s -pcap log.pcap -verbose wget
  295. %(prog)s -pcap log.pcap -ssl "*libssl.so*" com.bigfacecat.testdemo
  296. """)
  297. args = parser.add_argument_group("Arguments")
  298. args.add_argument("-pcap", '-p', metavar="<path>", required=False,
  299. help="Name of PCAP file to write")
  300. args.add_argument("-host", '-H', metavar="<192.168.1.1:27042>", required=False,
  301. help="connect to remote frida-server on HOST")
  302. args.add_argument("-verbose","-v", required=False, action="store_const", default=True,
  303. const=True, help="Show verbose output")
  304. args.add_argument("process", metavar="<process name | process id>",
  305. help="Process whose SSL calls to log")
  306. args.add_argument("-ssl", default="", metavar="<lib>",
  307. help="SSL library to hook")
  308. args.add_argument("--isUsb", "-U", default=False, action="store_true",
  309. help="connect to USB device")
  310. args.add_argument("--isSpawn", "-f", default=False, action="store_true",
  311. help="if spawned app")
  312. args.add_argument("-wait", "-w", type=int, metavar="<seconds>", default=0,
  313. help="Time to wait for the process")
  314. parsed = parser.parse_args()
  315. ssl_log(
  316. int(parsed.process) if parsed.process.isdigit() else parsed.process,
  317. parsed.pcap,
  318. parsed.host,
  319. parsed.verbose,
  320. isUsb=parsed.isUsb,
  321. isSpawn=parsed.isSpawn,
  322. ssllib=parsed.ssl,
  323. wait=parsed.wait
  324. )