Skip to content

Commit

Permalink
darwin_processes: add socket fds parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Feb 8, 2022
1 parent bfc0376 commit 879fad7
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 17 deletions.
34 changes: 29 additions & 5 deletions src/rpcclient/rpcclient/darwin_processes.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
from _socket import htons
from collections import namedtuple
from typing import Optional, List

from construct import Array

from rpcclient.exceptions import BadReturnValueError
from rpcclient.processes import Processes
from rpcclient.structs.consts import AF_INET, AF_INET6
from rpcclient.structs.darwin import pid_t, MAXPATHLEN, PROC_PIDLISTFDS, proc_fdinfo, PROX_FDTYPE_VNODE, \
vnode_fdinfowithpath, PROC_PIDFDVNODEPATHINFO, proc_taskallinfo, PROC_PIDTASKALLINFO
vnode_fdinfowithpath, PROC_PIDFDVNODEPATHINFO, proc_taskallinfo, PROC_PIDTASKALLINFO, PROX_FDTYPE_SOCKET, \
PROC_PIDFDSOCKETINFO, socket_fdinfo, so_kind_t

Process = namedtuple('Process', 'pid path')
Fd = namedtuple('Fd', 'fd path')
FileFd = namedtuple('FileFd', 'fd path')
Ipv4SocketFd = namedtuple('Ipv4SocketFd', 'fd local_port remote_port') # when remote 0, the socket is for listening
Ipv6SocketFd = namedtuple('Ipv6SocketFd', 'fd local_port remote_port') # when remote 0, the socket is for listening


class DarwinProcesses(Processes):
Expand All @@ -26,23 +31,42 @@ def get_fds(self, pid: int) -> Optional[list]:
result = []
size = self._client.symbols.proc_pidinfo(pid, PROC_PIDLISTFDS, 0, 0, 0)

vi_size = vnode_fdinfowithpath.sizeof()
vi_size = 8196 # should be enough for all structs
with self._client.safe_malloc(vi_size) as vi_buf:
with self._client.safe_malloc(size) as fdinfo_buf:
size = int(self._client.symbols.proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, size))
if not size:
raise BadReturnValueError('proc_pidinfo(PROC_PIDLISTFDS) failed')

for fd in Array(size // proc_fdinfo.sizeof(), proc_fdinfo).parse(fdinfo_buf.peek(size)):

if fd.proc_fdtype == PROX_FDTYPE_VNODE:
# file
vs = self._client.symbols.proc_pidfdinfo(pid, fd.proc_fd, PROC_PIDFDVNODEPATHINFO, vi_buf,
vi_size)
if not vs:
raise BadReturnValueError('proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed')

vi = vnode_fdinfowithpath.parse(vi_buf.peek(vi_size))
result.append(Fd(fd=fd.proc_fd, path=vi.pvip.vip_path))
vi = vnode_fdinfowithpath.parse(vi_buf.peek(vnode_fdinfowithpath.sizeof()))
result.append(FileFd(fd=fd.proc_fd, path=vi.pvip.vip_path))

elif fd.proc_fdtype == PROX_FDTYPE_SOCKET:
# socket
vs = self._client.symbols.proc_pidfdinfo(pid, fd.proc_fd, PROC_PIDFDSOCKETINFO, vi_buf,
vi_size)
if not vs:
raise BadReturnValueError('proc_pidinfo(PROC_PIDFDSOCKETINFO) failed')

vi = socket_fdinfo.parse(vi_buf.peek(vi_size))
if vi.psi.soi_family == AF_INET and vi.psi.soi_kind == so_kind_t.SOCKINFO_TCP:
local_port = htons(vi.psi.soi_proto.pri_in.insi_lport)
remote_port = htons(vi.psi.soi_proto.pri_in.insi_fport)
result.append(Ipv4SocketFd(fd=fd.proc_fd, local_port=local_port, remote_port=remote_port))

elif vi.psi.soi_family == AF_INET6 and vi.psi.soi_kind == so_kind_t.SOCKINFO_TCP:
local_port = htons(vi.psi.soi_proto.pri_in.insi_lport)
remote_port = htons(vi.psi.soi_proto.pri_in.insi_fport)
result.append(Ipv6SocketFd(fd=fd.proc_fd, local_port=local_port, remote_port=remote_port))
return result

def get_task_all_info(self, pid: int):
Expand Down
53 changes: 41 additions & 12 deletions src/rpcclient/rpcclient/structs/darwin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from construct import PaddedString, Struct, Int32ul, Int16ul, Int64ul, Int8ul, this, Int32sl, Padding, Array, Int64sl, \
Bytes, Computed, FlagsEnum, Int16sl, Union
Bytes, Computed, FlagsEnum, Int16sl, Union, Enum

from rpcclient.structs.generic import uid_t, gid_t, long, mode_t, uint64_t, short, u_short, uint32_t
from rpcclient.structs.generic import uid_t, gid_t, long, mode_t, uint64_t, short, u_short, uint32_t, u_int32_t, \
in_addr, uint8_t

MAXPATHLEN = 1024
_SYS_NAMELEN = 256
Expand Down Expand Up @@ -54,11 +55,11 @@
'st_rdev' / dev_t, # device type, for special file inode

'st_atimespec' / timespec, # time of last access
'st_atime' / Computed(this.st_atimespec.tv_sec + (this.st_atimespec.tv_nsec / 10**9)),
'st_atime' / Computed(this.st_atimespec.tv_sec + (this.st_atimespec.tv_nsec / 10 ** 9)),
'st_mtimespec' / timespec, # time of last data modification
'st_mtime' / Computed(this.st_mtimespec.tv_sec + (this.st_mtimespec.tv_nsec / 10**9)),
'st_mtime' / Computed(this.st_mtimespec.tv_sec + (this.st_mtimespec.tv_nsec / 10 ** 9)),
'st_ctimespec' / timespec, # time of last file status change
'st_ctime' / Computed(this.st_ctimespec.tv_sec + (this.st_ctimespec.tv_nsec / 10**9)),
'st_ctime' / Computed(this.st_ctimespec.tv_sec + (this.st_ctimespec.tv_nsec / 10 ** 9)),
'st_size' / off_t, # file size, in bytes
Padding(4),
'st_blocks' / blkcnt_t, # blocks allocated for file
Expand Down Expand Up @@ -358,6 +359,7 @@
'fi_status' / Int32ul,
'fi_offset' / off_t,
'fi_guardflags' / Int32ul,
Padding(8),
)

