Skip to content

Commit

Permalink
Merge pull request #116 from doronz88/feature/return_arm_registers
Browse files Browse the repository at this point in the history
protocol: add arm64 registers x0-x7,d0-d7 in return values
  • Loading branch information
doronz88 authored Mar 24, 2022
2 parents 70acad5 + 35f0e2d commit ff6b7af
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 88 deletions.
56 changes: 37 additions & 19 deletions src/rpcclient/rpcclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from socket import socket

import IPython
from construct import Int64sl, Float64l, Float32l
from construct import Int64sl, Float64l, Float32l, Float16l
from traitlets.config import Config

from rpcclient.darwin.structs import pid_t, exitcode_t
Expand All @@ -21,8 +21,9 @@
from rpcclient.lief import Lief
from rpcclient.network import Network
from rpcclient.processes import Processes
from rpcclient.protocol import protocol_message_t, cmd_type_t, exec_chunk_t, exec_chunk_type_t, UNAME_VERSION_LEN, \
reply_protocol_message_t, dummy_block_t, SERVER_MAGIC_VERSION, argument_type_t
from rpcclient.protocol import protocol_message_t, cmd_type_t, exec_chunk_t, exec_chunk_type_t, \
reply_protocol_message_t, dummy_block_t, SERVER_MAGIC_VERSION, argument_type_t, call_response_t, arch_t, \
protocol_handshake_t, call_response_t_size
from rpcclient.symbol import Symbol
from rpcclient.symbols_jar import SymbolsJar
from rpcclient.sysctl import Sysctl
Expand Down Expand Up @@ -61,7 +62,8 @@ class Client:
DEFAULT_ARGV = ['/bin/sh']
DEFAULT_ENVP = []

def __init__(self, sock, sysname: str, hostname: str, port: int = None):
def __init__(self, sock, sysname: str, arch: arch_t, hostname: str, port: int = None):
self._arch = arch
self._hostname = hostname
self._port = port
self._sock = sock
Expand Down Expand Up @@ -99,6 +101,11 @@ def uname(self):
""" get the utsname struct from remote """
raise NotImplementedError()

@property
def arch(self):
""" get remote arch """
return self._arch

