import struct
from enum import IntEnum
from rpcclient.darwin.symbol import DarwinSymbol
[docs]
class CTL(IntEnum):
UNSPEC = 0, # unused
KERN = 1, # "high kernel": proc, limits
VM = 2, # virtual memory
VFS = 3, # file system, mount type is next
NET = 4, # network, see socket.h
DEBUG = 5, # debugging parameters
HW = 6, # generic cpu/io
MACHDEP = 7, # machine dependent
USER = 8, # user-level
MAXID = 9, # number of valid top-level ids
[docs]
class KERN(IntEnum):
OSTYPE = 1 # string: system version
OSRELEASE = 2 # string: system release
OSREV = 3 # int: system revision
VERSION = 4 # string: compile time info
MAXVNODES = 5 # int: max vnodes
MAXPROC = 6 # int: max processes
MAXFILES = 7 # int: max open files
ARGMAX = 8 # int: max arguments to exec
SECURELVL = 9 # int: system security level
HOSTNAME = 10 # string: hostname
HOSTID = 11 # int: host identifier
CLOCKRATE = 12 # struct: struct clockrate
VNODE = 13 # struct: vnode structures
PROC = 14 # struct: process entries
FILE = 15 # struct: file entries
PROF = 16 # node: kernel profiling info
POSIX1 = 17 # int: POSIX.1 version
NGROUPS = 18 # int: # of supplemental group ids
JOB_CONTROL = 19 # int: is job control available
SAVED_IDS = 20 # int: saved set-user/group-ID
BOOTTIME = 21 # struct: time kernel was booted
NISDOMAINNAME = 22 # string: YP domain name
DOMAINNAME = NISDOMAINNAME # ERN_MAXPARTITIONS = 23 # int: number of partitions/disk
KDEBUG = 24 # int: kernel trace points
UPDATEINTERVAL = 25 # int: update process sleep time
OSRELDATE = 26 # int: OS release date
NTP_PLL = 27 # node: NTP PLL control
BOOTFILE = 28 # string: name of booted kernel
MAXFILESPERPROC = 29 # int: max open files per proc
MAXPROCPERUID = 30 # int: max processes per uid
DUMPDEV = 31 # dev_t: device to dump on
IPC = 32 # node: anything related to IPC
DUMMY = 33 # unused
PS_STRINGS = 34 # int: address of PS_STRINGS
USRSTACK32 = 35 # int: address of USRSTACK
LOGSIGEXIT = 36 # int: do we log sigexit procs?
SYMFILE = 37 # string: kernel symbol filename
PROCARGS = 38 # * 39 was PCSAMPLES... now obsolete
NETBOOT = 40 # int: are we netbooted? 1=yes,0=no
# 41 was PANICINFO : panic UI information (deprecated)
SYSV = 42 # node: System V IPC information
AFFINITY = 43 # xxx
TRANSLATE = 44 # xxx
CLASSIC = TRANSLATE # XXX backwards compat
EXEC = 45 # xxx
CLASSICHANDLER = EXEC # XXX backwards compatibility
AIOMAX = 46 # int: max aio requests
AIOPROCMAX = 47 # int: max aio requests per process
AIOTHREADS = 48 # int: max aio worker threads
# ifdef __APPLE_API_UNSTABLE
PROCARGS2 = 49 # endif # __APPLE_API_UNSTA
COREFILE = 50 # string: corefile format string
COREDUMP = 51 # int: whether to coredump at all
SUGID_COREDUMP = 52 # int: whether to dump SUGID cores
PROCDELAYTERM = 53 # int: set/reset current proc for delayed termination during shutdown
SHREG_PRIVATIZABLE = 54 # int: can shared regions be privatized ?
# 55 was PROC_LOW_PRI_IO... now deprecated
LOW_PRI_WINDOW = 56 # int: set/reset throttle window - milliseconds
LOW_PRI_DELAY = 57 # int: set/reset throttle delay - milliseconds
POSIX = 58 # node: posix tunables
USRSTACK64 = 59 # LP64 user stack query
NX_PROTECTION = 60 # int: whether no-execute protection is enabled
TFP = 61 # Task for pid settings
PROCNAME = 62 # setup process program name(2*MAXCOMLEN)
THALTSTACK = 63 # for compat with older x86 and does nothing
SPECULATIVE_READS = 64 # int: whether speculative reads are disabled
OSVERSION = 65 # for build number i.e. 9A127
SAFEBOOT = 66 # are we booted safe?
# 67 was LCTX (login context)
RAGEVNODE = 68 # ERN_TTY = 69 # node: tty settings
CHECKOPENEVT = 70 # spi: check the VOPENEVT flag on vnodes at open time
THREADNAME = 71 # set/get thread name
MAXID = 72 # number of valid kern ids
MAX_SIZE = 0x40000
[docs]
class Sysctl:
""" sysctl utils. read man page for sysctl(3) for more details """
def __init__(self, client):
self._client = client
[docs]
def get(self, ctl: CTL, kern: KERN, arg: int = None, size=MAX_SIZE) -> bytes:
""" call sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) on remote """
namelen = 2
with self._client.safe_malloc(4 * 3) as mib:
mib.item_size = 4
mib[0] = ctl
mib[1] = kern
if arg is not None:
mib[2] = int(arg)
namelen += 1
with self._client.safe_malloc(8) as oldenp:
oldenp[0] = size
with self._client.safe_malloc(size) as oldp:
if self._client.symbols.sysctl(mib, namelen, oldp, oldenp, 0, 0):
self._client.raise_errno_exception('sysctl() failed')
return oldp.peek(oldenp[0])
[docs]
def set(self, ctl: CTL, kern: KERN, oldp: DarwinSymbol, oldenp: DarwinSymbol, arg: int = None):
""" call sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) on remote """
namelen = 2
with self._client.safe_malloc(4 * 3) as mib:
mib.item_size = 4
mib[0] = ctl
mib[1] = kern
if arg is not None:
mib[2] = int(arg)
namelen += 1
if self._client.symbols.sysctl(mib, namelen, oldp, oldenp, 0, 0):
self._client.raise_errno_exception('sysctl() failed')
[docs]
def get_str_by_name(self, name: str) -> str:
""" equivalent of: sysctl <name> """
return self.get_by_name(name).strip(b'\x00').decode()
[docs]
def get_int_by_name(self, name: str) -> int:
""" equivalent of: sysctl <name> """
return struct.unpack('<I', self.get_by_name(name))[0]
[docs]
def set_int_by_name(self, name: str, value: int):
""" equivalent of: sysctl <name> -w value """
self.set_by_name(name, struct.pack('<I', value))
[docs]
def set_str_by_name(self, name: str, value: str):
""" equivalent of: sysctl <name> -w value """
self.set_by_name(name, value.encode() + b'\x00')
[docs]
def set_by_name(self, name: str, value: bytes):
""" equivalent of: sysctl <name> -w value """
if self._client.symbols.sysctlbyname(name, 0, 0, value, len(value)):
self._client.raise_errno_exception('sysctlbyname() failed')
[docs]
def get_by_name(self, name: str, size=MAX_SIZE) -> bytes:
""" equivalent of: sysctl <name> """
oldval_len = size
with self._client.safe_malloc(8) as p_oldval_len:
p_oldval_len[0] = oldval_len
with self._client.safe_malloc(oldval_len) as oldval:
if self._client.symbols.sysctlbyname(name, oldval, p_oldval_len, 0, 0):
self._client.raise_errno_exception('sysctlbyname() failed')
return oldval.peek(p_oldval_len[0])