Package rekall :: Package plugins :: Package overlays :: Package darwin :: Module darwin
[frames] | no frames]

Source Code for Module rekall.plugins.overlays.darwin.darwin

   1  # Rekall Memory Forensics 
   2  # Copyright 2013 Google Inc. All Rights Reserved. 
   3  # 
   4  # This program is free software; you can redistribute it and/or modify 
   5  # it under the terms of the GNU General Public License as published by 
   6  # the Free Software Foundation; either version 2 of the License, or (at 
   7  # your option) any later version. 
   8  # 
   9  # This program is distributed in the hope that it will be useful, but 
  10  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
  12  # General Public License for more details. 
  13  # 
  14  # You should have received a copy of the GNU General Public License 
  15  # along with this program; if not, write to the Free Software 
  16  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  17  # 
  18   
  19  __author__ = "Michael Cohen <scudette@gmail.com>" 
  20   
  21  from rekall import obj 
  22   
  23  from rekall.plugins.addrspaces import amd64 
  24  from rekall.plugins.overlays import basic 
  25  from rekall_lib import utils 
  26   
  27   
  28  darwin_overlay = { 
  29      "proc": [None, { 
  30          # Some standard fields for Darwin processes. 
  31          "name": lambda x: x.p_comm, 
  32          "pid": lambda x: x.p_pid.v(), 
  33          "dtb": lambda x: x.task.map.pmap.pm_cr3.v(), 
  34   
  35          "p_list": [None, ["LIST_ENTRY"]], 
  36          "p_sibling": [None, ["LIST_ENTRY"]], 
  37   
  38          "p_comm": [None, ["UnicodeString", dict(length=17)]], 
  39          "task": [None, ["Pointer", dict( 
  40              target="task" 
  41          )]], 
  42      }], 
  43   
  44      "task": [None, { 
  45          "bsd_info": [None, ["Pointer", dict(target="proc")]], 
  46      }], 
  47   
  48      "rtentry": [None, { 
  49          "base_calendartime": [None, ["UnixTimeStamp"]], 
  50   
  51          "source_ip": lambda x: x.rt_nodes[0].rn_u.rn_leaf.m( 
  52              "rn_Key").dereference_as("sockaddr"), 
  53   
  54          "dest_ip": lambda x: x.rt_gateway.deref(), 
  55   
  56          "name": lambda x: x.rt_ifp.name, 
  57   
  58          "sent": lambda x: x.m("rt_stats").nstat_txpackets, 
  59   
  60          "rx": lambda x: x.rt_expire if x.m("rt_stats") else None, 
  61   
  62          "delta": lambda x: (x.rt_expire - x.base_uptime 
  63                              if x.rt_expire else 0), 
  64   
  65      }], 
  66   
  67      "kmod_info": [None, { 
  68          "name": lambda x: utils.SmartUnicode(x.m("name").cast("UnicodeString")), 
  69          "base": lambda x: x.address.v(), 
  70          "end": lambda x: x.base + x.size, 
  71          "version": [None, ["String"]], 
  72   
  73          # Starting address of the kernel module. 
  74          "address": [None, ["Pointer"]], 
  75      }], 
  76   
  77      "sockaddr": [None, { 
  78          # github.com/opensource-apple/xnu/blob/10.9/bsd/sys/socket.h#L370 
  79          "sa_family": [None, ["Enumeration", dict( 
  80              enum_name="sa_family_t", 
  81              target="unsigned char", 
  82          )]], 
  83      }], 
  84   
  85      "sysctl_oid": [None, { 
  86          # xnu-2422.1.72/bsd/sys/sysctl.h: 148 
  87          # This field is reused for two purposes, the first is type of node, 
  88          # while the second is the permissions. 
  89          "oid_kind_type": lambda x: x.m("oid_kind").cast( 
  90              "Enumeration", choices={ 
  91                  1: "CTLTYPE_NODE", 
  92                  2: "CTLTYPE_INT", 
  93                  3: "CTLTYPE_STRING", 
  94                  4: "CTLTYPE_QUAD", 
  95                  5: "CTLTYPE_OPAQUE", 
  96              }, 
  97              target="BitField", 
  98              target_args=dict(start_bit=0, end_bit=8), 
  99          ), 
 100   
 101          "oid_perms": lambda x: x.m("oid_kind").cast( 
 102              "Flags", maskmap={ 
 103                  "CTLFLAG_RD": 0x80000000,  # Allow reads of variable */ 
 104                  "CTLFLAG_WR": 0x40000000,  # Allow writes to the variable 
 105                  # A node will handle locking by itself. 
 106                  "CTLFLAG_LOCKED": 0x00800000, 
 107              }, 
 108          ), 
 109   
 110          "oid_name": [None, ["Pointer", dict(target="String")]], 
 111   
 112      }], 
 113   
 114      "zone": [None, { 
 115          "zone_name": [None, ["Pointer", dict(target="String")]], 
 116          "free_elements": [None, ["Pointer", dict( 
 117              target="zone_free_element" 
 118          )]], 
 119      }], 
 120   
 121      "vm_map_entry": [None, { 
 122          # xnu-2422.1.72/osfmk/mach/vm_prot.h:81 
 123          "protection": [None, ["Flags", dict( 
 124              maskmap={ 
 125                  "VM_PROT_READ": 1, 
 126                  "VM_PROT_WRITE": 2, 
 127                  "VM_PROT_EXECUTE": 4, 
 128              }, 
 129              target="BitField", 
 130              target_args=dict( 
 131                  start_bit=7, 
 132                  end_bit=10 
 133              ) 
 134          )]], 
 135          "max_protection": [None, ["Flags", dict( 
 136              maskmap={ 
 137                  "VM_PROT_READ": 1, 
 138                  "VM_PROT_WRITE": 2, 
 139                  "VM_PROT_EXECUTE": 4, 
 140              }, 
 141              target="BitField", 
 142              target_args=dict( 
 143                  start_bit=10, 
 144                  end_bit=13 
 145              ) 
 146          )]], 
 147      }], 
 148   
 149      "vnode": [None, { 
 150          "v_name": [None, ["Pointer", dict(target="String")]], 
 151   
 152          "path": lambda self: "/".join(reversed( 
 153              [unicode(y.v_name.deref()) for y in self.walk_list("v_parent")])), 
 154   
 155          # xnu-2422.1.72/bsd/sys/vnode_internal.h:230 
 156          "v_flag": [None, ["Flags", dict( 
 157              maskmap={ 
 158                  "VROOT": 0x000001, 
 159                  "VTEXT": 0x000002, 
 160                  "VSYSTEM": 0x000004, 
 161                  "VISTTY": 0x000008, 
 162                  "VRAGE": 0x000010, 
 163              } 
 164          )]], 
 165      }], 
 166   
 167      "cat_attr": [None, { 
 168          "ca_atime": [None, ["UnixTimeStamp"]], 
 169          "ca_atimeondisk": [None, ["UnixTimeStamp"]], 
 170          "ca_mtime": [None, ["UnixTimeStamp"]], 
 171          "ca_ctime": [None, ["UnixTimeStamp"]], 
 172          "ca_itime": [None, ["UnixTimeStamp"]], 
 173          "ca_btime": [None, ["UnixTimeStamp"]], 
 174      }], 
 175   
 176      "ifnet": [None, { 
 177          "if_name": [None, ["Pointer", dict(target="String")]], 
 178      }], 
 179   
 180      "session": [None, { 
 181          "s_login": [None, ["String"]], 
 182      }], 
 183   
 184      "filedesc": [None, { 
 185          # Defined here: 
 186          # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/filedesc.h#L113 
 187          "fd_ofileflags": [None, ["Pointer", dict( 
 188              target="Array", 
 189              target_args=dict( 
 190                  target="Flags", 
 191                  target_args=dict( 
 192                      target="unsigned char", 
 193                      maskmap=utils.MaskMapFromDefines(""" 
 194  /* 
 195   * Per-process open flags. 
 196   */ 
 197  #define UF_EXCLOSE      0x01            /* auto-close on exec */ 
 198  #define UF_FORKCLOSE    0x02            /* auto-close on fork */ 
 199  #define UF_RESERVED     0x04            /* open pending / in progress */ 
 200  #define UF_CLOSING      0x08            /* close in progress */ 
 201   
 202  #define UF_RESVWAIT     0x10            /* close in progress */ 
 203  #define UF_INHERIT      0x20            /* "inherit-on-exec" */ 
 204  """))))]], 
 205   
 206          "fd_ofiles": [None, ["Pointer", dict( 
 207              target="Array", 
 208              target_args=dict( 
 209                  target="Pointer", 
 210                  count=lambda x: x.fd_lastfile, 
 211                  target_args=dict( 
 212                      target="fileproc" 
 213                  ) 
 214              ) 
 215          )]], 
 216      }], 
 217   
 218      "mount": [None, { 
 219          # xnu-2422.1.72/bsd/sys/mount.h 
 220          "mnt_flag": [None, ["Flags", dict( 
 221              maskmap={ 
 222                  "MNT_LOCAL": 0x00001000, 
 223                  "MNT_QUOTA": 0x00002000, 
 224                  "MNT_ROOTFS": 0x00004000, 
 225              } 
 226          )]], 
 227      }], 
 228   
 229      "vfsstatfs": [None, { 
 230          "f_mntonname": [None, ["String"]], 
 231          "f_mntfromname": [None, ["String"]], 
 232          "f_fstypename": [None, ["String"]], 
 233      }], 
 234   
 235      "sockaddr_un": [None, { 
 236          # sun_len is the number of bytes from start of the struct to the NULL 
 237          # terminator in the sun_path member. (Yes, really. [1]) The 
 238          # original/Volatility code used it as length of the string itself, 
 239          # leading to a subtle bug, when the sun_path wasn't NULL-terminated 
 240          # (which it doesn't have to be.) 
 241          # [1] 
 242          # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/un.h#L77 
 243          "sun_path": [None, ["String", dict( 
 244              length=lambda x: ( 
 245                  x.sun_len 
 246                  - x.obj_profile.get_obj_offset("sockaddr_un", "sun_path") 
 247              ), 
 248          )]], 
 249      }], 
 250   
 251      "domain": [None, { 
 252          # xnu-2422.1.72/bsd/sys/domain.h: 99 
 253          "dom_family": [None, ["Enumeration", dict( 
 254              enum_name="sa_family_t", 
 255              target="unsigned char", 
 256          )]], 
 257      }], 
 258   
 259      "tcpcb": [None, { 
 260          "t_state": [None, ["Enumeration", dict( 
 261              # xnu-2422.1.72/bsd/netinet/tcp_fsm.h: 75 
 262              choices={ 
 263                  0: "TCPS_CLOSED", 
 264                  1: "TCPS_LISTEN", 
 265                  2: "TCPS_SYN_SENT", 
 266                  3: "TCPS_SYN_RECEIVED", 
 267                  4: "TCPS_ESTABLISHED", 
 268                  5: "TCPS_CLOSE_WAIT", 
 269                  6: "TCPS_FIN_WAIT_1", 
 270                  7: "TCPS_CLOSING", 
 271                  8: "TCPS_LAST_ACK", 
 272                  9: "TCPS_FIN_WAIT_2", 
 273                  10: "TCPS_TIME_WAIT", 
 274              }, 
 275              target="int", 
 276          )]], 
 277      }], 
 278   
 279      "protosw": [None, { 
 280          "pr_protocol": [None, ["Enumeration", dict( 
 281              # xnu-2422.1.72/bsd/netinet/in.h: 99 
 282              choices={ 
 283                  0: "IPPROTO_IP", 
 284                  1: "IPPROTO_ICMP", 
 285                  2: "IPPROTO_IGMP", 
 286                  4: "IPPROTO_IPV4", 
 287                  6: "IPPROTO_TCP", 
 288                  17: "IPPROTO_UDP", 
 289                  41: "IPPROTO_IPV6", 
 290                  50: "IPPROTO_ESP", 
 291                  51: "IPPROTO_AH", 
 292                  58: "IPPROTO_ICMPV6", 
 293                  255: "IPPROTO_RAW", 
 294              }, 
 295              target="short", 
 296          )]], 
 297          "pr_type": [None, ["Enumeration", dict( 
 298              enum_name="pr_type", 
 299              target="short", 
 300          )]], 
 301      }], 
 302   
 303      "OSDictionary": [None, { 
 304          "dictionary": [None, ["Pointer", dict( 
 305              target="Array", 
 306              target_args=dict( 
 307                  target="dictEntry", 
 308                  count=lambda x: x.count, 
 309              ) 
 310          )]], 
 311      }], 
 312   
 313      "OSString": [None, { 
 314          "value": lambda x: x.obj_profile.UnicodeString( 
 315              offset=x.string, 
 316              length=x.length 
 317          ), 
 318      }], 
 319   
 320      "OSOrderedSet": [None, { 
 321          "array": [None, ["Pointer", dict( 
 322              target="Array", 
 323              target_args=dict( 
 324                  target="_Element", 
 325                  count=lambda x: x.count 
 326              ) 
 327          )]], 
 328      }], 
 329   
 330      "_Element": [8, { 
 331          "obj": [0, ["pointer", ["OSMetaClassBase"]]] 
 332      }], 
 333   
 334      "PE_state": [None, { 
 335          "bootArgs": [None, ["Pointer", dict(target="boot_args")]], 
 336      }], 
 337   
 338      "boot_args": [None, { 
 339          "CommandLine": [None, ["String"]] 
 340      }], 
 341   
 342      "EfiMemoryRange": [None, { 
 343          # xnu-1699.26.8/pexpert/pexpert/i386/boot.h: 46 
 344          "Type": [None, ["Enumeration", dict( 
 345              choices={ 
 346                  0: "kEfiReservedMemoryType", 
 347                  1: "kEfiLoaderCode", 
 348                  2: "kEfiLoaderData", 
 349                  3: "kEfiBootServicesCode", 
 350                  4: "kEfiBootServicesData", 
 351                  5: "kEfiRuntimeServicesCode", 
 352                  6: "kEfiRuntimeServicesData", 
 353                  7: "kEfiConventionalMemory", 
 354                  8: "kEfiUnusableMemory", 
 355                  9: "kEfiACPIReclaimMemory", 
 356                  10: "kEfiACPIMemoryNVS", 
 357                  11: "kEfiMemoryMappedIO", 
 358                  12: "kEfiMemoryMappedIOPortSpace", 
 359                  13: "kEfiPalCode", 
 360                  14: "kEfiMaxMemoryType", 
 361              }, 
 362              target="unsigned int" 
 363          )]], 
 364      }] 
 365  } 
 366   
 367   
 368  darwin_enums = { 
 369      # Socket address families, defined here because we use them in a couple of 
 370      # places. This is a "fake enum" in the sense that the values are declared 
 371      # as #defines, but only ever used in conjunction with a specific datatype 
 372      # (which is sa_family_t). 
 373      # 
 374      # From the horse's mouth: 
 375      # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/socket.h#L370 
 376      # 
 377      # The data type is defined here: 
 378      # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/_types/_sa_family_t.h?source=cc#L30 
 379      # 
 380      # Basically the exact same fake enumeration is used in the same file for 
 381      # protocol families here: 
 382      # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/socket.h#L493 
 383      # 
 384      # Because the numbers are the same (in fact, PF_ defines are mapped 1:1 
 385      # with addressing families) we just use AF_ for everything. 
 386      "sa_family_t": utils.EnumerationFromDefines(""" 
 387  /* 
 388   * Address families. 
 389   */ 
 390  #define AF_UNSPEC       0               /* unspecified */ 
 391  #define AF_UNIX         1               /* local to host (pipes) */ 
 392  #define AF_INET         2               /* internetwork: UDP, TCP, etc. */ 
 393  #define AF_IMPLINK      3               /* arpanet imp addresses */ 
 394  #define AF_PUP          4               /* pup protocols: e.g. BSP */ 
 395  #define AF_CHAOS        5               /* mit CHAOS protocols */ 
 396  #define AF_NS           6               /* XEROX NS protocols */ 
 397  #define AF_ISO          7               /* ISO protocols */ 
 398  #define AF_ECMA         8               /* European computer manufacturers */ 
 399  #define AF_DATAKIT      9               /* datakit protocols */ 
 400  #define AF_CCITT        10              /* CCITT protocols, X.25 etc */ 
 401  #define AF_SNA          11              /* IBM SNA */ 
 402  #define AF_DECnet       12              /* DECnet */ 
 403  #define AF_DLI          13              /* DEC Direct data link interface */ 
 404  #define AF_LAT          14              /* LAT */ 
 405  #define AF_HYLINK       15              /* NSC Hyperchannel */ 
 406  #define AF_APPLETALK    16              /* Apple Talk */ 
 407  #define AF_ROUTE        17              /* Internal Routing Protocol */ 
 408  #define AF_LINK         18              /* Link layer interface */ 
 409  #define pseudo_AF_XTP   19              /* eXpress Transfer Protocol (no AF) */ 
 410  #define AF_COIP         20              /* connection-oriented IP, aka ST II */ 
 411  #define AF_CNT          21              /* Computer Network Technology */ 
 412  #define pseudo_AF_RTIP  22              /* Help Identify RTIP packets */ 
 413  #define AF_IPX          23              /* Novell Internet Protocol */ 
 414  #define AF_SIP          24              /* Simple Internet Protocol */ 
 415  #define pseudo_AF_PIP   25              /* Help Identify PIP packets */ 
 416  #define AF_NDRV         27              /* Network Driver 'raw' access */ 
 417  #define AF_ISDN         28              /* Integrated Services Digital Network*/ 
 418  #define pseudo_AF_KEY   29              /* Internal key-management function */ 
 419  #define AF_INET6        30              /* IPv6 */ 
 420  #define AF_NATM         31              /* native ATM access */ 
 421  #define AF_SYSTEM       32              /* Kernel event messages */ 
 422  #define AF_NETBIOS      33              /* NetBIOS */ 
 423  #define AF_PPP          34              /* PPP communication protocol */ 
 424  #define pseudo_AF_HDRCMPLT 35           /* Used by BPF to not rewrite headers 
 425                                           * in interface output routine */ 
 426  #define AF_AFP          36              /* Used by AFP */ 
 427  #define AF_IEEE80211    37              /* IEEE 802.11 protocol */ 
 428  #define AF_UTUN         38 
 429  #define AF_MULTIPATH    39 
 430  #define AF_MAX          40 
 431  """), 
 432   
 433      # Socket types, defined here: 
 434      # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/socket.h#L133 
 435      "pr_type": utils.EnumerationFromDefines(""" 
 436  /* 
 437   * Types 
 438   */ 
 439  #define SOCK_STREAM     1               /* stream socket */ 
 440  #define SOCK_DGRAM      2               /* datagram socket */ 
 441  #define SOCK_RAW        3               /* raw-protocol interface */ 
 442  #define SOCK_RDM        4               /* reliably-delivered message */ 
 443  #define SOCK_SEQPACKET  5               /* sequenced packet stream */ 
 444  """), 
 445  } 
 446   
 447  darwin64_types = { 
 448      "LIST_ENTRY": [16, { 
 449          "le_next": [0, ["Pointer"]], 
 450          "le_prev": [8, ["Pointer", dict(target="Pointer")]], 
 451      }], 
 452  } 
