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