Skip to content

Commit

Permalink
fix bug on iOS 13 and do some other fix
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Apr 20, 2022
1 parent 1dcc9e2 commit 3778be7
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 117 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ See [DEVELOP](DEVELOP.md)
Python code style(ZH): https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/#comments

## Alternatives
- https://github.com/danielpaulus/go-ios
- Go implemented: https://github.com/electricbubble/gidevice

## Thanks
Expand Down
2 changes: 2 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ time.sleep(10)
perf.stop()
```

## Alternative
- https://github.com/danielpaulus/go-ios

## DEVELOP
See [DEVELOP](DEVELOP.md)
Expand Down
131 changes: 69 additions & 62 deletions scripts/plistdump-tcp-proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import argparse
import datetime
import logging
import os
import plistlib
import pprint
Expand All @@ -22,16 +23,20 @@
import threading
import time
import traceback
import typing
from collections import defaultdict

import hexdump
from logzero import logger
from logzero import logger as _logger

logger: logging.Logger = _logger
del(_logger)

# Changing the buffer_size and delay, you can improve the speed and bandwidth.
# But when buffer get to high or delay go too down, you can broke things
buffer_size = 40960
delay = 0.0001


_package_index = [0]


Expand All @@ -40,6 +45,12 @@ def next_package_index() -> int:
return _package_index[0]


def remove_from_list(_list: list, value):
try:
_list.remove(value)
except ValueError:
pass

def create_socket(addr) -> socket.socket:
if isinstance(addr, (tuple, list)):
return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Expand Down Expand Up @@ -67,6 +78,8 @@ class TheServer:
input_list = []
channel = {}
socket_tags = {}
s: socket.socket = None # current handled socket
data: bytes = None # current received data

def __init__(self,
listen_addr: str,
Expand All @@ -81,6 +94,7 @@ def __init__(self,
os.chmod(listen_addr, 0o777)
self.server.listen(200)

self.pemfile = pemfile
self.ssl_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ssl_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ssl_server.bind(('localhost', 10443))
Expand All @@ -97,12 +111,13 @@ def __init__(self,
self.forward_to = forward_to
self.parse_ssl = parse_ssl

self.__tempsocks = {} # Store SSLSocket
self.__port_service_map: dict = {}
self.__skip_ssl_tags: typing.Dict[str, bool] = defaultdict(bool)
self.__ssl_socks = {} # Store SSLSocket
self.__tag_ports = {}
self.__tag = 0
self._data_directory = "usbmuxd-dumpdata/" + time.strftime(
"%Y%m%d-%H-%M-%S")
self.__com_apple_instruments_remoteserver_port = 0
self.__unwrapsocks = {} # Store SSLSocket need unwrap

def __patch_ssl_unidirection_shutdown(self, sslctx):
from cffi import FFI
Expand Down Expand Up @@ -157,25 +172,27 @@ def main_loop(self):
self.on_recv()
except ssl.SSLError as e:
logger.warning("SSLError: tag: %d, %s",
self.socket_tags[self.s], e)
self.socket_tags.get(self.s, -1), e)
self.on_close()
except Exception as e:
traceback.print_exc()
#logger.warning("Unknown error: %s", e)

def gen_tag(self) -> int: # return uniq int
self.__tag += 1
return self.__tag

def pipe_socket(self, clientsock, serversock, tag=None):
def pipe_socket(self, clientsock, serversock, tag: int = None):
assert clientsock not in self.channel, (clientsock, "already piped")
self.input_list.append(clientsock)
self.input_list.append(serversock)
self.channel[clientsock] = serversock
self.channel[serversock] = clientsock
self.channel[clientsock] = serversock
if tag:
self.socket_tags[serversock] = -tag
self.socket_tags[clientsock] = tag
self.socket_tags[clientsock] = tag # client side
self.socket_tags[serversock] = -tag # server side
else:
self.socket_tags[serversock] = 0
self.socket_tags[clientsock] = 0

def unpipe_socket(self, clientsock) -> int:
"""
Expand All @@ -184,17 +201,11 @@ def unpipe_socket(self, clientsock) -> int:
serversock = self.channel[clientsock]
del self.channel[clientsock]
del self.channel[serversock]
self.remove_from_list(self.input_list, clientsock)
self.remove_from_list(self.input_list, serversock)
remove_from_list(self.input_list, clientsock)
remove_from_list(self.input_list, serversock)
self.socket_tags.pop(serversock, None) # socket_tags
return self.socket_tags.pop(clientsock, None)

def remove_from_list(self, _list, value):
try:
_list.remove(value)
except ValueError:
pass

def on_accept(self):
forward = create_socket(self.forward_to)
forward.connect(self.forward_to)
Expand All @@ -214,17 +225,10 @@ def on_ssl_accept(self):
header = recvall(sock, 4) # sock.recv(4)
(tag, ) = struct.unpack("I", header)
logger.info("on ssl accept: %s, tag: %d", addr, tag)
ssl_serversock = self.__tempsocks[tag]
ssl_serversock = self.__ssl_socks[tag]

def wait_ssl_socket():
ssock = self.sslctx.wrap_socket(sock, server_side=True)
if self.__unwrapsocks.get(tag):
print("unwrap ssock")
unwrap_sock = ssock.unwrap()
unwrap_serversock = ssl_serversock
self.pipe_socket(unwrap_sock, unwrap_serversock, tag)
return

self.pipe_socket(ssock, ssl_serversock, tag)

th = threading.Thread(target=wait_ssl_socket)
Expand All @@ -251,11 +255,31 @@ def _iter_pretty_format_data(self, data: bytes):
if 'PairRecordData' in pdata:
yield "... PairRecordData ..."
else:
if 'Service' in pdata and pdata['Service'] == "com.apple.instruments.remoteserver":
self.__com_apple_instruments_remoteserver_port = pdata['Port']
elif 'MessageType' in pdata and pdata['MessageType'] == "Connect" and pdata['PortNumber'] == socket.htons(self.__com_apple_instruments_remoteserver_port):
tag = self.socket_tags[self.s]
self.__unwrapsocks[tag] = True
tag = self.socket_tags[self.s]
if isinstance(pdata, dict):
if "Service" in pdata:
_service = pdata["Service"]
logger.info("Service: %s", _service)
self.__port_service_map[pdata['Port']] = _service
elif pdata.get('MessageType') == "Connect":
_port = pdata['PortNumber']
port: int = socket.htons(_port)
service_name = self.__port_service_map.get(port, "")
logger.info("ServiceName: %r, Port: %d, tag: %d", service_name, port, tag)
service_confs = {
"com.apple.instruments.remoteserver": True,
"com.apple.accessibility.axAuditDaemon.remoteserver": True,
"com.apple.testmanagerd.lockdown": True,
"com.apple.debugserver": True,
"com.apple.instruments.remoteserver.DVTSecureSocketProxy": False,
"com.apple.testmanagerd.lockdown.secure": False,
}
self.__skip_ssl_tags[tag] = service_confs.get(service_name, False)
self.__tag_ports[tag] = port

# patch
# if port == 62078:
# self.__skip_ssl_tags[tag] = True
yield pprint.pformat(pdata)
yield hexdump.hexdump(data[rindex:], "return")
except:
Expand All @@ -271,7 +295,6 @@ def _iter_pretty_format_data(self, data: bytes):
try:
pdata = plistlib.loads(plistdata)
yield pprint.pformat(pdata)
# yield hexdump.hexdump(data[rindex:], "return")
except:
yield hexdump.hexdump(data[rindex:], "return")
# while True:
Expand Down Expand Up @@ -311,12 +334,6 @@ def _iter_pretty_format_data(self, data: bytes):
yield hexdump.hexdump(data[-max_length // 2:], "return")
else:
yield hexdump.hexdump(data, "return")
# else:
# try:
# pdata = data.decode('utf-8')
# print(pdata)
# except UnicodeDecodeError:
# hexdump.hexdump(data)

def dump_data(self, data):
if self.s not in self.socket_tags:
Expand All @@ -332,6 +349,9 @@ def dump_data(self, data):

index = abs(tag)

if tag == 0:
return

if isinstance(self.s, ssl.SSLSocket): # secure tunnel
print('\33[47m', end="")
print(f' {index} '.center(50, direction), end="")
Expand All @@ -340,10 +360,6 @@ def dump_data(self, data):
print(f"Length={len(data)} 0x{len(data):02X}")
print("Time:", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])

# TODO
# if tag < 0:
# print("Skip show receving data")
# return
if True:
os.makedirs(self._data_directory, 0o755, exist_ok=True)
fpath = os.path.join(self._data_directory, f"{index}.txt")
Expand All @@ -359,32 +375,20 @@ def dump_data(self, data):
for line in self._iter_pretty_format_data(data):
print(line)

# else:
# try:
# pdata = data.decode('utf-8')
# print(pdata)
# except UnicodeDecodeError:
# hexdump.hexdump(data)

print('\33[0m', end="")

def man_in_middle_ssl(self, clientsock, ssl_hello_data: bytes):
serversock = self.channel[clientsock]
tag = self.unpipe_socket(clientsock)

ssl_serversock = self.sslctx.wrap_socket(
serversock)
if self.__unwrapsocks.get(tag):
ssl_serversock = ssl_serversock.unwrap()
print("ssl_serversock unwrapped")
ssl_serversock = self.sslctx.wrap_socket(serversock)
print("serversock secure ready, tag: {}".format(tag))

self.__tempsocks[tag] = ssl_serversock
self.__ssl_socks[tag] = ssl_serversock

mim_clientsock = socket.create_connection(('localhost', 10443))
mim_clientsock.sendall(struct.pack("I", tag))

hexdump.hexdump(ssl_hello_data[:1024])
# hexdump.hexdump(ssl_hello_data[:1024])
mim_clientsock.sendall(ssl_hello_data)
self.pipe_socket(clientsock, mim_clientsock)

Expand All @@ -393,11 +397,14 @@ def on_recv(self):
data = self.data

# Check SSL ClientHello message
tag = abs(self.socket_tags[self.s])
if self.parse_ssl and is_ssl_data(data):
logger.info("Mock SSL-Server")
clientsock = self.s
self.man_in_middle_ssl(clientsock, data)
return
logger.info("Detect SSL Handshake")
if not self.__skip_ssl_tags[tag]:
logger.info("Start SSL Inspect PORT: %d", self.__tag_ports.get(tag, -1))
clientsock = self.s
self.man_in_middle_ssl(clientsock, data)
return

# if b'PairRecordData' in data and b'DeviceCertificate' in data:
# print(".... PairRecordData ....")
Expand Down
28 changes: 16 additions & 12 deletions scripts/run-usbmuxd-proxy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,21 @@ while [[ $# -gt 0 ]]; do
esac
done


function safe_run_bg() {
local DEBUG_COLOR="\033[0;32m"
local RESET="\033[0m"
echo -e "${DEBUG_COLOR}>> $@""${RESET}"
"$@" &
PID=$!
trap "kill $PID" EXIT
}

function safe_run() {
local SHOULD_BACKGROUD=$0
shift
local DEBUG_COLOR="\033[0;32m"
local RESET="\033[0m"
echo -e "${DEBUG_COLOR}>> $@""${RESET}"
if [ $SHOULD_BACKGROUD=true ]; then
"$@" &
else
"$@"
fi
"$@"
}

if test $(whoami) != "root"; then
Expand Down Expand Up @@ -82,14 +86,14 @@ if ! test -f $PEMFILE; then
fi

function recover() {
echo "Recover"
echo "Recover environment"
# https://github.com/alibaba/taobao-iphone-device/commit/bb0c56eb05bf10fbd48c3f9dd0f811d3e7192306
# 当plistdump-tcp-proxy.py异常结束时,socat会继续运行导致端口占用,所以这里必须kill掉
kill %%
safe_run false mv ${BACKUP_SOCKET} ${SOCKET}
# kill %%
safe_run mv ${BACKUP_SOCKET} ${SOCKET}
}

safe_run false mv ${SOCKET} ${BACKUP_SOCKET}
safe_run mv ${SOCKET} ${BACKUP_SOCKET}
trap recover EXIT

# 启用 tcp redirect 后可以用 https://github.com/douniwan5788/usbmuxd_debug.git 在wireshark中抓包分析
Expand All @@ -99,7 +103,7 @@ trap recover EXIT
# sudo SSLKEYLOGFILE=./tlskeys.log ./run-usbmuxd-proxy.sh --ssl -t 9876
if [ x"$REDIRECT_PORT" != x"" ]; then
# Setup pipe over TCP that we can tap into
safe_run true socat -t100 "TCP-LISTEN:${REDIRECT_PORT},bind=127.0.0.1,reuseaddr,fork" "UNIX-CONNECT:${BACKUP_SOCKET}"
safe_run_bg socat -t100 "TCP-LISTEN:${REDIRECT_PORT},bind=127.0.0.1,reuseaddr,fork" "UNIX-CONNECT:${BACKUP_SOCKET}"
echo "SSLKEYLOGFILE: $SSLKEYLOGFILE"
export SSLKEYLOGFILE
fi
Expand Down
2 changes: 1 addition & 1 deletion tidevice/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ def cmd_savesslfile(args: argparse.Namespace):
pathlib.Path(f"ssl/{d.udid}_all.pem").write_bytes(
pr['HostPrivateKey']
+ pr['HostCertificate']
+ pr['RootCertificate']
# + pr['RootCertificate']
)


Expand Down
Loading

0 comments on commit 3778be7

Please sign in to comment.