# A copy of stat64 with static sized fields.
Expand All @@ -372,7 +374,6 @@

vnode_info_path = Struct(
'vip_vi' / vnode_info,
Padding(8),
'_vip_path' / Bytes(MAXPATHLEN),
'vip_path' / Computed(lambda x: x._vip_path.split(b'\x00', 1)[0].decode()),
)
Expand Down Expand Up @@ -400,8 +401,26 @@
TSI_T_2MSL = 3 # 2*msl quiet time timer
TSI_T_NTIMERS = 4

in4in6_addr = Struct(
'i46a_pad32' / u_int32_t[3],
'i46a_addr4' / in_addr,
)

in_sockinfo = Struct(
'insi_fport' / Int32sl,
'insi_lport' / Int32sl,
'insi_gencnt' / uint64_t,
'insi_flags' / uint32_t,
'insi_flow' / uint32_t,
'insi_vflag' / uint8_t,
'insi_ip_ttl' / uint8_t,
'rfu_1' / uint32_t,

# TODO: complete
)

tcp_sockinfo = Struct(
# 'tcpsi_ini' / in_sockinfo, # TODO: complete
'tcpsi_ini' / in_sockinfo,
'in_sockinfo' / Int32sl,
'tcpsi_timer' / Int32sl[TSI_T_NTIMERS],
'tcpsi_mss' / Int32sl,
Expand All @@ -410,6 +429,16 @@
'tcpsi_tp' / uint64_t, # opaque handle of TCP protocol control block
)

so_kind_t = Enum(Int32ul,
SOCKINFO_GENERIC=0,
SOCKINFO_IN=1,
SOCKINFO_TCP=2,
SOCKINFO_UN=3,
SOCKINFO_NDRV=4,
SOCKINFO_KERN_EVENT=5,
SOCKINFO_KERN_CTL=6
)

socket_info = Struct(
'soi_stat' / vinfo_stat,
'soi_so' / uint64_t, # opaque handle of socket
Expand All @@ -428,12 +457,12 @@
'soi_oobmark' / uint32_t,
'soi_rcv' / sockbuf_info,
'soi_snd' / sockbuf_info,
'soi_kind' / Int32sl,
'rfu_1' / uint32_t,
'soi_kind' / so_kind_t,
'rfu_1' / uint32_t, # reserved

'soi_proto' / Union(0,
# 'pri_in' / in_sockinfo,
# 'tcp_sockinfo' / pri_tcp, # TODO: complete
'soi_proto' / Union(None,
'pri_in' / in_sockinfo,
# 'tcp_sockinfo' / pri_tcp,
# 'un_sockinfo' / pri_un,
# 'ndrv_info' / pri_ndrv,
# 'kern_event_info' / pri_kern_event,
Expand Down
2 changes: 2 additions & 0 deletions src/rpcclient/rpcclient/structs/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

UNIX_PATH_MAX = 108

uint8_t = Int8ul
short = Int16sl
u_short = Int16ul
uint32_t = Int32ul
uint64_t = Int64ul
u_int32_t = uint32_t
uid_t = Int32ul
gid_t = Int32ul
time_t = Int32ul
Expand Down

0 comments on commit 879fad7

Please sign in to comment.