""" Please make sure you read the README COMPLETELY BEFORE reading anything below. It is very critical that you read coding guidelines in Section E in README file. """ from xnu import * from utils import * from string import * from net import * import xnudefines def IterateProcChannels(proc): """ Iterate through all channels in the given process params: proc - the proc object returns: nothing, this is meant to be used as a generator function kc - yields each kern_channel in the process """ proc_filedesc = addressof(proc.p_fd) proc_ofiles = proc_filedesc.fd_ofiles for fd in range(0, unsigned(proc_filedesc.fd_afterlast)): if unsigned(proc_ofiles[fd]) != 0: proc_fd_fglob = proc_ofiles[fd].fp_glob if (unsigned(proc_fd_fglob.fg_ops.fo_type) == 10): proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *') yield Cast(proc_fd_fglob_fg_data, 'kern_channel *') def IterateKernChannelRings(kc, kind): """ Iterate through all rings on a given channel """ NR_RX = 0 NR_TX = 1 NR_A = 2 NR_F = 3 if kind == NR_RX: rings = kc.ch_na.na_rx_rings elif kind == NR_TX : rings = kc.ch_na.na_tx_rings elif kind == NR_A : rings = kc.ch_na.na_alloc_rings else : rings = kc.ch_na.na_free_rings # note that ch_last is actually one greater than the last # as per the comment in ch_connect for i in range(kc.ch_first[kind], kc.ch_last[kind]): yield addressof(rings[i]) # Note this is broken if you have type summaries enabled # because we are summarizing the pointer to the structure # and not the structure itself. Unfortunately, that's # the pattern used elsewhere. # Trying to actually use the type summary will blow up # because it has a linked list pointer to itself # @lldb_type_summary(['kern_channel_t', 'kern_channel *']) @header('{:<20s} {:<36s}'.format('kern_channel', 'uuid')) def GetKernChannelSummary(kc): """ Summarizes a kern_channel and related information returns: str - summary of kern_channel """ format_string = '{o: <#020x} {u: <36s}' return format_string.format( o=kc, u=GetUUIDSummary(kc.ch_info.cinfo_ch_id)) @lldb_type_summary(['__kern_channel_ring *']) @header(' {:<20s} {:<16s} {:>10s} | {:<5s} {:<5s} | {:<5s} {:<5s}'.format( 'addr', 'name', 'flags', 'kh', 'kt', 'rh', 'rt')) #@header(' {:<20s} {:<16s} {:>10s} | {:<5s} {:<5s} | {:<5s} {:<5s} | {:<5s} {:<5s}'.format( # 'addr', 'name', 'flags', 'kh', 'kt', 'rh', 'rt', 'h', 't')) def GetKernChannelRingSummary(kring): """ Summarizes a __kern_channel_ring and related information returns: str - summary of kern_channel_ring """ format_string = ' {addr: <#020x} {name: <16s} {flags: >#010x} | {kh: <5d} {kt: <5d} | {rh: <5d} {rt: <5d}'# | {h: <5d} {t: <5d}' return format_string.format( addr=kring, name=kring.ckr_name, flags=kring.ckr_flags, kh=kring.ckr_khead, kt=kring.ckr_ktail, rh=kring.ckr_rhead, rt=kring.ckr_rtail) #h=kring.ckr_ring.ring_head, ## user_ring might not be present in memory #t=kring.ckr_ring.ring_tail) @lldb_command('showprocchannels') def ShowProcChannels(cmd_args=None): """ Show the skywalk channels for a given process. usage: showprocchannels """ if not cmd_args: raise ArgumentError('missing struct proc * argument') proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') print(GetKernChannelSummary.header) for kc in IterateProcChannels(proc): print(GetKernChannelSummary(kc)) @lldb_command('showchannelrings') def ShowChannelRings(cmd_args=None): """ Show the skywalk rings for a given channel. usage: showchannelrings """ if not cmd_args: raise ArgumentError('missing struct kern_channel * argument') kc = kern.GetValueFromAddress(cmd_args[0], 'kern_channel *') print("RX rings:") print(GetKernChannelRingSummary.header) for ring in IterateKernChannelRings(kc, 0) : print(GetKernChannelRingSummary(ring)) print("TX rings:") print(GetKernChannelRingSummary.header) for ring in IterateKernChannelRings(kc, 1) : print(GetKernChannelRingSummary(ring)) print("ALLOC rings:") print(GetKernChannelRingSummary.header) for ring in IterateKernChannelRings(kc, 2) : print(GetKernChannelRingSummary(ring)) print("FREE rings:") print(GetKernChannelRingSummary.header) for ring in IterateKernChannelRings(kc, 3) : print(GetKernChannelRingSummary(ring)) def SkmemCacheModeAsString(mode) : out_string = "" SKM_MODE_NOCACHE = 0x1 SKM_MODE_AUDIT = 0x2 if (mode & SKM_MODE_NOCACHE) : out_string += "n" else : out_string += "-" if (mode & SKM_MODE_AUDIT) : out_string += "a" else : out_string += "-" return out_string @lldb_command('showskmemcache') def ShowSkmemCache(cmd_args=None) : """ Show the global list of skmem caches """ format_string = "{:<4s} {:<18s} {:<4s} {:<4s} {:<4s} {:<4s} {:<4s} {:<4s} {:<4s} {:<4s} {:<4s} {:> 10, str(ar.ar_name))) i += 1 @lldb_command('showskmemregions') def ShowSkmemRegions(cmd_args=None) : """ Show the global list of skmem regions """ i = 1 skrhead = kern.globals.skmem_region_head for skr in IterateTAILQ_HEAD(skrhead, "skr_link") : format_string = "{:>4d}: 0x{:<08x} \"{: 70): ports_string += "\n\t" offs = len(ports_string) ports_string += " %u" % f.nsr_port """ return format_string.format( o=ns, p=proto, a=addr, n=ns.ns_n_reservations) + ports_string @lldb_command('shownetns') def ShowNetNS(cmd_args=None): """ Show the netns table """ print("\nnetns_namespaces:") print(GetStructNsSummary.header) namespaces = kern.globals.netns_namespaces for ns in IterateRBTreeEntry(namespaces, 'ns_link'): print(GetStructNsSummary(ns)) print("\nwild: (these should be duplicated above)") print(GetStructNsSummary.header) for i in range(0,4): print(GetStructNsSummary(kern.globals.netns_global_wild[i])) print("\nnon wild:") print(GetStructNsSummary.header) for i in range(0,4): print(GetStructNsSummary(kern.globals.netns_global_non_wild[i])) NETNS_LISTENER = 0x00 NETNS_SKYWALK = 0x01 NETNS_BSD = 0x02 NETNS_PF = 0x03 NETNS_OWNER_MASK = 0x7 netns_flag_strings = { NETNS_LISTENER : "LISTENER", NETNS_SKYWALK : "SKYWALK", NETNS_BSD : "BSD", NETNS_PF : "PF", } @lldb_type_summary(['struct ns_token *']) @header('{:<20s} {:<5s} {:<48s} {:<12s} {:<8s} {:<38s} {:<12s}'.format('nt', 'proto', 'addr', 'port', 'owner', 'ifp', 'flags')) def GetNsTokenSummary(nt): """ Summarizes a struct ns from the netns returns: str - summary of struct ns """ if (nt.nt_proto == IPPROTO_TCP): proto = "tcp" elif (nt.nt_proto == IPPROTO_UDP): proto = "udp" else: proto = str(nt.nt_proto) if (nt.nt_addr_len == sizeof('struct in_addr')): addr = GetInAddrAsString(addressof(nt.nt_inaddr)) elif (nt.nt_addr_len == sizeof('struct in6_addr')): addr = GetIn6AddrAsString(nt.nt_in6addr.__u6_addr.__u6_addr8) else: addr = str(nt_addr) + " bad len {:u}".format(nt.nt_addr_len) format_string = '{o:#020x} {p:<5s} {a:<48s} {pt:<12s} {wn:<8s} {ifp:38s} {f:#012x}' ports = "%u" % nt.nt_port ifp = "(ifnet *)" + hex(nt.nt_ifp) owner = netns_flag_strings[nt.nt_flags & NETNS_OWNER_MASK] return format_string.format( o=nt, p=proto, a=addr, pt=ports, wn=owner, ifp=ifp, f=nt.nt_flags) @lldb_command("showallnetnstokens") def ShowAllNetNSTokens(cmd_args=None): """ show all netns tokens """ tokenhead = kern.globals.netns_all_tokens print(GetNsTokenSummary.header) for nt in IterateListEntry(tokenhead, 'nt_all_link', list_prefix='s'): print(GetNsTokenSummary(nt)) @lldb_command("shownetnstokens") def ShowNetNSTokens(cmd_args=None): """ show netns tokens attached to an ifp with no args, shows unbound tokens """ if cmd_args is None or len(cmd_args) == 0: print("No ifp argument provided, showing unbound tokens") tokenhead = kern.globals.netns_unbound_tokens elif len(cmd_args) > 0: ifp = kern.GetValueFromAddress(cmd_args[0], 'ifnet *') print("Showing tokens for ifp %r" % ifp) tokenhead = ifp.if_netns_tokens else: print("Missing ifp argument 0 in shownetnstokens") print(cmd_args) return print(GetNsTokenSummary.header) for nt in IterateListEntry(tokenhead, 'nt_ifp_link', list_prefix=''): print(GetNsTokenSummary(nt)) def IterateSTAILQ_HEAD(headval, element_name): iter_val = headval.stqh_first while unsigned(iter_val) != 0 : yield iter_val iter_val = iter_val.__getattr__(element_name).stqe_next #end of yield loop @lldb_command("shownexuschannels") def ShowNexusChannels(cmd_args=None): """ show nexus channels """ if cmd_args is None or len(cmd_args) == 0: print("Missing argument 0 (kern_nexus address).") return nx = kern.GetValueFromAddress(cmd_args[0], 'kern_nexus *') i = 1 format_string = "{:>4s} {:<18s} {:>4s} {:<7s} {:<7s} {:<18s} {:<18s} {:<18s} {:>8s} {:6s} {:<18s} {:>4s} {:s}" print(format_string.format("", "addr", "refs", "txrings", "rxrings", "arena", "ioskmap", "mapaddr", "mapsize", "maprdr", "na", "fd", "process")) for ch in IterateSTAILQ_HEAD(nx.nx_ch_head, "ch_link"): format_string = "{:>4d}: 0x{:<08x} {:>4d} [{:2d},{:2d}] [{:2d},{:2d}] 0x{:<08x} 0x{:<08x} 0x{:<16x} {:>8d} {:>6d} 0x{:<08x} {:>4d} {:s}({:d})" print(format_string.format(i, ch, ch.ch_refcnt, ch.ch_first[0], ch.ch_last[0], ch.ch_first[1], ch.ch_last[1], ch.ch_mmap.ami_arena, ch.ch_mmap.ami_mapref, ch.ch_mmap.ami_mapaddr, ch.ch_mmap.ami_mapsize, ch.ch_mmap.ami_redirect, ch.ch_na, ch.ch_fd, ch.ch_name, ch.ch_pid)) i += 1 for ch in IterateSTAILQ_HEAD(nx.nx_ch_nonxref_head, "ch_link"): format_string = "{:>4d}: 0x{:<08x} {:>4d} [{:2d},{:2d}] [{:2d},{:2d}] 0x{:<08x} 0x{:<08x} 0x{:<16x} {:>8d} {:>6d} 0x{:<08x} {:>4d} {:s}({:d})" print(format_string.format(i, ch, ch.ch_refcnt, ch.ch_first[0], ch.ch_last[0], ch.ch_first[1], ch.ch_last[1], ch.ch_mmap.ami_arena, ch.ch_mmap.ami_mapref, ch.ch_mmap.ami_mapaddr, ch.ch_mmap.ami_mapsize, ch.ch_mmap.ami_redirect, ch.ch_na, ch.ch_fd, ch.ch_name, ch.ch_pid)) i += 1 def IterateProcNECP(proc): """ Iterate through all NECP descriptors in the given process params: proc - the proc object returns: nothing, this is meant to be used as a generator function necp - yields each necp_fd_data in the process """ proc_filedesc = addressof(proc.p_fd) proc_ofiles = proc_filedesc.fd_ofiles for fd in range(0, unsigned(proc_filedesc.fd_afterlast)): if unsigned(proc_ofiles[fd]) != 0: proc_fd_fglob = proc_ofiles[fd].fp_glob if (unsigned(proc_fd_fglob.fg_ops.fo_type) == 9): proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *') yield Cast(proc_fd_fglob_fg_data, 'necp_fd_data *') def GetNECPClientBitFields(necp): """ Return the bit fields in necp_client as string returns: str - string representation of necp_client bit fields """ bitfields_string = '' if necp.result_read != 0: bitfields_string += 'r' else: bitfields_string += '-' if necp.allow_multiple_flows != 0: bitfields_string += 'm' else: bitfields_string += '-' if necp.background != 0: bitfields_string += 'b' else: bitfields_string += '-' if necp.background_update != 0: bitfields_string += 'B' else: bitfields_string += '-' if necp.platform_binary != 0: bitfields_string += 'p' else: bitfields_string += '-' return bitfields_string def GetNECPFlowBitFields(flow_registration): """ Return the bit fields in necp_client_flow_registration as string returns: str - string representation of necp_client_flow_registration bit fields """ bitfields_string = '' if flow_registration.flow_result_read != 0: bitfields_string += 'r' else: bitfields_string += '-' if flow_registration.defunct != 0: bitfields_string += 'd' else: bitfields_string += '-' return bitfields_string @lldb_type_summary(['necp_fd_data *']) @header('{:<20s} {:<8s}'.format('necp_fd_data', "flags")) def GetNECPSummary(necp): """ Summarizes a necp_fd_data and related information returns: str - summary of necp_fd_data """ format_string = '{o: <#020x} {u:<#08x}' stats_arenas_string = "\n\n\t%-18s %-39s %-4s %-10s\n" % ("stats_arenas", "mmap", "refs", "flags") for sa in IterateListEntry(necp.stats_arena_list, 'nai_chain'): stats_arenas_string += "\t0x%016x " % sa stats_arenas_string += "[0x%016x-0x%016x) " % (sa.nai_mmap.ami_mapaddr,(sa.nai_mmap.ami_mapaddr+sa.nai_mmap.ami_mapsize)) stats_arenas_string += "%4u " % sa.nai_use_count stats_arenas_string += "0x%08x " % sa.nai_flags stats_arenas_string += "\n" clients_string = "" for c in IterateRBTreeEntry(necp.clients, 'link'): clients_string += "\n\t%-18s %-36s %-4s %-5s\n" % ("necp_clients", "client_id", "refs", "flags") clients_string += "\t0x%016x " % c clients_string += "%36s " % GetUUIDSummary(c.client_id) clients_string += "%4u " % c.reference_count clients_string += "%5s " % GetNECPClientBitFields(c) count = 0; for f in IterateRBTreeEntry(c.flow_registrations, 'client_link'): if count == 0: clients_string += "\n\t\t%-18s %-36s %-2s %-18s %-18s %-18s\n" % ("flow_registration", "registraton_id", "flags", "stats_arena", "kstats_obj", "ustats_obj") clients_string += "\t\t0x%016x " % f clients_string += "%36s " % GetUUIDSummary(f.registration_id) clients_string += "%2s " % GetNECPFlowBitFields(f) clients_string += "0x%016x " % f.stats_arena clients_string += "0x%016x " % f.kstats_kaddr clients_string += "0x%016x " % f.ustats_uaddr clients_string += "\n" return format_string.format( o=necp, u=necp.flags) + stats_arenas_string + clients_string @lldb_command('showprocnecp') def ShowProcNECP(cmd_args=None): """ Show NECP descriptors for a given process. usage: showprocnecp """ if not cmd_args: raise ArgumentError('missing struct proc * argument') proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') print(GetNECPSummary.header) for kc in IterateProcNECP(proc): print(GetNECPSummary(kc)) def NexusTypePtr(nx): if nx.nx_prov.nxprov_params.nxp_type == GetEnumValue("nexus_type_t::NEXUS_TYPE_FLOW_SWITCH"): type_str = "{:>18s}{:18s}".format("(nx_flowswitch *)", hex(nx.nx_arg)) elif nx.nx_prov.nxprov_params.nxp_type == GetEnumValue("nexus_type_t::NEXUS_TYPE_NET_IF"): type_str = "{:>18s}{:18s}".format("(nx_netif *)", hex(nx.nx_arg)) elif nx.nx_prov.nxprov_params.nxp_type == GetEnumValue("nexus_type_t::NEXUS_TYPE_USER_PIPE"): type_str = "{:>18s}{:18s}".format("(nx_upipe *)", hex(nx.nx_arg)) elif nx.nx_prov.nxprov_params.nxp_type == GetEnumValue("nexus_type_t::NEXUS_TYPE_KERNEL_PIPE"): type_str = "{:>36s}".format("kpipe") else: type_str = "unknown" return "(kern_nexus *){:18s} {:s}".format(hex(nx), type_str) def GetStructNexusSummary(nx): nexus_summary_string = "" nexus_summary_string += "{0:s} ".format(NexusTypePtr(nx)) nexus_summary_string += "{0:40s} ".format(str(Cast(addressof(nx.nx_prov.nxprov_params.nxp_name), 'char *'))) nexus_summary_string += "{:2d} {:2d} {:4d} {:4d} {:5d} {:5d} {:2d} ".format( nx.nx_prov.nxprov_params.nxp_rx_rings, nx.nx_prov.nxprov_params.nxp_tx_rings, nx.nx_prov.nxprov_params.nxp_rx_slots, nx.nx_prov.nxprov_params.nxp_tx_slots, nx.nx_prov.nxprov_params.nxp_buf_size, nx.nx_prov.nxprov_params.nxp_meta_size, nx.nx_prov.nxprov_params.nxp_mhints) return nexus_summary_string @lldb_command('shownexuses') def ShowNexuses(cmd_args=None): """ Show Nexus. usage: shownexues """ nexus_summaries = [] nexuses = kern.globals.nx_head for nx in IterateRBTreeEntry(nexuses, 'nx_link'): nexus_summaries.append(GetStructNexusSummary(nx)) nexus_summaries.sort() print("{:111s} rings slots".format("")) print("{:^32s} {:^36s} {:40s} rx tx rx tx buf md mhints". format("nexus", "instance", "name")) for nx_str in nexus_summaries: print("{0:s}".format(nx_str)) def GetSockAddr4(in_addr): return inet_ntoa(struct.pack("I", unsigned(in_addr.s_addr))) def GetSockAddr6(in6_addr): addr = in6_addr.__u6_addr.__u6_addr8 addr_raw_string = ":".join(["{0:02x}{1:02x}".format(unsigned(addr[i]), unsigned(addr[i+1])) for i in range(0, 16, 2)]) return inet_ntop(AF_INET6, inet_pton(AF_INET6, addr_raw_string)) def FlowKeyStr(fk): if fk.fk_ipver == 0x4: src_str = GetSockAddr4(fk.fk_src._v4) dst_str = GetSockAddr4(fk.fk_dst._v4) elif fk.fk_ipver == 0x60: src_str = GetSockAddr6(fk.fk_src._v6) dst_str = GetSockAddr6(fk.fk_dst._v6) else: return "unknown ipver" return "src={},dst={},proto={},sport={},dport={}".format(src_str, dst_str, unsigned(fk.fk_proto), ntohs(fk.fk_sport), ntohs(fk.fk_dport)) def FlowEntryStr(fe): return "(flow_entry*){} {} {} {}".format( hex(fe), GetUUIDSummary(fe.fe_uuid), FlowKeyStr(fe.fe_key), str(fe.fe_proc_name)) def GetFlowEntryPid(fe): return fe.fe_pid def GetFlowswitchFlowEntries(fsw): fm = kern.GetValueFromAddress(unsigned(fsw.fsw_flow_mgr), 'flow_mgr *') cht = kern.GetValueFromAddress(unsigned(fm.fm_flow_table), 'cuckoo_hashtable *') flows = [] def GetCuckooNodeAsFLowEntry(node, hashValue): fe = containerof(node, 'struct flow_entry', 'fe_cnode') flows.append(fe) CuckooHashtableForeach(cht, GetCuckooNodeAsFLowEntry) return flows def IsNexusAFlowswitch(nx): return nx.nx_prov.nxprov_params.nxp_type == GetEnumValue('nexus_type_t::NEXUS_TYPE_FLOW_SWITCH') def GetNexusAsFlowswitch(nx): return kern.GetValueFromAddress(unsigned(nx.nx_arg), 'nx_flowswitch *') def FlowswitchStr(fsw): return "{}:\n(nx_flowswitch *){}".format(str(fsw.fsw_ifp.if_xname), hex(fsw)) @lldb_command('showflowswitches') def ShowFlowswitches(cmd_args=None): """ Show flow switches usage: showflowswitches [ifname] """ ifname = "" if len(cmd_args) == 1: ifname = cmd_args[0] nexuses = kern.globals.nx_head for nx in IterateRBTreeEntry(nexuses, 'nx_link'): if not IsNexusAFlowswitch(nx): continue fsw = GetNexusAsFlowswitch(nx) if ifname not in str(fsw.fsw_ifp.if_xname): continue print("{}".format(FlowswitchStr(fsw))) flows = GetFlowswitchFlowEntries(fsw) flows.sort(key=GetFlowEntryPid) for fe in flows: print(" {}".format(FlowEntryStr(fe))) def CuckooHashtableForeachSlot(cht, slotHandler): for i in range(0, cht._n_buckets): b = cht._buckets[i] if unsigned(b._inuse) == 0: continue for j in range(0, kern.globals._CHT_BUCKET_SLOTS): s = b._slots[j] if unsigned(s._node) != 0: slotHandler(s) def CuckooHashtableForeach(cht, handler): def CuckooHashtableSlotHandler(s): if unsigned(s._node) == 0: return node = s._node while unsigned(node) != 0: handler(node, s._hash) node = node.next CuckooHashtableForeachSlot(cht, CuckooHashtableSlotHandler) @lldb_command('showcuckoohashtable') def ShowCuckooHashtable(cmd_args=None): """ Show Cuckoo Hashtable. usage: showcuckoohashtable """ if not cmd_args: raise ArgumentError('missing struct cuckoo_hashtable * argument') cht = kern.GetValueFromAddress(cmd_args[0], 'struct cuckoo_hashtable *') print("(cuckoo_hashtable *){:18s} capacity {:d} entries {:d}".format(hex(cht), cht._capacity, cht._n_entries)) def CuckooHashtablePrintNode(node, hashValue): print(" node {} hash 0x{:08x}".format(hex(node), int(hashValue))) CuckooHashtableForeach(cht, CuckooHashtablePrintNode) @lldb_command('showprotons') def ShowProtoNS(cmd_args=None): """ Show the protons table """ protons_tokens = kern.globals.protons_tokens for pt in IterateRBTreeEntry(protons_tokens, 'pt_link'): print("(protons_token *){} protocol {:3} pid {:5} epid {:5} ref {:2} flags {}".format( hex(pt), int(pt.pt_protocol), int(pt.pt_pid), int(pt.pt_epid), int(pt.pt_refcnt.ref_count), hex(pt.pt_flags)))