Skip to content

Commit

Permalink
Merge pull request #150 from doronz88/refactor/file_read_write
Browse files Browse the repository at this point in the history
fs: refactor: use size=-1 for whole file in `write()` and `read()`
  • Loading branch information
doronz88 authored Apr 12, 2022
2 parents 79ccd3d + 07d8696 commit 6bb235d
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 45 deletions.
5 changes: 3 additions & 2 deletions src/rpcclient/rpcclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
from rpcclient.darwin.structs import pid_t, exitcode_t
from rpcclient.exceptions import ArgumentError, SymbolAbsentError, SpawnError, ServerDiedError, \
InvalidServerVersionMagicError, BadReturnValueError, RpcFileExistsError, RpcNotEmptyError, RpcFileNotFoundError, \
RpcBrokenPipeError, RpcIsADirectoryError
RpcBrokenPipeError, RpcIsADirectoryError, RpcPermissionError
from rpcclient.fs import Fs
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, \
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.structs.consts import EEXIST, ENOTEMPTY, ENOENT, EPIPE, EISDIR
from rpcclient.structs.consts import EEXIST, ENOTEMPTY, ENOENT, EPIPE, EISDIR, EPERM
from rpcclient.symbol import Symbol
from rpcclient.symbols_jar import SymbolsJar
from rpcclient.sysctl import Sysctl
Expand Down Expand Up @@ -557,6 +557,7 @@ def _execution_loop(self, stdin: io_or_str = sys.stdin, stdout=sys.stdout):
def raise_errno_exception(self, message: str):
message += f' ({self.last_error})'
exceptions = {
EPERM: RpcPermissionError,
ENOENT: RpcFileNotFoundError,
EEXIST: RpcFileExistsError,
EISDIR: RpcIsADirectoryError,
Expand Down
2 changes: 1 addition & 1 deletion src/rpcclient/rpcclient/darwin/crash_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def list(self, prefixed='') -> List[CrashReport]:
for entry in self._client.fs.scandir(root):
if entry.is_file() and entry.name.endswith('.ips') and entry.name.startswith(prefixed):
with self._client.fs.open(entry.path, 'r') as f:
result.append(CrashReport(f.readall().decode(), filename=entry.path))
result.append(CrashReport(f.read().decode(), filename=entry.path))
return result

def clear(self, prefixed=''):
Expand Down
2 changes: 1 addition & 1 deletion src/rpcclient/rpcclient/darwin/darwin_lief.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class DarwinLief(Lief):
@path_to_str('path')
def get_entitlements(self, path: str) -> Mapping:
with self._client.fs.open(path, 'r') as f:
buf = f.readall()
buf = f.read()
parsed = lief.parse(buf)
code_signature = buf[parsed.code_signature.data_offset:
parsed.code_signature.data_offset + parsed.code_signature.data_size]
Expand Down
6 changes: 3 additions & 3 deletions src/rpcclient/rpcclient/darwin/location.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from enum import Enum
from typing import Mapping, Optional

from rpcclient.exceptions import MissingLibraryError, PermissionDeniedError
from rpcclient.exceptions import MissingLibraryError, RpcPermissionError
from rpcclient.structs.consts import RTLD_NOW


Expand Down Expand Up @@ -71,7 +71,7 @@ def last_sample(self) -> Optional[Mapping]:
def start_updating_location(self):
""" request location updates from CLLocationManager """
if self.authorization_status.value < CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways.value:
raise PermissionDeniedError()
raise RpcPermissionError()
self._location_manager.objc_call('startUpdatingLocation')

def stop_updating_location(self):
Expand All @@ -81,5 +81,5 @@ def stop_updating_location(self):
def request_oneshot_location(self):
""" requests the one-time delivery of the user’s current location """
if self.authorization_status.value < CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways.value:
raise PermissionDeniedError()
raise RpcPermissionError()
self._location_manager.objc_call('requestLocation')
2 changes: 1 addition & 1 deletion src/rpcclient/rpcclient/darwin/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ def get_logs(self, prefix='') -> List[Path]:
@property
def system_log(self) -> str:
with self._client.fs.open('/var/log/system.log', 'r') as f:
return f.readall().decode()
return f.read().decode()
10 changes: 5 additions & 5 deletions src/rpcclient/rpcclient/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ class MissingLibraryError(RpcClientException):
pass


class PermissionDeniedError(RpcClientException):
""" failed to access a certain something """
pass


class NoEntitlementsError(RpcClientException):
""" binary contains no entitlements """
pass
Expand Down Expand Up @@ -101,3 +96,8 @@ class RpcNotEmptyError(BadReturnValueError):
class RpcIsADirectoryError(BadReturnValueError):
""" RPC version for IsADirectoryError (errno = ENOTEMPTY) """
pass


class RpcPermissionError(BadReturnValueError):
""" RPC version for PermissionError (errno = EPERM) """
pass
36 changes: 17 additions & 19 deletions src/rpcclient/rpcclient/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rpcclient.allocated import Allocated
from rpcclient.common import path_to_str
from rpcclient.darwin.structs import MAXPATHLEN
from rpcclient.darwin.symbol import DarwinSymbol
from rpcclient.exceptions import BadReturnValueError, ArgumentError, RpcFileNotFoundError, RpcFileExistsError, \
RpcIsADirectoryError
from rpcclient.structs.consts import O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, S_IFMT, S_IFDIR, O_RDWR, SEEK_CUR, S_IFREG, \
Expand Down Expand Up @@ -96,7 +97,7 @@ def __repr__(self):


class File(Allocated):
CHUNK_SIZE = 1024
CHUNK_SIZE = 1024 * 64

def __init__(self, client, fd: int):
"""
Expand All @@ -123,39 +124,36 @@ def seek(self, offset: int, whence: int) -> int:
def tell(self) -> int:
return self.seek(0, SEEK_CUR)

def write(self, buf: bytes) -> int:
def _write(self, buf: bytes) -> int:
""" write(fd, buf, size) at remote. read man for more details. """
n = self._client.symbols.write(self.fd, buf, len(buf)).c_int64
if n < 0:
self._client.raise_errno_exception(f'failed to write on fd: {self.fd}')
return n

def writeall(self, buf: bytes):
def write(self, buf: bytes):
""" continue call write() until """
while buf:
err = self.write(buf)
err = self._write(buf)
buf = buf[err:]

def read(self, size: int = CHUNK_SIZE) -> bytes:
def _read(self, buf: DarwinSymbol, size: int) -> bytes:
""" read file at remote """
with self._client.safe_malloc(size) as chunk:
err = self._client.symbols.read(self.fd, chunk, self.CHUNK_SIZE).c_int64
if err < 0:
self._client.raise_errno_exception(f'read failed for fd: {self.fd}')
return chunk.peek(err)
err = self._client.symbols.read(self.fd, buf, size).c_int64
if err < 0:
self._client.raise_errno_exception(f'read failed for fd: {self.fd}')
return buf.peek(err)

def readall(self, chunk_size: int = CHUNK_SIZE) -> bytes:
def read(self, size: int = -1, chunk_size: int = CHUNK_SIZE) -> bytes:
""" read file at remote """
buf = b''
with self._client.safe_malloc(chunk_size) as chunk:
while True:
err = self._client.symbols.read(self.fd, chunk, chunk_size).c_int64
if err == 0:
while size == -1 or len(buf) < size:
read_chunk = self._read(chunk, chunk_size)
if not read_chunk:
# EOF
break
if err < 0:
self._client.raise_errno_exception(f'read failed for fd: {self.fd}')
buf += chunk.peek(err)
buf += read_chunk
return buf

def __repr__(self):
Expand Down Expand Up @@ -304,12 +302,12 @@ def open(self, file: str, mode: str, access: int = 0o777) -> File:
@path_to_str('file')
def write_file(self, file: str, buf: bytes, access: int = 0o777):
with self.open(file, 'w+', access=access) as f:
f.writeall(buf)
f.write(buf)

@path_to_str('file')
def read_file(self, file: str) -> bytes:
with self.open(file, 'r') as f:
return f.readall()
return f.read()

@path_to_str('file')
def touch(self, file: str, mode: int = None):
Expand Down
2 changes: 1 addition & 1 deletion src/rpcclient/rpcclient/ios/lockdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def pair_records(self) -> List[PairRecord]:
result = []
for entry in self._client.fs.scandir('/var/root/Library/Lockdown/pair_records'):
with self._client.fs.open(entry.path, 'r') as f:
record = plistlib.loads(f.readall())
record = plistlib.loads(f.read())
result.append(PairRecord(hostname=record['HostName'], host_id=record['HostID'],
certificate=record['HostCertificate']))

Expand Down
2 changes: 1 addition & 1 deletion src/rpcclient/rpcclient/lief.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, client):
@path_to_str('path')
def parse(self, path: str):
with self._client.fs.open(path, 'r') as f:
return lief.parse(f.readall())
return lief.parse(f.read())

@path_to_str('path')
def get_symbols(self, path: str) -> Mapping[str, Symbol]:
Expand Down
1 change: 1 addition & 0 deletions src/rpcclient/rpcclient/structs/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
SIGXCPU = 24
SIGXFSZ = 25

EPERM = 1
ENOENT = 2
EEXIST = 17
EISDIR = 21
Expand Down
18 changes: 7 additions & 11 deletions src/rpcclient/rpcclient/xonshrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,7 @@ def _rpc_cp(self, args, stdin, stdout, stderr):
parser.add_argument('src')
parser.add_argument('dst')
args = parser.parse_args(args)
with self.client.fs.open(args.src, 'r') as src:
with self.client.fs.open(args.dst, 'w') as dst:
dst.writeall(src.readall())
self.client.fs.write_file(args.dst, self.client.fs.read_file(args.src))

def _rpc_mkdir(self, args, stdin, stdout, stderr):
parser = ArgumentParser(description='create a directory')
Expand All @@ -297,7 +295,7 @@ def _rpc_cat(self, args, stdin, stdout, stderr):
args = parser.parse_args(args)
for filename in args.filename:
with self.client.fs.open(filename, 'r') as f:
print(f.readall(), file=stdout, end='', flush=True)
print(f.read(), file=stdout, end='', flush=True)
print('', file=stdout, flush=True)

def _rpc_bat(self, args, stdin, stdout, stderr):
Expand Down Expand Up @@ -351,7 +349,7 @@ def _rpc_plshow(self, args, stdin, stdout, stderr):
parser.add_argument('filename')
args = parser.parse_args(args)
with self.client.fs.open(args.filename, 'r') as f:
_print_json(plistlib.loads(f.readall()), file=stdout)
_print_json(plistlib.loads(f.read()), file=stdout)

def _rpc_record(self, args, stdin, stdout, stderr):
parser = ArgumentParser(description='start recording for specified duration')
Expand Down Expand Up @@ -451,14 +449,12 @@ def _listdir(self, path: str) -> List[str]:
return self.client.fs.listdir(path)

def _pull(self, remote_filename, local_filename):
with self.client.fs.open(remote_filename, 'r') as remote_fd:
with open(local_filename, 'wb') as local_fd:
local_fd.write(remote_fd.readall())
with open(local_filename, 'wb') as f:
f.write(self.client.fs.read_file(remote_filename))

def _push(self, local_filename, remote_filename):
with open(local_filename, 'rb') as from_fd:
with self.client.fs.open(remote_filename, 'w') as to_fd:
to_fd.write(from_fd.read())
with open(local_filename, 'rb') as f:
self.client.fs.write_file(remote_filename, f.read())


# actual RC contents
Expand Down

0 comments on commit 6bb235d

Please sign in to comment.