1from __future__ import absolute_import, division 2 3from builtins import hex 4 5from builtins import object 6import logging 7from . import target 8import struct 9 10from xnu import * 11from core.operating_system import Armv8_RegisterSet, Armv7_RegisterSet, I386_RegisterSet, X86_64RegisterSet 12from core import caching 13 14""" these defines should come from an authoritative header file """ 15CPU_TYPE_I386 = 0x00000007 16CPU_TYPE_X86_64 = 0x01000007 17CPU_TYPE_ARM = 0x0000000c 18CPU_TYPE_ARM64 = 0x0100000c 19CPU_TYPE_ARM64_32 = 0x0200000c 20 21def GetRegisterSetForCPU(cputype, subtype): 22 if cputype == CPU_TYPE_ARM64: 23 retval = Armv8_RegisterSet 24 elif cputype == CPU_TYPE_ARM64_32: 25 retval = Armv8_RegisterSet 26 elif cputype == CPU_TYPE_ARM: 27 retval = Armv7_RegisterSet 28 elif cputype == CPU_TYPE_I386: 29 retval = I386_RegisterSet 30 elif cputype == CPU_TYPE_X86_64: 31 retval = X86_64RegisterSet 32 33 """ crash if unknown cputype """ 34 35 return retval.register_info['registers'] 36 37 38class UserThreadObject(object): 39 """representation of userspace thread""" 40 def __init__(self, thr_obj, cputype, cpusubtype): 41 super(UserThreadObject, self).__init__() 42 self.thread = thr_obj 43 self.registerset = GetRegisterSetForCPU(cputype, cpusubtype) 44 self.thread_id = unsigned(self.thread.thread_id) 45 self.is64Bit = bool(cputype & 0x01000000) 46 47 if self.is64Bit: 48 if cputype == CPU_TYPE_X86_64: 49 self.reg_type = "x86_64" 50 self.saved_state = Cast(self.thread.machine.iss, 'x86_saved_state_t *').uss.ss_64 51 if cputype == CPU_TYPE_ARM64: 52 self.reg_type = "arm64" 53 self.saved_state = self.thread.machine.upcb.uss.ss_64 54 else: 55 if cputype == CPU_TYPE_I386: 56 self.reg_type = "i386" 57 self.saved_state = Cast(self.thread.machine.iss, 'x86_saved_state_t *').uss.ss_32 58 if cputype == CPU_TYPE_ARM: 59 self.reg_type = "arm" 60 self.saved_state = self.thread.machine.contextData.ss.uss.ss_32 61 if cputype == CPU_TYPE_ARM64_32: 62 self.reg_type = "arm64" 63 self.saved_state = self.thread.machine.upcb.uss.ss_64 64 65 logging.debug("created thread id 0x%x of type %s, cputype 0x%x" 66 % (self.thread_id, self.reg_type, cputype)) 67 68 def getRegisterValueByName(self, name): 69 if self.reg_type == 'arm64': 70 if name in ('x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28'): 71 return unsigned(getattr(self.saved_state, 'x')[int(name.strip('x'))]) 72 73 return unsigned(getattr(self.saved_state, name)) 74 75 if self.reg_type == "x86_64": 76 if name in ('rip', 'rflags', 'cs', 'rsp', 'cpu'): 77 return unsigned(getattr(self.saved_state.isf, name)) 78 return unsigned(getattr(self.saved_state, name)) 79 80 if self.reg_type == "arm": 81 if name in ('r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12'): 82 retval = unsigned(getattr(self.saved_state, 'r')[int(name.strip('r'))]) 83 else: 84 retval = unsigned(getattr(self.saved_state, name)) 85 return retval 86 87 #TODO for i386 88 89 def getName(self): 90 return str(self.thread_id) 91 92 def getRegisterData(self, reg_num): 93 """ returns None if there is error """ 94 if reg_num < 0 or reg_num >= len(self.registerset): 95 logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, self.thread_id)) 96 return None 97 return self.getRegisterValueByName(self.registerset[reg_num]['name']) 98 99 100class UserProcess(target.Process): 101 """ Represent a user process and thread states """ 102 def __init__(self, task): 103 self.task = task 104 self.proc = GetProcFromTask(task) 105 self.cache = {} 106 if not self.proc: 107 raise ValueError("Task has no associated BSD process.") 108 dataregisters64bit = False 109 ptrsize = 4 110 111 if task.t_flags & 0x1: 112 ptrsize = 8 113 if task.t_flags & 0x2: 114 dataregisters64bit = True 115 116 self.cputype = unsigned(self.proc.p_cputype) 117 self.cpusubtype = unsigned(self.proc.p_cpusubtype) 118 119 super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize) 120 dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype) 121 self.proc_platform = int(GetProcPlatform(self.proc)) 122 if self.proc_platform == xnudefines.P_PLATFORM_MACOS: 123 self.hinfo['ostype'] = 'macosx' 124 elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS: 125 self.hinfo['ostype'] = 'watchos' 126 elif self.proc_platform == xnudefines.P_PLATFORM_TVOS: 127 self.hinfo['ostype'] = 'tvos' 128 else: 129 self.hinfo['ostype'] = 'ios' 130 dbg_message += " ostype:%s" % self.hinfo['ostype'] 131 132 if str(kern.arch).lower().startswith('arm'): 133 addressing_bits = 64 - int(kern.globals.gT1Sz) 134 self.hinfo['addressing_bits'] = addressing_bits 135 dbg_message += " addressing_bits:%d" % addressing_bits 136 137 self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype) 138 logging.info(dbg_message) 139 self.threads = {} 140 self.threads_ids_list = [] 141 logging.debug("iterating over threads in process") 142 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 143 self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype) 144 self.threads_ids_list.append(unsigned(thval.thread_id)) 145 146 def getRegisterDataForThread(self, th_id, reg_num): 147 if th_id not in self.threads: 148 logging.critical("0x%x thread id is not found in this task") 149 return '' 150 if reg_num < 0 or reg_num >= len(self.registerset): 151 logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id)) 152 return '' 153 value = self.threads[th_id].getRegisterData(reg_num) 154 return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8)) 155 156 def getRegisterCombinedDataForThread(self, th_id): 157 if th_id not in self.threads: 158 logging.critical("0x%x thread id is not found in this task" % th_id) 159 return '' 160 cur_thread = self.threads[th_id] 161 retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName()) 162 pos = 0 163 for rinfo in self.registerset: 164 name = rinfo['name'] 165 format = "%02x:%s;" 166 value = cur_thread.getRegisterValueByName(name) 167 value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8)) 168 retval += format % (pos, value_endian_correct_str) 169 pos += 1 170 return retval 171 172 def getThreadStopInfo(self, th_id): 173 if th_id not in self.threads: 174 logging.critical("0x%x thread id is not found in this task") 175 return '' 176 return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';' 177 178 def getRegisterInfo(self, regnum): 179 #something similar to 180 #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;" 181 if regnum >= len(self.registerset): 182 logging.debug("No register_info for number %d." % regnum) 183 return 'E45' 184 185 rinfo = self.registerset[regnum] 186 retval = '' 187 for i in list(rinfo.keys()): 188 i_val = str(rinfo[i]) 189 if i == 'set': 190 i_val = 'General Purpose Registers' 191 retval += '%s:%s;' % (str(i), i_val) 192 193 return retval 194 195 def getProcessInfo(self): 196 retval = '' 197 #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3; 198 #ostype:macosx;vendor:apple;endian:little;ptrsize:8; 199 pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'} 200 pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task)) 201 pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid)) 202 pinfo['ptrsize'] = str(self.ptrsize) 203 pinfo['ostype'] = 'macosx' 204 pinfo['cputype'] = "%x" % self.cputype 205 pinfo['cpusubtype'] = "%x" % self.cpusubtype 206 pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid)) 207 pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid)) 208 if str(kern.arch).find('arm') >= 0: 209 pinfo['ostype'] = 'ios' 210 for i in list(pinfo.keys()): 211 i_val = str(pinfo[i]) 212 retval += '%s:%s;' % (str(i), i_val) 213 return retval 214 215 def readMemory(self, address, size): 216 cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size) 217 cache_data = self.cache.get(cache_key, None) 218 if cache_data: 219 return self.encodeByteString(cache_data) 220 data = GetUserDataAsString(self.task, address, size) 221 if not data: 222 logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size)) 223 else: 224 self.cache[cache_key] = data 225 return self.encodeByteString(data) 226 227 def getSharedLibInfoAddress(self): 228 return unsigned(self.task.all_image_info_addr) 229