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, is_kern_64bit): 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 if not is_kern_64bit: 61 self.saved_state = self.thread.machine.PcbData 62 else: 63 self.saved_state = self.thread.machine.contextData.ss.uss.ss_32 64 if cputype == CPU_TYPE_ARM64_32: 65 self.reg_type = "arm64" 66 self.saved_state = self.thread.machine.upcb.uss.ss_64 67 68 logging.debug("created thread id 0x%x of type %s, is_kern_64bit 0x%x cputype 0x%x" 69 % (self.thread_id, self.reg_type, is_kern_64bit, cputype)) 70 71 def getRegisterValueByName(self, name): 72 if self.reg_type == 'arm64': 73 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'): 74 return unsigned(getattr(self.saved_state, 'x')[int(name.strip('x'))]) 75 76 return unsigned(getattr(self.saved_state, name)) 77 78 if self.reg_type == "x86_64": 79 if name in ('rip', 'rflags', 'cs', 'rsp', 'cpu'): 80 return unsigned(getattr(self.saved_state.isf, name)) 81 return unsigned(getattr(self.saved_state, name)) 82 83 if self.reg_type == "arm": 84 if name in ('r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12'): 85 retval = unsigned(getattr(self.saved_state, 'r')[int(name.strip('r'))]) 86 else: 87 retval = unsigned(getattr(self.saved_state, name)) 88 return retval 89 90 #TODO for i386 91 92 def getName(self): 93 return str(self.thread_id) 94 95 def getRegisterData(self, reg_num): 96 """ returns None if there is error """ 97 if reg_num < 0 or reg_num >= len(self.registerset): 98 logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, self.thread_id)) 99 return None 100 return self.getRegisterValueByName(self.registerset[reg_num]['name']) 101 102 103class UserProcess(target.Process): 104 """ Represent a user process and thread states """ 105 def __init__(self, task): 106 self.task = task 107 self.proc = Cast(task.bsd_info, 'proc_t') 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 is_kern_64bit = kern.arch in ['x86_64', 'x86_64h', 'arm64', 'arm64e'] 117 118 self.cputype = unsigned(self.proc.p_cputype) 119 self.cpusubtype = unsigned(self.proc.p_cpusubtype) 120 121 super(UserProcess, self).__init__(self.cputype, self.cpusubtype, ptrsize) 122 dbg_message = "process:%s is64bit:%d ptrsize:%d cputype:0x%x cpusubtype:0x%x" % (hex(self.proc), int(dataregisters64bit), ptrsize, self.cputype, self.cpusubtype) 123 self.proc_platform = int(GetProcPlatform(self.proc)) 124 if self.proc_platform == xnudefines.P_PLATFORM_MACOS: 125 self.hinfo['ostype'] = 'macosx' 126 elif self.proc_platform == xnudefines.P_PLATFORM_WATCHOS: 127 self.hinfo['ostype'] = 'watchos' 128 elif self.proc_platform == xnudefines.P_PLATFORM_TVOS: 129 self.hinfo['ostype'] = 'tvos' 130 else: 131 self.hinfo['ostype'] = 'ios' 132 dbg_message += " ostype:%s" % self.hinfo['ostype'] 133 134 if is_kern_64bit and str(kern.arch).lower().startswith('arm'): 135 addressing_bits = 64 - int(kern.globals.gT1Sz) 136 self.hinfo['addressing_bits'] = addressing_bits 137 dbg_message += " addressing_bits:%d" % addressing_bits 138 139 self.registerset = GetRegisterSetForCPU(self.cputype, self.cpusubtype) 140 logging.info(dbg_message) 141 self.threads = {} 142 self.threads_ids_list = [] 143 logging.debug("iterating over threads in process") 144 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 145 self.threads[unsigned(thval.thread_id)] = UserThreadObject(thval, self.cputype, self.cpusubtype, is_kern_64bit) 146 self.threads_ids_list.append(unsigned(thval.thread_id)) 147 148 def getRegisterDataForThread(self, th_id, reg_num): 149 if th_id not in self.threads: 150 logging.critical("0x%x thread id is not found in this task") 151 return '' 152 if reg_num < 0 or reg_num >= len(self.registerset): 153 logging.warning("regnum %d is not defined for thread_id 0x%x" % (reg_num, th_id)) 154 return '' 155 value = self.threads[th_id].getRegisterData(reg_num) 156 return self.encodeRegisterData(value, bytesize=(self.registerset[reg_num]['bitsize'] // 8)) 157 158 def getRegisterCombinedDataForThread(self, th_id): 159 if th_id not in self.threads: 160 logging.critical("0x%x thread id is not found in this task" % th_id) 161 return '' 162 cur_thread = self.threads[th_id] 163 retval = 'thread:%s;name:%s;' % (self.encodeThreadID(th_id), cur_thread.getName()) 164 pos = 0 165 for rinfo in self.registerset: 166 name = rinfo['name'] 167 format = "%02x:%s;" 168 value = cur_thread.getRegisterValueByName(name) 169 value_endian_correct_str = self.encodeRegisterData(value, bytesize=(rinfo['bitsize'] // 8)) 170 retval += format % (pos, value_endian_correct_str) 171 pos += 1 172 return retval 173 174 def getThreadStopInfo(self, th_id): 175 if th_id not in self.threads: 176 logging.critical("0x%x thread id is not found in this task") 177 return '' 178 return 'T02' + self.getRegisterCombinedDataForThread(th_id) + 'threads:' + self.getThreadsInfo()+';' 179 180 def getRegisterInfo(self, regnum): 181 #something similar to 182 #"name:x1;bitsize:64;offset:8;encoding:uint;format:hex;gcc:1;dwarf:1;set:General Purpose Registers;" 183 if regnum >= len(self.registerset): 184 logging.debug("No register_info for number %d." % regnum) 185 return 'E45' 186 187 rinfo = self.registerset[regnum] 188 retval = '' 189 for i in list(rinfo.keys()): 190 i_val = str(rinfo[i]) 191 if i == 'set': 192 i_val = 'General Purpose Registers' 193 retval += '%s:%s;' % (str(i), i_val) 194 195 return retval 196 197 def getProcessInfo(self): 198 retval = '' 199 #pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3; 200 #ostype:macosx;vendor:apple;endian:little;ptrsize:8; 201 pinfo = {'effective-uid': 'ecf', 'effective-gid': 'b', 'endian': 'little', 'vendor': 'apple'} 202 pinfo['pid'] = "%x" % (GetProcPIDForTask(self.task)) 203 pinfo['parent-pid'] = "%x" % (unsigned(self.proc.p_ppid)) 204 pinfo['ptrsize'] = str(self.ptrsize) 205 pinfo['ostype'] = 'macosx' 206 pinfo['cputype'] = "%x" % self.cputype 207 pinfo['cpusubtype'] = "%x" % self.cpusubtype 208 pinfo['real-uid'] = "%x" % (unsigned(self.proc.p_ruid)) 209 pinfo['real-gid'] = "%x" % (unsigned(self.proc.p_rgid)) 210 if str(kern.arch).find('arm') >= 0: 211 pinfo['ostype'] = 'ios' 212 for i in list(pinfo.keys()): 213 i_val = str(pinfo[i]) 214 retval += '%s:%s;' % (str(i), i_val) 215 return retval 216 217 def readMemory(self, address, size): 218 cache_key = "{}-{}-{}".format(hex(self.task), hex(address), size) 219 cache_data = caching.GetDynamicCacheData(cache_key) 220 if cache_data: 221 return self.encodeByteString(cache_data) 222 data = GetUserDataAsString(self.task, address, size) 223 if not data: 224 logging.error("Failed to read memory task:{: <#018x} {: <#018x} {:d}".format(self.task, address, size)) 225 else: 226 caching.SaveDynamicCacheData(cache_key, data) 227 return self.encodeByteString(data) 228 229 def getSharedLibInfoAddress(self): 230 return unsigned(self.task.all_image_info_addr) 231