An incomplete `lsof` for iOS implemented in frida
June 9, 2023 ยท View on GitHub
/*
- This is example shows how to use CModule, Typescript, and ObjC.
- It lets us see what files are opened by the target process (
getpid()). - It is lsof for iOS but implemented in frida.
- How to run?
- frida -U -n
-l proc.ts - In REPL:
- rpc.exports.fds();
- Original work: https://github.com/doronz88/hilda/blob/main/hilda/lsof.m
- More details can be found:
const enum ProcFDType { Vnode = "vnode", Socket = "socket", Kqueue = "kqueue", }
const code: string = `#include <glib.h> /* required to use (u)int16_t, (u)int32_t, (u)int64_t */
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/arm/_types.h / typedef unsigned char __uint8_t; typedef unsigned short __uint16_t; typedef unsigned int __uint32_t; typedef __uint32_t __darwin_uid_t; / [???] user IDs / typedef __uint32_t __darwin_gid_t; / [???] process and group IDs / typedef long long __int64_t; typedef __int64_t __darwin_off_t; / [???] Used for file sizes / typedef __uint32_t __darwin_socklen_t; / socklen_t (duh) */
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_uid_t.h / typedef __darwin_uid_t uid_t; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_gid_t.h / typedef __darwin_gid_t gid_t; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_off_t.h / typedef __darwin_off_t off_t; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_u_short.h / typedef unsigned short u_short; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_u_int32_t.h / typedef unsigned int u_int32_t; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_fsid_t.h / typedef struct fsid { int32_t val[2]; } fsid_t; / file system id type / / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_u_char.h / typedef unsigned char u_char; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_sa_family_t.h / typedef __uint8_t sa_family_t; / defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_socklen_t.h */ typedef __darwin_socklen_t socklen_t;
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/syslimits.h / #define PATH_MAX 1024 / max bytes in pathname */
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/param.h / /
- MAXPATHLEN defines the longest permissable path length after expanding
- symbolic links. It is used to allocate a temporary buffer from the buffer
- pool in which to do the name expansion, hence should be a power of two,
- and must be less than or equal to MAXBSIZE. */ #define MAXPATHLEN PATH_MAX
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/_types/_in_addr_t.h / typedef __uint32_t in_addr_t; / base type for internet address */
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/netinet/in.h / #define ntohs(x) ((__uint16_t)(x)) /
- Internet address (a structure for historical reasons) */ struct in_addr { in_addr_t s_addr; };
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/netinet6/in6.h / #define INET6_ADDRSTRLEN 46 /
- IPv6 address / typedef struct in6_addr { union { __uint8_t __u6_addr8[16]; __uint16_t __u6_addr16[8]; __uint32_t __u6_addr32[4]; } __u6_addr; / 128-bit IP6 address */ } in6_addr_t;
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/proc_info.h / /
- A copy of stat64 with static sized fields. / struct vinfo_stat { uint32_t vst_dev; / [XSI] ID of device containing file / uint16_t vst_mode; / [XSI] Mode of file (see below) / uint16_t vst_nlink; / [XSI] Number of hard links / uint64_t vst_ino; / [XSI] File serial number / uid_t vst_uid; / [XSI] User ID of the file / gid_t vst_gid; / [XSI] Group ID of the file / int64_t vst_atime; / [XSI] Time of last access / int64_t vst_atimensec; / nsec of last access / int64_t vst_mtime; / [XSI] Last data modification time / int64_t vst_mtimensec; / last data modification nsec / int64_t vst_ctime; / [XSI] Time of last status change / int64_t vst_ctimensec; / nsec of last status change / int64_t vst_birthtime; / File creation time(birth) / int64_t vst_birthtimensec; / nsec of File creation time / off_t vst_size; / [XSI] file size, in bytes / int64_t vst_blocks; / [XSI] blocks allocated for file / int32_t vst_blksize; / [XSI] optimal blocksize for I/O / uint32_t vst_flags; / user defined flags for file / uint32_t vst_gen; / file generation number / uint32_t vst_rdev; / [XSI] Device ID / int64_t vst_qspare[2]; / RESERVED: DO NOT USE! */ };
struct vnode_info { struct vinfo_stat vi_stat; int vi_type; int vi_pad; fsid_t vi_fsid; };
struct proc_fdinfo { int32_t proc_fd; uint32_t proc_fdtype; };
struct vnode_info_path { struct vnode_info vip_vi; char vip_path[MAXPATHLEN]; /* tail end of it */ };
struct proc_fileinfo { uint32_t fi_openflags; uint32_t fi_status; off_t fi_offset; int32_t fi_type; uint32_t fi_guardflags; };
struct vnode_fdinfo { struct proc_fileinfo pfi; struct vnode_info pvi; };
struct vnode_fdinfowithpath { struct proc_fileinfo pfi; struct vnode_info_path pvip; };
struct in4in6_addr { u_int32_t i46a_pad32[3]; struct in_addr i46a_addr4; };
struct in_sockinfo { int insi_fport; /* foreign port / int insi_lport; / local port / uint64_t insi_gencnt; / generation count of this instance / uint32_t insi_flags; / generic IP/datagram flags */ uint32_t insi_flow;
uint8_t insi_vflag; /* ini_IPV4 or ini_IPV6 */
uint8_t insi_ip_ttl; /* time to live proto */
uint32_t rfu_1; /* reserved */
/* protocol dependent part */
union {
struct in4in6_addr ina_46;
struct in6_addr ina_6;
} insi_faddr; /* foreign host table entry */
union {
struct in4in6_addr ina_46;
struct in6_addr ina_6;
} insi_laddr; /* local host table entry */
struct {
u_char in4_tos; /* type of service */
} insi_v4;
struct {
uint8_t in6_hlim;
int in6_cksum;
u_short in6_ifindex;
short in6_hops;
} insi_v6;
};
/*
- TCP Sockets */
#define TSI_T_NTIMERS 4
struct tcp_sockinfo { struct in_sockinfo tcpsi_ini; int tcpsi_state; int tcpsi_timer[TSI_T_NTIMERS]; int tcpsi_mss; uint32_t tcpsi_flags; uint32_t rfu_1; /* reserved / uint64_t tcpsi_tp; / opaque handle of TCP protocol control block */ };
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/un.h / /
- [XSI] Definitions for UNIX IPC domain. / struct sockaddr_un { unsigned char sun_len; / sockaddr len including null / sa_family_t sun_family; / [XSI] AF_UNIX / char sun_path[104]; / [XSI] path name (gag) */ };
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/socket.h / #define AF_UNIX 1 / local to host (pipes) / #define AF_INET 2 / internetwork: UDP, TCP, etc. / #define AF_INET6 30 / IPv6 / #define SOCK_MAXADDRLEN 255 / longest possible addresses */
/*
- Unix Domain Sockets */
struct un_sockinfo { uint64_t unsi_conn_so; /* opaque handle of connected socket / uint64_t unsi_conn_pcb; / opaque handle of connected protocol control block / union { struct sockaddr_un ua_sun; char ua_dummy[SOCK_MAXADDRLEN]; } unsi_addr; / bound address / union { struct sockaddr_un ua_sun; char ua_dummy[SOCK_MAXADDRLEN]; } unsi_caddr; / address of socket connected to */ };
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/net/if.h */ #define IF_NAMESIZE 16
/*
- PF_NDRV Sockets */ struct ndrv_info { uint32_t ndrvsi_if_family; uint32_t ndrvsi_if_unit; char ndrvsi_if_name[IF_NAMESIZE]; };
/*
- Kernel Event Sockets */ struct kern_event_info { uint32_t kesi_vendor_code_filter; uint32_t kesi_class_filter; uint32_t kesi_subclass_filter; };
/* defined in https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/bsd/sys/kern_control.h / /!
-
@defined MAX_KCTL_NAME - @discussion Kernel control names must be no longer than
-
MAX_KCTL_NAME.
*/ #define MAX_KCTL_NAME 96
/*
- Kernel Control Sockets / struct kern_ctl_info { uint32_t kcsi_id; uint32_t kcsi_reg_unit; uint32_t kcsi_flags; / support flags / uint32_t kcsi_recvbufsize; / request more than the default buffer size / uint32_t kcsi_sendbufsize; / request more than the default buffer size / uint32_t kcsi_unit; char kcsi_name[MAX_KCTL_NAME]; / unique nke identifier, provided by DTS */ };
/*
- VSock Sockets */ struct vsock_sockinfo { uint32_t local_cid; uint32_t local_port; uint32_t remote_cid; uint32_t remote_port; };
struct sockbuf_info { uint32_t sbi_cc; uint32_t sbi_hiwat; /* SO_RCVBUF, SO_SNDBUF */ uint32_t sbi_mbcnt; uint32_t sbi_mbmax; uint32_t sbi_lowat; short sbi_flags; short sbi_timeo; };
enum { SOCKINFO_IN = 1, SOCKINFO_TCP = 2, };
struct socket_info { struct vinfo_stat soi_stat; uint64_t soi_so; /* opaque handle of socket / uint64_t soi_pcb; / opaque handle of protocol control block / int soi_type; int soi_protocol; int soi_family; short soi_options; short soi_linger; short soi_state; short soi_qlen; short soi_incqlen; short soi_qlimit; short soi_timeo; u_short soi_error; uint32_t soi_oobmark; struct sockbuf_info soi_rcv; struct sockbuf_info soi_snd; int soi_kind; uint32_t rfu_1; / reserved / union { struct in_sockinfo pri_in; / SOCKINFO_IN / struct tcp_sockinfo pri_tcp; / SOCKINFO_TCP / struct un_sockinfo pri_un; / SOCKINFO_UN / struct ndrv_info pri_ndrv; / SOCKINFO_NDRV / struct kern_event_info pri_kern_event; / SOCKINFO_KERN_EVENT / struct kern_ctl_info pri_kern_ctl; / SOCKINFO_KERN_CTL / struct vsock_sockinfo pri_vsock; / SOCKINFO_VSOCK */ } soi_proto; };
struct socket_fdinfo { struct proc_fileinfo pfi; struct socket_info psi; };
/* defns of process file desc type */ #define PROX_FDTYPE_VNODE 1 #define PROX_FDTYPE_SOCKET 2 #define PROX_FDTYPE_KQUEUE 5 #define PROX_FDTYPE_NETPOLICY 9
/* Flavors for proc_pidinfo() */ #define PROC_PIDLISTFDS 1 #define PROC_PIDLISTFD_SIZE (sizeof(struct proc_fdinfo))
/* Flavors for proc_pidfdinfo */
#define PROC_PIDFDVNODEPATHINFO 2 #define PROC_PIDFDVNODEPATHINFO_SIZE (sizeof(struct vnode_fdinfowithpath))
#define PROC_PIDFDSOCKETINFO 3 #define PROC_PIDFDSOCKETINFO_SIZE (sizeof(struct socket_fdinfo))
/* https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getservbyport.3.html#//apple_ref/doc/man/3/getservbyport */ struct servent { char s_name; / official name of service */ char *s_aliases; / alias list / int s_port; / port service resides at */ char s_proto; / protocol to use */ };
/* https://raw.githubusercontent.com/apple-oss-distributions/xnu/main/libsyscall/wrappers/libproc/libproc.h */ extern int proc_pidinfo(int pid, int flavor, uint64_t arg, void * buffer, int buffersize); extern int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize);
/* other functions */ extern int getpid(); extern void *malloc(size_t); extern int snprintf(char *, size_t, const char *, ...); extern const char *inet_ntop(int, const void , char , socklen_t); / in libkern/ extern struct servent * getservbyport(int, const char *);
/* my functions */ extern void push_vnode(int32_t, char *); extern void push_socket(int32_t, char *, char *, int, char *, char *, int, char *); extern void error(char *);
void fds(void) { // Figure out the size of the buffer needed to hold the list of open FDs int bufferSize = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, NULL, 0); if (bufferSize == 0) error("Unable to get open file handles");
// Get the list of open FDs
struct proc_fdinfo *procFDInfo = (struct proc_fdinfo *)malloc(bufferSize);
if (!procFDInfo) error("Out of memory. Unable to allocate buffer with %d bytes");
/* https://github.com/palominolabs/get_process_handles/issues/1#issue-314083604 */
/* http://disq.us/p/dvodv4 */
int returnedSize = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, procFDInfo, bufferSize);
int numberOfProcFDs = returnedSize / PROC_PIDLISTFD_SIZE;
for (int i = 0; i < numberOfProcFDs; i++) {
struct proc_fdinfo *finfo = &procFDInfo[i];
switch (finfo->proc_fdtype) {
case PROX_FDTYPE_VNODE: {
// A file is open
struct vnode_fdinfowithpath vnodeInfo;
int bytesUsed = proc_pidfdinfo(getpid(), finfo->proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
if (bytesUsed == PROC_PIDFDVNODEPATHINFO_SIZE) push_vnode(finfo->proc_fd, vnodeInfo.pvip.vip_path);
break;
}
case PROX_FDTYPE_SOCKET: {
// A socket is open
struct socket_fdinfo socketInfo;
int bytesUsed = proc_pidfdinfo(getpid(), finfo->proc_fd, PROC_PIDFDSOCKETINFO, &socketInfo, PROC_PIDFDSOCKETINFO_SIZE);
if (bytesUsed == PROC_PIDFDSOCKETINFO_SIZE) {
switch (socketInfo.psi.soi_kind) {
case SOCKINFO_TCP: // Type: TCP
case SOCKINFO_IN: { // Type: UDP
int family, lport, rport;
char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];
struct in_sockinfo *s;
family = socketInfo.psi.soi_family;
s = socketInfo.psi.soi_kind == SOCKINFO_TCP ? &socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini : &socketInfo.psi.soi_proto.pri_in;
if ((family == AF_INET) || (family == AF_INET6)) {
if (family == AF_INET) {
inet_ntop(AF_INET, &s->insi_laddr.ina_46.i46a_addr4, lip, sizeof(lip));
inet_ntop(AF_INET, &s->insi_faddr.ina_46.i46a_addr4, rip, sizeof(rip));
} else {
inet_ntop(AF_INET6, &s->insi_laddr.ina_6, lip, sizeof(lip));
inet_ntop(AF_INET6, &s->insi_faddr.ina_6, rip, sizeof(rip));
}
struct servent any = {"*"}; // \u2731
struct servent *lsp = 0, *fsp = 0;
lport = ntohs(s->insi_lport);
rport = ntohs(s->insi_fport);
lsp = lport ? getservbyport(lport, 0) : &any;
fsp = rport ? getservbyport(rport, 0) : &any;
char *protocol;
if (family == AF_INET6) {
protocol = (socketInfo.psi.soi_kind == SOCKINFO_TCP) ? "TCP6" : "UDP6";
} else {
protocol = (socketInfo.psi.soi_kind == SOCKINFO_TCP) ? "TCP" : "UDP";
}
char *lsp_name = (lsp) ? lsp->s_name : "Unknown (local) service name";
char *fsp_name = (fsp) ? fsp->s_name : "Unknown (remote) service name";
push_socket(finfo->proc_fd, protocol, lip, lport, lsp_name, rip, rport, fsp_name);
} else if (family == AF_UNIX) {
}
}
}
}
break;
}
case PROX_FDTYPE_KQUEUE: {
// TODO: missing body
break;
}
case PROX_FDTYPE_NETPOLICY: {
// TODO: missing body
break;
}
default: {
char str[100];
snprintf(str, sizeof(str), "Unknown process file desc type: %d!", finfo->proc_fdtype);
error(str);
break;
}
}
}
}`;
const LIBPROC_PATH: string = '/usr/lib/libproc.dylib'; const LIBSYSTEM_MALLOC_PATH: string = '/usr/lib/system/libsystem_malloc.dylib';
const fds: {[key: string]: any}[] = [];
const push_vnode = new NativeCallback( function (fd: number, path: NativePointer): void { fds.push({ "fd": fd, "path": path.readUtf8String(), "type": ProcFDType.Vnode, }); }, 'void', ['int32', 'pointer'] // TODO: more details? );
const push_socket = new NativeCallback( function (fd: number, protocol: NativePointer, lip: NativePointer, lport: number, lsp: NativePointer, rip: NativePointer, rport: number, fsp: NativePointer): void { fds.push({ "fd": fd, "protocol": protocol.readUtf8String(), "lip": lip.readUtf8String(), "lport": lport, "lsp": lsp.readUtf8String(), "rip": rip.readUtf8String(), // Remote port will be 0 when the FD represents a listening socket "rport": rport, "fsp": fsp.readUtf8String(), "type": ProcFDType.Socket, }); }, 'void', ['int32', 'pointer', 'pointer', 'int', 'pointer', 'pointer', 'int', 'pointer'] // TODO: more details? );
const error = new NativeCallback( function (msg: NativePointer): void { throw new Error(msg.readUtf8String()!); }, 'void', ['pointer'] );
const cm: CModule = new CModule(code, { proc_pidinfo: Module.getExportByName(LIBPROC_PATH, 'proc_pidinfo'), proc_pidfdinfo: Module.getExportByName(LIBPROC_PATH, 'proc_pidfdinfo'), getpid: Module.getExportByName(null, 'getpid'), malloc: Module.getExportByName(LIBSYSTEM_MALLOC_PATH, 'malloc'), snprintf: Module.getExportByName(null, 'snprintf'), inet_ntop: Module.getExportByName(null, 'inet_ntop'), getservbyport: Module.getExportByName(null, 'getservbyport'), push_vnode, push_socket, error, });
rpc.exports = { fds(): void { new NativeFunction(cm.fds, 'void', [])(); console.log(JSON.stringify(fds, null, 2)); fds.length = 0; }, }