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 if not self.proc: 106 raise ValueError("Task has no associated BSD process.") 107 dataregisters64bit = False 108 ptrsize = 4 109 110 if task.t_flags & 0x1: 111 ptrsize = 8 112 if task.t_flags & 0x2: 113 dataregisters64bit = True 114 115 self.cputype = unsigned(self.proc.p_cputype) 116 self.cpusubtype = unsigned(self.proc.p_cpusubtype) 117 118 super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize) 119 dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype) 120 self.proc_platform = int(GetProcPlatform(self.proc)) 121 if self.proc_platform == xnudefines.P_PLATFORM_MACOS: 122 self.hinfo['ostype'] = 'macosx' 123 elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS: 124 self.hinfo['ostype'] = 'watchos' 125 elif self.proc_platform == xnudefines.P_PLATFORM_TVOS: 126 self.hinfo['ostype'] = 'tvos' 127 else: 128 self.hinfo['ostype'] = 'ios' 129 dbg_message += " ostype:%s" % self.hinfo['ostype'] 130 131 if str(kern.arch).lower().startswith('arm'): 132 addressing_bits = 64 - int(kern.globals.gT1Sz) 133 self.hinfo['addressing_bits'] = addressing_bits 134 dbg_message += " addressing_bits:%d" % addressing_bits 135 136 self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype) 137 logging.info(dbg_message) 138 self.threads = {} 139 self.threads_ids_list = [] 140 logging.debug("iterating over threads in process") 141 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 142 self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype) 143 self.threads_ids_list.append(unsigned(thval.thread_id)) 144 145 def getRegisterDataForThread(self, th_id, reg_num): 146 if th_id not in self.threads: 147 logging.critical("0x%x thread id is not found in this task") 148 return '' 149 if reg_num < 0 or reg_num >= len(self.registerset): 150 logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id)) 151 return '' 152 value = self.threads[th_id].getRegisterData(reg_num) 153 return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8)) 154 155 def getRegisterCombinedDataForThread(self, th_id): 156 if th_id not in self.threads: 157 logging.critical("0x%x thread id is not found in this task" % th_id) 158 return '' 159 cur_thread = self.threads[th_id] 160 retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName()) 161 pos = 0 162 for rinfo in self.registerset: 163 name = rinfo['name'] 164 format = "%02x:%s;" 165 value = cur_thread.getRegisterValueByName(name) 166 value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8)) 167 retval += format % (pos, value_endian_correct_str) 168 pos += 1 169 return retval 170 171 def getThreadStopInfo(self, th_id): 172 if th_id not in self.threads: 173 logging.critical("0x%x thread id is not found in this task") 174 return '' 175 return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';' 176 177 def getRegisterInfo(self, regnum): 178 #something similar to 179 #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;" 180 if regnum >= len(self.registerset): 181 logging.debug("No register_info for number %d." % regnum) 182 return 'E45' 183 184 rinfo = self.registerset[regnum] 185 retval = '' 186 for i in list(rinfo.keys()): 187 i_val = str(rinfo[i]) 188 if i == 'set': 189 i_val = 'General Purpose Registers' 190 retval += '%s:%s;' % (str(i), i_val) 191 192 return retval 193 194 def getProcessInfo(self): 195 retval = '' 196 #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3; 197 #ostype:macosx;vendor:apple;endian:little;ptrsize:8; 198 pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'} 199 pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task)) 200 pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid)) 201 pinfo['ptrsize'] = str(self.ptrsize) 202 pinfo['ostype'] = 'macosx' 203 pinfo['cputype'] = "%x" % self.cputype 204 pinfo['cpusubtype'] = "%x" % self.cpusubtype 205 pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid)) 206 pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid)) 207 if str(kern.arch).find('arm') >= 0: 208 pinfo['ostype'] = 'ios' 209 for i in list(pinfo.keys()): 210 i_val = str(pinfo[i]) 211 retval += '%s:%s;' % (str(i), i_val) 212 return retval 213 214 def readMemory(self, address, size): 215 cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size) 216 cache_data = caching.GetDynamicCacheData(cache_key) 217 if cache_data: 218 return self.encodeByteString(cache_data) 219 data = GetUserDataAsString(self.task, address, size) 220 if not data: 221 logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size)) 222 else: 223 caching.SaveDynamicCacheData(cache_key, data) 224 return self.encodeByteString(data) 225 226 def getSharedLibInfoAddress(self): 227 return unsigned(self.task.all_image_info_addr) 228