453 454 455 -class LIST_ENTRY(obj.Struct):
456 """XNU defines lists inline using an annonymous struct. This makes it hard 457 for us to automatically support lists because the debugging symbols dont 458 indicate this inner struct is of any particular type (since its annonymous). 459 460 We therefore depend on the overlays to redefine each list memeber as a 461 LIST_ENTRY member. For example we see code like: 462 463 struct proc { 464 LIST_ENTRY(proc) p_list; 465 ... 466 467 Where: 468 469 #define LIST_ENTRY(type) \ 470 struct { \ 471 struct type *le_next; /* next element */ \ 472 struct type **le_prev; /* address of previous next element */ \ 473 } 474 """ 475 _forward = "le_next" 476 _backward = "le_prev" 477
478 - def is_valid(self):
479 """Must have both valid next and prev pointers.""" 480 return (self.m(self._forward).is_valid() and 481 self.m(self._backward).is_valid())
482
483 - def _GetNextEntry(self, type, member):
484 return self.m(self._forward).dereference_as(type).m(member)
485
486 - def _GetPreviousEntry(self):
487 return self.m(self._backward).dereference_as(self.obj_type)
488
489 - def dereference_as(self, type, member, vm=None):
490 """Recasts the list entry as a member in a type, and return the type. 491 492 Args: 493 type: The name of this Struct type. 494 member: The name of the member of this Struct. 495 address_space: An optional address space to switch during 496 deferencing. 497 """ 498 offset = self.obj_profile.get_obj_offset(type, member) 499 500 item = self.obj_profile.Object( 501 type_name=type, offset=self.obj_offset - offset, 502 vm=vm or self.obj_vm, parent=self.obj_parent, 503 name=type, context=self.obj_context) 504 505 return item
506
507 - def find_all_lists(self, type, member, seen=None):
508 """Follows all the list entries starting from lst. 509 510 We basically convert the list to a tree and recursively search it for 511 new nodes. From each node we follow the Flink and then the Blink. When 512 we see a node we already have, we backtrack. 513 """ 514 if seen is None: 515 seen = set() 516 517 if not self.is_valid(): 518 return seen 519 520 elif self in seen: 521 return seen 522 523 seen.add(self) 524 525 flink = self._GetNextEntry(type, member) 526 flink.find_all_lists(type, member, seen=seen) 527 528 blink = self._GetPreviousEntry() 529 blink.find_all_lists(type, member, seen=seen) 530 531 return seen
532
533 - def list_of_type(self, type, member=None, include_current=True):
534 # We sort here to ensure we have stable ordering as the output of this 535 # call. 536 result = sorted(self.find_all_lists(type, member), 537 key=lambda x: x.obj_offset) 538 539 if member is None: 540 member = self.obj_name 541 542 # Return ourselves as the first item. 543 if include_current: 544 yield self.dereference_as(type, member) 545 546 # We traverse all the _LIST_ENTRYs we can find, and cast them all back 547 # to the required member. 548 for lst in result: 549 # Skip ourselves in this (list_of_type is usually invoked on a list 550 # head). 551 if lst.obj_offset == self.obj_offset: 552 continue 553 554 task = lst.dereference_as(type, member) 555 if task: 556 # Only yield valid objects (In case of dangling links). 557 yield task
558
559 - def reflect(self, vm=None):
560 """Reflect this list element by following its Flink and Blink. 561 562 This is basically the same as Flink.Blink except that it also checks 563 Blink.Flink. It also ensures that Flink and Blink are dereferences to 564 the correct type in case the vtypes do not specify them as pointers. 565 566 Returns: 567 the result of Flink.Blink. 568 """ 569 result1 = self.m(self._forward).dereference_as( 570 self.obj_type, vm=vm).m(self._backward).deref().cast( 571 self.obj_type) 572 573 if not result1: 574 return obj.NoneObject("Flink not valid.") 575 576 result2 = self.Blink.deref().dereference_as( 577 self.obj_type, vm=vm).m( 578 self._forward).dereference_as(self.obj_type) 579 580 if result1 != result2: 581 return obj.NoneObject("Flink and Blink not consistent.") 582 583 return result1
584
585 - def __nonzero__(self):
586 # List entries are valid when both Flinks and Blink are valid 587 return bool(self.m(self._forward)) or bool(self.m(self._backward))
588
589 - def __iter__(self):
590 return self.list_of_type(self.obj_parent.obj_type, self.obj_name)
591
592 593 -class llinfo_arp(obj.Struct):
594 @utils.safe_property
595 - def isvalid(self):
596 try: 597 return self.la_rt.rt_llinfo.v() == self.obj_offset 598 except AttributeError: 599 return False
600
601 602 -class queue_entry(basic.ListMixIn, obj.Struct):
603 """A queue_entry is an externalized linked list. 604 605 Although the queue_entry is defined as: 606 607 struct queue_entry { 608 struct queue_entry *next; /* next element */ 609 struct queue_entry *prev; /* previous element */ 610 }; 611 612 This is in fact not correct since the next, and prev pointers 613 point to the start of the next struct. A queue_entry has a queue 614 head which is also a queue entry and this can eb iterated over 615 using the list_of_type method. 616 617 NOTE: list_of_type should only be called on the head queue_entry. 618 """ 619 _forward = "next" 620 _backward = "prev" 621
622 - def list_of_type(self, type, member):
623 seen = set() 624 seen.add(self.prev.v()) 625 626 item = self.next.dereference_as(type) 627 while item != None: 628 yield item 629 if item.obj_offset in seen: 630 return 631 seen.add(item.obj_offset) 632 item = item.m(member).next.dereference_as(type)
633
634 635 -class sockaddr_dl(obj.Struct):
636 - def __unicode__(self):
637 result = [] 638 for i in xrange(self.sdl_alen): 639 result.append( 640 "%.02X" % ord(self.sdl_data[self.sdl_nlen + i].v())) 641 642 return ":".join(result)
643
644 645 -class fileproc(obj.Struct):
646 """Represents an open file, owned by a process.""" 647 648 DTYPE_TO_HUMAN = { 649 "DTYPE_SOCKET": "socket", 650 "DTYPE_VNODE": "vnode", 651 "DTYPE_PSXSEM": "POSIX Semaphore", 652 "DTYPE_PSXSHM": "POSIX Shared Mem.", 653 "DTYPE_KQUEUE": "kernel queue", 654 "DTYPE_PIPE": "pipe", 655 "DTYPE_FSEVENTS": "FS Events", # needs more research 656 657 # I (Adam) /believe/ this is a remnant of the AppleTalk support, 658 # however nowadays it's unfortunately often used to mean basically 659 # DTYPE_OTHER (which XNU doesn't have). Example of how this might be 660 # used in current code: 661 # opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/netat/sys_dep.c 662 "DTYPE_ATALK": "<unknown>", 663 "-": "INVALID", 664 } 665 666 @utils.safe_property
667 - def fg_type(self):
668 """Returns type of the fileglob (e.g. vnode, socket, etc.)""" 669 return self.multi_m( 670 # OS X 10.8 and earlier 671 "f_fglob.fg_type", 672 673 # OS X 10.9 and later 674 "f_fglob.fg_ops.fo_type")
675 676 @property
677 - def socket(self):
678 """Return the associated socket if the dtype is for socket.""" 679 if self.fg_type == "DTYPE_SOCKET": 680 return self.f_fglob.fg_data.dereference_as("socket")
681 682 @property
683 - def vnode(self):
684 """Return the associated vnode if the dtype is for vnode.""" 685 if self.fg_type == "DTYPE_VNODE": 686 return self.f_fglob.fg_data.dereference_as("vnode")
687
688 - def autocast_fg_data(self):
689 """Returns the correct struct with fg_type-specific information. 690 691 This can be one of vnode, socket, shared memory or semaphore [1]. 692 693 Of those four, we currently only get extra information for vnode and 694 socket. For everything else, we return a NoneObject. 695 696 [1]: 697 https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/file_internal.h#L184 698 """ 699 dtype = self.fg_type 700 701 # Semaphore and shared memory are known structs, but we currently don't 702 # know of anything interesting that should be extracted from them. 703 if dtype == "DTYPE_SOCKET": 704 return self.f_fglob.fg_data.dereference_as("socket") 705 elif dtype == "DTYPE_VNODE": 706 return self.f_fglob.fg_data.dereference_as("vnode") 707 # elif dtype == "DTYPE_PSXSEM": 708 # return self.f_fglob.fg_data.dereference_as("semaphore") 709 # elif dtype == "DTYPE_PSXSHM": 710 # return self.f_fglob.fg_data.dereference_as("vm_shared_region") 711 712 # That would be an unknown DTYPE. 713 return self.f_fglob.fg_data
714 715 @utils.safe_property
716 - def human_name(self):
717 return getattr(self.autocast_fg_data(), "human_name", None)
718 719 @utils.safe_property
720 - def human_type(self):
721 # Delegate to fg_data if it thinks it knows what it is. 722 return getattr( 723 self.autocast_fg_data(), 724 "human_type", 725 self.DTYPE_TO_HUMAN[str(self.fg_type)] 726 )
727
728 729 -class socket(obj.Struct):
730 """Provides human-readable accessors for sockets of the more common AFs. 731 732 This class has two basic ways of getting information. Most attributes are 733 computed using the method fill_socketinfo, which is directly adapted from 734 the kernel function of the same name. For the few things that 735 fill_socketinfo doesn't care about, the properties themselves get the 736 data and provide references to the kernel source for anyone wondering 737 why and how all this works. 738 """ 739 740 cached_socketinfo = None 741
742 - def fill_socketinfo(self):
743 """Computes information about sockets of some addressing families. 744 745 This function is directly adapted from the kernel function 746 fill_socketinfo [1]. The original function is used to fill a struct 747 with addressing and other useful information about sockets of a few 748 key addressing families. All families are supported, but only the 749 following will return useful information: 750 - AF_INET (IPv4) 751 - AF_INET6 (IPv6) 752 - AF_UNIX (Unix socket) 753 - AF_NDRV (Network driver raw access) 754 - AF_SYSTEM (Darwin-specific; see documentation [3]) 755 756 Differences between the kernel function and this adaptation: 757 - The kernel uses Protocol Families (prefixed with PF_). Rekall 758 relies on Addressing Families (AF_) which are exactly the same. 759 760 - The kernel fills a struct; this function returns a dict with the 761 same members. 762 763 - The kernel returns the data raw. This function converts endianness 764 and unions to human-readable representations, as appropriate. 765 766 - Only a subset of members are filled in. 767 768 - Other differences as documented in code. 769 770 Returns: 771 A dict with the same members as struct socket_info and related. 772 Only member that's always filled is "soi_kind". That's not Spanish, 773 but one of the values in this anonymous enum [2], which determines 774 what other members are present. (Read the code.) 775 776 [1] 777 https://github.com/opensource-apple/xnu/blob/10.9/bsd/kern/socket_info.c#L98 778 [2] 779 https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/proc_info.h#L503 780 [3] "KEXT Controls and Notifications" 781 https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/NKEConceptual/control/control.html 782 """ 783 domain = self.so_proto.pr_domain.dom_family 784 type_name = self.so_proto.pr_type 785 protocol = self.so_proto.pr_protocol 786 787 si = {"soi_kind": "SOCKINFO_GENERIC"} 788 789 # The kind of socket is determined by the triplet 790 # {domain, type, protocol} 791 if domain in ["AF_INET", "AF_INET6"]: 792 si["soi_kind"] = "SOCKINFO_IN" 793 794 inp = self.so_pcb.dereference_as("inpcb") 795 796 si["insi_fport"] = utils.ntoh(inp.inp_fport) 797 si["insi_lport"] = utils.ntoh(inp.inp_lport) 798 si["insi_ip_ttl"] = inp.inp_ip_ttl.v() 799 800 # Different from kernel: insi_[df]addr is a union, and by setting 801 # the IPv6 address, you set the IPv4 address too. We instead return 802 # a string with the appropriately formatted address. 803 if domain == "AF_INET": 804 si["insi_faddr"] = utils.FormatIPAddress( 805 "AF_INET", 806 inp.inp_dependfaddr.inp46_foreign.ia46_addr4.s_addr 807 ) 808 si["insi_laddr"] = utils.FormatIPAddress( 809 "AF_INET", 810 inp.inp_dependladdr.inp46_local.ia46_addr4.s_addr 811 ) 812 else: 813 si["insi_faddr"] = utils.FormatIPAddress( 814 "AF_INET6", 815 inp.inp_dependfaddr.inp6_foreign.m("__u6_addr") 816 ) 817 si["insi_laddr"] = utils.FormatIPAddress( 818 "AF_INET6", 819 inp.inp_dependladdr.inp6_local.m("__u6_addr") 820 ) 821 822 if (type_name == "SOCK_STREAM" 823 and (protocol == 0 or protocol == "IPPROTO_TCP") 824 and inp.inp_ppcb != None): 825 826 tp = inp.inp_ppcb.dereference_as("tcpcb") 827 si["soi_kind"] = "SOCKINFO_TCP" 828 829 si["tcpsi_state"] = tp.t_state 830 si["tcpsi_flags"] = tp.t_flags 831 elif domain == "AF_UNIX": 832 unp = self.so_pcb.dereference_as("unpcb") 833 si["soi_kind"] = "SOCKINFO_UN" 834 835 if unp.unp_addr: 836 # Difference from kernel: instead of copying the whole unp_addr 837 # struct, we just get delegate getting the actual string to the 838 # unp_addr struct. (Because it's trickier than it looks.) 839 si["unsi_addr"] = unp.unp_addr.sun_path 840 841 elif domain == "AF_NDRV": 842 # This is how we get the pcb if we need to: 843 # ndrv_cb = self.so_pcb.dereference_as("ndrv_cb") 844 si["soi_kind"] = "SOCKINFO_NDRV" 845 elif domain == "AF_SYSTEM": 846 # AF_SYSTEM domain needs more research. It looks like it's used to 847 # communicate between user space and kernel extensions, and allows 848 # the former to control the latter. Naively, this looks ripe for 849 # rootkits to me. 850 if protocol == "SYSPROTO_EVENT": 851 # This is how we get the pcb if we need to: 852 # ev_pcb = self.so_pcb.dereference_as("kern_event_pcb") 853 si["soi_kind"] = "SOCKINFO_KERN_EVENT" 854 elif protocol == "SYSPROTO_CONTROL": 855 kcb = self.so_pcb.dereference_as("ctl_cb") 856 kctl = kcb.kctl 857 si["soi_kind"] = "SOCKINFO_KERN_CTL" 858 859 if kctl: 860 si["kcsi_id"] = kctl.id 861 si["kcsi_name"] = kctl.name 862 863 return si
864
865 - def get_socketinfo_attr(self, attr):
866 """Run fill_socketinfo if needed, cache result, return value of attr.""" 867 if not self.cached_socketinfo: 868 self.cached_socketinfo = self.fill_socketinfo() 869 870 if attr not in self.cached_socketinfo: 871 return obj.NoneObject( 872 "socket of family {}/{} has no member {}".format( 873 self.addressing_family, 874 self.cached_socketinfo["soi_kind"], 875 attr)) 876 877 return self.cached_socketinfo[attr]
878 879 @utils.safe_property
880 - def src_addr(self):
881 """For IPv[46] sockets, return source IP as string.""" 882 return self.get_socketinfo_attr("insi_laddr")
883 884 @utils.safe_property
885 - def dst_addr(self):
886 """For IPv[46] sockets, return destination IP as string.""" 887 return self.get_socketinfo_attr("insi_faddr")
888 889 @utils.safe_property
890 - def addressing_family(self):
891 """The Addressing Family corresponds roughly to OSI layer 3.""" 892 return self.so_proto.pr_domain.dom_family
893 894 @utils.safe_property
895 - def tcp_state(self):
896 return self.get_socketinfo_attr("tcpsi_state")
897 898 @utils.safe_property
899 - def vnode(self):
900 """For Unix sockets, pointer to vnode, if any. 901 902 This is the same way that OS gathers this information in response to 903 syscall [1] (this is the API used by netstat, among others). 904 905 1: 906 https://github.com/opensource-apple/xnu/blob/10.9/bsd/kern/uipc_usrreq.c#L1683 907 """ 908 if self.addressing_family == "AF_UNIX": 909 return self.so_pcb.dereference_as("unpcb").unp_vnode
910 911 @utils.safe_property
912 - def unp_conn(self):
913 """For Unix sockets, the pcb of the paired socket. [1] 914 915 You most likely want to do sock.conn_pcb.unp_socket to get at the 916 other socket in the pair. However, because the sockets are paired 917 through the protocol control block, it's actually useful to have 918 a direct pointer at it in order to be able to spot paired sockets. 919 920 1: 921 https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/unpcb.h#L128 922 """ 923 if self.addressing_family == "AF_UNIX": 924 return self.so_pcb.dereference_as("unpcb").unp_conn
925 926 @utils.safe_property
927 - def src_port(self):
928 return self.get_socketinfo_attr("insi_lport")
929 930 @utils.safe_property
931 - def dst_port(self):
932 return self.get_socketinfo_attr("insi_fport")
933 934 @utils.safe_property
935 - def l4_protocol(self):
936 if self.addressing_family in ["AF_INET", "AF_INET6"]: 937 # All the values start with IPPROTO_. 938 return str(self.so_proto.pr_protocol).replace("IPPROTO_", "")
939 940 @utils.safe_property
941 - def unix_type(self):
942 if self.addressing_family == "AF_UNIX": 943 pr_type = str(self.so_proto.pr_type) 944 945 if pr_type: 946 # All values begin with SOCK_. 947 return pr_type.replace("SOCK_", "") 948 else: 949 # I am about 80% sure that this should never happen. Before 950 # deciding how this should be handled (possibly by logging an 951 # error), I'll need to do more research. 952 return "Unix Socket"
953 954 @utils.safe_property
955 - def human_name(self):
956 if self.addressing_family in ["AF_INET", "AF_INET6"]: 957 if self.l4_protocol in ["TCP", "UDP"]: 958 return "{} ({}) -> {} ({})".format( 959 self.src_addr, self.src_port, 960 self.dst_addr, self.dst_port) 961 962 return "{} -> {}".format(self.src_addr, self.dst_addr) 963 964 if self.addressing_family == "AF_UNIX": 965 return self.get_socketinfo_attr("unsi_addr") 966 967 return None
968 969 @utils.safe_property
970 - def human_type(self):
971 if self.addressing_family == "AF_INET": 972 return "{}v4".format(self.l4_protocol) 973 974 if self.addressing_family == "AF_INET6": 975 proto = self.l4_protocol 976 977 # Some v6 protocols are already named with v6 in the name. 978 if proto.endswith("6"): 979 return proto 980 981 return "{}v6".format(self.l4_protocol) 982 983 if self.addressing_family == "AF_UNIX": 984 return self.unix_type 985 986 return "Sock: {}".format(self.addressing_family)
987
988 989 -class sockaddr(obj.Struct):
990 - def _get_address_obj(self):
991 addr = obj.NoneObject("Unknown socket family") 992 993 if self.sa_family == "AF_INET": 994 addr = self.cast("sockaddr_in").sin_addr.s_addr 995 996 elif self.sa_family == "AF_INET6": 997 addr = self.cast("sockaddr_in6").sin6_addr.m("__u6_addr") 998 999 elif self.sa_family == "AF_LINK": 1000 addr = self.cast("sockaddr_dl") 1001 1002 return addr
1003 1004 @utils.safe_property
1005 - def address(self):
1006 result = "" 1007 addr = self._get_address_obj() 1008 if addr: 1009 if self.sa_family in ("AF_INET6", "AF_INET"): 1010 result = utils.FormatIPAddress(self.sa_family, addr) 1011 1012 elif self.sa_family == "AF_LINK": 1013 result = addr 1014 1015 return str(result)
1016
1017 - def __unicode__(self):
1018 return self.address
1019
1020 1021 -class vm_map_entry(obj.Struct):
1022 - def find_vnode_object(self):
1023 """Find the underlying vnode object for the given vm_map_entry. 1024 1025 xnu-2422.1.72/osfmk/vm/bsd_vm.c: 1339. 1026 """ 1027 if not self.is_sub_map.v(): 1028 #/* 1029 #* The last object in the shadow chain has the 1030 #* relevant pager information. 1031 #*/ 1032 1033 shadow = self.last_shadow 1034 if not shadow: 1035 return shadow 1036 1037 if (shadow and not shadow.internal.v() and 1038 shadow.pager_ready.v() and 1039 not shadow.terminating.v() and 1040 shadow.alive.v()): 1041 memory_object = shadow.pager 1042 pager_ops = memory_object.mo_pager_ops 1043 1044 # If this object points to the vnode_pager_ops, then we 1045 # found what we're looking for. Otherwise, this 1046 # vm_map_entry doesn't have an underlying vnode and so we 1047 # fall through to the bottom and return NULL. 1048 1049 if pager_ops == self.obj_profile.get_constant( 1050 "_vnode_pager_ops", is_address=True): 1051 return shadow.pager.dereference_as( 1052 "vnode_pager").vnode_handle 1053 1054 return obj.NoneObject("vnode not found")
1055 1056 @utils.safe_property
1057 - def sharing_mode(self):
1058 """Returns the sharing mode of the backing vm_object. 1059 1060 This is losely adapted from vm_map.c, void vm_map_region_top_walk(), 1061 except we're not filling page counts for resident/reusable, etc. 1062 """ 1063 if not self.vmo_object or self.is_sub_map: 1064 return "SM_EMPTY" # Nada. 1065 1066 vmobj = self.vmo_object 1067 ref_count = vmobj.ref_count 1068 if vmobj.paging_in_progress: 1069 ref_count -= 1 1070 1071 if vmobj.shadow: 1072 return "SM_COW" # Copy on write. 1073 1074 if self.superpage_size: 1075 return "SM_LARGE_PAGE" # Shared large (huge) page. 1076 1077 if self.needs_copy: 1078 return "SM_COW" 1079 1080 if ref_count == 1 or (not vmobj.pager_trusted and not 1081 vmobj.internal): 1082 return "SM_PRIVATE" 1083 1084 return "SM_SHARED"
1085 1086 @utils.safe_property
1087 - def code_signed(self):
1088 return self.last_shadow.code_signed
1089 1090 @utils.safe_property
1091 - def last_shadow(self):
1092 shadow = self.vmo_object 1093 if not shadow: 1094 return obj.NoneObject("no vm_object found") 1095 1096 while shadow.shadow: 1097 shadow = shadow.shadow 1098 1099 return shadow
1100 1101 @utils.safe_property
1102 - def start(self):
1103 return self.links.start.v()
1104 1105 @utils.safe_property
1106 - def end(self):
1107 return self.links.end.v()
1108 1109 @utils.safe_property
1110 - def vmo_object(self):
1111 """Return the vm_object instance for this entry. 1112 1113 There's an intermediate link called struct vm_map_entry. 1114 1115 The members will be called either 'object' and 'vm_object' or 1116 'vme_object' and 'vmo_object'. 1117 1118 There is no easy heuristic for which it will be in a particular kernel 1119 version* so we just try both, since they mean the same thing. 1120 1121 * The kernel version numbers could be identical for kernels built from 1122 a feature branch and a kernel build from trunk, and the two could be 1123 months apart. Furthermore, the profiles are generated not from the 1124 kernel itself but from a debug kit and can end up using out of date 1125 naming conventions. 1126 """ 1127 vme_object = self.multi_m("vme_object", "object") 1128 return vme_object.multi_m("vmo_object", "vm_object")
1129
1130 1131 -class clist(obj.Struct):
1132 1133 @utils.safe_property
1134 - def recovered_contents(self):
1135 """Gets the full contents of the ring buffer, which may be freed. 1136 1137 This is different from getting the legal contents as with b_to_q [1] 1138 because clists are only used by TTYs and they seem to always be all 1139 marked as consumed, so b_to_q wouldn't let us see any content. 1140 1141 1: github.com/opensource-apple/xnu/blob/10.9/bsd/kern/tty_subr.c#L358 1142 """ 1143 return utils.HexDumpedString( 1144 self.obj_vm.read(self.c_cs, self.c_cn))
1145 1146 @utils.safe_property
1147 - def size(self):
1148 return int(self.c_cn)
1149
1150 1151 -class tty(obj.Struct):
1152 @utils.safe_property
1153 - def vnode(self):
1154 return self.t_session.s_ttyvp
1155 1156 @utils.safe_property
1157 - def input_buffer(self):
1158 return self.t_rawq
1159 1160 @utils.safe_property
1161 - def output_buffer(self):
1162 return self.t_outq
1163
1164 1165 -class proc(obj.Struct):
1166 """Represents a Darwin process.""" 1167 1168 @utils.safe_property
1169 - def vads(self):
1170 return self.task.map.hdr.walk_list("links.next", include_current=False)
1171
1172 - def get_open_files(self):
1173 """Gets all open files (sockets, pipes...) owned by this proc. 1174 1175 Yields: 1176 tuple of (fd, fileproc, flags) 1177 """ 1178 # lastfile is a high water mark of valid fds [1]. That doesn't mean 1179 # there are no invalid fds at lower indexes! fd_freefile is a free 1180 # descriptor that tends to gravitate towards the lowest index as 1181 # as seen here [2]. When the kernel frees an fd it sets the pointer 1182 # to NULL and also clears the corresponding index in fd_ofilesflags 1183 # [3]. This creates a sparse array, so the search has to skip over 1184 # invalid fds along the way, just as the kernel does [4]. We skip 1185 # NULL pointers (and invalid pointers) but don't check for cleared 1186 # flags, since they're usually zero anyway. 1187 # 1188 # [1]: 1189 # https://github.com/opensource-apple/xnu/blob/10.9/bsd/sys/filedesc.h#L96 1190 # [2]: 1191 # https://github.com/opensource-apple/xnu/blob/10.9/bsd/kern/kern_descrip.c#L412 1192 # [3]: 1193 # https://github.com/opensource-apple/xnu/blob/10.9/bsd/kern/kern_descrip.c#L384 1194 # [4]: 1195 # https://github.com/opensource-apple/xnu/blob/10.9/bsd/kern/kern_descrip.c#L2960 1196 last_fd = self.p_fd.fd_lastfile 1197 ofiles = self.p_fd.fd_ofiles.deref() 1198 ofileflags = self.p_fd.fd_ofileflags 1199 1200 for fd in xrange(last_fd + 1): # xrange stops at N-1. 1201 file_obj = ofiles[fd].deref() 1202 1203 # file_obj will be None if the pointer is NULL (see ref [4]), and 1204 # also when the pointer is simply invalid, which can happen 1205 # sometimes. Currently, I chalk it up to inconsistencies in the 1206 # volatile RAM image (since it's rare) but it might have another 1207 # explanation. 1208 if file_obj: 1209 yield (fd, file_obj, ofileflags[fd])
1210
1211 - def get_process_address_space(self):
1212 cr3 = self.task.map.pmap.pm_cr3 1213 as_class = self.obj_vm.__class__ 1214 if self.task.map.pmap.pm_task_map == "TASK_MAP_64BIT_SHARED": 1215 as_class = amd64.AMD64PagedMemory 1216 1217 return as_class(base=self.obj_vm.base, session=self.obj_vm.session, 1218 dtb=cr3, name="Pid %s" % self.p_pid)
1219 1220 @utils.safe_property
1221 - def command(self):
1222 return utils.SmartUnicode(self.p_comm)
1223 1224 @utils.safe_property
1225 - def cr3(self):
1226 return self.task.map.pmap.pm_cr3
1227 1228 @utils.safe_property
1229 - def is_64bit(self):
1230 return proc.task.map.pmap.pm_task_map == "TASK_MAP_64BIT"
1231 1232 @utils.safe_property
1233 - def argv(self):
1234 result = [] 1235 array = self.obj_profile.ListArray( 1236 target="String", 1237 offset=self.user_stack - self.p_argslen, 1238 vm=self.get_process_address_space(), 1239 maximum_size=self.p_argslen, 1240 ) 1241 1242 for item in array: 1243 # Total size of the argv array is specified in argc (not counting 1244 # padding). 1245 if len(result) >= self.p_argc: 1246 break 1247 1248 item = unicode(item) 1249 1250 # The argv array may have null padding for alignment. Discard these 1251 # empty strings. 1252 if not len(item): 1253 continue 1254 1255 result.append(item) 1256 1257 # argv[0] is often repeated as the executable name, to avoid confusion, 1258 # we just discard it. 1259 if len(result) > 1 and result[0] == result[1]: 1260 result.pop(0) 1261 1262 return result
1263
1264 - def validate(self):
1265 """Use heuristics to guess whether this proc is valid.""" 1266 return (self.p_argc > 0 1267 and len(self.p_comm) > 0 1268 and self.p_start.v() > 0 1269 and 99999 > self.pid > 0)
1270
1271 1272 -class vnode(obj.Struct):
1273 @utils.safe_property
1274 - def full_path(self):
1275 # TODO: Speed this up by caching the paths in the session. 1276 result = [] 1277 _vnode = self 1278 1279 # Iterate here until we hit the root of the filesystem. 1280 while not (_vnode.v_flag.VROOT and 1281 _vnode.v_mount.mnt_flag.MNT_ROOTFS): 1282 result.append(_vnode.v_name.deref()) 1283 1284 # If there is no parent skip to the mount point. 1285 _vnode = _vnode.v_parent or _vnode.v_mount.mnt_vnodecovered 1286 1287 # This is rare, but it does happen. I currently don't understand 1288 # why, so we just log a warning and report the node as an orphan. 1289 if not _vnode: 1290 self.obj_session.logging.warning("vnode at 0x%x is orphaned.", 1291 int(_vnode)) 1292 return "<Orphan>" 1293 1294 path = "/" + "/".join((str(x) for x in reversed(result) if x)) 1295 return unicode(path.encode("string-escape"))
1296 # return "/" + "/".join((unicode(x) for x in reversed(result) if x)) 1297 1298 @utils.safe_property
1299 - def human_type(self):
1300 return "Reg. File"
1301 1302 @utils.safe_property
1303 - def human_name(self):
1304 return self.full_path
1305 1306 @utils.safe_property
1307 - def cnode(self):
1308 """If this is an HFS vnode, then v_data is a cnode.""" 1309 node = self.v_data.dereference_as("cnode") 1310 if node.c_rwlock != node: 1311 return obj.NoneObject("This vnode has no valid cnode.") 1312 1313 return node
1314 1315 @utils.safe_property
1316 - def uid(self):
1317 uid = self.v_cred.cr_posix.cr_ruid 1318 if uid: 1319 return uid 1320 1321 return obj.NoneObject("Could not retrieve POSIX creds.")
1322
1323 1324 -class cnode(obj.Struct):
1325 @utils.safe_property
1326 - def created_at(self):
1327 return self.c_cattr.ca_ctime.as_datetime()
1328 1329 @utils.safe_property
1330 - def modified_at(self):
1331 return self.c_cattr.ca_mtime.as_datetime()
1332 1333 @utils.safe_property
1334 - def accessed_at(self):
1335 return self.c_cattr.ca_atime.as_datetime()
1336 1337 @utils.safe_property
1338 - def backedup_at(self):
1339 return self.c_cattr.ca_btime.as_datetime()
1340
1341 1342 -class zone(obj.Struct):
1343 @utils.safe_property
1344 - def name(self):
1345 return utils.SmartUnicode(self.zone_name.deref())
1346 1347 @utils.safe_property
1348 - def count_active(self):
1349 return int(self.count)
1350 1351 @utils.safe_property
1352 - def count_free(self):
1353 return int(self.m("sum_count") - self.count)
1354 1355 @utils.safe_property
1356 - def tracks_pages(self):
1357 return bool(self.m("use_page_list"))
1358 1359 @utils.safe_property
1360 - def known_offsets(self):
1361 """Find valid offsets in the zone as tuples of (state, offset). 1362 1363 Allocation zones keep track of potential places where an element of 1364 fixed size may be stored. The most basic zones only keep track of free 1365 pointers, so as to speed up allocation. Some zones also track already 1366 allocated data, using a separate mechanism. We support both. 1367 1368 Returns a set of tuples of: 1369 - State, which can be "freed", "allocated" or "unknown". 1370 - Object offset, at which a struct may be located. 1371 (You will want to validate the struct itself for sanity.) 1372 """ 1373 # Tracks what offsets we've looked at. 1374 seen_offsets = set() 1375 1376 # Tracks pages we've tried to iterate through for possible offsets. 1377 seen_pages = set() 1378 1379 # Let's walk the freed elements first. It's just a linked list: 1380 for element in self.free_elements.walk_list("next"): 1381 seen_offsets.add(element.obj_offset) 1382 1383 # If we found just one known offset in a given page we actually know 1384 # that the whole page is dedicated to the zone allocator and other 1385 # offsets in it are also likely to be valid elements. Here we try to 1386 # discover such elements. 1387 for offset in seen_offsets.copy(): 1388 # We assume pages are 4K. The zone allocator presently doesn't use 1389 # 2MB pages, as far as I know. 1390 page_start = offset & ~0xfff 1391 if page_start in seen_pages: 1392 continue 1393 1394 seen_pages.add(page_start) 1395 seen_offsets.update(set(self._generate_page_offsets(page_start))) 1396 1397 # Lastly, if we happen to track pages after they've been filled up 1398 # then we can go look at those pages. The relevant flag is 1399 # use_page_list. 1400 page_lists = {"all_free", "all_used", "intermediate"} 1401 1402 # Field not present on OSX 10.7 1403 if self.m("use_page_list"): 1404 for page_list in page_lists: 1405 for page_start in self.m(page_list).walk_list("next"): 1406 if page_start in seen_pages: 1407 continue 1408 1409 seen_pages.add(page_start) 1410 seen_offsets.update(self._generate_page_offsets(page_start)) 1411 1412 return seen_offsets
1413
1414 - def _generate_page_offsets(self, page_start):
1415 limit = page_start + 0x1000 - self.elem_size 1416 # Page metadata is always inlined at the end of the page. So that's 1417 # space that contain valid elements. 1418 limit -= self.obj_profile.get_obj_size("zone_page_metadata") 1419 1420 return xrange(page_start, limit, self.elem_size)
1421
1422 1423 -class ifnet(obj.Struct):
1424 @utils.safe_property
1425 - def name(self):
1426 return "%s%d" % (self.if_name.deref(), self.if_unit)
1427 1428 @utils.safe_property
1429 - def addresses(self):
1430 # There should be exactly one link layer address. 1431 for tqe in self.if_addrhead.tqh_first.walk_list( 1432 "ifa_link.tqe_next"): 1433 family = tqe.ifa_addr.sa_family 1434 1435 # Found the L2 address (MAC) 1436 if family == "AF_LINK": 1437 l2_addr = utils.SmartUnicode(tqe.ifa_addr.deref()) 1438 yield ("MAC", l2_addr) 1439 continue 1440 elif family == "AF_INET": 1441 l3_proto = "IPv4" 1442 elif family == "AF_INET6": 1443 l3_proto = "IPv6" 1444 else: 1445 l3_proto = utils.SmartUnicode(family).replace("AF_", "") 1446 1447 l3_addr = utils.SmartUnicode(tqe.ifa_addr.deref()) 1448 yield (l3_proto, l3_addr)
1449 1450 @utils.safe_property
1451 - def l2_addr(self):
1452 for proto, addr in self.addresses: 1453 if proto == "MAC": 1454 return addr
1455 1456 @utils.safe_property
1457 - def l3_addrs(self):
1458 return [(proto, addr) for proto, addr in self.addresses 1459 if proto != "MAC"]
1460 1461 @utils.safe_property
1462 - def ipv4_addr(self):
1463 result = [] 1464 for proto, addr in self.addresses: 1465 if proto == "IPv4": 1466 result.append(addr) 1467 1468 return ", ".join(result)
1469 1470 @utils.safe_property
1471 - def ipv6_addr(self):
1472 result = [] 1473 for proto, addr in self.addresses: 1474 if proto == "IPv6": 1475 result.append(addr) 1476 1477 return ", ".join(result)
1478
1479 1480 -class session(obj.Struct):
1481 @utils.safe_property
1482 - def tty(self):
1483 return self.session.s_ttyp
1484 1485 @utils.safe_property
1486 - def name(self):
1487 return "Session %d (%s)" % (self.s_sid, self.s_leader.command)
1488 1489 @utils.safe_property
1490 - def username(self):
1491 return utils.SmartUnicode(self.s_login)
1492 1493 @utils.safe_property
1494 - def uid(self):
1495 return self.tty.vnode.uid
1496
1497 1498 -class OSDictionary(obj.Struct):
1499 """The OSDictionary is a general purpose associative array described: 1500 1501 xnu-1699.26.8/libkern/libkern/c++/OSDictionary.h 1502 """ 1503
1504 - def items(self, value_class=None):
1505 """Iterate over the associative array and yield key, value pairs.""" 1506 for entry in self.dictionary: 1507 key = entry.key.dereference_as("OSString").value 1508 if value_class: 1509 yield key, entry.value.dereference_as(value_class) 1510 else: 1511 yield key, entry.value
1512
1513 1514 -class OSOrderedSet(obj.Struct):
1515 """An OSOrderedSet is a list of OSObject instances. 1516 1517 xnu-1699.26.8/libkern/libkern/c++/OSOrderedSet.h 1518 """ 1519
1520 - def list_of_type(self, type_name):
1521 for item in self.array: 1522 yield item.obj.dereference_as(type_name)
1523
1524 1525 -class Darwin32(basic.Profile32Bits, basic.BasicClasses):
1526 """A Darwin profile.""" 1527 METADATA = dict( 1528 os="darwin", 1529 arch="I386", 1530 type="Kernel") 1531 1532 @classmethod
1533 - def Initialize(cls, profile):
1534 super(Darwin32, cls).Initialize(profile) 1535 1536 # Some Darwin profiles add a suffix to IOKIT objects. So OSDictionary 1537 # becomes OSDictionary_class. We automatically generate the overlays and 1538 # classes to account for this. 1539 for k in profile.vtypes.keys(): 1540 if k.endswith("_class"): 1541 stripped_k = k[:-len("_class")] 1542 if stripped_k not in profile.vtypes: 1543 profile.vtypes[stripped_k] = profile.vtypes[k] 1544 if stripped_k in darwin_overlay: 1545 darwin_overlay[k] = darwin_overlay[stripped_k] 1546 1547 profile.add_classes( 1548 LIST_ENTRY=LIST_ENTRY, queue_entry=queue_entry, 1549 sockaddr=sockaddr, sockaddr_dl=sockaddr_dl, 1550 vm_map_entry=vm_map_entry, proc=proc, vnode=vnode, 1551 socket=socket, clist=clist, zone=zone, ifnet=ifnet, tty=tty, 1552 # Support both forms with and without _class suffix. 1553 OSDictionary=OSDictionary, OSDictionary_class=OSDictionary, 1554 OSOrderedSet=OSOrderedSet, OSOrderedSet_class=OSOrderedSet, 1555 fileproc=fileproc, session=session, cnode=cnode, 1556 llinfo_arp=llinfo_arp) 1557 profile.add_enums(**darwin_enums) 1558 profile.add_overlay(darwin_overlay) 1559 profile.add_constants(dict(default_text_encoding="utf8"))
1560
1561 - def get_constant_cpp_object(self, constant, **kwargs):
1562 """A variant of get_constant_object which accounts for name mangling.""" 1563 for key in self.constants: 1564 if constant in key: 1565 return self.get_constant_object(key, **kwargs)
1566
1567 1568 -class Darwin64(basic.RelativeOffsetMixin, basic.ProfileLP64, Darwin32):
1569 """Support for 64 bit darwin systems.""" 1570 METADATA = dict( 1571 os="darwin", 1572 arch="AMD64", 1573 type="Kernel") 1574 1575 image_base = 0 1576 1577 @classmethod
1578 - def Initialize(cls, profile):
1581
1582 - def GetImageBase(self):
1583 if not self.image_base: 1584 self.image_base = self.session.GetParameter( 1585 "vm_kernel_slide", 0) 1586 1587 return self.image_base
1588