1import os, json, struct, signal 2 3from typing import Any, Dict 4 5import lldb 6from lldb.plugins.scripted_process import ScriptedProcess 7from lldb.plugins.scripted_process import ScriptedThread 8 9 10class StackCoreScriptedProcess(ScriptedProcess): 11 def get_module_with_name(self, target, name): 12 for module in target.modules: 13 if name in module.GetFileSpec().GetFilename(): 14 return module 15 return None 16 17 def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData): 18 super().__init__(exe_ctx, args) 19 20 self.corefile_target = None 21 self.corefile_process = None 22 23 self.backing_target_idx = args.GetValueForKey("backing_target_idx") 24 if self.backing_target_idx and self.backing_target_idx.IsValid(): 25 if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger: 26 idx = self.backing_target_idx.GetIntegerValue(42) 27 if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString: 28 idx = int(self.backing_target_idx.GetStringValue(100)) 29 self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx) 30 self.corefile_process = self.corefile_target.GetProcess() 31 for corefile_thread in self.corefile_process: 32 structured_data = lldb.SBStructuredData() 33 structured_data.SetFromJSON( 34 json.dumps( 35 { 36 "backing_target_idx": idx, 37 "thread_idx": corefile_thread.GetIndexID(), 38 } 39 ) 40 ) 41 42 self.threads[corefile_thread.GetThreadID()] = StackCoreScriptedThread( 43 self, structured_data 44 ) 45 46 if len(self.threads) == 2: 47 self.threads[len(self.threads) - 1].is_stopped = True 48 49 corefile_module = self.get_module_with_name( 50 self.corefile_target, "libbaz.dylib" 51 ) 52 if not corefile_module or not corefile_module.IsValid(): 53 return 54 module_path = os.path.join( 55 corefile_module.GetFileSpec().GetDirectory(), 56 corefile_module.GetFileSpec().GetFilename(), 57 ) 58 if not os.path.exists(module_path): 59 return 60 module_load_addr = corefile_module.GetObjectFileHeaderAddress().GetLoadAddress( 61 self.corefile_target 62 ) 63 64 self.loaded_images.append({"path": module_path, "load_addr": module_load_addr}) 65 66 def get_memory_region_containing_address( 67 self, addr: int 68 ) -> lldb.SBMemoryRegionInfo: 69 mem_region = lldb.SBMemoryRegionInfo() 70 error = self.corefile_process.GetMemoryRegionInfo(addr, mem_region) 71 if error.Fail(): 72 return None 73 return mem_region 74 75 def read_memory_at_address( 76 self, addr: int, size: int, error: lldb.SBError 77 ) -> lldb.SBData: 78 data = lldb.SBData() 79 bytes_read = self.corefile_process.ReadMemory(addr, size, error) 80 81 if error.Fail(): 82 return data 83 84 data.SetDataWithOwnership( 85 error, 86 bytes_read, 87 self.corefile_target.GetByteOrder(), 88 self.corefile_target.GetAddressByteSize(), 89 ) 90 91 return data 92 93 def get_loaded_images(self): 94 return self.loaded_images 95 96 def get_process_id(self) -> int: 97 return 42 98 99 def should_stop(self) -> bool: 100 return True 101 102 def is_alive(self) -> bool: 103 return True 104 105 def get_scripted_thread_plugin(self): 106 return ( 107 StackCoreScriptedThread.__module__ + "." + StackCoreScriptedThread.__name__ 108 ) 109 110 111class StackCoreScriptedThread(ScriptedThread): 112 def __init__(self, process, args): 113 super().__init__(process, args) 114 backing_target_idx = args.GetValueForKey("backing_target_idx") 115 thread_idx = args.GetValueForKey("thread_idx") 116 self.is_stopped = False 117 118 def extract_value_from_structured_data(data, default_val): 119 if data and data.IsValid(): 120 if data.GetType() == lldb.eStructuredDataTypeInteger: 121 return data.GetIntegerValue(default_val) 122 if data.GetType() == lldb.eStructuredDataTypeString: 123 return int(data.GetStringValue(100)) 124 return None 125 126 # TODO: Change to Walrus operator (:=) with oneline if assignment 127 # Requires python 3.8 128 val = extract_value_from_structured_data(thread_idx, 0) 129 if val is not None: 130 self.idx = val 131 132 self.corefile_target = None 133 self.corefile_process = None 134 self.corefile_thread = None 135 136 # TODO: Change to Walrus operator (:=) with oneline if assignment 137 # Requires python 3.8 138 val = extract_value_from_structured_data(backing_target_idx, 42) 139 if val is not None: 140 self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(val) 141 self.corefile_process = self.corefile_target.GetProcess() 142 self.corefile_thread = self.corefile_process.GetThreadByIndexID(self.idx) 143 144 if self.corefile_thread: 145 self.id = self.corefile_thread.GetThreadID() 146 147 def get_thread_id(self) -> int: 148 return self.id 149 150 def get_name(self) -> str: 151 return StackCoreScriptedThread.__name__ + ".thread-" + str(self.id) 152 153 def get_stop_reason(self) -> Dict[str, Any]: 154 stop_reason = {"type": lldb.eStopReasonInvalid, "data": {}} 155 156 if ( 157 self.corefile_thread 158 and self.corefile_thread.IsValid() 159 and self.get_thread_id() == self.corefile_thread.GetThreadID() 160 ): 161 stop_reason["type"] = lldb.eStopReasonNone 162 163 if self.is_stopped: 164 if "arm64" in self.scripted_process.arch: 165 stop_reason["type"] = lldb.eStopReasonException 166 stop_reason["data"][ 167 "desc" 168 ] = self.corefile_thread.GetStopDescription(100) 169 elif self.scripted_process.arch == "x86_64": 170 stop_reason["type"] = lldb.eStopReasonSignal 171 stop_reason["data"]["signal"] = signal.SIGTRAP 172 else: 173 stop_reason["type"] = self.corefile_thread.GetStopReason() 174 175 return stop_reason 176 177 def get_register_context(self) -> str: 178 if not self.corefile_thread or self.corefile_thread.GetNumFrames() == 0: 179 return None 180 frame = self.corefile_thread.GetFrameAtIndex(0) 181 182 GPRs = None 183 registerSet = frame.registers # Returns an SBValueList. 184 for regs in registerSet: 185 if "general purpose" in regs.name.lower(): 186 GPRs = regs 187 break 188 189 if not GPRs: 190 return None 191 192 for reg in GPRs: 193 self.register_ctx[reg.name] = int(reg.value, base=16) 194 195 return struct.pack( 196 "{}Q".format(len(self.register_ctx)), *self.register_ctx.values() 197 ) 198 199 200def __lldb_init_module(debugger, dict): 201 if not "SKIP_SCRIPTED_PROCESS_LAUNCH" in os.environ: 202 debugger.HandleCommand( 203 "process launch -C %s.%s" % (__name__, StackCoreScriptedProcess.__name__) 204 ) 205 else: 206 print( 207 "Name of the class that will manage the scripted process: '%s.%s'" 208 % (__name__, StackCoreScriptedProcess.__name__) 209 ) 210