diff --git a/.github/workflows/macos-pytest.yml b/.github/workflows/macos-pytest.yml index 56b2b27c..51c66ee6 100644 --- a/.github/workflows/macos-pytest.yml +++ b/.github/workflows/macos-pytest.yml @@ -34,7 +34,7 @@ jobs: - os: macos-latest arch: arm64 python-version: "3.12" - + - os: macos-latest arch: x86_64 python-version: "3.8" @@ -60,11 +60,21 @@ jobs: - name: Run server working-directory: ./src/rpcserver run: | - make SERVER_CFLAGS=-DSAFE_READ_WRITES - sudo nohup ./rpcserver & + brew install protobuf protobuf-c + make -C ../protos/ C + + mkdir build + cd build + cmake .. -DTARGET=OSX + make + sudo nohup ./rpcserver_macosx & - name: Install python package + pytest working-directory: ./src/rpcclient run: | + pip3 install mypy-protobuf + pip3 install protobuf + make -C ../protos/ Python + python -m pip install --upgrade pip python -m pip install -U pytest python -m pip install -U . diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 1f572779..080397df 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -30,13 +30,17 @@ jobs: - name: Lint with flake8 run: | python -m pip install flake8 - flake8 . --max-complexity=14 --max-line-length=127 --statistics + flake8 . --max-complexity=14 --max-line-length=127 --statistics --exclude=rpcclient/protos/* - name: Verify sorted imports run: | python -m pip install isort isort . -m HANGING_INDENT -l 120 --check-only - name: Test install run: | + pip3 install mypy-protobuf + pip3 install protobuf + make -C ../protos/ Python + python -m pip install --upgrade pip python -m pip install -U . - name: Test show usage diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index ec815b51..6a3f90fc 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -24,6 +24,11 @@ jobs: python-version: '3.x' - name: Install dependencies run: | + pip3 install mypy-protobuf + pip3 install protobuf + + make -C ../protos/ Python + python -m pip install --upgrade pip pip install -U build setuptools wheel twine - name: Build and publish diff --git a/.github/workflows/server-app.yml b/.github/workflows/server-app.yml index df9fe62a..4b90c87c 100644 --- a/.github/workflows/server-app.yml +++ b/.github/workflows/server-app.yml @@ -18,17 +18,32 @@ jobs: include: - os: macos-latest arch: arm64 - - os: macos-latest - arch: x86_64 - os: ubuntu-latest arch: aarch64 - - os: ubuntu-latest - arch: i686 - os: ubuntu-latest arch: x86_64 steps: - uses: actions/checkout@v3 - - name: Test make + - name: Test build run: | - make clean all + if [ "${{ runner.os }}" == "macOS" ]; then + brew install protobuf protobuf-c + pip3 install mypy-protobuf + make -C ../protos/ C + + mkdir build + cd build + cmake .. -DTARGET=IOS + make + cmake .. -DTARGET=OSX + make + elif [ "${{ runner.os }}" == "Linux" ]; then + sudo apt-get update + sudo apt-get install -y protobuf-compiler libprotobuf-dev libprotoc-dev protobuf-c-compiler + make -C ../protos/ C + mkdir build + cd build + cmake .. -DTARGET=LINUX + make + fi \ No newline at end of file diff --git a/.github/workflows/server-publish.yml b/.github/workflows/server-publish.yml index b2a99348..cec50085 100644 --- a/.github/workflows/server-publish.yml +++ b/.github/workflows/server-publish.yml @@ -1,8 +1,8 @@ name: Uploading release artifacts on: - release: - types: [created] + pull_request: + branches: [ "master" ] jobs: build: @@ -25,38 +25,46 @@ jobs: name: Build server on ubuntu working-directory: ./src/rpcserver run: | - make SERVER_CFLAGS=-DSAFE_READ_WRITES + sudo apt-get update + sudo apt-get install -y protobuf-compiler libprotobuf-dev libprotoc-dev protobuf-c-compiler + make -C ../protos/ C + + mkdir build + cd build + cmake .. -DTARGET=LINUX + make - if: matrix.os == 'ubuntu-latest' name: Upload ubuntu server artifact uses: actions/upload-artifact@v3 with: - name: rpcserver_ubuntu_x86_64 - path: ./src/rpcserver/rpcserver + name: rpcserver_linux + path: ./src/rpcserver/build/rpcserver_linux - if: matrix.os == 'macos-latest' name: Build server on macos working-directory: ./src/rpcserver run: | - ./build_darwin.sh - + brew install protobuf protobuf-c + pip3 install mypy-protobuf + make -C ../protos/ C + + mkdir build + cd build + cmake .. -DTARGET=IOS + make + cmake .. -DTARGET=OSX + make - if: matrix.os == 'macos-latest' name: Upload iphoneos server artifact uses: actions/upload-artifact@v3 with: - name: rpcserver_iphoneos_arm64 - path: ./src/rpcserver/rpcserver_iphoneos_arm64 + name: rpcserver_ios + path: ./src/rpcserver/build/rpcserver_ios - if: matrix.os == 'macos-latest' - name: Upload macos,x86_64 server artifact - uses: actions/upload-artifact@v3 - with: - name: rpcserver_macosx_x86_64 - path: ./src/rpcserver/rpcserver_macosx_x86_64 - - - if: matrix.os == 'macos-latest' - name: Upload macos,arm64 server artifact + name: Upload macos server artifact uses: actions/upload-artifact@v3 with: - name: rpcserver_macosx_arm64 - path: ./src/rpcserver/rpcserver_macosx_arm64 + name: rpcserver_macosx + path: ./src/rpcserver/build/rpcserver_macosx diff --git a/.gitignore b/.gitignore index 55ff5c0e..3ba2298f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ _CodeSignature # Build **/_version.py + +# Third party +src/rpcserver/protobuf-c/ diff --git a/README.md b/README.md index b35ed335..05748855 100644 --- a/README.md +++ b/README.md @@ -80,22 +80,36 @@ Install the latest client from PyPi: python3 -m pip install -U rpcclient ``` -## Building +## Construction -macOS & Linux: +**Note:** Cross-platform support is currently not available. -```shell +### macOS + +For macOS/iOS (Ensure that Xcode is installed): + +```bash +brew install protobuf protobuf-c git clone git@github.com:doronz88/rpc-project.git cd src/rpcserver +mkdir build +cd build +cmake .. -DTARGET=OSX # Build for macOS +# OR +cmake .. -DTARGET=IOS # Build for iOS make ``` -iOS (Make sure to have XCode installed): +### Linux -```shell +```bash +sudo apt-get install -y protobuf-compiler libprotobuf-dev libprotoc-dev protobuf-c-compiler git clone git@github.com:doronz88/rpc-project.git cd src/rpcserver -./build_darwin.sh +mkdir build +cd build +cmake .. -DTARGET=LINUX +make ``` ## Quickstart diff --git a/src/protos/Makefile b/src/protos/Makefile new file mode 100644 index 00000000..48fcc4f0 --- /dev/null +++ b/src/protos/Makefile @@ -0,0 +1,21 @@ +PROTO_FILES = rpc.proto +PYTHON_OUT_DIR=../rpcclient/rpcclient/protos/ +C_OUT_DIR=../rpcserver/protos/ + +.PHONY: all C Python clean + +all: C Python + +C: $(PROTO_FILES) + # Compile Protocol Buffers for C + mkdir -p $(C_OUT_DIR) + protoc --c_out=$(C_OUT_DIR) --proto_path=. $(PROTO_FILES) + +Python: $(PROTO_FILES) + # Compile Protocol Buffers for Python + mkdir -p $(PYTHON_OUT_DIR) + python3 -m grpc_tools.protoc --python_out=$(PYTHON_OUT_DIR) --mypy_out=$(PYTHON_OUT_DIR) --proto_path=. $(PROTO_FILES) + +clean: + # Clean up generated files + rm -rf $(C_OUT_DIR) $(PYTHON_OUT_DIR) diff --git a/src/protos/gen_protos.sh b/src/protos/gen_protos.sh new file mode 100755 index 00000000..e7769aba --- /dev/null +++ b/src/protos/gen_protos.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Define variables for input and output directories +PYTHON_OUT_DIR="../rpcclient/rpcclient/protos/" +C_OUT_DIR="../rpcserver/protos/" + +# Get the directory of the current script +proto_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Change the working directory to the script directory +cd "$proto_dir" + +# Create the output directories if they don't exist +mkdir -p "$PYTHON_OUT_DIR" +mkdir -p "$C_OUT_DIR" + +# Run protoc with specified output directories +protoc rpc.proto --python_out="$PYTHON_OUT_DIR" --mypy_out="$PYTHON_OUT_DIR" --c_out="$C_OUT_DIR" \ No newline at end of file diff --git a/src/protos/rpc.proto b/src/protos/rpc.proto new file mode 100644 index 00000000..0750ac3f --- /dev/null +++ b/src/protos/rpc.proto @@ -0,0 +1,193 @@ +syntax = "proto3"; + +package rpc; + +enum Arch { + ARCH_UNKNOWN = 0; + ARCH_ARM64 = 1; +} + +message Command { + int32 magic = 1; + oneof type { + CmdExec exec = 2; + CmdDlopen dlopen = 3; + CmdDlclose dlclose = 4; + CmdDlsym dlsym = 5; + CmdCall call = 6; + CmdPeek peek = 7; + CmdPoke poke = 8; + CmdListDir list_dir = 9; + CmdShowObject show_object = 10; + CmdShowClass show_class = 11; + CmdDummyBlock dummy_block = 12; + CmdClose close = 13; + CmdGetClassList class_list = 14; + CmdExecChunk exec_chunk = 15; + CmdCustom custom = 16; + } +} +message Response { + oneof type { + ResponseCmdExec exec = 1; + ResponseCmdExecChunk exec_chunk = 2; + ResponseDlopen dlopen = 3; + ResponseDlclose dlclose = 4; + ResponseDlsym dlsym = 5; + ResponsePeek peek = 6; + ResponsePoke poke = 7; + ResponseCall call = 8; + ResponseError error = 9; + ResponseDummyBlock dummy_block = 10; + ResponseShowObject show_object = 11; + ResponseGetClassList class_list = 12; + ResponseShowClass show_class = 13; + ResponseListdir list_dir = 14; + ResponseCustom custom = 15; + } +} + +message ReturnRegistersArm { + uint64 x0 = 1; + uint64 x1 = 2; + uint64 x2 = 3; + uint64 x3 = 4; + uint64 x4 = 5; + uint64 x5 = 6; + uint64 x6 = 7; + uint64 x7 = 8; + + double d0 = 9; + double d1 = 10; + double d2 = 11; + double d3 = 12; + double d4 = 13; + double d5 = 14; + double d6 = 15; + double d7 = 16; +} +message Argument { + oneof type { + uint64 v_int = 1; + double v_double = 2; + string v_str = 3; + bytes v_bytes = 4; + } +} +message Handshake { + uint32 magic = 1; + Arch arch = 2; + string sysname = 3; + string machine = 4; +} +message ObjcClass { + uint64 address = 1; + string name = 2; +} + +message CmdShowObject { uint64 address = 1; } +message ResponseShowObject { string description = 1; } + +message CmdShowClass { uint64 address = 1; } +message ResponseShowClass { string description = 1; } + +message CmdExecChunk { bytes buffer = 1; } +message ResponseCmdExecChunk { + oneof type { + bytes buffer = 1; + uint32 exit_code = 2; + } +} + +message CmdDlopen { + string filename = 1; + int32 mode = 2; +} +message ResponseDlopen { uint64 handle = 1; } + +message CmdDlclose { uint64 handle = 1; } +message ResponseDlclose { uint32 res = 1; } + +message CmdDlsym { + uint64 handle = 1; + string symbol_name = 2; +} +message ResponseDlsym { uint64 ptr = 1; } + +message CmdExec { + bool background = 1; + repeated string argv = 2; + repeated string envp = 3; +} +message ResponseCmdExec { uint32 pid = 1; } + +message CmdCall { + uint64 address = 1; + uint64 va_list_index = 2; + repeated Argument argv = 3; +} +message ResponseCall { + oneof return_values { + ReturnRegistersArm arm_registers = 1; + uint64 return_value = 2; + } +} + +message CmdPeek { + uint64 address = 1; + uint64 size = 2; +} +message ResponsePeek { bytes data = 1; } + +message CmdPoke { + uint64 address = 1; + bytes data = 2; +} +message ResponsePoke { uint64 result = 1; } + +message CmdListDir { string path = 1; } + +message CmdDummyBlock {} +message ResponseDummyBlock { + uint64 address = 1; + uint64 size = 2; +} + +message CmdGetClassList {} +message ResponseGetClassList { repeated ObjcClass classes = 1; } + +message ResponseError { string func_name = 1; } + +message ResponseListdir { + uint64 magic = 1; + uint64 dirp = 2; + repeated DirEntry dir_entries = 3; +} + +message DirEntry { + uint64 d_type = 1; + string d_name = 2; + DirEntryStat lstat = 3; + DirEntryStat stat = 4; +} +message DirEntryStat { + uint64 errno1 = 1; + uint64 st_dev = 2; + uint64 st_mode = 3; + uint64 st_nlink = 4; + uint64 st_ino = 5; + uint64 st_uid = 6; + uint64 st_gid = 7; + uint64 st_rdev = 8; + uint64 st_size = 9; + uint64 st_blocks = 10; + uint64 st_blksize = 11; + uint64 st_atime1 = 12; + uint64 st_mtime1 = 13; + uint64 st_ctime1 = 14; +} + +message CmdClose {} + +message CmdCustom { bytes content = 1; } +message ResponseCustom { bytes content = 1; } diff --git a/src/rpcclient/pyproject.toml b/src/rpcclient/pyproject.toml index 4b015d34..8ecbbaca 100644 --- a/src/rpcclient/pyproject.toml +++ b/src/rpcclient/pyproject.toml @@ -7,11 +7,13 @@ license = { file = "LICENSE" } keywords = ["ios", "macos", "linux", "automation", "remote-shell", "remote-control", "ipython"] authors = [ { name = "doronz88", email = "doron88@gmail.com" }, - { name = "matan", email = "matan1008@gmail.com" } + { name = "matan", email = "matan1008@gmail.com" }, + { name = "netanelc305", email = "netanelc305@pm.me" } ] maintainers = [ { name = "doronz88", email = "doron88@gmail.com" }, - { name = "matan", email = "matan1008@gmail.com" } + { name = "matan", email = "matan1008@gmail.com" }, + { name = "netanelc305", email = "netanelc305@pm.me" } ] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -27,7 +29,7 @@ classifiers = [ dynamic = ["dependencies", "version"] [project.optional-dependencies] -test = ["pytest"] +test = ["pytest", "pytest-repeat"] docs = ["toml", "myst_parser", "sphinx", "sphinx-rtd-theme"] [project.urls] diff --git a/src/rpcclient/requirements.txt b/src/rpcclient/requirements.txt index 7a5c32fd..f7cd87e6 100644 --- a/src/rpcclient/requirements.txt +++ b/src/rpcclient/requirements.txt @@ -17,3 +17,4 @@ tqdm osstatus>=1.0.2 parameter-decorators requests +protobuf \ No newline at end of file diff --git a/src/rpcclient/rpcclient/__main__.py b/src/rpcclient/rpcclient/__main__.py index b4c2adda..d5dd84dd 100644 --- a/src/rpcclient/rpcclient/__main__.py +++ b/src/rpcclient/rpcclient/__main__.py @@ -3,8 +3,7 @@ import click import coloredlogs -from rpcclient.client_factory import create_local, create_tcp_client -from rpcclient.protocol import DEFAULT_PORT +from rpcclient.client_factory import DEFAULT_PORT, create_local, create_tcp_client coloredlogs.install(level=logging.DEBUG) diff --git a/src/rpcclient/rpcclient/client.py b/src/rpcclient/rpcclient/client.py index 7212e7c8..c1c3c9a6 100644 --- a/src/rpcclient/rpcclient/client.py +++ b/src/rpcclient/rpcclient/client.py @@ -1,36 +1,35 @@ import ast import builtins import contextlib +import ctypes import dataclasses +import enum import logging import os import sys import threading import typing from collections import namedtuple -from enum import Enum from pathlib import Path from select import select +from typing import Any import IPython -from construct import Float16l, Float32l, Float64l, Int64sl, Int64ul from traitlets.config import Config from xonsh.built_ins import XSH from xonsh.main import main as xonsh_main import rpcclient -from rpcclient.darwin.structs import exitcode_t, pid_t -from rpcclient.exceptions import ArgumentError, BadReturnValueError, InvalidServerVersionMagicError, \ - RpcBrokenPipeError, RpcConnectionRefusedError, RpcFileExistsError, RpcFileNotFoundError, RpcIsADirectoryError, \ - RpcNotADirectoryError, RpcNotEmptyError, RpcPermissionError, RpcResourceTemporarilyUnavailableError, \ - ServerDiedError, SpawnError, SymbolAbsentError +from rpcclient.exceptions import ArgumentError, BadReturnValueError, RpcBrokenPipeError, RpcConnectionRefusedError, \ + RpcFileExistsError, RpcFileNotFoundError, RpcIsADirectoryError, RpcNotADirectoryError, RpcNotEmptyError, \ + RpcPermissionError, RpcResourceTemporarilyUnavailableError, ServerResponseError, SpawnError, SymbolAbsentError from rpcclient.fs import Fs from rpcclient.lief import Lief from rpcclient.network import Network from rpcclient.processes import Processes -from rpcclient.protocol import MAGIC, SERVER_MAGIC_VERSION, arch_t, argument_type_t, call_response_t, \ - call_response_t_size, cmd_type_t, dummy_block_t, exec_chunk_t, exec_chunk_type_t, listdir_entry_t, \ - protocol_handshake_t, protocol_message_t, reply_protocol_message_t +from rpcclient.protos.rpc_pb2 import Argument, CmdCall, CmdDlclose, CmdDlopen, CmdDlsym, CmdDummyBlock, CmdExec, \ + CmdListDir, CmdPeek, CmdPoke, Response +from rpcclient.protosocket import ProtoSocket from rpcclient.structs.consts import EAGAIN, ECONNREFUSED, EEXIST, EISDIR, ENOENT, ENOTDIR, ENOTEMPTY, EPERM, EPIPE, \ RTLD_NEXT from rpcclient.symbol import Symbol @@ -98,7 +97,8 @@ class Client: DEFAULT_ARGV = ['/bin/sh'] DEFAULT_ENVP = [] - def __init__(self, sock, sysname: str, arch: arch_t, create_socket_cb: typing.Callable, dlsym_global_handle=RTLD_NEXT): + def __init__(self, sock: ProtoSocket, sysname: str, arch, create_socket_cb: typing.Callable, + dlsym_global_handle=RTLD_NEXT): self._arch = arch self._create_socket_cb = create_socket_cb self._sock = sock @@ -148,183 +148,116 @@ def arch(self): 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({ - 'cmd_type': cmd_type_t.CMD_DLOPEN, - 'data': {'filename': filename, 'mode': mode}, - }) - with self._protocol_lock: - self._sock.sendall(message) - address = Int64sl.parse(self._recvall(Int64sl.sizeof())) - return self.symbol(address) + command = CmdDlopen(filename=filename, mode=mode) + try: + response = self._sock.send_recv(command) + ret = self.symbol(response.handle) + except AttributeError: + ret = 0 + except ServerResponseError: + ret = 0 + return ret def dlclose(self, lib: int): """ call dlclose() at remote and return its handle. see the man page for more details. """ - lib &= 0xffffffffffffffff - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_DLCLOSE, - 'data': {'lib': lib}, - }) - with self._protocol_lock: - self._sock.sendall(message) - err = Int64sl.parse(self._recvall(Int64sl.sizeof())) - return err + command = CmdDlclose(handle=ctypes.c_uint64(lib).value) + response = self._sock.send_recv(command) + return response.res def dlsym(self, lib: int, symbol_name: str): """ call dlsym() at remote and return its handle. see the man page for more details. """ - lib &= 0xffffffffffffffff - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_DLSYM, - 'data': {'lib': lib, 'symbol_name': symbol_name}, - }) - with self._protocol_lock: - self._sock.sendall(message) - address = Int64sl.parse(self._recvall(Int64sl.sizeof())) - return address + command = CmdDlsym(handle=ctypes.c_uint64(lib).value, symbol_name=symbol_name) + try: + response = self._sock.send_recv(command) + ret = response.ptr + except ServerResponseError: + ret = 0 + return ret def call(self, address: int, argv: typing.List[int] = None, return_float64=False, return_float32=False, - return_float16=False, return_raw=False, va_list_index: int = 0xffff) -> Symbol: + return_raw=False, va_list_index: int = 0xffff) -> typing.Union[float, Symbol, Any]: """ call a remote function and retrieve its return value as Symbol object """ - fixed_argv = [] - free_list = [] - + args = [] for arg in argv: - if isinstance(arg, Enum): - # if it's a python enum, then first get its real value and only then attempt to convert - arg = arg.value - - tmp = arg - - if isinstance(arg, bool): - tmp = int(arg) - + if isinstance(arg, float): + args.append(Argument(v_double=arg)) elif isinstance(arg, str): - tmp = self.symbols.malloc(len(arg) + 1) - tmp.poke(arg.encode() + b'\0') - free_list.append(tmp) - + args.append(Argument(v_str=arg)) + elif isinstance(arg, int): + args.append(Argument(v_int=ctypes.c_uint64(arg).value)) elif isinstance(arg, bytes): - tmp = self.symbols.malloc(len(arg)) - tmp.poke(arg) - free_list.append(tmp) - - if isinstance(tmp, int): - tmp &= 0xffffffffffffffff - fixed_argv.append({'type': argument_type_t.Integer, 'value': tmp}) - - elif isinstance(tmp, float): - fixed_argv.append({'type': argument_type_t.Double, 'value': tmp}) - + args.append(Argument(v_bytes=arg)) + elif isinstance(arg, enum.Enum): + args.append(Argument(v_int=ctypes.c_uint64(arg.value).value)) else: - [self.symbols.free(f) for f in free_list] - raise ArgumentError(f'invalid parameter type: {arg}') - - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_CALL, - 'data': {'address': address, 'va_list_index': va_list_index, 'argv': fixed_argv}, - }) - - with self._protocol_lock: - self._sock.sendall(message) - response = call_response_t.parse(self._recvall(call_response_t_size)) - - for f in free_list: - self.symbols.free(f) - - 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 + raise ArgumentError() + command = CmdCall(address=address, va_list_index=va_list_index, argv=args) + response = self._sock.send_recv(command) + if response.HasField('arm_registers'): + double = response.arm_registers.d0 if return_float32: - return float32_err - + return ctypes.c_float(double).value if return_float64: - return float64_err - + return double if return_raw: - return response.return_values.arm_registers - - return self.symbol(response.return_values.arm_registers.x[0]) - - return self.symbol(response.return_values.return_value) + return response.arm_registers + return self.symbol(response.arm_registers.x0) + return self.symbol(response.return_value) def peek(self, address: int, size: int) -> bytes: """ peek data at given address """ - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_PEEK, - 'data': {'address': address, 'size': size}, - }) - with self._protocol_lock: - self._sock.sendall(message) - reply = protocol_message_t.parse(self._recvall(reply_protocol_message_t.sizeof())) - if reply.cmd_type == cmd_type_t.CMD_REPLY_ERROR: - raise ArgumentError(f'failed to read {size} bytes from {address}') - return self._recvall(size) + command = CmdPeek(address=address, size=size) + try: + response = self._sock.send_recv(command) + return response.data + except ServerResponseError: + raise ArgumentError() def poke(self, address: int, data: bytes): """ poke data at given address """ - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_POKE, - 'data': {'address': address, 'size': len(data), 'data': data}, - }) - with self._protocol_lock: - self._sock.sendall(message) - reply = protocol_message_t.parse(self._recvall(reply_protocol_message_t.sizeof())) - if reply.cmd_type == cmd_type_t.CMD_REPLY_ERROR: - raise ArgumentError(f'failed to write {len(data)} bytes to {address}') + command = CmdPoke(address=address, data=data) + try: + response = self._sock.send_recv(command) + return response.result + except ServerResponseError: + raise ArgumentError() - def get_dummy_block(self) -> Symbol: + def get_dummy_block(self) -> typing.Union[Symbol, int]: """ get an address for a stub block containing nothing """ - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_GET_DUMMY_BLOCK, - 'data': None, - }) - - with self._protocol_lock: - self._sock.sendall(message) - result = dummy_block_t.parse(self._recvall(dummy_block_t.sizeof())) - - return self.symbol(result) + command = CmdDummyBlock() + try: + response = self._sock.send_recv(command) + return self.symbol(response.address) + except ServerResponseError: + return 0 def listdir(self, filename: str): """ get an address for a stub block containing nothing """ - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_LISTDIR, - 'data': {'filename': filename}, - }) - + command = CmdListDir(path=filename) entries = [] - with self._protocol_lock: - self._sock.sendall(message) - dirp = Int64ul.parse(self._recvall(Int64ul.sizeof())) - while dirp: - magic = Int64ul.parse(self._recvall(Int64ul.sizeof())) - if magic != MAGIC: - break - entry = listdir_entry_t.parse(self._recvall(listdir_entry_t.sizeof())) - name = self._recvall(entry.d_namlen).decode() - lstat = ProtocolDitentStat( - errno=entry.lstat.errno, st_blocks=entry.lstat.st_blocks, st_blksize=entry.lstat.st_blksize, - st_atime=entry.lstat.st_atime, st_ctime=entry.lstat.st_ctime, st_mtime=entry.lstat.st_mtime, - st_nlink=entry.lstat.st_nlink, st_mode=entry.lstat.st_mode, st_rdev=entry.lstat.st_rdev, - st_size=entry.lstat.st_size, st_dev=entry.lstat.st_dev, st_gid=entry.lstat.st_gid, - st_ino=entry.lstat.st_ino, st_uid=entry.lstat.st_uid) - stat = ProtocolDitentStat( - errno=entry.stat.errno, st_blocks=entry.stat.st_blocks, st_blksize=entry.stat.st_blksize, - st_atime=entry.stat.st_atime, st_ctime=entry.stat.st_ctime, st_mtime=entry.stat.st_mtime, - st_nlink=entry.stat.st_nlink, st_mode=entry.stat.st_mode, st_rdev=entry.stat.st_rdev, - st_size=entry.stat.st_size, st_dev=entry.stat.st_dev, st_gid=entry.stat.st_gid, - st_ino=entry.stat.st_ino, st_uid=entry.stat.st_uid) - entries.append(ProtocolDirent(d_inode=entry.lstat.st_ino, d_type=entry.d_type, d_name=name, lstat=lstat, - stat=stat)) - - if not dirp: + try: + response = self._sock.send_recv(command) + except ServerResponseError: self.raise_errno_exception(f'failed to listdir: {filename}') + for entry in response.dir_entries: + lstat = ProtocolDitentStat( + errno=entry.lstat.errno1, st_blocks=entry.lstat.st_blocks, st_blksize=entry.lstat.st_blksize, + st_atime=entry.lstat.st_atime1, st_ctime=entry.lstat.st_ctime1, st_mtime=entry.lstat.st_mtime1, + st_nlink=entry.lstat.st_nlink, st_mode=entry.lstat.st_mode, st_rdev=entry.lstat.st_rdev, + st_size=entry.lstat.st_size, st_dev=entry.lstat.st_dev, st_gid=entry.lstat.st_gid, + st_ino=entry.lstat.st_ino, st_uid=entry.lstat.st_uid) + stat = ProtocolDitentStat( + errno=entry.stat.errno1, st_blocks=entry.stat.st_blocks, st_blksize=entry.stat.st_blksize, + st_atime=entry.stat.st_atime1, st_ctime=entry.stat.st_ctime1, st_mtime=entry.stat.st_mtime1, + st_nlink=entry.stat.st_nlink, st_mode=entry.stat.st_mode, st_rdev=entry.stat.st_rdev, + st_size=entry.stat.st_size, st_dev=entry.stat.st_dev, st_gid=entry.stat.st_gid, + st_ino=entry.stat.st_ino, st_uid=entry.stat.st_uid) + entries.append( + ProtocolDirent(d_inode=entry.lstat.st_ino, d_type=entry.d_type, d_name=entry.d_name, + lstat=lstat, + stat=stat)) return entries def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, stdin: io_or_str = sys.stdin, @@ -362,7 +295,7 @@ def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, st self._prepare_terminal() try: # the socket must be non-blocking for using select() - self._sock.setblocking(False) + self._sock._sock.setblocking(False) error = self._execution_loop(stdin, stdout) except Exception: # noqa: E722 # this is important to really catch every exception here, even exceptions not inheriting from Exception @@ -371,7 +304,7 @@ def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, st self._restore_terminal() raise finally: - self._sock.setblocking(True) + self._sock._sock.setblocking(True) if raw_tty: self._restore_terminal() @@ -497,16 +430,10 @@ def _ipython_run_cell_hook(self, info): ) def _close(self): - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_CLOSE, - 'data': None, - }) - self._sock.sendall(message) self._sock.close() def close(self): - with self._protocol_lock: - self._close() + self._close() def shell(self, reuse_client: bool = False): self._logger.disabled = True @@ -535,25 +462,18 @@ def reconnect(self): with self.reconnect_lock: with self._protocol_lock: self._close() - self._sock = self._create_socket_cb() - handshake = protocol_handshake_t.parse(self._recvall(protocol_handshake_t.sizeof())) - - if handshake.magic != SERVER_MAGIC_VERSION: - raise InvalidServerVersionMagicError() - + s = self._create_socket_cb() + self._sock = ProtoSocket(s) # new clients are handled in new processes so all symbols may reside in different addresses self._init_process_specific() def _execute(self, argv: typing.List[str], envp: typing.List[str], background=False) -> int: - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_EXEC, - 'data': {'background': background, 'argv': argv, 'envp': envp}, - }) - self._sock.sendall(message) - pid = pid_t.parse(self._sock.recv(pid_t.sizeof())) - if pid == INVALID_PID: + command = CmdExec(background=background, argv=argv, envp=envp) + try: + response = self._sock.send_recv(command) + return response.pid + except ServerResponseError: raise SpawnError(f'failed to spawn: {argv}') - return pid def _restore_terminal(self): if not tty_support: @@ -567,34 +487,22 @@ def _prepare_terminal(self): self._old_settings = termios.tcgetattr(fd) tty.setraw(fd) - def _recvall(self, size: int) -> bytes: - buf = b'' - while size: - try: - chunk = self._sock.recv(size) - except BlockingIOError: - continue - if self._sock.gettimeout() == 0 and not chunk: - # TODO: replace self._sock.gettimeout() == 0 on -> self._sock.getblocking() on python37+ - raise ServerDiedError() - size -= len(chunk) - buf += chunk - return buf - def _execution_loop(self, stdin: io_or_str = sys.stdin, stdout=sys.stdout): """ if stdin is a file object, we need to select between the fds and give higher priority to stdin. otherwise, we can simply write all stdin contents directly to the process """ fds = [] + raw_socket = self._sock._sock if hasattr(stdin, 'fileno'): fds.append(stdin) else: # assume it's just raw bytes - self._sock.sendall(stdin.encode()) - fds.append(self._sock) + raw_socket.sendall(stdin.encode()) + fds.append(raw_socket) while True: + response = Response() rlist, _, _ = select(fds, [], []) for fd in rlist: @@ -603,23 +511,20 @@ def _execution_loop(self, stdin: io_or_str = sys.stdin, stdout=sys.stdout): buf = os.read(stdin.fileno(), CHUNK_SIZE) else: buf = stdin.read(CHUNK_SIZE) - self._sock.sendall(buf) - elif fd == self._sock: + raw_socket.sendall(buf) + elif fd == raw_socket: try: - buf = self._recvall(exec_chunk_t.sizeof()) + size, buf = self._sock._receive() + response.ParseFromString(buf) except ConnectionResetError: print('Bye. 👋') return - - exec_chunk = exec_chunk_t.parse(buf) - data = self._recvall(exec_chunk.size) - - if exec_chunk.chunk_type == exec_chunk_type_t.CMD_EXEC_CHUNK_TYPE_STDOUT: - stdout.write(data.decode()) + _type = response.exec_chunk.WhichOneof('type') + if _type == 'buffer': + stdout.write(response.exec_chunk.buffer.decode()) stdout.flush() - elif exec_chunk.chunk_type == exec_chunk_type_t.CMD_EXEC_CHUNK_TYPE_ERRORCODE: - # WEXITSTATUS(x) - return exitcode_t.parse(data) >> 8 + elif _type == 'exit_code': + return response.exec_chunk.exit_code def raise_errno_exception(self, message: str): message += f' ({self.last_error})' diff --git a/src/rpcclient/rpcclient/client_factory.py b/src/rpcclient/rpcclient/client_factory.py index 17fa17cc..13ba656b 100644 --- a/src/rpcclient/rpcclient/client_factory.py +++ b/src/rpcclient/rpcclient/client_factory.py @@ -10,28 +10,18 @@ import requests from rpcclient.client import Client -from rpcclient.exceptions import FailedToConnectError, InvalidServerVersionMagicError +from rpcclient.exceptions import FailedToConnectError from rpcclient.ios.client import IosClient from rpcclient.linux.client import LinuxClient from rpcclient.macos.client import MacosClient -from rpcclient.protocol import DEFAULT_PORT, SERVER_MAGIC_VERSION, protocol_handshake_t +from rpcclient.protosocket import ProtoSocket PROJECT_URL = 'https://github.com/doronz88/rpc-project/archive/refs/heads/master.zip' RPCSERVER_SUBDIR = 'rpc-project-master/src/rpcserver' HOMEDIR = Path('~/.rpc-project').expanduser() logger = logging.getLogger(__name__) - - -def recvall(sock, size: int) -> bytes: - buf = b'' - while size: - chunk = sock.recv(size) - if not chunk: - raise FailedToConnectError() - size -= len(chunk) - buf += chunk - return buf +DEFAULT_PORT = 5910 def create_local() -> typing.Union[Client, IosClient, MacosClient, LinuxClient]: @@ -76,23 +66,20 @@ def tcp_connect() -> socket: def create_client(create_socket_cb: typing.Callable) -> typing.Union[Client, IosClient, MacosClient, LinuxClient]: sock = create_socket_cb() - handshake = protocol_handshake_t.parse(recvall(sock, protocol_handshake_t.sizeof())) - - if handshake.magic != SERVER_MAGIC_VERSION: - raise InvalidServerVersionMagicError(f'got {handshake.magic:x} instead of {SERVER_MAGIC_VERSION:x}') + proto_sock = ProtoSocket(sock) - sysname = handshake.sysname.lower() - machine = handshake.machine.lower() - arch = handshake.arch + sysname = proto_sock.handshake.sysname.lower() + machine = proto_sock.handshake.machine.lower() + arch = proto_sock.handshake.arch logging.info(f'connection uname.sysname: {sysname} uname.machine: {machine}') if sysname == 'darwin': if machine.startswith('iphone'): - return IosClient(sock, sysname, arch, create_socket_cb) + return IosClient(proto_sock, sysname, arch, create_socket_cb) else: - return MacosClient(sock, sysname, arch, create_socket_cb) + return MacosClient(proto_sock, sysname, arch, create_socket_cb) elif sysname == 'linux': - return LinuxClient(sock, sysname, arch, create_socket_cb) + return LinuxClient(proto_sock, sysname, arch, create_socket_cb) - return Client(sock, sysname, arch, create_socket_cb) + return Client(proto_sock, sysname, arch, create_socket_cb) diff --git a/src/rpcclient/rpcclient/darwin/client.py b/src/rpcclient/rpcclient/darwin/client.py index 766d6dc6..3191389a 100644 --- a/src/rpcclient/rpcclient/darwin/client.py +++ b/src/rpcclient/rpcclient/darwin/client.py @@ -10,8 +10,7 @@ from typing import Mapping from cached_property import cached_property -from construct import Int8ul, Int32ul, Int64sl, Int64ul -from tqdm import tqdm, trange +from tqdm import tqdm from rpcclient.client import Client from rpcclient.darwin import objective_c_class @@ -37,7 +36,7 @@ from rpcclient.darwin.time import Time from rpcclient.darwin.xpc import Xpc from rpcclient.exceptions import CfSerializationError, GettingObjectiveCClassError, MissingLibraryError -from rpcclient.protocol import arch_t, cmd_type_t, protocol_message_t +from rpcclient.protos.rpc_pb2 import CmdGetClassList, CmdShowClass, CmdShowObject from rpcclient.structs.consts import RTLD_GLOBAL, RTLD_NOW from rpcclient.symbol import Symbol from rpcclient.symbols_jar import SymbolsJar @@ -72,7 +71,7 @@ class DyldImage: class DarwinClient(Client): - def __init__(self, sock, sysname: str, arch: arch_t, create_socket_cb: typing.Callable): + def __init__(self, sock, sysname: str, arch, create_socket_cb: typing.Callable): super().__init__(sock, sysname, arch, create_socket_cb, dlsym_global_handle=RTLD_GLOBAL) def _init_process_specific(self): @@ -134,47 +133,21 @@ def roots(self) -> typing.List[str]: return ['/', '/var/root'] def showobject(self, object_address: Symbol) -> Mapping: - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_SHOWOBJECT, - 'data': {'address': object_address}, - }) - with self._protocol_lock: - self._sock.sendall(message) - response_len = Int64sl.parse(self._recvall(Int64sl.sizeof())) - response = self._recvall(response_len) - return json.loads(response) + command = CmdShowObject(address=object_address) + response = self._sock.send_recv(command) + return json.loads(response.description) def showclass(self, class_address: Symbol) -> Mapping: - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_SHOWCLASS, - 'data': {'address': class_address}, - }) - with self._protocol_lock: - self._sock.sendall(message) - response_len = Int64sl.parse(self._recvall(Int64sl.sizeof())) - response = self._recvall(response_len) - return json.loads(response) + command = CmdShowClass(address=class_address) + response = self._sock.send_recv(command) + return json.loads(response.description) def get_class_list(self) -> typing.Mapping[str, objective_c_class.Class]: - message = protocol_message_t.build({ - 'cmd_type': cmd_type_t.CMD_GET_CLASS_LIST, - 'data': b'', - }) result = {} - with self._protocol_lock: - self._sock.sendall(message) - count = Int32ul.parse(self._recvall(Int32ul.sizeof())) - for _ in trange(count): - name_len = Int8ul.parse(self._recvall(Int8ul.sizeof())) - try: - name = self._recvall(name_len).decode() - except UnicodeDecodeError: - self._recvall(Int64ul.sizeof()) - continue - - class_ = objective_c_class.Class(self, self.symbol(Int64ul.parse(self._recvall(Int64ul.sizeof()))), - lazy=True) - result[name] = class_ + command = CmdGetClassList() + response = self._sock.send_recv(command) + for _class in response.classes: + result[_class.name] = objective_c_class.Class(self, self.symbol(_class.address), lazy=True) return result def symbol(self, symbol: int): diff --git a/src/rpcclient/rpcclient/darwin/processes.py b/src/rpcclient/rpcclient/darwin/processes.py index cfc1e805..1293b403 100644 --- a/src/rpcclient/rpcclient/darwin/processes.py +++ b/src/rpcclient/rpcclient/darwin/processes.py @@ -25,7 +25,7 @@ from rpcclient.exceptions import ArgumentError, BadReturnValueError, MissingLibraryError, ProcessSymbolAbsentError, \ RpcClientException, SymbolAbsentError from rpcclient.processes import Processes -from rpcclient.protocol import arch_t +from rpcclient.protos.rpc_pb2 import ARCH_ARM64 from rpcclient.structs.consts import RTLD_NOW, SEEK_SET, SIGKILL, SIGTERM from rpcclient.symbol import ADDRESS_SIZE_TO_STRUCT_FORMAT, Symbol from rpcclient.sysctl import CTL, KERN @@ -313,7 +313,7 @@ def __init__(self, client, pid: int): self._client = client self._pid = pid - if self._client.arch == arch_t.ARCH_ARM64: + if self._client.arch == ARCH_ARM64: self._thread_class = ArmThread64 else: self._thread_class = IntelThread64 @@ -355,7 +355,7 @@ def poke(self, address: int, buf: bytes): raise BadReturnValueError('vm_write() failed') def get_symbol_name(self, address: int) -> str: - if self._client.arch != arch_t.ARCH_ARM64: + if self._client.arch != ARCH_ARM64: raise NotImplementedError('implemented only on ARCH_ARM64') x = self.vmu_object_identifier.objc_call('symbolForAddress:', address, return_raw=True).x if x[0] == 0 and x[1] == 0: @@ -621,7 +621,7 @@ def rgid(self) -> int: @cached_property def start_time(self) -> datetime: - if self._client.arch != arch_t.ARCH_ARM64: + if self._client.arch != ARCH_ARM64: raise NotImplementedError('implemented only on ARCH_ARM64') val = self.vmu_proc_info.objc_call('startTime', return_raw=True) tv_sec = val.x[0] diff --git a/src/rpcclient/rpcclient/exceptions.py b/src/rpcclient/rpcclient/exceptions.py index f7ac5bf2..ca14b5ba 100644 --- a/src/rpcclient/rpcclient/exceptions.py +++ b/src/rpcclient/rpcclient/exceptions.py @@ -8,6 +8,18 @@ class InvalidServerVersionMagicError(RpcClientException): pass +class ResponseNotFoundError(RpcClientException): + """ Response not found """ + pass + + +class ServerResponseError(RpcClientException): + """ Server returned error """ + def __init__(self, error): + super().__init__() + self.error = error + + class ServerDiedError(RpcClientException): """ server became disconnected during an operation """ pass diff --git a/src/rpcclient/rpcclient/fs.py b/src/rpcclient/rpcclient/fs.py index de3ddbc5..49745c0b 100644 --- a/src/rpcclient/rpcclient/fs.py +++ b/src/rpcclient/rpcclient/fs.py @@ -88,7 +88,7 @@ def _fetch_stat(self, follow_symlinks): if result.errno != 0: self._client.errno = result.errno - self._client.raise_errno_exception(f'failed to stat: {self._entry.name}') + self._client.raise_errno_exception(f'failed to stat: {self._entry.d_name}') return result def __repr__(self): diff --git a/src/rpcclient/rpcclient/ios/client.py b/src/rpcclient/rpcclient/ios/client.py index 74da53e4..92d53d71 100644 --- a/src/rpcclient/rpcclient/ios/client.py +++ b/src/rpcclient/rpcclient/ios/client.py @@ -11,13 +11,12 @@ from rpcclient.ios.sprinboard import SpringBoard from rpcclient.ios.telephony import Telephony from rpcclient.ios.wifi import IosWifi -from rpcclient.protocol import arch_t CRASH_REPORTS_DIR = 'Library/Logs/CrashReporter' class IosClient(DarwinClient): - def __init__(self, sock, sysname: str, arch: arch_t, create_socket_cb: typing.Callable): + def __init__(self, sock, sysname: str, arch, create_socket_cb: typing.Callable): super().__init__(sock, sysname, arch, create_socket_cb) self.backlight = Backlight(self) self.reports = Reports(self, CRASH_REPORTS_DIR) diff --git a/src/rpcclient/rpcclient/linux/client.py b/src/rpcclient/rpcclient/linux/client.py index f92f9ba4..9c4d0c22 100644 --- a/src/rpcclient/rpcclient/linux/client.py +++ b/src/rpcclient/rpcclient/linux/client.py @@ -4,11 +4,10 @@ from rpcclient.client import Client from rpcclient.linux.structs import utsname -from rpcclient.protocol import arch_t class LinuxClient(Client): - def __init__(self, sock, sysname: str, arch: arch_t, create_socket_cb: Callable): + def __init__(self, sock, sysname: str, arch, create_socket_cb: Callable): super().__init__(sock, sysname, arch, create_socket_cb) @cached_property diff --git a/src/rpcclient/rpcclient/macos/client.py b/src/rpcclient/rpcclient/macos/client.py index f44ebd8e..d389d926 100644 --- a/src/rpcclient/rpcclient/macos/client.py +++ b/src/rpcclient/rpcclient/macos/client.py @@ -3,13 +3,12 @@ from rpcclient.darwin.client import DarwinClient from rpcclient.darwin.reports import Reports from rpcclient.macos.apple_script import AppleScript -from rpcclient.protocol import arch_t CRASH_REPORTS_DIR = 'Library/Logs/DiagnosticReports' class MacosClient(DarwinClient): - def __init__(self, sock, sysname: str, arch: arch_t, create_socket_cb: typing.Callable): + def __init__(self, sock, sysname: str, arch, create_socket_cb: typing.Callable): super().__init__(sock, sysname, arch, create_socket_cb) self.reports = Reports(self, CRASH_REPORTS_DIR) self.apple_script = AppleScript(self) diff --git a/src/rpcclient/rpcclient/protocol.py b/src/rpcclient/rpcclient/protocol.py deleted file mode 100644 index 346372db..00000000 --- a/src/rpcclient/rpcclient/protocol.py +++ /dev/null @@ -1,168 +0,0 @@ -from construct import Array, Bytes, Const, Enum, Float64l, Hex, IfThenElse, Int8ul, Int32ul, Int64ul, PaddedString, \ - PascalString, PrefixedArray, Struct, Switch, Union, this - -cmd_type_t = Enum(Int32ul, - CMD_EXEC=0, - CMD_DLOPEN=1, - CMD_DLCLOSE=2, - CMD_DLSYM=3, - CMD_CALL=4, - CMD_PEEK=5, - CMD_POKE=6, - CMD_REPLY_ERROR=7, - CMD_REPLY_PEEK=8, - CMD_GET_DUMMY_BLOCK=9, - CMD_CLOSE=10, - CMD_REPLY_POKE=11, - CMD_LISTDIR=12, - CMD_SHOWOBJECT=13, - CMD_SHOWCLASS=14, - CMD_GET_CLASS_LIST=15 - ) - -arch_t = Enum(Int32ul, - ARCH_UNKNOWN=0, - ARCH_ARM64=1, - ) - -DEFAULT_PORT = 5910 -SERVER_MAGIC_VERSION = 0x88888807 -MAGIC = 0x12345678 -MAX_PATH_LEN = 1024 - -protocol_handshake_t = Struct( - 'magic' / Hex(Int32ul), - 'arch' / arch_t, - 'sysname' / PaddedString(256, 'utf8'), - 'machine' / PaddedString(256, 'utf8'), -) - -cmd_exec_t = Struct( - 'background' / Int8ul, - 'argv' / PrefixedArray(Int32ul, PascalString(Int32ul, 'utf8')), - 'envp' / PrefixedArray(Int32ul, PascalString(Int32ul, 'utf8')), -) - -cmd_dlopen_t = Struct( - 'filename' / PaddedString(MAX_PATH_LEN, 'utf8'), - 'mode' / Int32ul, -) - -cmd_dlclose_t = Struct( - 'lib' / Int64ul, -) - -cmd_dlsym_t = Struct( - 'lib' / Int64ul, - 'symbol_name' / PaddedString(MAX_PATH_LEN, 'utf8'), -) - -argument_type_t = Enum(Int64ul, - Integer=0, - Double=1) - -argument_t = Struct( - 'type' / argument_type_t, - 'value' / IfThenElse(this.type == argument_type_t.Integer, Int64ul, Float64l), -) - -cmd_call_t = Struct( - 'address' / Int64ul, - 'va_list_index' / Int64ul, - 'argv' / PrefixedArray(Int64ul, argument_t), -) - -cmd_peek_t = Struct( - 'address' / Int64ul, - 'size' / Int64ul, -) - -cmd_poke_t = Struct( - 'address' / Int64ul, - 'size' / Int64ul, - 'data' / Bytes(this.size), -) - -cmd_dirlist_t = Struct( - 'filename' / PaddedString(MAX_PATH_LEN, 'utf8'), -) - -cmd_showobject_t = Struct( - 'address' / Int64ul -) - -cmd_showclass_t = Struct( - 'address' / Int64ul -) - -listdir_entry_stat_t = Struct( - 'errno' / Int64ul, - 'st_dev' / Int64ul, # device inode resides on - 'st_mode' / Int64ul, # inode protection mode - 'st_nlink' / Int64ul, # number of hard links to the file - 'st_ino' / Int64ul, # inode's number - 'st_uid' / Int64ul, # user-id of owner - 'st_gid' / Int64ul, # group-id of owner - 'st_rdev' / Int64ul, # device type, for special file inode - 'st_size' / Int64ul, # file size, in bytes - 'st_blocks' / Int64ul, # blocks allocated for file - 'st_blksize' / Int64ul, # optimal blocksize for I/O - 'st_atime' / Int64ul, - 'st_mtime' / Int64ul, - 'st_ctime' / Int64ul, -) - -listdir_entry_t = Struct( - 'd_type' / Int64ul, - 'd_namlen' / Int64ul, - 'lstat' / listdir_entry_stat_t, - 'stat' / listdir_entry_stat_t, -) - -protocol_message_t = Struct( - 'magic' / Const(MAGIC, Hex(Int32ul)), - 'cmd_type' / cmd_type_t, - 'data' / Switch(this.cmd_type, { - cmd_type_t.CMD_EXEC: cmd_exec_t, - cmd_type_t.CMD_DLOPEN: cmd_dlopen_t, - cmd_type_t.CMD_DLCLOSE: cmd_dlclose_t, - cmd_type_t.CMD_DLSYM: cmd_dlsym_t, - cmd_type_t.CMD_CALL: cmd_call_t, - cmd_type_t.CMD_PEEK: cmd_peek_t, - cmd_type_t.CMD_POKE: cmd_poke_t, - cmd_type_t.CMD_LISTDIR: cmd_dirlist_t, - cmd_type_t.CMD_SHOWOBJECT: cmd_showobject_t, - cmd_type_t.CMD_SHOWCLASS: cmd_showclass_t, - }) -) - -reply_protocol_message_t = Struct( - 'magic' / Const(MAGIC, Int32ul), - 'cmd_type' / cmd_type_t, -) - -exec_chunk_type_t = Enum(Int32ul, - CMD_EXEC_CHUNK_TYPE_STDOUT=0, - CMD_EXEC_CHUNK_TYPE_ERRORCODE=1, - ) - -exec_chunk_t = Struct( - 'chunk_type' / exec_chunk_type_t, - '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 diff --git a/src/rpcclient/rpcclient/protosocket.py b/src/rpcclient/rpcclient/protosocket.py new file mode 100644 index 00000000..d4d06b82 --- /dev/null +++ b/src/rpcclient/rpcclient/protosocket.py @@ -0,0 +1,81 @@ +import struct +import threading + +from rpcclient.exceptions import InvalidServerVersionMagicError, ResponseNotFoundError, ServerDiedError, \ + ServerResponseError +from rpcclient.protos.rpc_pb2 import CmdClose, Command, Handshake, Response + +# field[0] is MAGIC - skip +COMMAND_MAPPING = {field.message_type.name: field.name for field in Command.DESCRIPTOR.fields[1:]} +SERVER_MAGIC_VERSION = 0x88888807 +MAGIC = 0x12345678 +MAX_PATH_LEN = 1024 + + +class ProtoSocket: + def __init__(self, sock): + self.handshake = None + self._sock = sock + self._protocol_lock = threading.Lock() + self._do_handshake() + + def _do_handshake(self): + self.handshake = Handshake() + size, buff = self._receive() + self.handshake.ParseFromString(buff) + if self.handshake.magic != SERVER_MAGIC_VERSION: + raise InvalidServerVersionMagicError(f'got {self.handshake.magic:x} instead of {SERVER_MAGIC_VERSION:x}') + + def send_recv(self, sub_command): + command = Command(magic=MAGIC, **{COMMAND_MAPPING.get(sub_command.DESCRIPTOR.name): sub_command}) + response = Response() + with self._protocol_lock: + self._send(command.SerializeToString()) + size, buff = self._receive() + command_type = command.WhichOneof('type') + response.ParseFromString(buff) + + # Iterate through all possible response types + if response.HasField(command_type): + return getattr(response, command_type.lower(), None) + elif response.HasField('error'): + raise ServerResponseError(response.error) + else: + raise ResponseNotFoundError() + # Handle unrecognized command or return a default response + + def _receive(self): + try: + size = struct.unpack(' None: + buff = struct.pack(' bytes: + buf = b'' + while size: + try: + chunk = self._sock.recv(size) + except BlockingIOError: + continue + if self._sock.gettimeout() == 0 and not chunk: + # TODO: replace self._sock.gettimeout() == 0 on -> self._sock.getblocking() on python37+ + raise ServerDiedError() + size -= len(chunk) + buf += chunk + return buf + + def close(self): + command = Command(magic=MAGIC, close=CmdClose()) + self._send(command.SerializeToString()) + self._sock.close() + + @staticmethod + def hex_dump(data): + hex_data = ' '.join([format(byte, '02X') for byte in data]) + print(hex_data) diff --git a/src/rpcclient/rpcclient/symbol.py b/src/rpcclient/rpcclient/symbol.py index 883a6feb..dcaf2d69 100644 --- a/src/rpcclient/rpcclient/symbol.py +++ b/src/rpcclient/rpcclient/symbol.py @@ -7,7 +7,7 @@ from capstone import CS_ARCH_ARM64, CS_ARCH_X86, CS_MODE_64, CS_MODE_LITTLE_ENDIAN, Cs, CsInsn from construct import Container -from rpcclient.protocol import arch_t +from rpcclient.protos.rpc_pb2 import ARCH_ARM64 from rpcclient.structs.generic import Dl_info ADDRESS_SIZE_TO_STRUCT_FORMAT = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'} @@ -107,7 +107,7 @@ def tell(self): def disass(self, size=40) -> List[CsInsn]: """ peek disassembled lines of 'size' bytes """ - if self._client.arch == arch_t.ARCH_ARM64: + if self._client.arch == ARCH_ARM64: return list(Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN).disasm(self.peek(size), self)) else: # assume x86_64 by default diff --git a/src/rpcclient/tests/conftest.py b/src/rpcclient/tests/conftest.py index 9fbbcad3..b2366ebf 100644 --- a/src/rpcclient/tests/conftest.py +++ b/src/rpcclient/tests/conftest.py @@ -7,6 +7,7 @@ from rpcclient.client_factory import create_tcp_client from rpcclient.darwin.client import DarwinClient from rpcclient.exceptions import BadReturnValueError +from rpcclient.protos.rpc_pb2 import ARCH_ARM64 @pytest.fixture @@ -42,15 +43,18 @@ def pytest_configure(config): ) config.addinivalue_line('markers', 'darwin: marks tests that require darwin platform to run') config.addinivalue_line('markers', 'local_machine: marks tests that require local_machine to run') + config.addinivalue_line('markers', 'arm: marks tests that require arm architecture to run') def pytest_collection_modifyitems(config, items): skip_local_only = pytest.mark.skip(reason='remove --ci option to run') skip_not_darwin = pytest.mark.skip(reason='Darwin system is required for this test') + skip_not_arm = pytest.mark.skip(reason='Arm arch is required for this test') skip_not_local_machine = pytest.mark.skip(reason='Local machine is required for this test') with closing(create_tcp_client('127.0.0.1')) as c: is_darwin = isinstance(c, DarwinClient) + is_arm = c.arch == ARCH_ARM64 for item in items: if 'local_only' in item.keywords and config.getoption('--ci'): @@ -59,6 +63,9 @@ def pytest_collection_modifyitems(config, items): if 'darwin' in item.keywords and not is_darwin: # Skip test that require Darwin on non Darwin system item.add_marker(skip_not_darwin) + if 'arm' in item.keywords and not is_arm: + # Skip tests that require arm on non arm architecture + item.add_marker(skip_not_arm) if 'local_machine' in item.keywords and not config.getoption('--local-machine'): # Skip test that require local_machine item.add_marker(skip_not_local_machine) diff --git a/src/rpcclient/tests/test_allocation_cleanup.py b/src/rpcclient/tests/test_allocation_cleanup.py index 36e14229..caaed8b8 100644 --- a/src/rpcclient/tests/test_allocation_cleanup.py +++ b/src/rpcclient/tests/test_allocation_cleanup.py @@ -18,3 +18,9 @@ def test_allocate_file_fd_explicit_deallocate(client, tmp_path): assert fds_count + 1 == len(client.processes.get_by_pid(client.pid).fds) fd.deallocate() assert fds_count == len(client.processes.get_by_pid(client.pid).fds) + + +def test_listdir_fd_release(client, tmp_path): + fds_count = len(client.processes.get_by_pid(client.pid).fds) + client.fs.listdir('/') + assert fds_count == len(client.processes.get_by_pid(client.pid).fds) diff --git a/src/rpcclient/tests/test_client.py b/src/rpcclient/tests/test_client.py index 1c678555..5d048dae 100644 --- a/src/rpcclient/tests/test_client.py +++ b/src/rpcclient/tests/test_client.py @@ -19,11 +19,51 @@ def test_poke(client): client.poke(peekable, b'a' * 0x100) +@pytest.mark.parametrize('params', [ + ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) +]) +def test_16args(client, params): + """ + :param rpcclient.client.Client client: + """ + with client.safe_malloc(0x100) as peekable: + client.symbols.test_16args(peekable, *params) + for i in range(len(params)): + assert peekable[i] == params[i] + + +@pytest.mark.parametrize('va_list_index,params,expected', [ + (2, ['%f', 15.5], '15.500000'), + (2, ['%f %f', 15.5, 7.3], '15.500000 7.300000'), + (2, ['%f %f %d', 15.5, 7.3, 42], '15.500000 7.300000 42'), + (2, ['%f %f %d %d', 15.5, 7.3, 1, 2], '15.500000 7.300000 1 2'), + (2, ['%f %f %d %d %s', 15.5, 7.3, 1, 2, 'test'], '15.500000 7.300000 1 2 test'), + (2, ['%f %f %d %d %s %s', 15.5, 7.3, 1, 2, 'test', 'test2'], '15.500000 7.300000 1 2 test test2'), +]) +@pytest.mark.arm +def test_va_list_call(client, va_list_index, params, expected): + """ + :param rpcclient.client.Client client: + """ + with client.safe_malloc(0x100) as peekable: + client.symbols.sprintf(peekable, *params, va_list_index=va_list_index) + assert expected == peekable.peek_str() + + +@pytest.mark.arm +def test_floating_point_call(client): + """ + :param rpcclient.client.Client client: + """ + assert client.symbols.sqrt(16.0, return_float64=True) == 4.0 + + def test_peek_invalid_address(client): """ :param rpcclient.client.Client client: """ - with pytest.raises(ArgumentError): + with pytest.raises((ArgumentError, ConnectionError)): client.peek(0, 0x10) @@ -31,7 +71,7 @@ def test_poke_invalid_address(client): """ :param rpcclient.client.Client client: """ - with pytest.raises(ArgumentError): + with pytest.raises((ArgumentError, ConnectionError)): client.poke(0, b'a') diff --git a/src/rpcclient/tests/test_processes.py b/src/rpcclient/tests/test_processes.py index 8dada8ab..64047335 100644 --- a/src/rpcclient/tests/test_processes.py +++ b/src/rpcclient/tests/test_processes.py @@ -1,6 +1,6 @@ from pathlib import Path -from rpcclient.protocol import DEFAULT_PORT +from rpcclient.client_factory import DEFAULT_PORT LAUNCHD_PID = 1 LAUNCHD_PATH = '/sbin/launchd' @@ -23,7 +23,6 @@ def test_get_process_by_listening_port(client): def test_process_object(client): server = client.processes.get_self() assert server.pid > 0 - assert len(server.regions) > 0 assert len(server.images) > 0 assert len([img for img in server.images if Path(img.path).resolve() == Path(server.path).resolve()]) > 0 fds = server.fds diff --git a/src/rpcserver/.clang-format b/src/rpcserver/.clang-format new file mode 100644 index 00000000..848d5783 --- /dev/null +++ b/src/rpcserver/.clang-format @@ -0,0 +1,66 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignOperands: DontAlign +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: MultiLine +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 4 +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/src/rpcserver/CMakeLists.txt b/src/rpcserver/CMakeLists.txt new file mode 100644 index 00000000..be1db1ec --- /dev/null +++ b/src/rpcserver/CMakeLists.txt @@ -0,0 +1,136 @@ +cmake_minimum_required(VERSION 3.26) +project(rpcserver) +set(TARGET_NAME rpcserver) +set(apple_specific "") + +include(ExternalProject) +include_directories(.) +include_directories(protobuf-c) +#add_compile_definitions(SINGLE_THREAD) +if (NOT DEFINED TARGET) + message(FATAL_ERROR "Please specify the target using -DTARGET=OSX|IOS|LINUX") +endif () + +# Convert target to uppercase for case-insensitive comparison +string(TOUPPER ${TARGET} TARGET_UPPERCASE) +if ("${TARGET_UPPERCASE}" MATCHES "IOS") + set(IOS_TARGET ON) + set(SDK iphoneos) + set(CMAKE_OSX_ARCHITECTURES arm64 CACHE STRING "Build architecture" FORCE) + set(ENTITLEMENTS_FILE "${CMAKE_SOURCE_DIR}/ents.plist" CACHE STRING "Path to entitlements file") + set(TARGET_NAME "${TARGET_NAME}_ios") + message(STATUS "Entitlements File: ${ENTITLEMENTS_FILE}") +elseif ("${TARGET_UPPERCASE}" MATCHES "OSX") + set(OSX_TARGET ON) + set(SDK macosx) + set(CMAKE_OSX_ARCHITECTURES arm64;x86_64 CACHE STRING "Build architecture" FORCE) + set(TARGET_NAME "${TARGET_NAME}_macosx") +elseif ("${TARGET_UPPERCASE}" MATCHES "LINUX") + set(LINUX_TARGET ON) + set(TARGET_NAME "${TARGET_NAME}_linux") +else () + message(FATAL_ERROR "Invalid target specified ${TARGET}. Use -DTARGET=OSX|IOS|LINUX") +endif () + +if ((IOS_TARGET OR OSX_TARGET) AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + message(FATAL_ERROR "Cross-compile is not supported") +elseif (LINUX_TARGET AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") + message(FATAL_ERROR "Cross-compile is not supported") +endif () + +# Set CXX_COMPILER and CMAKE_SYSROOT +if (APPLE) + unset(CMAKE_OSX_DEPLOYMENT_TARGET CACHE) + execute_process(COMMAND xcrun --sdk ${SDK} --show-sdk-path OUTPUT_VARIABLE CMAKE_OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND xcrun -f --sdk ${SDK} clang OUTPUT_VARIABLE CMAKE_C_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_C_FLAGS -DSAFE_READ_WRITES) + message(STATUS "CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}") + message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") +endif () + +### Check if protobuf-c source not found and download if necessary +if (NOT EXISTS ${CMAKE_SOURCE_DIR}/protobuf-c) + message(STATUS "protobuf-c not found, downloading...") + ExternalProject_Add( + protobuf-c-download + PREFIX "protobuf-c-download" + GIT_REPOSITORY https://github.com/protobuf-c/protobuf-c.git + GIT_TAG v1.5.0 + SOURCE_DIR ${CMAKE_SOURCE_DIR}/protobuf-c/ + # Disable all other steps + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + ) +endif () +# Build protobuf-c after downloading +set(LIBPROTO_C "${CMAKE_BINARY_DIR}/protobuf-c-build") +# When compiling for multiple architectures in CMake, replace ; with $ (e.g., "arm64$x86_64") +string(REPLACE ";" "$" CMAKE_OSX_ARCHITECTURES_ "${CMAKE_OSX_ARCHITECTURES}") +if (EXISTS ${CMAKE_SOURCE_DIR}/protobuf-c) + message(STATUS "protobuf-c source code found, building...") + ExternalProject_Add( + protobuf-c + PREFIX "protobuf-c-build" + SOURCE_DIR ${CMAKE_SOURCE_DIR}/protobuf-c/ + BINARY_DIR ${LIBPROTO_C} + CONFIGURE_COMMAND "${CMAKE_COMMAND}" -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_} "/build-cmake" + BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} protobuf-c + # Disable all other steps + INSTALL_COMMAND "" + DOWNLOAD_COMMAND "" + ) +endif () +message(STATUS "Target: ${TARGET_NAME}") + + +if (APPLE) + # Specify the library paths manually for Apple platforms + find_library(COREFOUNDATION_LIBRARY + NAMES CoreFoundation + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks + NO_DEFAULT_PATH + NO_CACHE + ) + + find_library(FOUNDATION_LIBRARY + NAMES Foundation + PATHS ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks + NO_DEFAULT_PATH + NO_CACHE + ) + + # Print the paths for verification + message("CoreFoundation Library: ${COREFOUNDATION_LIBRARY}") + message("Foundation Library: ${FOUNDATION_LIBRARY}") + + add_library(darwin_utils OBJECT darwin/darwin_utils.m) + add_library(darwin_class OBJECT darwin/darwin_class.m) + add_library(darwin_object OBJECT darwin/darwin_object.m) + add_library(darwin_get_class_list OBJECT darwin/darwin_get_class_list.m) + + # Add Apple-specific dependencies to the apple_specific variable + set(apple_specific + darwin_utils + darwin_class + darwin_object + darwin_get_class_list + ${COREFOUNDATION_LIBRARY} + ${FOUNDATION_LIBRARY} + ) + +endif () + + +add_executable(${TARGET_NAME} rpcserver.c common.c protos/rpc.pb-c.c) +add_dependencies(${TARGET_NAME} protobuf-c) +target_link_libraries(${TARGET_NAME} PRIVATE ${LIBPROTO_C}/libprotobuf-c.a ${apple_specific}) + +if (IOS_TARGET) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND codesign -s - --entitlements ${ENTITLEMENTS_FILE} --generate-entitlement-der $ + COMMENT "Signing server binary" + ) +endif () diff --git a/src/rpcserver/Makefile b/src/rpcserver/Makefile deleted file mode 100644 index c12f0d0f..00000000 --- a/src/rpcserver/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -SERVER_CC=clang -LDFLAGS= -UNAME_S:=$(shell uname -s) -EXTRA_O_FILES=common.o - -ifeq ($(UNAME_S),Darwin) - LDFLAGS += -framework CoreFoundation -framework Foundation - EXTRA_O_FILES += darwin_class.o darwin_object.o darwin_utils.o darwin_get_class_list.o -endif - -all: rpcserver - -common.o: common.c - $(SERVER_CC) $(SERVER_CFLAGS) -c common.c -o common.o - -darwin_utils.o: darwin/darwin_utils.m - $(SERVER_CC) $(SERVER_CFLAGS) darwin/darwin_utils.m -c -ObjC -o darwin_utils.o - -darwin_class.o: darwin/darwin_class.m - $(SERVER_CC) $(SERVER_CFLAGS) darwin/darwin_class.m -c -ObjC -o darwin_class.o - -darwin_object.o: darwin/darwin_object.m - $(SERVER_CC) $(SERVER_CFLAGS) darwin/darwin_object.m -c -ObjC -o darwin_object.o - -darwin_get_class_list.o: darwin/darwin_get_class_list.m - $(SERVER_CC) $(SERVER_CFLAGS) darwin/darwin_get_class_list.m -c -ObjC -o darwin_get_class_list.o - -rpcserver: rpcserver.c $(EXTRA_O_FILES) - $(SERVER_CC) $(SERVER_CFLAGS) $(LDFLAGS) -Wno-unguarded-availability-new rpcserver.c -lpthread -ldl $(EXTRA_O_FILES) -o rpcserver - -clean: - rm -rf *.o rpcserver - -install: rpcserver - mv rpcserver /usr/local/bin - -uninstall: - rm /usr/local/bin/rpcserver \ No newline at end of file diff --git a/src/rpcserver/build_darwin.sh b/src/rpcserver/build_darwin.sh deleted file mode 100755 index 1f0816ea..00000000 --- a/src/rpcserver/build_darwin.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -NC='\033[0m' # No Color -RED='\033[0;31m' -GREEN='\033[0;32m' - -for i in iphoneos,arm64 macosx,x86_64 macosx,arm64 -do - IFS="," - set -- $i; - - echo "${GREEN}Building $1:$2 ${NC}" - - SDK="$1" - ARCH="$2" - SYSROOT="$(xcrun --sdk $SDK --show-sdk-path)" - CC="$(xcrun -f --sdk $SDK clang)" - CFLAGS="-arch $ARCH --sysroot=$SYSROOT -DSAFE_READ_WRITES" - - if test "$SDK" = 'iphoneos'; then - CFLAGS="$CFLAGS -miphoneos-version-min=5.0" - fi - - make clean - make all SERVER_CC="$CC" SERVER_CFLAGS="$CFLAGS" - - server_binary=rpcserver_${SDK}_${ARCH} - cp rpcserver $server_binary - - if test "$SDK" = 'iphoneos'; then - codesign -s - --entitlements ents.plist --generate-entitlement-der $server_binary - fi -done - -# clean old binaries -make clean diff --git a/src/rpcserver/common.c b/src/rpcserver/common.c index 0830ba2d..b4d9082d 100644 --- a/src/rpcserver/common.c +++ b/src/rpcserver/common.c @@ -1,24 +1,23 @@ +#include #include -#include -#include #include -#include -#include -#include +#include +#include #include +#include +#include #include "common.h" #ifdef __APPLE__ #include -struct os_log_s -{ - int a; +struct os_log_s { + int a; }; struct os_log_s _os_log_default; -#endif // __APPLE__ +#endif// __APPLE__ bool g_stdout = false; bool g_syslog = false; @@ -26,139 +25,198 @@ FILE *g_file = NULL; #define BT_BUF_SIZE (100) -void print_backtrace() -{ - int nptrs; - void *buffer[BT_BUF_SIZE]; - char **strings; +void print_backtrace() { + int nptrs; + void *buffer[BT_BUF_SIZE]; + char **strings; - nptrs = backtrace(buffer, BT_BUF_SIZE); - trace("BACKTRACE", "backtrace() returned %d addresses", nptrs); + nptrs = backtrace(buffer, BT_BUF_SIZE); + trace("BACKTRACE", "backtrace() returned %d addresses", nptrs); - /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) + /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ - strings = backtrace_symbols(buffer, nptrs); - if (strings == NULL) - { - perror("backtrace_symbols"); - return; - } + strings = backtrace_symbols(buffer, nptrs); + if (strings == NULL) { + perror("backtrace_symbols"); + return; + } - for (int j = 0; j < nptrs; j++) - { - trace("BACKTRACE:\t", "%s", strings[j]); - } + for (int j = 0; j < nptrs; j++) { + trace("BACKTRACE:\t", "%s", strings[j]); + } - free(strings); + free(strings); } -void trace(const char *prefix, const char *fmt, ...) -{ - if (!g_stdout && !g_syslog) - { - return; - } +void trace(const char *prefix, const char *fmt, ...) { + if (!g_stdout && !g_syslog) { + return; + } - char line[1022]; - char prefixed_line[1024]; + char line[1022]; + char prefixed_line[1024]; - va_list args; - va_start(args, fmt); - vsprintf(line, fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + vsprintf(line, fmt, args); + va_end(args); - sprintf(prefixed_line, "%s: %s", prefix, line); + sprintf(prefixed_line, "%s: %s", prefix, line); - if (g_stdout) - { - puts(prefixed_line); - fflush(stdout); - } - if (g_syslog) - { + if (g_stdout) { + puts(prefixed_line); + fflush(stdout); + } + if (g_syslog) { #ifdef __APPLE__ - os_log(&_os_log_default, "%{public}s", prefixed_line); -#else // __APPLE__ - syslog(LOG_DEBUG, "%s", prefixed_line); -#endif // !__APPLE__ - } - if (g_file) - { - fprintf(g_file, "%s\n", prefixed_line); - fflush(g_file); - } + os_log(&_os_log_default, "%{public}s", prefixed_line); +#else // __APPLE__ + syslog(LOG_DEBUG, "%s", prefixed_line); +#endif// !__APPLE__ + } + if (g_file) { + fprintf(g_file, "%s\n", prefixed_line); + fflush(g_file); + } } -bool recvall_ext(int sockfd, char *buf, size_t len, bool *disconnected) -{ - size_t total_bytes = 0; - size_t bytes = 0; - *disconnected = false; - - while (len > 0) - { - bytes = recv(sockfd, buf + total_bytes, len, 0); - if (0 == bytes) - { - TRACE("client fd: %d disconnected", sockfd); - *disconnected = true; - return false; - } - CHECK(bytes > 0); - - total_bytes += bytes; - len -= bytes; +bool recvall_ext(int sockfd, char *buf, size_t len, bool *disconnected) { + bool ret = RPC_FAILURE; + size_t total_bytes = 0; + size_t bytes; + *disconnected = false; + + while (len > 0) { + bytes = recv(sockfd, buf + total_bytes, len, 0); + if (0 == bytes) { + TRACE("client fd: %d disconnected", sockfd); + *disconnected = true; + goto error; } + CHECK(bytes > 0); - return true; + total_bytes += bytes; + len -= bytes; + } + ret = RPC_SUCCESS; error: - return false; + return ret; } -bool recvall(int sockfd, char *buf, size_t len) -{ - bool disconnected; - return recvall_ext(sockfd, buf, len, &disconnected); +bool message_receive(int sockfd, char *buf, size_t *size) { + bool ret = RPC_FAILURE; + recv(sockfd, size, sizeof(size_t), 0); + CHECK(*size != 0); + CHECK(recvall(sockfd, buf, *size)); + ret = RPC_SUCCESS; +error: + return ret; } -bool sendall(int sockfd, const char *buf, size_t len) -{ - size_t total_bytes = 0; - size_t bytes = 0; - - while (len > 0) - { - bytes = send(sockfd, buf + total_bytes, len, MSG_NOSIGNAL); - CHECK(bytes != -1); +bool recvall(int sockfd, char *buf, size_t len) { + bool disconnected; + return recvall_ext(sockfd, buf, len, &disconnected); +} - total_bytes += bytes; - len -= bytes; - } +bool message_send(int sockfd, const uint8_t *buf, size_t len) { + bool ret = RPC_FAILURE; - return true; + size_t total_bytes = 0; + size_t bytes; + CHECK(send(sockfd, &len, sizeof(size_t), MSG_NOSIGNAL) != -1); + while (len > 0) { + bytes = send(sockfd, buf + total_bytes, len, MSG_NOSIGNAL); + CHECK(bytes != -1); + total_bytes += bytes; + len -= bytes; + } + ret = RPC_SUCCESS; error: - return false; + return ret; } -bool writeall(int fd, const char *buf, size_t len) -{ - size_t total_bytes = 0; - size_t bytes = 0; +bool writeall(int fd, const char *buf, size_t len) { + bool ret = RPC_FAILURE; - while (len > 0) - { - bytes = write(fd, buf + total_bytes, len); - CHECK(bytes != -1); + size_t total_bytes = 0; + size_t bytes; - total_bytes += bytes; - len -= bytes; - } + while (len > 0) { + bytes = write(fd, buf + total_bytes, len); + CHECK(bytes != -1); + + total_bytes += bytes; + len -= bytes; + } + ret = RPC_SUCCESS; +error: + return ret; +} - return true; +bool send_response(int sockfd, ProtobufCMessage *resp) { + bool ret = RPC_FAILURE; + Rpc__Response response = RPC__RESPONSE__INIT; + char *c_name = (char *) (ProtobufCMessageDescriptor *) resp->descriptor->c_name; + + if (strcmp("Rpc__ResponseCmdExec", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_EXEC; + response.exec = (Rpc__ResponseCmdExec *) resp; + } else if (strcmp("Rpc__ResponseCmdExecChunk", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_EXEC_CHUNK; + response.exec_chunk = (Rpc__ResponseCmdExecChunk *) resp; + } else if (strcmp("Rpc__ResponseDlopen", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_DLOPEN; + response.dlopen = (Rpc__ResponseDlopen *) resp; + } else if (strcmp("Rpc__ResponseDlclose", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_DLCLOSE; + response.dlclose = (Rpc__ResponseDlclose *) resp; + } else if (strcmp("Rpc__ResponseDlsym", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_DLSYM; + response.dlsym = (Rpc__ResponseDlsym *) resp; + } else if (strcmp("Rpc__ResponsePeek", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_PEEK; + response.peek = (Rpc__ResponsePeek *) resp; + } else if (strcmp("Rpc__ResponsePoke", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_POKE; + response.poke = (Rpc__ResponsePoke *) resp; + } else if (strcmp("Rpc__ResponseCall", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_CALL; + response.call = (Rpc__ResponseCall *) resp; + } else if (strcmp("Rpc__ResponseError", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_ERROR; + response.error = (Rpc__ResponseError *) resp; + } else if (strcmp("Rpc__ResponseDummyBlock", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_DUMMY_BLOCK; + response.dummy_block = (Rpc__ResponseDummyBlock *) resp; + } else if (strcmp("Rpc__ResponseShowObject", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_SHOW_OBJECT; + response.show_object = (Rpc__ResponseShowObject *) resp; + } else if (strcmp("Rpc__ResponseGetClassList", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_CLASS_LIST; + response.class_list = (Rpc__ResponseGetClassList *) resp; + } else if (strcmp("Rpc__ResponseShowClass", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_SHOW_CLASS; + response.show_class = (Rpc__ResponseShowClass *) resp; + } else if (strcmp("Rpc__ResponseListdir", c_name) == 0) { + response.type_case = RPC__RESPONSE__TYPE_LIST_DIR; + response.list_dir = (Rpc__ResponseListdir *) resp; + } else { + TRACE("Unknown response type_case: %s\n", c_name); + goto error; + } + uint8_t *buffer = NULL; + size_t len = rpc__response__get_packed_size(&response); + CHECK(len > 0); + buffer = (uint8_t *) malloc(len * sizeof(uint8_t)); + CHECK(buffer != NULL); + rpc__response__pack(&response, buffer); + CHECK(message_send(sockfd, buffer, len)); + ret = RPC_SUCCESS; error: - return false; + SAFE_FREE(buffer); + return ret; } \ No newline at end of file diff --git a/src/rpcserver/common.h b/src/rpcserver/common.h index ee251748..4099b097 100644 --- a/src/rpcserver/common.h +++ b/src/rpcserver/common.h @@ -1,15 +1,15 @@ #ifndef __COMMON_H_ #define __COMMON_H_ -#include +#include "protos/rpc.pb-c.h" +#include #include -#include #include -#include +#include +#include #include typedef unsigned char u8; -typedef unsigned short u16; typedef unsigned int u32; typedef signed int s32; typedef unsigned long u64; @@ -19,23 +19,66 @@ extern bool g_stdout; extern bool g_syslog; extern FILE *g_file; +#ifdef __ARM_ARCH_ISA_A64 +#define MAX_STACK_ARGS (16) +#define MAX_REGS_ARGS (8) +#define GPR_COUNT (30) + +typedef struct { + uint64_t x[MAX_REGS_ARGS]; + double d[MAX_REGS_ARGS]; + uint64_t stack[MAX_STACK_ARGS]; +} arm_args_t; +#else +#define MAX_ARGS (17) +#endif + +typedef enum { + RPC_FAILURE = false, + RPC_SUCCESS = true +} rpc_result_t; + +#define COPY_ARR_WITH_NULL(dest, src, n_src) \ + do { \ + if (n_src > 0) { \ + dest = (char **) calloc(n_src + 1, sizeof(char *)); \ + CHECK(dest != NULL); \ + memcpy(dest, src, sizeof(char *) * n_src); \ + } \ + } while (0) + +#define SAFE_FREE(ptr) \ + do { \ + if (ptr) { \ + free(ptr); \ + ptr = NULL; \ + } \ + } while (0) + #define TRACE(...) trace(__PRETTY_FUNCTION__, __VA_ARGS__) -#define CHECK(expression) \ - if (!(expression)) \ - { \ - if (errno) \ - { \ - trace(__PRETTY_FUNCTION__, "ERROR: errno: %d (%s)", errno, strerror(errno)); \ - } \ - print_backtrace(); \ - goto error; \ +#define CHECK(expression) \ + if (!(expression)) { \ + if (errno) { \ + trace(__PRETTY_FUNCTION__, "ERROR on expression: %s: errno: %d (%s)", #expression, errno, strerror(errno)); \ + } \ + print_backtrace(); \ + goto error; \ } void print_backtrace(); + void trace(const char *prefix, const char *fmt, ...); + bool recvall_ext(int sockfd, char *buf, size_t len, bool *disconnected); + bool recvall(int sockfd, char *buf, size_t len); -bool sendall(int sockfd, const char *buf, size_t len); + bool writeall(int fd, const char *buf, size_t len); -#endif // __COMMON_H_ \ No newline at end of file +bool message_send(int sockfd, const uint8_t *buf, size_t len); + +bool send_response(int sockfd, ProtobufCMessage *resp); + +bool message_receive(int sockfd, char *buf, size_t *size); + +#endif// __COMMON_H_ \ No newline at end of file diff --git a/src/rpcserver/darwin/darwin.h b/src/rpcserver/darwin/darwin.h index 24696459..90aba593 100644 --- a/src/rpcserver/darwin/darwin.h +++ b/src/rpcserver/darwin/darwin.h @@ -2,16 +2,14 @@ #define __DARWIN_H_ #include +#include #include #include -#include - -#include "../protocol.h" void addProtocolsToDictionary(Class objcClass, NSDictionary *outDictionary); void addIvarsToDictionary(Class objcClass, NSDictionary *outDictionary, id objcObject); void addPropertiesToDictionary(Class objcClass, NSDictionary *outDictionary); void addMethodsToDictionary(Class objcClass, NSDictionary *outDictionary); -NSString* getDictionaryJsonString(NSDictionary *classDescription); +NSString *getDictionaryJsonString(NSDictionary *classDescription); -#endif // __DARWIN_H_ \ No newline at end of file +#endif// __DARWIN_H_ \ No newline at end of file diff --git a/src/rpcserver/darwin/darwin_class.m b/src/rpcserver/darwin/darwin_class.m index 6ca0ed7a..0a8b6790 100644 --- a/src/rpcserver/darwin/darwin_class.m +++ b/src/rpcserver/darwin/darwin_class.m @@ -1,51 +1,39 @@ -#include "darwin.h" #include "../common.h" +#include "darwin.h" +#include #include #include -#include -static NSDictionary* getClassDescription(Class objcClass) -{ - NSDictionary *classDescription = @{ - @"protocols": [NSMutableArray new], - @"ivars": [NSMutableArray new], - @"properties": [NSMutableArray new], - @"methods": [NSMutableArray new], - @"name": [NSString stringWithCString:class_getName(objcClass) encoding:NSUTF8StringEncoding], - @"address": [NSNumber numberWithLong:(uintptr_t)objcClass], - @"super": [NSNumber numberWithLong:(uintptr_t)class_getSuperclass(objcClass)], - }; +static NSDictionary *getClassDescription(Class objcClass) { + NSDictionary *classDescription = @{ + @"protocols": [NSMutableArray new], + @"ivars": [NSMutableArray new], + @"properties": [NSMutableArray new], + @"methods": [NSMutableArray new], + @"name": [NSString stringWithCString:class_getName(objcClass) encoding:NSUTF8StringEncoding], + @"address": [NSNumber numberWithLong:(uintptr_t) objcClass], + @"super": [NSNumber numberWithLong:(uintptr_t) class_getSuperclass(objcClass)], + }; - addProtocolsToDictionary(objcClass, classDescription); - addIvarsToDictionary(objcClass, classDescription, nil); - addPropertiesToDictionary(objcClass, classDescription); - addMethodsToDictionary(objcClass, classDescription); + addProtocolsToDictionary(objcClass, classDescription); + addIvarsToDictionary(objcClass, classDescription, nil); + addPropertiesToDictionary(objcClass, classDescription); + addMethodsToDictionary(objcClass, classDescription); - return classDescription; + return classDescription; } -static NSString* getClassDescriptionStr(Class objcClass) -{ - NSDictionary *classDescription = getClassDescription(objcClass); - return getDictionaryJsonString(classDescription); +static NSString *getClassDescriptionStr(Class objcClass) { + NSDictionary *classDescription = getClassDescription(objcClass); + return getDictionaryJsonString(classDescription); } -int handle_showclass(int sockfd) -{ - TRACE("Entered showclass"); - cmd_showclass_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - NSString *response_str = getClassDescriptionStr((id)cmd.address); +int handle_showclass(int sockfd, Rpc__CmdShowClass *cmd) { + TRACE("Entered showclass"); + Rpc__ResponseShowClass resp_show_class = RPC__RESPONSE_SHOW_CLASS__INIT; - TRACE("Sending response"); - size_t response_len = [response_str length]; - const char *response_cstr = [response_str UTF8String]; - CHECK(sendall(sockfd, (char *)&response_len, sizeof(response_len))); - CHECK(sendall(sockfd, response_cstr, response_len)); - TRACE("Sent response"); - return 0; + NSString *response_str = getClassDescriptionStr((id) cmd->address); + resp_show_class.description = (char *) [response_str UTF8String]; -error: - TRACE("Failed to show class"); - return -1; + return send_response(sockfd, (ProtobufCMessage *) &resp_show_class); } diff --git a/src/rpcserver/darwin/darwin_get_class_list.m b/src/rpcserver/darwin/darwin_get_class_list.m index 7c82fe7c..9a5e27f3 100644 --- a/src/rpcserver/darwin/darwin_get_class_list.m +++ b/src/rpcserver/darwin/darwin_get_class_list.m @@ -1,41 +1,45 @@ -#include "darwin.h" #include "../common.h" +#include "darwin.h" +#include #include #include -#include -int handle_get_class_list(int sockfd) -{ - TRACE("handle_get_class_list"); +bool handle_get_class_list(int sockfd, Rpc__CmdGetClassList *cmd) { + TRACE("handle_get_class_list"); + Rpc__ResponseGetClassList resp_class_list = RPC__RESPONSE_GET_CLASS_LIST__INIT; + Class *class_list = NULL; + bool ret = RPC_FAILURE; - Class* classes = NULL; - u32 count = objc_getClassList(NULL, 0); + u32 count = objc_getClassList(NULL, 0); + CHECK(count > 0) + TRACE("reporting class list: %d", count); + resp_class_list.classes = malloc(sizeof(Rpc__ObjcClass *) * count); + CHECK(resp_class_list.classes != NULL); - TRACE("reporting class list: %d", count); - CHECK(sendall(sockfd, (char *)&count, sizeof(count))); - - if (count > 0 ) { - classes = malloc(sizeof(Class) * count); - CHECK(classes != NULL); - count = objc_getClassList(classes, count); + class_list = malloc(sizeof(Class) * count); + CHECK(class_list != NULL); - for (int i=0; iaddress = (uint64_t) class_list[i]; + resp_class_list.classes[i]->name = strdup(name); + } + resp_class_list.n_classes = count; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_class_list)); + ret = RPC_SUCCESS; error: - if (classes) { - free(classes); + if (resp_class_list.classes) { + for (int i = 0; i < resp_class_list.n_classes; i++) { + SAFE_FREE(resp_class_list.classes[i]->name); + SAFE_FREE(resp_class_list.classes[i]); } - - TRACE("Failed to show class"); - return -1; + SAFE_FREE(resp_class_list.classes); + } + SAFE_FREE(class_list); + return ret; } diff --git a/src/rpcserver/darwin/darwin_object.m b/src/rpcserver/darwin/darwin_object.m index 84aff6fa..cfd4306d 100644 --- a/src/rpcserver/darwin/darwin_object.m +++ b/src/rpcserver/darwin/darwin_object.m @@ -1,53 +1,40 @@ -#include "darwin.h" #include "../common.h" +#include "darwin.h" +#include #include #include -#include -static NSDictionary* getObjectData(id objcObject) -{ - Class objcClass = [objcObject class]; +static NSDictionary *getObjectData(id objcObject) { + Class objcClass = [objcObject class]; - NSDictionary *objectData = @{ - @"protocols": [NSMutableArray new], - @"ivars": [NSMutableArray new], - @"properties": [NSMutableArray new], - @"methods": [NSMutableArray new], - @"class_name": [NSString stringWithCString:class_getName(objcClass) encoding:NSUTF8StringEncoding], - @"class_address": [NSNumber numberWithUnsignedLongLong:(uintptr_t)objcClass], - @"class_super": [NSNumber numberWithLong:(uintptr_t)class_getSuperclass(objcClass)], - }; + NSDictionary *objectData = @{ + @"protocols": [NSMutableArray new], + @"ivars": [NSMutableArray new], + @"properties": [NSMutableArray new], + @"methods": [NSMutableArray new], + @"class_name": [NSString stringWithCString:class_getName(objcClass) encoding:NSUTF8StringEncoding], + @"class_address": [NSNumber numberWithUnsignedLongLong:(uintptr_t) objcClass], + @"class_super": [NSNumber numberWithLong:(uintptr_t) class_getSuperclass(objcClass)], + }; - addProtocolsToDictionary(objcClass, objectData); - addIvarsToDictionary(objcClass, objectData, objcObject); - addPropertiesToDictionary(objcClass, objectData); - addMethodsToDictionary(objcClass, objectData); - return objectData; + addProtocolsToDictionary(objcClass, objectData); + addIvarsToDictionary(objcClass, objectData, objcObject); + addPropertiesToDictionary(objcClass, objectData); + addMethodsToDictionary(objcClass, objectData); + return objectData; } -static NSString* getObjectStr(id object) -{ - NSDictionary *objectData = getObjectData(object); - return getDictionaryJsonString(objectData); +static NSString *getObjectStr(id object) { + NSDictionary *objectData = getObjectData(object); + return getDictionaryJsonString(objectData); } -bool handle_showobject(int sockfd) -{ - TRACE("Entered showobject"); - cmd_showobject_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - TRACE("Calling objc_show_object with %p", cmd.address); - NSString *response_str = getObjectStr((id)cmd.address); +bool handle_showobject(int sockfd, Rpc__CmdShowObject *cmd) { + TRACE("Calling objc_show_object with %p", cmd->address); + Rpc__ResponseShowObject resp_show_object = RPC__RESPONSE_SHOW_OBJECT__INIT; + NSString *response_str = getObjectStr((id) cmd->address); - TRACE("Sending response"); - size_t response_len = [response_str length]; - const char *response_cstr = [response_str UTF8String]; - CHECK(sendall(sockfd, (char *)&response_len, sizeof(response_len))); - CHECK(sendall(sockfd, response_cstr, response_len)); - TRACE("Sent response"); - return true; + resp_show_object.description = (char *) [response_str UTF8String]; -error: - TRACE("Failed to show object"); - return false; + return send_response(sockfd, (ProtobufCMessage *) &resp_show_object); } diff --git a/src/rpcserver/darwin/darwin_utils.m b/src/rpcserver/darwin/darwin_utils.m index 0037a6ff..7dc4feb5 100644 --- a/src/rpcserver/darwin/darwin_utils.m +++ b/src/rpcserver/darwin/darwin_utils.m @@ -1,171 +1,163 @@ +#include #include #include -#include -void addProtocolsToDictionary(Class objcClass, NSDictionary *outDictionary) -{ - uint outCount = 0; - id *protocols = class_copyProtocolList(objcClass, &outCount); +void addProtocolsToDictionary(Class objcClass, NSDictionary *outDictionary) { + uint outCount = 0; + id *protocols = class_copyProtocolList(objcClass, &outCount); - for (uint i = 0; i < outCount; ++i) { - [outDictionary[@"protocols"] addObject: [NSString stringWithCString:protocol_getName(protocols[i]) encoding:NSUTF8StringEncoding]]; - } - if (protocols) { - free(protocols); - } + for (uint i = 0; i < outCount; ++i) { + [outDictionary[@"protocols"] addObject:[NSString stringWithCString:protocol_getName(protocols[i]) encoding:NSUTF8StringEncoding]]; + } + if (protocols) { + free(protocols); + } } -static void addToIvars(Class objcClass, NSDictionary *outDictionary, id objcObject, Ivar ivar) -{ - NSMutableDictionary *currIvarObject = [NSMutableDictionary new]; - NSString *ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; - [currIvarObject setObject:ivarName forKey:@"name"]; - NSString *ivarType = [NSString stringWithCString:ivar_getTypeEncoding(ivar) encoding:NSUTF8StringEncoding]; - [currIvarObject setObject:ivarType forKey:@"type"]; - NSNumber *ivarOffset = [NSNumber numberWithInt:ivar_getOffset(ivar)]; - [currIvarObject setObject:ivarOffset forKey:@"offset"]; +static void addToIvars(Class objcClass, NSDictionary *outDictionary, id objcObject, Ivar ivar) { + NSMutableDictionary *currIvarObject = [NSMutableDictionary new]; + NSString *ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; + [currIvarObject setObject:ivarName forKey:@"name"]; + NSString *ivarType = [NSString stringWithCString:ivar_getTypeEncoding(ivar) encoding:NSUTF8StringEncoding]; + [currIvarObject setObject:ivarType forKey:@"type"]; + NSNumber *ivarOffset = [NSNumber numberWithInt:ivar_getOffset(ivar)]; + [currIvarObject setObject:ivarOffset forKey:@"offset"]; - if (objcObject) { - id value = [NSNumber numberWithUnsignedLongLong:(uintptr_t)object_getIvar(objcObject, ivar)]; - [currIvarObject setObject:value forKey:@"value"]; - } - [outDictionary[@"ivars"] addObject:currIvarObject]; + if (objcObject) { + id value = [NSNumber numberWithUnsignedLongLong:(uintptr_t) object_getIvar(objcObject, ivar)]; + [currIvarObject setObject:value forKey:@"value"]; + } + [outDictionary[@"ivars"] addObject:currIvarObject]; } -void addIvarsToDictionary(Class objcClass, NSDictionary *outDictionary, id objcObject) -{ - uint outCount = 0; - Ivar *ivars = class_copyIvarList(objcClass, &outCount); - for (uint i = 0; i < outCount; ++i) { - addToIvars(objcClass, outDictionary, objcObject, ivars[i]); +void addIvarsToDictionary(Class objcClass, NSDictionary *outDictionary, id objcObject) { + uint outCount = 0; + Ivar *ivars = class_copyIvarList(objcClass, &outCount); + for (uint i = 0; i < outCount; ++i) { + addToIvars(objcClass, outDictionary, objcObject, ivars[i]); + } + if (ivars) { + free(ivars); + } + + for (Class superClass = class_getSuperclass(objcClass); superClass; superClass = class_getSuperclass(superClass)) { + ivars = class_copyIvarList(superClass, &outCount); + for (size_t i = 0; i < outCount; ++i) { + addToIvars(objcClass, outDictionary, objcObject, ivars[i]); } if (ivars) { - free(ivars); + free(ivars); } + } +} - for (Class superClass = class_getSuperclass(objcClass); superClass; superClass = class_getSuperclass(superClass)) { - ivars = class_copyIvarList(superClass, &outCount); - for (size_t i = 0; i < outCount; ++i) { - addToIvars(objcClass, outDictionary, objcObject, ivars[i]); - } - if (ivars) { - free(ivars); - } +void addPropertiesToDictionary(Class objcClass, NSDictionary *outDictionary) { + uint outCount = 0; + NSMutableArray *fetchedProperties = [NSMutableArray new]; + objc_property_t *properties = class_copyPropertyList(objcClass, &outCount); + NSString *propertyName; + for (uint i = 0; i < outCount; ++i) { + propertyName = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]; + if ([fetchedProperties containsObject:propertyName]) { + continue; + } else { + [fetchedProperties addObject:propertyName]; } -} + [outDictionary[@"properties"] addObject:@{ + @"name": propertyName, + @"attributes": [NSString stringWithCString:property_getAttributes(properties[i]) encoding:NSUTF8StringEncoding], + }]; + } + if (properties) { + free(properties); + } -void addPropertiesToDictionary(Class objcClass, NSDictionary *outDictionary) -{ - uint outCount = 0; - NSMutableArray *fetchedProperties = [NSMutableArray new]; - objc_property_t *properties = class_copyPropertyList(objcClass, &outCount); - NSString *propertyName; + for (Class superClass = class_getSuperclass(objcClass); superClass; superClass = class_getSuperclass(superClass)) { + properties = class_copyPropertyList(superClass, &outCount); for (uint i = 0; i < outCount; ++i) { - propertyName = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]; - if ([fetchedProperties containsObject:propertyName]) { - continue; - } - else { - [fetchedProperties addObject:propertyName]; - } - [outDictionary[@"properties"] addObject:@{ - @"name": propertyName, - @"attributes": [NSString stringWithCString:property_getAttributes(properties[i]) encoding:NSUTF8StringEncoding], - }]; + propertyName = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]; + if ([fetchedProperties containsObject:propertyName]) { + continue; + } else { + [fetchedProperties addObject:propertyName]; + } + [outDictionary[@"properties"] addObject:@{ + @"name": propertyName, + @"attributes": [NSString stringWithCString:property_getAttributes(properties[i]) encoding:NSUTF8StringEncoding], + }]; } if (properties) { - free(properties); - } - - for (Class superClass = class_getSuperclass(objcClass); superClass; superClass = class_getSuperclass(superClass)) { - properties = class_copyPropertyList(superClass, &outCount); - for (uint i = 0; i < outCount; ++i) { - propertyName = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]; - if ([fetchedProperties containsObject:propertyName]) { - continue; - } - else { - [fetchedProperties addObject:propertyName]; - } - [outDictionary[@"properties"] addObject:@{ - @"name": propertyName, - @"attributes": [NSString stringWithCString:property_getAttributes(properties[i]) encoding:NSUTF8StringEncoding], - }]; - } - if (properties) { - free(properties); - } + free(properties); } + } } -void addMethodsToDictionary(Class objcClass, NSDictionary *outDictionary) -{ - uint outCount = 0; - Method *methods = class_copyMethodList(object_getClass(objcClass), &outCount); - uint argsCount = 0; - NSMutableArray *argsTypes; - char *methodArgumentsTypes; - char *methodReturnType; - for (uint i = 0; i < outCount; ++i) { - argsCount = method_getNumberOfArguments(methods[i]); - argsTypes = [NSMutableArray new]; - for (uint j = 0; j < argsCount; ++j) { - methodArgumentsTypes = method_copyArgumentType(methods[i], j); - [argsTypes addObject: [NSString stringWithCString:methodArgumentsTypes encoding:NSUTF8StringEncoding]]; - if (methodArgumentsTypes) { - free(methodArgumentsTypes); - } - } - methodReturnType = method_copyReturnType(methods[i]); - [outDictionary[@"methods"] addObject:@{ - @"name": [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding], - @"address": [NSNumber numberWithLong:(uintptr_t)methods[i]], - @"imp": [NSNumber numberWithLong:(uintptr_t)method_getImplementation(methods[i])], - @"is_class": @YES, - @"type": [NSString stringWithCString:method_getTypeEncoding(methods[i]) encoding:NSUTF8StringEncoding], - @"return_type": [NSString stringWithCString:methodReturnType encoding:NSUTF8StringEncoding], - @"args_types": argsTypes, - }]; - if (methodReturnType) { - free(methodReturnType); - } +void addMethodsToDictionary(Class objcClass, NSDictionary *outDictionary) { + uint outCount = 0; + Method *methods = class_copyMethodList(object_getClass(objcClass), &outCount); + uint argsCount = 0; + NSMutableArray *argsTypes; + char *methodArgumentsTypes; + char *methodReturnType; + for (uint i = 0; i < outCount; ++i) { + argsCount = method_getNumberOfArguments(methods[i]); + argsTypes = [NSMutableArray new]; + for (uint j = 0; j < argsCount; ++j) { + methodArgumentsTypes = method_copyArgumentType(methods[i], j); + [argsTypes addObject:[NSString stringWithCString:methodArgumentsTypes encoding:NSUTF8StringEncoding]]; + if (methodArgumentsTypes) { + free(methodArgumentsTypes); + } } - if (methods) { - free(methods); + methodReturnType = method_copyReturnType(methods[i]); + [outDictionary[@"methods"] addObject:@{ + @"name": [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding], + @"address": [NSNumber numberWithLong:(uintptr_t) methods[i]], + @"imp": [NSNumber numberWithLong:(uintptr_t) method_getImplementation(methods[i])], + @"is_class": @YES, + @"type": [NSString stringWithCString:method_getTypeEncoding(methods[i]) encoding:NSUTF8StringEncoding], + @"return_type": [NSString stringWithCString:methodReturnType encoding:NSUTF8StringEncoding], + @"args_types": argsTypes, + }]; + if (methodReturnType) { + free(methodReturnType); } + } + if (methods) { + free(methods); + } - methods = class_copyMethodList(objcClass, &outCount); - for (uint i = 0; i < outCount; ++i) { - argsCount = method_getNumberOfArguments(methods[i]); - argsTypes = [NSMutableArray new]; - for (uint j = 0; j < argsCount; ++j) { - methodArgumentsTypes = method_copyArgumentType(methods[i], j); - [argsTypes addObject: [NSString stringWithCString:methodArgumentsTypes encoding:NSUTF8StringEncoding]]; - if (methodArgumentsTypes) { - free(methodArgumentsTypes); - } - } - methodReturnType = method_copyReturnType(methods[i]); - [outDictionary[@"methods"] addObject:@{ - @"name": [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding], - @"address": [NSNumber numberWithLong:(uintptr_t)methods[i]], - @"imp": [NSNumber numberWithLong:(uintptr_t)method_getImplementation(methods[i])], - @"is_class": @NO, - @"type": [NSString stringWithCString:method_getTypeEncoding(methods[i]) encoding:NSUTF8StringEncoding], - @"return_type": [NSString stringWithCString:methodReturnType encoding:NSUTF8StringEncoding], - @"args_types": argsTypes, - }]; - if (methodReturnType) { - free(methodReturnType); - } + methods = class_copyMethodList(objcClass, &outCount); + for (uint i = 0; i < outCount; ++i) { + argsCount = method_getNumberOfArguments(methods[i]); + argsTypes = [NSMutableArray new]; + for (uint j = 0; j < argsCount; ++j) { + methodArgumentsTypes = method_copyArgumentType(methods[i], j); + [argsTypes addObject:[NSString stringWithCString:methodArgumentsTypes encoding:NSUTF8StringEncoding]]; + if (methodArgumentsTypes) { + free(methodArgumentsTypes); + } } - if (methods) { - free(methods); + methodReturnType = method_copyReturnType(methods[i]); + [outDictionary[@"methods"] addObject:@{ + @"name": [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding], + @"address": [NSNumber numberWithLong:(uintptr_t) methods[i]], + @"imp": [NSNumber numberWithLong:(uintptr_t) method_getImplementation(methods[i])], + @"is_class": @NO, + @"type": [NSString stringWithCString:method_getTypeEncoding(methods[i]) encoding:NSUTF8StringEncoding], + @"return_type": [NSString stringWithCString:methodReturnType encoding:NSUTF8StringEncoding], + @"args_types": argsTypes, + }]; + if (methodReturnType) { + free(methodReturnType); } + } + if (methods) { + free(methods); + } } -NSString* getDictionaryJsonString(NSDictionary *classDescription) -{ - NSData *data = [NSJSONSerialization dataWithJSONObject:classDescription options:0 error:nil]; - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +NSString *getDictionaryJsonString(NSDictionary *classDescription) { + NSData *data = [NSJSONSerialization dataWithJSONObject:classDescription options:0 error:nil]; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } diff --git a/src/rpcserver/protocol.h b/src/rpcserver/protocol.h deleted file mode 100644 index c1cd50b1..00000000 --- a/src/rpcserver/protocol.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef __PROTOCOL_H_ -#define __PROTOCOL_H_ -#include - -#include "common.h" - -#define SERVER_MAGIC_VERSION (0x88888807) -#define HANDSHAKE_SYSNAME_LEN (256) -#define HANDSHAKE_MACHINE_LEN (256) -#define MAX_PATH_LEN (1024) - -typedef enum -{ - CMD_EXEC = 0, - CMD_DLOPEN = 1, - CMD_DLCLOSE = 2, - CMD_DLSYM = 3, - CMD_CALL = 4, - CMD_PEEK = 5, - CMD_POKE = 6, - CMD_REPLY_ERROR = 7, - CMD_REPLY_PEEK = 8, - CMD_GET_DUMMY_BLOCK = 9, - CMD_CLOSE = 10, - CMD_REPLY_POKE = 11, - CMD_LISTDIR = 12, - CMD_SHOWOBJECT = 13, - CMD_SHOWCLASS = 14, - CMD_GET_CLASS_LIST = 15 -} cmd_type_t; - -typedef enum -{ - CMD_EXEC_CHUNK_TYPE_STDOUT = 0, - CMD_EXEC_CHUNK_TYPE_EXITCODE = 1, -} cmd_exec_chunk_type_t; - -typedef enum -{ - ARCH_UNKNOWN = 0, - ARCH_ARM64 = 1, -} arch_t; - -typedef struct -{ - u32 magic; - u32 arch; // arch_t - char sysname[HANDSHAKE_SYSNAME_LEN]; - char machine[HANDSHAKE_MACHINE_LEN]; -} protocol_handshake_t; - -typedef struct -{ - u32 type; - u32 size; -} cmd_exec_chunk_t; - -typedef struct -{ - char filename[MAX_PATH_LEN]; - u32 mode; -} cmd_dlopen_t; - -typedef struct -{ - u64 lib; -} cmd_dlclose_t; - -typedef struct -{ - u64 lib; - char symbol_name[MAX_PATH_LEN]; -} cmd_dlsym_t; - -typedef struct -{ - u64 type; - u64 value; -} argument_t; - -typedef struct -{ - u64 address; - u64 va_list_index; - u64 argc; - argument_t argv[0]; -} cmd_call_t; - -typedef struct -{ - u32 magic; - u32 cmd_type; -} protocol_message_t; - -typedef struct -{ - u64 x[8]; - u64 d[8]; -} return_registers_arm_t; - -typedef struct -{ - union - { - return_registers_arm_t arm_registers; - u64 return_value; - } return_values; -} call_response_t; - -typedef struct -{ - u64 address; - u64 size; -} cmd_peek_t; - -typedef struct -{ - u64 address; - u64 size; - u8 data[0]; -} cmd_poke_t; - -typedef struct -{ - char filename[MAX_PATH_LEN]; -} cmd_listdir_t; - -typedef struct -{ - u64 errno1; - u64 st_dev; /* [XSI] ID of device containing file */ - u64 st_mode; /* [XSI] Mode of file (see below) */ - u64 st_nlink; /* [XSI] Number of hard links */ - u64 st_ino; /* [XSI] File serial number */ - u64 st_uid; /* [XSI] User ID of the file */ - u64 st_gid; /* [XSI] Group ID of the file */ - u64 st_rdev; /* [XSI] Device ID */ - u64 st_size; /* [XSI] file size, in bytes */ - u64 st_blocks; /* [XSI] blocks allocated for file */ - u64 st_blksize; /* [XSI] optimal blocksize for I/O */ - u64 st_atime1; /* time of last access */ - u64 st_mtime1; /* time of last data modification */ - u64 st_ctime1; /* time of last file status change */ -} listdir_entry_stat_t; - -typedef struct -{ - u64 magic; - u64 type; - u64 namelen; - - listdir_entry_stat_t lstat; - listdir_entry_stat_t stat; - - char name[0]; -} listdir_entry_t; - -typedef struct -{ - uint64_t address; -} cmd_showobject_t; - -typedef struct -{ - uint64_t address; -} cmd_showclass_t; - -#endif // __PROTOCOL_H_ \ No newline at end of file diff --git a/src/rpcserver/rpcserver.c b/src/rpcserver/rpcserver.c index ee3a3c04..c93ef49d 100644 --- a/src/rpcserver/rpcserver.c +++ b/src/rpcserver/rpcserver.c @@ -1,50 +1,55 @@ #ifndef __APPLE__ #define _XOPEN_SOURCE (600) #define _GNU_SOURCE (1) -#endif // __APPLE__ -#include +#endif// __APPLE__ + +#include "common.h" +#include "protos/rpc.pb-c.h" #include +#include +#include #include #include #include #include +#include #include +#include +#include +#include #include +#include #include +#include #include #include +#include #include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -int handle_showobject(int sockfd); -int handle_showclass(int sockfd); -int handle_get_class_list(int sockfd); +bool handle_showobject(int sockfd, Rpc__CmdShowObject *cmd); + +bool handle_showclass(int sockfd, Rpc__CmdShowClass *cmd); + +bool handle_get_class_list(int sockfd, Rpc__CmdGetClassList *cmd); #ifdef __APPLE__ + #include #include -#else -int handle_showobject(int sockfd) { return 0; } -int handle_showclass(int sockfd) { return 0; } -int handle_get_class_list(int sockfd) { return 0; } -#endif // __APPLE__ -#include "common.h" -#include "protocol.h" +#else +bool handle_showobject(int sockfd, Rpc__CmdShowObject *cmd) { return 0; } +bool handle_showclass(int sockfd, Rpc__CmdShowClass *cmd) { return 0; } +bool handle_get_class_list(int sockfd, Rpc__CmdGetClassList *cmd) { return 0; } +#endif// __APPLE__ #define DEFAULT_PORT ("5910") -#define DEFAULT_SHELL ("/bin/sh") -#define USAGE ("Usage: %s [-p port] [-o (stdout|syslog|file:filename)] \n\ +#define USAGE \ + ("Usage: %s [-p port] [-o (stdout|syslog|file:filename)] \n\ -h show this help message \n\ -o output. can be all of the following: stdout, syslog and file:filename. can be passed multiple times \n\ \n\ @@ -57,23 +62,34 @@ Example usage: \n\ #define BUFFERSIZE (64 * 1024) #define INVALID_PID (0xffffffff) #define WORKER_CLIENT_SOCKET_FD (3) +#define CLOBBERD_LIST \ + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x19", "x20", "x21", \ + "x22", "x23", "x24", "x25", "x26" +#define SERVER_MAGIC_VERSION (0x88888807) extern char **environ; -void *get_in_addr(struct sockaddr *sa) // get sockaddr, IPv4 or IPv6: +typedef struct { + int sockfd; + pid_t pid; +} thread_notify_client_spawn_error_t; + +void *get_in_addr(struct sockaddr *sa)// get sockaddr, IPv4 or IPv6: { - return sa->sa_family == AF_INET ? (void *)&(((struct sockaddr_in *)sa)->sin_addr) : (void *)&(((struct sockaddr_in6 *)sa)->sin6_addr); + return sa->sa_family == AF_INET + ? (void *) &(((struct sockaddr_in *) sa)->sin_addr) + : (void *) &(((struct sockaddr_in6 *) sa)->sin6_addr); } -bool internal_spawn(bool background, char *const *argv, char *const *envp, pid_t *pid, int *master_fd) -{ - bool success = false; +bool internal_spawn(bool background, char **argv, char **envp, pid_t *pid, + int *master_fd) { + bool ret = RPC_FAILURE; int slave_fd = -1; *master_fd = -1; *pid = INVALID_PID; - // call setsid() on child so Ctrl-C and all other control characters are set in a different terminal - // and process group + // call setsid() on child so Ctrl-C and all other control characters are set + // in a different terminal and process group posix_spawnattr_t attr; CHECK(0 == posix_spawnattr_init(&attr)); CHECK(0 == posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSID)); @@ -81,11 +97,10 @@ bool internal_spawn(bool background, char *const *argv, char *const *envp, pid_t posix_spawn_file_actions_t actions; CHECK(0 == posix_spawn_file_actions_init(&actions)); - if (!background) - { - // We need a new pseudoterminal to avoid bufferring problems. The 'atos' tool - // in particular detects when it's talking to a pipe and forgets to flush the - // output stream after sending a response. + if (!background) { + // We need a new pseudoterminal to avoid bufferring problems. The 'atos' + // tool in particular detects when it's talking to a pipe and forgets to + // flush the output stream after sending a response. *master_fd = posix_openpt(O_RDWR); CHECK(-1 != *master_fd); CHECK(0 == grantpt(*master_fd)); @@ -104,9 +119,7 @@ bool internal_spawn(bool background, char *const *argv, char *const *envp, pid_t CHECK(0 == posix_spawn_file_actions_adddup2(&actions, slave_fd, STDERR_FILENO)); CHECK(0 == posix_spawn_file_actions_addclose(&actions, slave_fd)); CHECK(0 == posix_spawn_file_actions_addclose(&actions, *master_fd)); - } - else - { + } else { CHECK(0 == posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0)); CHECK(0 == posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)); CHECK(0 == posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, "/dev/null", O_WRONLY, 0)); @@ -118,33 +131,28 @@ bool internal_spawn(bool background, char *const *argv, char *const *envp, pid_t posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); - success = true; + ret = RPC_SUCCESS; error: - if (slave_fd != -1) - { + if (slave_fd != -1) { close(slave_fd); } - if (!success) - { - if (*master_fd != -1) - { + if (!ret) { + if (*master_fd != -1) { close(*master_fd); } *pid = INVALID_PID; } - return success; + return ret; } -bool spawn_worker_server(int client_socket, const char *argv[], int argc) -{ - bool success = false; +bool spawn_worker_server(int client_socket, const char *argv[], int argc) { + bool ret = RPC_FAILURE; // append -w to original argv int new_argc = argc + 1; const char **new_argv = malloc((new_argc + 1) * sizeof(char *)); - for (int i = 0; i < argc; ++i) - { + for (int i = 0; i < argc; ++i) { new_argv[i] = argv[i]; } new_argv[new_argc - 1] = "-w"; @@ -158,112 +166,55 @@ bool spawn_worker_server(int client_socket, const char *argv[], int argc) CHECK(0 == posix_spawn_file_actions_adddup2(&actions, STDERR_FILENO, STDERR_FILENO)); CHECK(0 == posix_spawn_file_actions_adddup2(&actions, client_socket, WORKER_CLIENT_SOCKET_FD)); - CHECK(0 == posix_spawnp(&pid, new_argv[0], &actions, NULL, (char *const *)new_argv, environ)); + CHECK(0 == posix_spawnp(&pid, new_argv[0], &actions, NULL, (char *const *) new_argv, environ)); CHECK(pid != INVALID_PID); TRACE("Spawned Worker Process: %d", pid); - success = true; + ret = RPC_SUCCESS; error: posix_spawn_file_actions_destroy(&actions); free(new_argv); close(client_socket); - return success; + return ret; } -bool send_reply(int sockfd, cmd_type_t type) -{ - protocol_message_t protocol_message = {.magic = MAGIC, .cmd_type = type}; - CHECK(sendall(sockfd, (char *)&protocol_message, sizeof(protocol_message))); - return true; -error: - return false; -} - -typedef struct -{ - int sockfd; - pid_t pid; -} thread_notify_client_spawn_error_t; - -void thread_waitpid(pid_t pid) -{ +void thread_waitpid(pid_t pid) { TRACE("enter"); s32 err; waitpid(pid, &err, 0); } -bool handle_exec(int sockfd) -{ +bool handle_exec(int sockfd, Rpc__CmdExec *cmd) { + bool ret = RPC_FAILURE; + Rpc__ResponseCmdExec resp_exec = RPC__RESPONSE_CMD_EXEC__INIT; + u8 byte; pthread_t thread = 0; thread_notify_client_spawn_error_t *thread_params = NULL; pid_t pid = INVALID_PID; int master = -1; - int success = false; char **argv = NULL; char **envp = NULL; u32 argc; u32 envc; u8 background; - CHECK(recvall(sockfd, (char *)&background, sizeof(background))); - - CHECK(recvall(sockfd, (char *)&argc, sizeof(argc))); - CHECK(argc > 0); - - // +1 for additional NULL at end of list - size_t argv_size = (argc + 1) * sizeof(char *); - argv = (char **)malloc(argv_size * sizeof(char *)); - memset(argv, 0, argv_size * sizeof(char *)); - - for (u32 i = 0; i < argc; ++i) - { - u32 len; - CHECK(recvall(sockfd, (char *)&len, sizeof(len))); - - // +1 for additional \0 at end of each string - size_t str_size = (len + 1) * sizeof(char); - argv[i] = malloc(str_size * sizeof(char)); - CHECK(argv[i] != NULL); - - CHECK(recvall(sockfd, argv[i], len * sizeof(char))); - argv[i][len] = '\0'; - } - - CHECK(recvall(sockfd, (char *)&envc, sizeof(envc))); + CHECK(cmd->n_argv > 0); + COPY_ARR_WITH_NULL(argv, cmd->argv, cmd->n_argv); + COPY_ARR_WITH_NULL(envp, cmd->envp, cmd->n_envp); - // +1 for additional NULL at end of list - size_t envp_size = (envc + 1) * sizeof(char *); - envp = (char **)malloc(envp_size * sizeof(char *)); - memset(envp, 0, envp_size * sizeof(char *)); + CHECK(internal_spawn(cmd->background, argv, cmd->n_envp ? envp : environ, &pid, &master)); - for (u32 i = 0; i < envc; ++i) - { - u32 len; - CHECK(recvall(sockfd, (char *)&len, sizeof(len))); + resp_exec.pid = pid; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_exec)); - // +1 for additional \0 at end of each string - size_t str_size = (len + 1) * sizeof(char); - envp[i] = malloc(str_size * sizeof(char)); - CHECK(envp[i] != NULL); - - CHECK(recvall(sockfd, envp[i], len * sizeof(char))); - envp[i][len] = '\0'; - } - - CHECK(internal_spawn(background, (char *const *)argv, envc ? (char *const *)envp : environ, &pid, &master)); - CHECK(sendall(sockfd, (char *)&pid, sizeof(u32))); - - if (background) - { - CHECK(0 == pthread_create(&thread, NULL, (void *(*)(void *))thread_waitpid, (void *)(intptr_t)pid)); - } - else - { + if (cmd->background) { + CHECK(0 == pthread_create(&thread, NULL, (void *(*) (void *) ) thread_waitpid, (void *) (intptr_t) pid)); + } else { + Rpc__ResponseCmdExecChunk resp_exec_chunk = RPC__RESPONSE_CMD_EXEC_CHUNK__INIT; // make sure we have the process fd for its stdout and stderr CHECK(master >= 0); - fd_set readfds; char buf[BUFFERSIZE]; int maxfd = master > sockfd ? master : sockfd; @@ -271,609 +222,351 @@ bool handle_exec(int sockfd) fd_set errfds; - while (true) - { + while (true) { FD_ZERO(&readfds); FD_SET(master, &readfds); FD_SET(sockfd, &readfds); CHECK(select(maxfd + 1, &readfds, NULL, &errfds, NULL) != -1); - if (FD_ISSET(master, &readfds)) - { + if (FD_ISSET(master, &readfds)) { nbytes = read(master, buf, BUFFERSIZE); - if (nbytes < 1) - { + if (nbytes < 1) { TRACE("read master failed. break"); break; } TRACE("master->sock"); - - cmd_exec_chunk_t chunk; - chunk.type = CMD_EXEC_CHUNK_TYPE_STDOUT; - chunk.size = nbytes; - - CHECK(sendall(sockfd, (char *)&chunk, sizeof(chunk))); - CHECK(sendall(sockfd, buf, chunk.size)); + resp_exec_chunk.buffer.len = nbytes; + resp_exec_chunk.buffer.data = (uint8_t *) buf; + resp_exec_chunk.type_case = RPC__RESPONSE_CMD_EXEC_CHUNK__TYPE_BUFFER; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_exec_chunk)); } - - if (FD_ISSET(sockfd, &readfds)) - { + if (FD_ISSET(sockfd, &readfds)) { nbytes = recv(sockfd, buf, BUFFERSIZE, 0); - if (nbytes < 1) - { + if (nbytes < 1) { break; } - TRACE("sock->master"); - CHECK(writeall(master, buf, nbytes)); } } - - TRACE("wait for process to finish"); s32 error; + TRACE("wait for process to finish"); +#ifndef SINGLE_THREAD CHECK(pid == waitpid(pid, &error, 0)); - - cmd_exec_chunk_t chunk; - chunk.type = CMD_EXEC_CHUNK_TYPE_EXITCODE; - chunk.size = sizeof(error); - - CHECK(sendall(sockfd, (const char *)&chunk, sizeof(chunk))); - CHECK(sendall(sockfd, (const char *)&error, sizeof(error))); +#endif + resp_exec_chunk.type_case = RPC__RESPONSE_CMD_EXEC_CHUNK__TYPE_EXIT_CODE; + resp_exec_chunk.exit_code = error; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_exec_chunk)); } - success = true; + ret = RPC_SUCCESS; error: - if (thread_params) - { - free(thread_params); - } + SAFE_FREE(argv); + SAFE_FREE(envp); + SAFE_FREE(thread_params); - if (INVALID_PID == pid) - { + if (INVALID_PID == pid) { TRACE("invalid pid"); - // failed to create process somewhere in the prolog, at least notify - sendall(sockfd, (char *)&pid, sizeof(u32)); - } - - if (argv) - { - for (u32 i = 0; i < argc; ++i) - { - if (argv[i]) - { - free(argv[i]); - } - } - free(argv); - } - - if (envp) - { - for (u32 i = 0; i < envc; ++i) - { - if (envp[i]) - { - free(envp[i]); - } - } - free(envp); + Rpc__ResponseError error = RPC__RESPONSE_ERROR__INIT; + error.func_name = (char *) __func__; + CHECK(send_response(sockfd, (ProtobufCMessage *) &error)); } - if (-1 != master) - { + if (-1 != master) { TRACE("close master: %d", master); - if (0 != close(master)) - { + if (0 != close(master)) { perror("close"); } } - - return success; + return ret; } -bool handle_dlopen(int sockfd) -{ - int result = false; - cmd_dlopen_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - - u64 err = (u64)dlopen(cmd.filename, cmd.mode); - CHECK(sendall(sockfd, (char *)&err, sizeof(err))); - - result = true; - -error: - return result; +bool handle_dlopen(int sockfd, Rpc__CmdDlopen *cmd) { + Rpc__ResponseDlopen resp_dlopen = RPC__RESPONSE_DLOPEN__INIT; + resp_dlopen.handle = (uint64_t) dlopen(cmd->filename, cmd->mode); + return send_response(sockfd, (ProtobufCMessage *) &resp_dlopen); } -bool handle_dlclose(int sockfd) -{ - int result = false; - cmd_dlclose_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - - u64 err = (u64)dlclose((void *)cmd.lib); - CHECK(sendall(sockfd, (char *)&err, sizeof(err))); - - result = true; - -error: - return result; +bool handle_dlclose(int sockfd, Rpc__CmdDlclose *cmd) { + Rpc__ResponseDlclose resp_dlclose = RPC__RESPONSE_DLCLOSE__INIT; + resp_dlclose.res = (uint64_t) dlclose((void *) cmd->handle); + return send_response(sockfd, (ProtobufCMessage *) &resp_dlclose); } -bool handle_dlsym(int sockfd) -{ - int result = false; - cmd_dlsym_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - - u64 ptr = (u64)dlsym((void *)cmd.lib, cmd.symbol_name); - CHECK(sendall(sockfd, (char *)&ptr, sizeof(ptr))); - - TRACE("%s = %p", cmd.symbol_name, ptr); - - result = true; - -error: - return result; +bool handle_dlsym(int sockfd, Rpc__CmdDlsym *cmd) { + Rpc__ResponseDlsym resp_dlsym = RPC__RESPONSE_DLSYM__INIT; + resp_dlsym.ptr = (uint64_t) dlsym((void *) cmd->handle, cmd->symbol_name); + TRACE("%s = %p", cmd->symbol_name, resp_dlsym.ptr); + return send_response(sockfd, (ProtobufCMessage *) &resp_dlsym); } #ifdef __ARM_ARCH_ISA_A64 -bool call_function(int sockfd, intptr_t address, size_t va_list_index, size_t argc, argument_t **p_argv); -__asm__( - "_call_function:\n" - // the "stack_arguments" must be the first local ([sp, 0]) to serialize stack arguments - // the 0x100 is enough to store 0x100/8 = 32 stack arguments which should be enough for - // every function - ".set stack_arguments, 0\n" - ".set result, stack_arguments+0x100\n" - ".set address, result+0x08\n" - ".set va_list_index, address+0x08\n" - ".set argc, va_list_index+0x08\n" - ".set argv, argc+0x08\n" - ".set sockfd, argv+0x08\n" - - ".set register_x0, sockfd+0x08\n" - ".set register_x1, register_x0+0x08\n" - ".set register_x2, register_x1+0x08\n" - ".set register_x3, register_x2+0x08\n" - ".set register_x4, register_x3+0x08\n" - ".set register_x5, register_x4+0x08\n" - ".set register_x6, register_x5+0x08\n" - ".set register_x7, register_x6+0x08\n" - - ".set register_d0, register_x7+0x08\n" - ".set register_d1, register_d0+0x08\n" - ".set register_d2, register_d1+0x08\n" - ".set register_d3, register_d2+0x08\n" - ".set register_d4, register_d3+0x08\n" - ".set register_d5, register_d4+0x08\n" - ".set register_d6, register_d5+0x08\n" - ".set register_d7, register_d6+0x08\n" - - ".set register_end, register_d7+0x08\n" - - ".set size, register_end+0x00\n" - - // backup registers x19 -> x30 = 8*12 bytes = 0x60 bytes - // according to arm abi, the stack is structured as follows: - // ------------------------- - // | x30(lr) | 0x58 - // | x29(frame pointer) | 0x50 - // | register backup | 0x00 - // ------------------------- - "stp x28, x27, [sp, -0x60]!\n" - "stp x26, x25, [sp, 0x10]\n" - "stp x23, x24, [sp, 0x20]\n" - "stp x21, x22, [sp, 0x30]\n" - "stp x19, x20, [sp, 0x40]\n" - "stp x29, x30, [sp, 0x50]\n" - // current x29 should point to previous x29 - "add x29, sp, 0x50\n" - - // allocate stack locals - "sub sp, sp, size\n" - - // backup arguments to stack - "str x0, [sp, sockfd]\n" - "str x1, [sp, address]\n" - "str x2, [sp, va_list_index]\n" - "str x3, [sp, argc]\n" - "str x4, [sp, argv]\n" - - // result = 0 - "str xzr, [sp, result]\n" - - // x19 = argument = *argv - // x24 = argc - // x28 = va_list_index - // x21 = integer_offset = 0 - // x22 = double_offset = 0 - // x23 = current_arg_index = 0 - // x26 = stack_offset = 0 - "ldr x19, [sp, argv]\n" - "ldr x19, [x19]\n" - "ldr x24, [sp, argc]\n" - "ldr x28, [sp, va_list_index]\n" - "mov x21, 0\n" - "mov x22, 0\n" - "mov x23, 0\n" - "mov x26, 0\n" - - "1:\n" - // if (current_arg_index == argc) goto 3 - "cmp x23, x24\n" - "beq 3f\n" - - // x20 = argument.type - "ldr x20, [x19]\n" - - // argument++ - "add x19, x19, 8\n" - - // if (current_arg_index >= va_list_index) goto 7 - "cmp x23, x28\n" - "bge 7f\n" - - // if (argument.type == INTEGER) goto 2 - "cmp x20, 0\n" - "beq 2f\n" - - // else { - - // -- double argument - - // x20 = argument.value - "ldr x20, [x19]\n" - "add x19, x19, 8\n" - - // if (double_offset*8 >= MAX_DOUBLE_REG*8) goto 7 - "cmp x22, 8 * 8\n" - "bge 7f\n" - - // 6[double_offset]() - "adr x25, 6f\n" - "add x25, x25, x22\n" - "blr x25\n" - - // double_offset += 8 - "add x22, x22, 8\n" - - // current_arg_index += 1 - "add x23, x23, 1\n" - - // goto 1 - "b 1b\n" - - "2:\n" - // -- integer argument - - // x20 = argument.value - "ldr x20, [x19]\n" - - // argument++ - "add x19, x19, 8\n" - - // if (integer_offset*8 >= MAX_INT_REG*8) goto 7 - "cmp x21, 8 * 8\n" - "bge 7f\n" - - // 5[integer_offset]() - "adr x25, 5f\n" - "add x25, x25, x21\n" - "blr x25\n" - - // integer_offset += 8 - "add x21, x21, 8\n" - - // current_arg_index += 1 - "add x23, x23, 1\n" - - // goto 1 - "b 1b\n" - - "3:\n" - // err.integer, err.double = address(params) - "ldr x19, [sp, address]\n" - "blr x19\n" - - "str x0, [sp, register_x0]\n" - "str x1, [sp, register_x1]\n" - "str x2, [sp, register_x2]\n" - "str x3, [sp, register_x3]\n" - "str x4, [sp, register_x4]\n" - "str x5, [sp, register_x5]\n" - "str x6, [sp, register_x6]\n" - "str x7, [sp, register_x7]\n" - - "str d0, [sp, register_d0]\n" - "str d1, [sp, register_d1]\n" - "str d2, [sp, register_d2]\n" - "str d3, [sp, register_d3]\n" - "str d4, [sp, register_d4]\n" - "str d5, [sp, register_d5]\n" - "str d6, [sp, register_d6]\n" - "str d7, [sp, register_d7]\n" - - // if (!sendall(sockfd, &err, register_end - register_x0)) goto 4; - "ldr x0, [sp, sockfd]\n" - "add x1, sp, register_x0\n" - "mov x2, register_end - register_x0\n" - "bl _sendall\n" - "cmp x0, 0\n" - "beq 4f\n" - - // result = true - "mov x0, 1\n" - "str x0, [sp, result]\n" - - "4:\n" - // return result - "ldr x0, [sp, result]\n" - "add sp, sp, size\n" - - // restore backed up registers - "ldp x29, x30, [sp, 0x50]\n" - "ldp x19, x20, [sp, 0x40]\n" - "ldp x21, x22, [sp, 0x30]\n" - "ldp x23, x24, [sp, 0x20]\n" - "ldp x25, x26, [sp, 0x10]\n" - "ldp x27, x28, [sp], 0x60\n" - - "ret\n" - - "5:\n" - "mov x0, x20\n" - "ret\n" - "mov x1, x20\n" - "ret\n" - "mov x2, x20\n" - "ret\n" - "mov x3, x20\n" - "ret\n" - "mov x4, x20\n" - "ret\n" - "mov x5, x20\n" - "ret\n" - "mov x6, x20\n" - "ret\n" - "mov x7, x20\n" - "ret\n" - - "6:\n" - "fmov d0, x20\n" - "ret\n" - "fmov d1, x20\n" - "ret\n" - "fmov d2, x20\n" - "ret\n" - "fmov d3, x20\n" - "ret\n" - "fmov d4, x20\n" - "ret\n" - "fmov d5, x20\n" - "ret\n" - "fmov d6, x20\n" - "ret\n" - "fmov d7, x20\n" - "ret\n" - - "7:\n" - // -- stack argument - - // x20 = argument.value - "ldr x20, [x19]\n" - "add x19, x19, 8\n" - - // pack into stack (into stack_arguments+x26) - "str x20, [sp, x26]\n" - // current_arg_index += 1 - "add x23, x23, 1\n" - // stack_offset += 8 - "add x26, x26, 8\n" - - "b 1b\n"); + +volatile bool call_function(intptr_t address, size_t va_list_index, size_t argc, + Rpc__Argument **p_argv, Rpc__ResponseCall *resp) { + + arm_args_t args = {0}; + uint64_t regs_backup[GPR_COUNT] = {0}; + uint32_t idx_fp = 0, idx_gp = 0, idx_stack = 0, idx_argv = 0; + intptr_t *current_target = NULL, *current_arg = NULL; + for (idx_argv = 0; idx_argv < argc; idx_argv++) { + switch (p_argv[idx_argv]->type_case) { + case RPC__ARGUMENT__TYPE_V_STR: + case RPC__ARGUMENT__TYPE_V_BYTES: + case RPC__ARGUMENT__TYPE_V_INT: + // Assign target register if available, otherwise set to `NULL` + current_target = + (idx_gp < MAX_REGS_ARGS) ? (intptr_t *) &args.x[idx_gp++] : NULL; + break; + case RPC__ARGUMENT__TYPE_V_DOUBLE: + // Assign target register if available, otherwise set to `NULL` + current_target = + (idx_fp < MAX_REGS_ARGS) ? (intptr_t *) &args.d[idx_fp++] : NULL; + break; + default: + break; + } + // Use the stack if `va_list_index` or if the target register is not + // available + if (idx_argv >= va_list_index || !current_target) { + current_target = (intptr_t *) &args.stack[idx_stack++]; + } + // `v_int`, `v_str`, and `v_double` all point to the same place, so we use + // `v_int` for convenience. However, `v_bytes` requires access to + // `v_bytes.data`. + current_arg = (p_argv[idx_argv]->type_case == RPC__ARGUMENT__TYPE_V_BYTES + ? (intptr_t *) &p_argv[idx_argv]->v_bytes.data + : (intptr_t *) &p_argv[idx_argv]->v_int); + *current_target = *current_arg; + } + + __asm__ __volatile__( + "mov x19, %[address]\n" + "mov x20, %[args_registers]\n" + "mov x21, %[max_args]\n" + "mov x22, %[args_stack]\n" + "mov x23, %[regs_backup]\n" + "mov x24, %[result_registers]\n" + "mov x25, #0\n"// counter + "mov x26, #0\n"// temp stack current_arg + + // Backup registers + "stp x8, x9, [x23]\n" + "stp x10, x11, [x23, #16]\n" + "stp x12, x13, [x23, #32]\n" + "stp x14, x15, [x23, #48]\n" + "stp x16, x17, [x23, #64]\n" + "stp x18, x19, [x23, #80]\n" + "stp x20, x21, [x23, #96]\n" + "stp x22, x23, [x23, #112]\n" + "stp x24, x25, [x23, #128]\n" + "stp x26, x27, [x23, #144]\n" + + // Prepare register arguments + "ldp x0, x1, [x20]\n" + "ldp x2, x3, [x20, #16]\n" + "ldp x4, x5, [x20, #32]\n" + "ldp x6, x7, [x20, #48]\n" + "ldp d0, d1, [x20, #64]\n" + "ldp d2, d3, [x20, #80]\n" + "ldp d4, d5, [x20, #96]\n" + "ldp d6, d7, [x20, #112]\n" + + // Prepare stack arguments + "sub sp, sp, x21\n" + "1:\n" + "ldr x26, [x22, x25, lsl #3]\n" + "str x26, [sp, x25, lsl #3]\n" + "add x25, x25, #1\n" + "cmp x25, x21\n" + "bne 1b\n" + + // Call function + "blr x19\n" + + // Deallocate space on the stack + "add sp, sp, x21\n" + + // Get return values + "stp x0, x1, [x24]\n" + "stp x2, x3, [x24, #16]\n" + "stp x4, x5, [x24, #32]\n" + "stp x6, x7, [x24, #48]\n" + "stp d0, d1, [x24, #64]\n" + "stp d2, d3, [x24, #80]\n" + "stp d4, d5, [x24, #96]\n" + "stp d6, d7, [x24, #112]\n" + + // Restore + "ldp x8, x9, [x23]\n" + "ldp x10, x11, [x23, #16]\n" + "ldp x12, x13, [x23, #32]\n" + "ldp x14, x15, [x23, #48]\n" + "ldp x16, x17, [x23, #64]\n" + "ldp x18, x19, [x23, #80]\n" + "ldp x20, x21, [x23, #96]\n" + "ldp x22, x23, [x23, #112]\n" + "ldp x24, x25, [x23, #128]\n" + "ldp x26, x27, [x23, #144]\n" + : + : [regs_backup] "r"(®s_backup), [args_registers] "r"(&args), + [args_stack] "r"(&args.stack), [max_args] "r"((uint64_t) MAX_STACK_ARGS), + [address] "r"(address), [result_registers] "r"(&resp->arm_registers->x0) + : CLOBBERD_LIST); + return RPC_SUCCESS; +} #else -bool call_function(int sockfd, intptr_t address, size_t va_list_index, size_t argc, argument_t **p_argv) -{ - typedef u64 (*call_argc0_t)(); - typedef u64 (*call_argc1_t)(u64); - typedef u64 (*call_argc2_t)(u64, u64); - typedef u64 (*call_argc3_t)(u64, u64, u64); - typedef u64 (*call_argc4_t)(u64, u64, u64, u64); - typedef u64 (*call_argc5_t)(u64, u64, u64, u64, u64); - typedef u64 (*call_argc6_t)(u64, u64, u64, u64, u64, u64); - typedef u64 (*call_argc7_t)(u64, u64, u64, u64, u64, u64, u64); - typedef u64 (*call_argc8_t)(u64, u64, u64, u64, u64, u64, u64, u64); - typedef u64 (*call_argc9_t)(u64, u64, u64, u64, u64, u64, u64, u64, u64); - typedef u64 (*call_argc10_t)(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64); - typedef u64 (*call_argc11_t)(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64); - - bool result = false; - s64 err; - - argument_t *argv = *p_argv; - TRACE("enter"); +typedef u64 (*call_argc_t)(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, + u64, u64, u64, u64, u64, u64, u64); - switch (argc) - { - case 0: - err = ((call_argc0_t)address)(); - break; - case 1: - err = ((call_argc1_t)address)(argv[0].value); - break; - case 2: - err = ((call_argc2_t)address)(argv[0].value, argv[1].value); - break; - case 3: - err = ((call_argc3_t)address)(argv[0].value, argv[1].value, argv[2].value); - break; - case 4: - err = ((call_argc4_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value); - break; - case 5: - err = ((call_argc5_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value); - break; - case 6: - err = ((call_argc6_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value); - break; - case 7: - err = ((call_argc7_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value, argv[6].value); - break; - case 8: - err = ((call_argc8_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value, argv[6].value, argv[7].value); - break; - case 9: - err = ((call_argc9_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value, argv[6].value, argv[7].value, argv[8].value); - break; - case 10: - err = ((call_argc10_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value, argv[6].value, argv[7].value, argv[8].value, argv[9].value); - break; - case 11: - err = ((call_argc11_t)address)(argv[0].value, argv[1].value, argv[2].value, argv[3].value, argv[4].value, argv[5].value, argv[6].value, argv[7].value, argv[8].value, argv[9].value, argv[10].value); - break; +volatile bool call_function(intptr_t address, size_t va_list_index, size_t argc, + Rpc__Argument **p_argv, + Rpc__ResponseCall *response) { + s64 return_val; + TRACE("enter"); + call_argc_t call = (call_argc_t) address; + u64 args[MAX_ARGS] = {0}; + for (size_t i = 0; i < argc; i++) { + switch (p_argv[i]->type_case) { + case RPC__ARGUMENT__TYPE_V_DOUBLE: + args[i] = p_argv[i]->v_double; + break; + case RPC__ARGUMENT__TYPE_V_INT: + args[i] = p_argv[i]->v_int; + break; + case RPC__ARGUMENT__TYPE_V_STR: + args[i] = (uint64_t) p_argv[i]->v_str; + break; + case RPC__ARGUMENT__TYPE_V_BYTES: + args[i] = (uint64_t) p_argv[i]->v_bytes.data; + break; + default: + break; + } } + return_val = call(args[0], args[1], args[2], args[3], args[4], args[5], + args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16]); + response->return_values_case = RPC__RESPONSE_CALL__RETURN_VALUES_RETURN_VALUE; + response->return_value = return_val; - call_response_t response = {0}; - response.return_values.return_value = err; - - CHECK(sendall(sockfd, (char *)&response, sizeof(response))); - - result = true; - -error: - return result; + return RPC_SUCCESS; } -#endif // __ARM_ARCH_ISA_A64 - -bool handle_call(int sockfd) -{ - TRACE("enter"); - s64 err = 0; - int result = false; - argument_t *argv = NULL; - cmd_call_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - argv = (argument_t *)malloc(sizeof(argument_t) * cmd.argc); - CHECK(recvall(sockfd, (char *)argv, sizeof(argument_t) * cmd.argc)); - - TRACE("address: %p", cmd.address); - CHECK(call_function(sockfd, cmd.address, cmd.va_list_index, cmd.argc, (argument_t **)cmd.argv)); - - result = true; +#endif// __ARM_ARCH_ISA_A64 +bool handle_call(int sockfd, Rpc__CmdCall *cmd) { + TRACE("enter"); + bool ret = RPC_FAILURE; + Rpc__ResponseCall resp_call = RPC__RESPONSE_CALL__INIT; +#ifdef __ARM_ARCH_ISA_A64 + Rpc__ReturnRegistersArm regs = RPC__RETURN_REGISTERS_ARM__INIT; + resp_call.arm_registers = ®s; + resp_call.return_values_case = RPC__RESPONSE_CALL__RETURN_VALUES_ARM_REGISTERS; +#else + resp_call.return_values_case = RPC__RESPONSE_CALL__RETURN_VALUES_RETURN_VALUE; +#endif + TRACE("address: %p", cmd->address); + CHECK(call_function(cmd->address, cmd->va_list_index, cmd->n_argv, cmd->argv, + &resp_call)); + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_call)); + ret = RPC_SUCCESS; error: - if (argv) - { - free(argv); - } - return result; + return ret; } -bool handle_peek(int sockfd) -{ +bool handle_peek(int sockfd, Rpc__CmdPeek *cmd) { TRACE("enter"); - s64 err = 0; - int result = false; - u64 *argv = NULL; - cmd_peek_t cmd; + uint8_t *buffer = NULL; + Rpc__ResponsePeek resp_peek = RPC__RESPONSE_PEEK__INIT; + bool ret = RPC_FAILURE; #if defined(SAFE_READ_WRITES) && defined(__APPLE__) - vm_offset_t data = 0; mach_msg_type_number_t size; - - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - if (vm_read(mach_task_self(), cmd.address, cmd.size, &data, &size) == KERN_SUCCESS) - { - CHECK(send_reply(sockfd, CMD_REPLY_PEEK)); - CHECK(sendall(sockfd, (char *)cmd.address, cmd.size)); - CHECK(vm_deallocate(mach_task_self(), data, size) == KERN_SUCCESS); - } - else - { - CHECK(send_reply(sockfd, CMD_REPLY_ERROR)); - } -#else // __APPLE__ - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - CHECK(send_reply(sockfd, CMD_REPLY_PEEK)); - CHECK(sendall(sockfd, (char *)cmd.address, cmd.size)); -#endif // __APPLE__ - - result = true; - + if (vm_read(mach_task_self(), cmd->address, cmd->size, (vm_offset_t *) &buffer, + &size) + == KERN_SUCCESS) { + resp_peek.data.data = (uint8_t *) buffer; + resp_peek.data.len = size; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_peek)); + CHECK(vm_deallocate(mach_task_self(), (vm_address_t) buffer, size) == KERN_SUCCESS); + buffer = NULL; + ret = RPC_SUCCESS; + + } else { + Rpc__ResponseError error = RPC__RESPONSE_ERROR__INIT; + error.func_name = (char *) __func__; + CHECK(send_response(sockfd, (ProtobufCMessage *) &error)) + } +#else // __APPLE__ + resp_peek.data.data = (uint8_t *) cmd->address; + resp_peek.data.len = cmd->size; + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_peek)); +#endif// __APPLE__ + ret = RPC_SUCCESS; error: - if (argv) - { - free(argv); - } - - return result; + SAFE_FREE(buffer); + return ret; } -bool handle_poke(int sockfd) -{ - TRACE("enter"); - s64 err = 0; - int success = false; - u64 *argv = NULL; +bool handle_poke(int sockfd, Rpc__CmdPoke *cmd) { + TRACE("Enter"); + bool ret = RPC_FAILURE; char *data = NULL; - cmd_poke_t cmd; + Rpc__ResponsePoke resp_poke = RPC__RESPONSE_POKE__INIT; #if defined(SAFE_READ_WRITES) && defined(__APPLE__) - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - - // TODO: consider splitting recieve chunks - data = malloc(cmd.size); - CHECK(data); - CHECK(recvall(sockfd, data, cmd.size)); - - if (vm_write(mach_task_self(), cmd.address, (vm_offset_t)data, cmd.size) == KERN_SUCCESS) - { - CHECK(send_reply(sockfd, CMD_REPLY_POKE)); - } - else - { - CHECK(send_reply(sockfd, CMD_REPLY_ERROR)); - } -#else // __APPLE__ - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); - CHECK(recvall(sockfd, (char *)cmd.address, cmd.size)); - CHECK(send_reply(sockfd, CMD_REPLY_POKE)); -#endif // __APPLE__ - - success = true; + if (vm_write(mach_task_self(), cmd->address, (vm_offset_t) cmd->data.data, cmd->data.len) == KERN_SUCCESS) { + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_poke)); + resp_poke.result = RPC_SUCCESS; + } else { + Rpc__ResponseError error = RPC__RESPONSE_ERROR__INIT; + error.func_name = (char *) __func__; + CHECK(send_response(sockfd, (ProtobufCMessage *) &error)); + } +#else // __APPLE__ + memcpy((uint64_t *) cmd->address, cmd->data.data, cmd->data.len); + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_poke)); + resp_poke.result = RPC_SUCCESS; +#endif// __APPLE__ + ret = RPC_SUCCESS; error: - if (argv) - { - free(argv); - } - if (data) - { - free(data); - } - return success; + SAFE_FREE(data); + return ret; } // exported for client hooks -bool get_true() -{ - return true; -} +bool get_true() { return true; } // exported for client hooks -bool get_false() -{ - return false; +bool get_false() { return false; } + +// exported for testing +void test_16args(uint64_t *out, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, + uint64_t arg7, uint64_t arg8, uint64_t arg9, uint64_t arg10, uint64_t arg11, uint64_t arg12, + uint64_t arg13, uint64_t arg14, uint64_t arg15, uint64_t arg16) { + out[0] = arg1; + out[1] = arg2; + out[2] = arg3; + out[3] = arg4; + out[4] = arg5; + out[5] = arg6; + out[6] = arg7; + out[7] = arg8; + out[8] = arg9; + out[9] = arg10; + out[10] = arg11; + out[11] = arg12; + out[12] = arg13; + out[13] = arg14; + out[14] = arg15; + out[15] = arg16; } #if __APPLE__ @@ -881,244 +574,242 @@ bool get_false() void (^dummy_block)(void) = ^{ }; -bool handle_get_dummy_block(int sockfd) -{ +bool handle_get_dummy_block(int sockfd, Rpc__CmdDummyBlock *cmd) { TRACE("enter"); - CHECK(sendall(sockfd, (const char *)&dummy_block, sizeof(&dummy_block))); - return true; + bool ret = RPC_FAILURE; + Rpc__ResponseDummyBlock resp_dummy_block = RPC__RESPONSE_DUMMY_BLOCK__INIT; + resp_dummy_block.address = (uint64_t) &dummy_block; + resp_dummy_block.size = sizeof(&dummy_block); + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_dummy_block)); + ret = RPC_SUCCESS; error: - return false; + return ret; } -#else // !__APPLE__ +#else// !__APPLE__ -bool handle_get_dummy_block(int sockfd) -{ - return true; +bool handle_get_dummy_block(int sockfd, Rpc__CmdDummyBlock *cmd) { + return RPC_SUCCESS; } -#endif // __APPLE__ +#endif// __APPLE__ + +bool handle_listdir(int sockfd, Rpc__CmdListDir *cmd) { -bool handle_listdir(int sockfd) -{ TRACE("enter"); - bool result = false; + bool ret = RPC_FAILURE; DIR *dirp = NULL; - struct dirent *direntp = NULL; - - cmd_listdir_t cmd; - CHECK(recvall(sockfd, (char *)&cmd, sizeof(cmd))); + size_t entry_count = 0; + size_t idx = 0; + struct dirent *entry = {0}; - dirp = opendir(cmd.filename); - CHECK(sendall(sockfd, (const char *)&dirp, sizeof(dirp))); + Rpc__ResponseListdir resp_list_dir = RPC__RESPONSE_LISTDIR__INIT; + Rpc__DirEntry *d_entry = NULL; + Rpc__DirEntryStat *d_stat = NULL, *l_stat = NULL; - if (!dirp) - { - TRACE("invalid dir"); - goto error; + dirp = opendir(cmd->path); + CHECK(dirp != NULL); + for (entry = readdir(dirp); entry != NULL; entry = readdir(dirp)) { + entry_count++; } + closedir(dirp); - while (1) - { - errno = 0; - direntp = readdir(dirp); - if (!direntp) - { - CHECK(errno == 0); - break; - } + dirp = opendir(cmd->path); + CHECK(dirp != NULL); + + resp_list_dir.magic = MAGIC; + resp_list_dir.dirp = (uint64_t) dirp; + resp_list_dir.n_dir_entries = entry_count; + resp_list_dir.dir_entries = + (Rpc__DirEntry **) malloc(sizeof(Rpc__DirEntry *) * entry_count); + CHECK(resp_list_dir.dir_entries != NULL); + while ((entry = readdir(dirp)) != NULL) { struct stat system_lstat = {0}; struct stat system_stat = {0}; - - char fullpath[FILENAME_MAX]; - CHECK(0 < sprintf(fullpath, "%s/%s", cmd.filename, direntp->d_name)); + char fullpath[FILENAME_MAX] = {0}; + CHECK(0 < sprintf(fullpath, "%s/%s", cmd->path, entry->d_name)); u64 lstat_error = 0; u64 stat_error = 0; - if (!lstat(fullpath, &system_lstat)) { + if (lstat(fullpath, &system_lstat)) { lstat_error = errno; } - if (!stat(fullpath, &system_stat)) { + if (stat(fullpath, &system_stat)) { stat_error = errno; } - listdir_entry_t entry = { - .magic = MAGIC, - .type = direntp->d_type, - -#ifdef __APPLE__ - .namelen = direntp->d_namlen, -#else - .namelen = strlen(direntp->d_name), -#endif - - .lstat.errno1 = lstat_error, - .lstat.st_dev = system_lstat.st_dev, - .lstat.st_mode = system_lstat.st_mode, - .lstat.st_nlink = system_lstat.st_nlink, - .lstat.st_ino = system_lstat.st_ino, - .lstat.st_uid = system_lstat.st_uid, - .lstat.st_gid = system_lstat.st_gid, - .lstat.st_rdev = system_lstat.st_rdev, - .lstat.st_size = system_lstat.st_size, - .lstat.st_blocks = system_lstat.st_blocks, - .lstat.st_blksize = system_lstat.st_blksize, - .lstat.st_atime1 = system_lstat.st_atime, - .lstat.st_mtime1 = system_lstat.st_mtime, - .lstat.st_ctime1 = system_lstat.st_ctime, - - .stat.errno1 = stat_error, - .stat.st_dev = system_stat.st_dev, - .stat.st_mode = system_stat.st_mode, - .stat.st_nlink = system_stat.st_nlink, - .stat.st_ino = system_stat.st_ino, - .stat.st_uid = system_stat.st_uid, - .stat.st_gid = system_stat.st_gid, - .stat.st_rdev = system_stat.st_rdev, - .stat.st_size = system_stat.st_size, - .stat.st_blocks = system_stat.st_blocks, - .stat.st_blksize = system_stat.st_blksize, - .stat.st_atime1 = system_stat.st_atime, - .stat.st_mtime1 = system_stat.st_mtime, - .stat.st_ctime1 = system_stat.st_ctime, - }; - - CHECK(sendall(sockfd, (const char *)&entry, sizeof(entry))); - CHECK(sendall(sockfd, direntp->d_name, entry.namelen)); - } - - u64 wrong_magic = 0; - CHECK(sendall(sockfd, (const char *)&wrong_magic, sizeof(wrong_magic))); - - TRACE("sent magic"); - - result = true; + d_entry = (Rpc__DirEntry *) malloc(sizeof(Rpc__DirEntry)); + d_stat = (Rpc__DirEntryStat *) malloc(sizeof(Rpc__DirEntryStat)); + l_stat = (Rpc__DirEntryStat *) malloc(sizeof(Rpc__DirEntryStat)); + CHECK(d_entry != NULL && d_stat != NULL && l_stat != NULL); + + rpc__dir_entry__init(d_entry); + rpc__dir_entry_stat__init(d_stat); + rpc__dir_entry_stat__init(l_stat); + + // Init d_stat + d_stat->errno1 = stat_error; + d_stat->st_dev = system_stat.st_dev; + d_stat->st_mode = system_stat.st_mode; + d_stat->st_nlink = system_stat.st_nlink; + d_stat->st_ino = system_stat.st_ino; + d_stat->st_uid = system_stat.st_uid; + d_stat->st_gid = system_stat.st_gid; + d_stat->st_rdev = system_stat.st_rdev; + d_stat->st_size = system_stat.st_size; + d_stat->st_blocks = system_stat.st_blocks; + d_stat->st_blksize = system_stat.st_blksize; + d_stat->st_atime1 = system_stat.st_atime; + d_stat->st_mtime1 = system_stat.st_mtime; + d_stat->st_ctime1 = system_stat.st_ctime; + + // Init l_stat + l_stat->errno1 = lstat_error; + l_stat->st_dev = system_lstat.st_dev; + l_stat->st_mode = system_lstat.st_mode; + l_stat->st_nlink = system_lstat.st_nlink; + l_stat->st_ino = system_lstat.st_ino; + l_stat->st_uid = system_lstat.st_uid; + l_stat->st_gid = system_lstat.st_gid; + l_stat->st_rdev = system_lstat.st_rdev; + l_stat->st_size = system_lstat.st_size; + l_stat->st_blocks = system_lstat.st_blocks; + l_stat->st_blksize = system_lstat.st_blksize; + l_stat->st_atime1 = system_lstat.st_atime; + l_stat->st_mtime1 = system_lstat.st_mtime; + l_stat->st_ctime1 = system_lstat.st_ctime; + + d_entry->d_type = entry->d_type; + d_entry->d_name = strdup(entry->d_name); + d_entry->stat = d_stat; + d_entry->lstat = l_stat; + + resp_list_dir.dir_entries[idx] = d_entry; + idx++; + } + CHECK(send_response(sockfd, (ProtobufCMessage *) &resp_list_dir)); + ret = RPC_SUCCESS; error: - if (dirp) - { + if (dirp) { closedir(dirp); } - return result; + for (uint64_t i = 0; i < entry_count; i++) { + SAFE_FREE(resp_list_dir.dir_entries[i]->d_name); + SAFE_FREE(resp_list_dir.dir_entries[i]->stat); + SAFE_FREE(resp_list_dir.dir_entries[i]->lstat); + SAFE_FREE(resp_list_dir.dir_entries[i]); + } + SAFE_FREE(resp_list_dir.dir_entries); + TRACE("exit"); + + return ret; } -void handle_client(int sockfd) -{ +void handle_client(int sockfd) { bool disconnected = false; TRACE("enter. fd: %d", sockfd); struct utsname uname_buf; - CHECK(0 == uname(&uname_buf)); + uint8_t buffer[BUFFERSIZE] = {0}; + size_t message_size; - protocol_handshake_t handshake = {0}; + CHECK(0 == uname(&uname_buf)); + Rpc__Handshake handshake = RPC__HANDSHAKE__INIT; handshake.magic = SERVER_MAGIC_VERSION; - handshake.arch = ARCH_UNKNOWN; - strncpy(handshake.sysname, uname_buf.sysname, HANDSHAKE_SYSNAME_LEN - 1); - strncpy(handshake.machine, uname_buf.machine, HANDSHAKE_MACHINE_LEN - 1); - + handshake.arch = RPC__ARCH__ARCH_UNKNOWN; + handshake.sysname = uname_buf.sysname; + handshake.machine = uname_buf.machine; CHECK(-1 != fcntl(sockfd, F_SETFD, FD_CLOEXEC)); #ifdef __ARM_ARCH_ISA_A64 - handshake.arch = ARCH_ARM64; + handshake.arch = RPC__ARCH__ARCH_ARM64; #endif + message_size = rpc__handshake__pack(&handshake, buffer); + CHECK(0 != message_size); + CHECK(message_send(sockfd, (const uint8_t *) &buffer, message_size)); - CHECK(sendall(sockfd, (char *)&handshake, sizeof(handshake))); + while (true) { + Rpc__Command *cmd; + message_size = 0; + CHECK(message_receive(sockfd, (char *) &buffer, &message_size)) - while (true) - { - protocol_message_t cmd; TRACE("recv"); - if (!recvall_ext(sockfd, (char *)&cmd, sizeof(cmd), &disconnected)) - { - goto error; - } - CHECK(cmd.magic == MAGIC); + cmd = rpc__command__unpack(NULL, message_size, buffer); + TRACE("client fd: %d, cmd type: %d", sockfd, cmd->type_case); + CHECK(cmd->magic == MAGIC); - TRACE("client fd: %d, cmd type: %d", sockfd, cmd.cmd_type); - - switch (cmd.cmd_type) - { - case CMD_EXEC: - { - handle_exec(sockfd); + switch (cmd->type_case) { + case RPC__COMMAND__TYPE_EXEC: { + CHECK(handle_exec(sockfd, cmd->exec)); break; } - case CMD_DLOPEN: - { - handle_dlopen(sockfd); + case RPC__COMMAND__TYPE_DLOPEN: { + CHECK(handle_dlopen(sockfd, cmd->dlopen)); break; } - case CMD_DLSYM: - { - handle_dlsym(sockfd); + case RPC__COMMAND__TYPE_DLSYM: { + CHECK(handle_dlsym(sockfd, cmd->dlsym)); break; } - case CMD_CALL: - { - handle_call(sockfd); + case RPC__COMMAND__TYPE_DLCLOSE: { + CHECK(handle_dlclose(sockfd, cmd->dlclose)); break; } - case CMD_PEEK: - { - handle_peek(sockfd); + case RPC__COMMAND__TYPE_CALL: { + CHECK(handle_call(sockfd, cmd->call)); break; } - case CMD_POKE: - { - handle_poke(sockfd); + case RPC__COMMAND__TYPE_PEEK: { + CHECK(handle_peek(sockfd, cmd->peek)); break; } - case CMD_GET_DUMMY_BLOCK: - { - handle_get_dummy_block(sockfd); + case RPC__COMMAND__TYPE_POKE: { + CHECK(handle_poke(sockfd, cmd->poke)); break; } - case CMD_CLOSE: - { - // client requested to close connection - goto error; + case RPC__COMMAND__TYPE_DUMMY_BLOCK: { + CHECK(handle_get_dummy_block(sockfd, cmd->dummy_block)); + break; } - case CMD_LISTDIR: - { - handle_listdir(sockfd); + case RPC__COMMAND__TYPE_LIST_DIR: { + CHECK(handle_listdir(sockfd, cmd->list_dir)); break; } - case CMD_SHOWOBJECT: - { - handle_showobject(sockfd); + case RPC__COMMAND__TYPE_SHOW_OBJECT: { + CHECK(handle_showobject(sockfd, cmd->show_object)); break; } - case CMD_SHOWCLASS: - { - handle_showclass(sockfd); + case RPC__COMMAND__TYPE_SHOW_CLASS: { + CHECK(handle_showclass(sockfd, cmd->show_class)); break; } - case CMD_GET_CLASS_LIST: - { - handle_get_class_list(sockfd); + case RPC__COMMAND__TYPE_CLASS_LIST: { + CHECK(handle_get_class_list(sockfd, cmd->class_list)); break; } - default: - { - TRACE("unknown cmd: %d", cmd.cmd_type); + case RPC__COMMAND__TYPE_CLOSE: {// client requested to close connection + goto error; + } + default: { + TRACE("unknown cmd: %d", cmd->type_case); } } + rpc__command__free_unpacked(cmd, NULL); } error: close(sockfd); } -void signal_handler(int sig) -{ +void signal_handler(int sig) { int status; pid_t pid; - if (SIGCHLD == sig) - { + if (SIGCHLD == sig) { pid = waitpid(-1, &status, 0); TRACE("PID: %d exited with status: %d", pid, status); return; @@ -1127,44 +818,34 @@ void signal_handler(int sig) TRACE("entered with signal code: %d", sig); } -int main(int argc, const char *argv[]) -{ +int main(int argc, const char *argv[]) { int opt; bool worker_spawn = false; char port[MAX_OPTION_LEN] = DEFAULT_PORT; - while ((opt = getopt(argc, (char *const *)argv, "hp:o:w")) != -1) - { - switch (opt) - { - case 'p': - { + while ((opt = getopt(argc, (char *const *) argv, "hp:o:w")) != -1) { + switch (opt) { + case 'p': { strncpy(port, optarg, sizeof(port) - 1); break; } - case 'o': - { - if (0 == strcmp(optarg, "stdout")) - { + case 'o': { + if (0 == strcmp(optarg, "stdout")) { g_stdout = true; } - if (0 == strcmp(optarg, "syslog")) - { + if (0 == strcmp(optarg, "syslog")) { g_syslog = true; } char *file = strstr(optarg, "file:"); - if (file) - { + if (file) { g_file = fopen(file + 5, "wb"); - if (!g_file) - { + if (!g_file) { printf("failed to open %s for writing\n", optarg); } } break; } - case 'w': - { + case 'w': { worker_spawn = true; break; } @@ -1180,8 +861,7 @@ int main(int argc, const char *argv[]) signal(SIGPIPE, signal_handler); - if (worker_spawn) - { + if (worker_spawn) { TRACE("New worker spawned"); handle_client(WORKER_CLIENT_SOCKET_FD); exit(EXIT_SUCCESS); @@ -1192,19 +872,21 @@ int main(int argc, const char *argv[]) struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; // use my IP. "| AI_ADDRCONFIG" - hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version - hints.ai_family = AF_INET6; // IPv4 addresses will be like ::ffff:127.0.0.1 + hints.ai_flags = AI_PASSIVE;// use my IP. "| AI_ADDRCONFIG" + hints.ai_family = AF_UNSPEC;// AF_INET or AF_INET6 to force version + hints.ai_family = AF_INET6; // IPv4 addresses will be like ::ffff:127.0.0.1 struct addrinfo *servinfo; CHECK(0 == getaddrinfo(NULL, port, &hints, &servinfo)); - struct addrinfo *servinfo2 = servinfo; // servinfo->ai_next; + struct addrinfo *servinfo2 = servinfo;// servinfo->ai_next; char ipstr[INET6_ADDRSTRLEN]; - CHECK(inet_ntop(servinfo2->ai_family, get_in_addr(servinfo2->ai_addr), ipstr, sizeof(ipstr))); + CHECK(inet_ntop(servinfo2->ai_family, get_in_addr(servinfo2->ai_addr), ipstr, + sizeof(ipstr))); TRACE("Waiting for connections on [%s]:%s", ipstr, port); - server_fd = socket(servinfo2->ai_family, servinfo2->ai_socktype, servinfo2->ai_protocol); + server_fd = socket(servinfo2->ai_family, servinfo2->ai_socktype, + servinfo2->ai_protocol); CHECK(server_fd >= 0); CHECK(-1 != fcntl(server_fd, F_SETFD, FD_CLOEXEC)); @@ -1212,38 +894,43 @@ int main(int argc, const char *argv[]) CHECK(0 == setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes_1, sizeof(yes_1))); CHECK(0 == bind(server_fd, servinfo2->ai_addr, servinfo2->ai_addrlen)); - freeaddrinfo(servinfo); // all done with this structure + freeaddrinfo(servinfo);// all done with this structure CHECK(0 == listen(server_fd, MAX_CONNECTIONS)); #ifdef __APPLE__ pthread_t runloop_thread; - CHECK(0 == pthread_create(&runloop_thread, NULL, (void *(*)(void *))CFRunLoopRun, NULL)); -#endif // __APPLE__ + CHECK(0 == pthread_create(&runloop_thread, NULL, (void *(*) (void *) ) CFRunLoopRun, NULL)); +#endif// __APPLE__ signal(SIGCHLD, signal_handler); - while (1) - { - struct sockaddr_storage their_addr; // connector's address information + while (1) { + struct sockaddr_storage their_addr;// connector's address information socklen_t addr_size = sizeof(their_addr); - int client_fd = accept(server_fd, (struct sockaddr *)&their_addr, &addr_size); + int client_fd = + accept(server_fd, (struct sockaddr *) &their_addr, &addr_size); CHECK(client_fd >= 0); CHECK(-1 != fcntl(client_fd, F_SETFD, FD_CLOEXEC)); char ipstr[INET6_ADDRSTRLEN]; - CHECK(inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), ipstr, sizeof(ipstr))); + CHECK(inet_ntop(their_addr.ss_family, + get_in_addr((struct sockaddr *) &their_addr), ipstr, + sizeof(ipstr))); TRACE("Got a connection from %s [%d]", ipstr, client_fd); - +#ifdef SINGLE_THREAD + handle_client(client_fd); + continue; +#else CHECK(spawn_worker_server(client_fd, argv, argc)); +#endif } error: err = 1; clean: - if (-1 != server_fd) - { + if (-1 != server_fd) { close(server_fd); }