def dlopen(self, filename: str, mode: int) -> Symbol:
""" call dlopen() at remote and return its handle. see the man page for more details. """
message = protocol_message_t.build({
Expand Down Expand Up @@ -131,7 +138,8 @@ def dlsym(self, lib: int, symbol_name: str):
err = Int64sl.parse(self._recvall(Int64sl.sizeof()))
return err

def call(self, address: int, argv: typing.List[int] = None, return_float64=False, return_float32=False) -> Symbol:
def call(self, address: int, argv: typing.List[int] = None, return_float64=False, return_float32=False,
return_float16=False, return_raw=False) -> Symbol:
""" call a remote function and retrieve its return value as Symbol object """
fixed_argv = []
free_list = []
Expand Down Expand Up @@ -171,22 +179,33 @@ def call(self, address: int, argv: typing.List[int] = None, return_float64=False
'data': {'address': address, 'argv': fixed_argv},
})
self._sock.sendall(message)
integer_err = Int64sl.parse(self._recvall(Int64sl.sizeof()))

double_buf = self._recvall(Float64l.sizeof())
float32_err = Float32l.parse(double_buf)
float64_err = Float64l.parse(double_buf)
response = call_response_t.parse(self._recvall(call_response_t_size))

for f in free_list:
self.symbols.free(f)

if return_float32:
return float32_err
if self.arch == arch_t.ARCH_ARM64:
double_buf = Float64l.build(response.return_values.arm_registers.d[0])
float16_err = Float16l.parse(double_buf)
float32_err = Float32l.parse(double_buf)
float64_err = response.return_values.arm_registers.d[0]

if return_float16:
return float16_err

if return_float32:
return float32_err

if return_float64:
return float64_err

if return_raw:
return response.return_values.arm_registers

if return_float64:
return float64_err
return self.symbol(response.return_values.arm_registers.x[0])

return self.symbol(integer_err)
return self.symbol(response.return_values.return_value)

def peek(self, address: int, size: int) -> bytes:
""" peek data at given address """
Expand Down Expand Up @@ -399,12 +418,11 @@ def reconnect(self):
self.close()
self._sock = socket()
self._sock.connect((self._hostname, self._port))
magic = self._recvall(len(SERVER_MAGIC_VERSION))

if magic != SERVER_MAGIC_VERSION:
raise InvalidServerVersionMagicError(f'got an invalid server magic: {magic.hex()}')
handshake = protocol_handshake_t.parse(self._recvall(protocol_handshake_t.sizeof()))

self._recvall(UNAME_VERSION_LEN)
if handshake.magic != SERVER_MAGIC_VERSION:
raise InvalidServerVersionMagicError()

def _execute(self, argv: typing.List[str], envp: typing.List[str], background=False) -> int:
message = protocol_message_t.build({
Expand Down Expand Up @@ -485,6 +503,6 @@ def _execution_loop(self, stdin: io_or_str = sys.stdin, stdout=sys.stdout):
def __repr__(self):
buf = f'<{self.__class__.__name__} '
buf += f'PID:{self.symbols.getpid():d} UID:{self.symbols.getuid():d} GID:{self.symbols.getgid():d} ' \
f'SYSNAME:{self._sysname}'
f'SYSNAME:{self._sysname} ARCH:{self.arch}'
buf += '>'
return buf
27 changes: 15 additions & 12 deletions src/rpcclient/rpcclient/client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

from rpcclient.client import Client
from rpcclient.darwin.client import DarwinClient
from rpcclient.exceptions import FailedToConnectError, InvalidServerVersionMagicError
from rpcclient.ios.client import IosClient
from rpcclient.linux.client import LinuxClient
from rpcclient.exceptions import FailedToConnectError, InvalidServerVersionMagicError
from rpcclient.macos.client import MacosClient
from rpcclient.protocol import UNAME_VERSION_LEN, DEFAULT_PORT, SERVER_MAGIC_VERSION
from rpcclient.protocol import DEFAULT_PORT, SERVER_MAGIC_VERSION, protocol_handshake_t


def recvall(sock, size: int) -> bytes:
Expand All @@ -29,21 +29,24 @@ def create_client(hostname: str, port: int = DEFAULT_PORT):
# wrap in our own exception
raise FailedToConnectError() from e

magic = recvall(sock, len(SERVER_MAGIC_VERSION))
if magic != SERVER_MAGIC_VERSION:
raise InvalidServerVersionMagicError(f'got an invalid server magic: {magic.hex()}')
handshake = protocol_handshake_t.parse(recvall(sock, protocol_handshake_t.sizeof()))

if handshake.magic != SERVER_MAGIC_VERSION:
raise InvalidServerVersionMagicError()

sysname = handshake.sysname.lower()
arch = handshake.arch

sysname = recvall(sock, UNAME_VERSION_LEN).split(b'\x00', 1)[0].decode().lower()
logging.info(f'connection uname.sysname: {sysname}')
logging.info(f'connection uname.sysname: {handshake.sysname}')

if sysname == 'darwin':
client = DarwinClient(sock, sysname, hostname, port)
client = DarwinClient(sock, sysname, arch, hostname, port)

if client.uname.machine.startswith('iPhone'):
return IosClient(sock, sysname, hostname, port)
return IosClient(sock, sysname, arch, hostname, port)
else:
return MacosClient(sock, sysname, hostname, port)
return MacosClient(sock, sysname, arch, hostname, port)
elif sysname == 'linux':
return LinuxClient(sock, sysname, hostname, port)
return LinuxClient(sock, sysname, arch, hostname, port)

return Client(sock, sysname, hostname, port)
return Client(sock, sysname, arch, hostname, port)
8 changes: 4 additions & 4 deletions src/rpcclient/rpcclient/darwin/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from rpcclient.darwin.time import Time
from rpcclient.darwin.xpc import Xpc
from rpcclient.exceptions import RpcClientException, MissingLibraryError, CfSerializationError
from rpcclient.protocol import arch_t
from rpcclient.structs.consts import RTLD_NOW

IsaMagic = namedtuple('IsaMagic', 'mask value')
Expand All @@ -41,9 +42,8 @@


class DarwinClient(Client):

def __init__(self, sock, sysname: str, hostname: str, port: int = None):
super().__init__(sock, sysname, hostname, port)
def __init__(self, sock, sysname: str, arch: arch_t, hostname: str, port: int = None):
super().__init__(sock, sysname, arch, hostname, port)
self._dlsym_global_handle = -2 # RTLD_GLOBAL

if 0 == self.dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_NOW):
Expand Down Expand Up @@ -163,7 +163,7 @@ def _cf_encode_dict(self, o: object) -> DarwinSymbol:
for i in range(len(cfvalues)):
values_buf[i] = cfvalues[i]
return self.symbols.CFDictionaryCreate(
kCFAllocatorDefault, keys_buf, values_buf, len(cfvalues), 0, 0, 0)
kCFAllocatorDefault, keys_buf, values_buf, len(cfvalues), 0, 0)

def cf(self, o: object) -> DarwinSymbol:
""" construct a CFObject from a given python object """
Expand Down
4 changes: 1 addition & 3 deletions src/rpcclient/rpcclient/darwin/crash_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def set_symbolicated(self, enabled: bool = True):
enable/disable crash reports symbolication
https://github.com/dlevi309/Symbolicator
"""
with self._client.sc.open(
'/var/root/Library/Preferences/com.apple.CrashReporter.plist') as pref:
pref.set_dict({'SymbolicateCrashes': enabled})
self._client.preferences.cf.set('SymbolicateCrashes', enabled, 'com.apple.CrashReporter', 'root')

def list(self, prefixed='') -> List[CrashReport]:
""" get a list of all crash reports as CrashReport parsed objects """
Expand Down
4 changes: 2 additions & 2 deletions src/rpcclient/rpcclient/darwin/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@


class DarwinSymbol(Symbol):
def objc_call(self, selector, *params):
def objc_call(self, selector, *params, **kwargs):
""" call an objc method on a given object """
sel = self._client.symbols.sel_getUid(selector)
if not self._client.symbols.objc_msgSend(self, self._client.symbols.sel_getUid("respondsToSelector:"), sel):
raise UnrecognizedSelectorError(f"unrecognized selector '{selector}' sent to class")

return self._client.symbols.objc_msgSend(self, sel, *params)
return self._client.symbols.objc_msgSend(self, sel, *params, **kwargs)

@property
def cfdesc(self):
Expand Down
7 changes: 5 additions & 2 deletions src/rpcclient/rpcclient/ios/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@
from rpcclient.ios.backlight import Backlight
from rpcclient.ios.lockdown import Lockdown
from rpcclient.ios.mobile_gestalt import MobileGestalt
from rpcclient.ios.screen_capture import ScreenCapture
from rpcclient.ios.telephony import Telephony
from rpcclient.protocol import arch_t

CRASH_REPORTS_DIR = 'Library/Logs/CrashReporter'


class IosClient(DarwinClient):
def __init__(self, sock, sysname: str, hostname: str, port: int = None):
super().__init__(sock, sysname, hostname, port)
def __init__(self, sock, sysname: str, arch: arch_t, hostname: str, port: int = None):
super().__init__(sock, sysname, arch, hostname, port)
self.backlight = Backlight(self)
self.reports = Reports(self, CRASH_REPORTS_DIR)
self.mobile_gestalt = MobileGestalt(self)
self.lockdown = Lockdown(self)
self.telephony = Telephony(self)
self.screen_capture = ScreenCapture(self)

@property
def roots(self) -> typing.List[str]:
Expand Down
25 changes: 25 additions & 0 deletions src/rpcclient/rpcclient/ios/screen_capture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from collections import namedtuple

from rpcclient.darwin.symbol import DarwinSymbol

CGRect = namedtuple('CGRect', 'x0 y0 x1 y1')


class ScreenCapture:
""" monitor screen events """

def __init__(self, client):
self._client = client

@property
def main_display(self) -> DarwinSymbol:
return self._client.symbols.objc_getClass('CADisplay').objc_call('mainDisplay')

@property
def bounds(self) -> CGRect:
d = self.main_display.objc_call('bounds', return_raw=True).d
return CGRect(x0=d[0], y0=d[1], x1=d[2], y1=d[3])

@property
def screenshot(self) -> bytes:
return self._client.symbols.UIImagePNGRepresentation(self._client.symbols._UICreateScreenUIImage()).py
5 changes: 3 additions & 2 deletions src/rpcclient/rpcclient/linux/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from rpcclient.client import Client
from rpcclient.linux.fs import LinuxFs
from rpcclient.linux.structs import utsname
from rpcclient.protocol import arch_t


class LinuxClient(Client):
def __init__(self, sock, uname_version: str, hostname: str, port: int = None):
super().__init__(sock, uname_version, hostname, port)
def __init__(self, sock, sysname: str, arch: arch_t, hostname: str, port: int = None):
super().__init__(sock, sysname, arch, hostname, port)
self.fs = LinuxFs(self)

@cached_property
Expand Down
5 changes: 3 additions & 2 deletions src/rpcclient/rpcclient/macos/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

from rpcclient.darwin.client import DarwinClient
from rpcclient.darwin.reports import Reports
from rpcclient.protocol import arch_t

CRASH_REPORTS_DIR = 'Library/Logs/DiagnosticReports'


class MacosClient(DarwinClient):

def __init__(self, sock, sysname: str, hostname: str, port: int = None):
super().__init__(sock, sysname, hostname, port)
def __init__(self, sock, sysname: str, arch: arch_t, hostname: str, port: int = None):
super().__init__(sock, sysname, arch, hostname, port)
self.reports = Reports(self, CRASH_REPORTS_DIR)

@property
Expand Down
32 changes: 29 additions & 3 deletions src/rpcclient/rpcclient/protocol.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from construct import Struct, Int32ul, PrefixedArray, Const, Enum, this, PascalString, Switch, PaddedString, Bytes, \
Int64ul, Int8ul, IfThenElse, Float64l
Int64ul, Int8ul, IfThenElse, Float64l, Array, Union

cmd_type_t = Enum(Int32ul,
CMD_EXEC=0,
Expand All @@ -15,11 +15,23 @@
CMD_CLOSE=10,
CMD_REPLY_POKE=11,
)

arch_t = Enum(Int32ul,
ARCH_UNKNOWN=0,
ARCH_ARM64=1,
)


DEFAULT_PORT = 5910
SERVER_MAGIC_VERSION = Int32ul.build(0x88888800)
SERVER_MAGIC_VERSION = 0x88888801
MAGIC = 0x12345678
MAX_PATH_LEN = 1024
UNAME_VERSION_LEN = 256

protocol_handshake_t = Struct(
'magic' / Int32ul,
'arch' / arch_t,
'sysname' / PaddedString(256, 'utf8'),
)

cmd_exec_t = Struct(
'background' / Int8ul,
Expand Down Expand Up @@ -95,4 +107,18 @@
'size' / Int32ul,
)

return_registers_arm_t = Struct(
'x' / Array(8, Int64ul),
'd' / Array(8, Float64l),
)

call_response_t_size = 128

call_response_t = Struct(
'return_values' / Union(None,
'arm_registers' / return_registers_arm_t,
'return_value' / Int64ul,
),
)

dummy_block_t = Int64ul
11 changes: 11 additions & 0 deletions src/rpcserver/ents.plist
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<string>kTCCServiceCamera</string>
<string>kTCCServiceLocation</string>
<string>kTCCServiceBluetoothPeripheral</string>
<string>kTCCServiceScreenCapture</string>
</array>
<key>com.apple.private.icfcallserver</key>
<true/>
Expand All @@ -63,5 +64,15 @@
<string>voice</string>
<string>spi</string>
</array>
<key>com.apple.QuartzCore.global-capture</key>
<true/>
<key>com.apple.QuartzCore.get-client-port</key>
<true/>
<key>com.apple.accessibility.api</key>
<true/>
<key>com.apple.security.iokit-user-client-class</key>
<array>
<string>IOSurfaceRootUserClient</string>
</array>
</dict>
</plist>
Loading

0 comments on commit ff6b7af

Please sign in to comment.