]>
Commit | Line | Data |
---|---|---|
39236c6e A |
1 | |
2 | """ Please make sure you read the README COMPLETELY BEFORE reading anything below. | |
3 | It is very critical that you read coding guidelines in Section E in README file. | |
4 | """ | |
5 | ||
6 | from cvalue import * | |
7 | from lazytarget import * | |
8 | from configuration import * | |
9 | import caching | |
10 | import lldb | |
11 | ||
12 | def IterateTAILQ_HEAD(headval, element_name): | |
13 | """ iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h | |
14 | params: | |
15 | headval - value : value object representing the head of the list | |
16 | element_name- str : string name of the field which holds the list links. | |
17 | returns: | |
18 | A generator does not return. It is used for iterating. | |
19 | value : an object that is of type as headval->tqh_first. Always a pointer object | |
20 | example usage: | |
21 | list_head = kern.GetGlobalVariable('mountlist') | |
22 | for entryobj in IterateTAILQ_HEAD(list_head, 'mnt_list'): | |
23 | print GetEntrySummary(entryobj) | |
24 | """ | |
25 | iter_val = headval.tqh_first | |
26 | while unsigned(iter_val) != 0 : | |
27 | yield iter_val | |
28 | iter_val = iter_val.__getattr__(element_name).tqe_next | |
29 | #end of yield loop | |
30 | ||
31 | def IterateLinkedList(element, field_name): | |
32 | """ iterate over a linked list. | |
33 | This is equivalent to elt = element; while(elt) { do_work(elt); elt = elt-><field_name>; } | |
34 | params: | |
35 | element - value : value object representing element in the list. | |
36 | field_name - str : name of field that holds pointer to next element | |
37 | returns: Nothing. This is used as iterable | |
38 | example usage: | |
39 | first_zone = kern.GetGlobalVariable('first_zone') | |
40 | for zone in IterateLinkedList(first_zone, 'next_zone'): | |
41 | print GetZoneSummary(zone) | |
42 | """ | |
43 | elt = element | |
44 | while unsigned(elt) != 0: | |
45 | yield elt | |
46 | elt = elt.__getattr__(field_name) | |
47 | #end of while loop | |
48 | ||
49 | def IterateListEntry(element, element_type, field_name): | |
50 | """ iterate over a list as defined with LIST_HEAD in bsd/sys/queue.h | |
51 | params: | |
52 | element - value : Value object for lh_first | |
53 | element_type - str : Type of the next element | |
54 | field_name - str : Name of the field in next element's structure | |
55 | returns: | |
56 | A generator does not return. It is used for iterating | |
57 | value : an object thats of type (element_type) head->le_next. Always a pointer object | |
58 | example usage: | |
59 | headp = kern.globals.initproc.p_children | |
60 | for pp in IterateListEntry(headp, 'struct proc *', 'p_sibling'): | |
61 | print GetProcInfo(pp) | |
62 | """ | |
63 | elt = element.lh_first | |
64 | if type(element_type) == str: | |
65 | element_type = gettype(element_type) | |
66 | while unsigned(elt) != 0: | |
67 | yield elt | |
68 | next_el = elt.__getattr__(field_name).le_next | |
69 | elt = cast(next_el, element_type) | |
70 | ||
71 | def IterateQueue(queue_head, element_ptr_type, element_field_name): | |
72 | """ iterate over a queue in kernel of type queue_head_t. refer to osfmk/kern/queue.h | |
73 | params: | |
74 | queue_head - value : Value object for queue_head. | |
75 | element_ptr_type - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc.. | |
76 | - str : OR a string describing the type. ex. 'task *' | |
77 | element_field_name - str : name of the field in target struct. | |
78 | returns: | |
79 | A generator does not return. It is used for iterating. | |
80 | value : an object thats of type (element_type) queue_head->next. Always a pointer object | |
81 | """ | |
82 | if type(element_ptr_type) == str : | |
83 | element_ptr_type = gettype(element_ptr_type) | |
84 | ||
85 | queue_head = queue_head.GetSBValue() | |
86 | queue_head_addr = 0x0 | |
87 | if queue_head.TypeIsPointerType(): | |
88 | queue_head_addr = queue_head.GetValueAsUnsigned() | |
89 | else: | |
90 | queue_head_addr = queue_head.GetAddress().GetLoadAddress(LazyTarget.GetTarget()) | |
91 | cur_elt = queue_head.GetChildMemberWithName('next') | |
92 | while True: | |
93 | ||
94 | if not cur_elt.IsValid() or cur_elt.GetValueAsUnsigned() == 0 or cur_elt.GetValueAsUnsigned() == queue_head_addr: | |
95 | break | |
96 | elt = cur_elt.Cast(element_ptr_type) | |
97 | yield value(elt) | |
98 | cur_elt = elt.GetChildMemberWithName(element_field_name).GetChildMemberWithName('next') | |
99 | ||
100 | ||
101 | ||
102 | class KernelTarget(object): | |
103 | """ A common kernel object that provides access to kernel objects and information. | |
104 | The class holds global lists for task, terminated_tasks, procs, zones, zombroc etc. | |
105 | It also provides a way to symbolicate an address or create a value from an address. | |
106 | """ | |
107 | def __init__(self, debugger): | |
108 | """ Initialize the kernel debugging environment. | |
109 | Target properties like architecture and connectedness are lazy-evaluted. | |
110 | """ | |
111 | self._debugger = debugger # This holds an lldb.SBDebugger object for debugger state | |
112 | self._threads_list = [] | |
113 | self._tasks_list = [] | |
114 | self._allproc = [] | |
115 | self._terminated_tasks_list = [] | |
116 | self._zones_list = [] | |
117 | self._zombproc_list = [] | |
118 | self._kernel_types_cache = {} #this will cache the Type objects as and when requested. | |
119 | self._version = None | |
120 | self._arch = None | |
121 | self._ptrsize = None # pointer size of kernel, not userspace | |
122 | self.symbolicator = None | |
123 | class _GlobalVariableFind(object): | |
124 | def __init__(self, kern): | |
125 | self._xnu_kernobj_12obscure12 = kern | |
126 | def __getattr__(self, name): | |
127 | v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name) | |
128 | if not v.GetSBValue().IsValid(): | |
129 | raise ValueError('no such global variable by name: %s '%str(name)) | |
130 | return v | |
131 | self.globals = _GlobalVariableFind(self) | |
132 | LazyTarget.Initialize(debugger) | |
133 | ||
134 | def _GetSymbolicator(self): | |
135 | """ Internal function: To initialize the symbolication from lldb.utils | |
136 | """ | |
137 | if not self.symbolicator is None: | |
138 | return self.symbolicator | |
139 | ||
140 | from lldb.utils import symbolication | |
141 | symbolicator = symbolication.Symbolicator() | |
142 | symbolicator.target = LazyTarget.GetTarget() | |
143 | self.symbolicator = symbolicator | |
144 | return self.symbolicator | |
145 | ||
146 | def Symbolicate(self, addr): | |
147 | """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress' | |
148 | params: | |
149 | addr - int : typically hex value like 0xffffff80002c0df0 | |
150 | returns: | |
151 | str - '' if no symbol found else the symbol name. | |
152 | Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress() | |
153 | """ | |
154 | ret_str = '' | |
155 | syms = self.SymbolicateFromAddress(addr) | |
156 | if len(syms) > 0: | |
157 | ret_str +=syms[0].GetName() | |
158 | return ret_str | |
159 | ||
160 | def SymbolicateFromAddress(self, addr): | |
161 | """ symbolicates any given address based on modules loaded in the target. | |
162 | params: | |
163 | addr - int : typically hex value like 0xffffff80002c0df0 | |
164 | returns: | |
165 | [] of SBSymbol: In case we don't find anything than empty array is returned. | |
166 | Note: a type of symbol can be figured out by gettype() function of SBSymbol. | |
167 | example usage: | |
168 | syms = kern.Symbolicate(0xffffff80002c0df0) | |
169 | for s in syms: | |
170 | if s.GetType() == lldb.eSymbolTypeCode: | |
171 | print "Function", s.GetName() | |
172 | if s.GetType() == lldb.eSymbolTypeData: | |
173 | print "Variable", s.GetName() | |
174 | """ | |
175 | if type(int(1)) != type(addr): | |
176 | if str(addr).strip().find("0x") == 0 : | |
177 | addr = int(addr, 16) | |
178 | else: | |
179 | addr = int(addr) | |
180 | ret_array = [] | |
181 | symbolicator = self._GetSymbolicator() | |
182 | syms = symbolicator.symbolicate(addr) | |
183 | if not syms: | |
184 | return ret_array | |
185 | for s in syms: | |
186 | ret_array.append(s.get_symbol_context().symbol) | |
187 | return ret_array | |
188 | ||
189 | def IsDebuggerConnected(self): | |
190 | proc_state = LazyTarget.GetProcess().state | |
191 | if proc_state == lldb.eStateInvalid : return False | |
192 | if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True | |
193 | ||
194 | def GetGlobalVariable(self, name): | |
195 | """ Get the value object representation for a kernel global variable | |
196 | params: | |
197 | name : str - name of the variable. ex. version | |
198 | returns: value - python object representing global variable. | |
199 | raises : Exception in case the variable is not found. | |
200 | """ | |
201 | return value(LazyTarget.GetTarget().FindGlobalVariables(name, 0).GetValueAtIndex(0)) | |
202 | ||
203 | def GetLoadAddressForSymbol(self, name): | |
204 | """ Get the load address of a symbol in the kernel. | |
205 | params: | |
206 | name : str - name of the symbol to lookup | |
207 | returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value. | |
208 | raises : LookupError - if the symbol is not found. | |
209 | """ | |
210 | name = str(name) | |
211 | target = LazyTarget.GetTarget() | |
212 | syms_arr = target.FindSymbols(name) | |
213 | if syms_arr.IsValid() and len(syms_arr) > 0: | |
214 | symbol = syms_arr[0].GetSymbol() | |
215 | if symbol.IsValid(): | |
216 | return int(symbol.GetStartAddress().GetLoadAddress(target)) | |
217 | ||
218 | raise LookupError("Symbol not found: " + name) | |
219 | ||
220 | def GetValueFromAddress(self, addr, type_str = 'void *'): | |
221 | """ convert a address to value | |
222 | params: | |
223 | addr - int : typically hex value like 0xffffff80008dc390 | |
224 | type_str - str: type to cast to. Default type will be void * | |
225 | returns: | |
226 | value : a value object which has address as addr and type is type_str | |
227 | """ | |
228 | obj = value(self.globals.version.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr))) | |
229 | obj = cast(obj, type_str) | |
230 | return obj | |
231 | ||
232 | def GetValueAsType(self, v, t): | |
233 | """ Retrieves a global variable 'v' of type 't' wrapped in a vue object. | |
234 | If 'v' is an address, creates a vue object of the appropriate type. | |
235 | If 'v' is a name, looks for the global variable and asserts its type. | |
236 | Throws: | |
237 | NameError - If 'v' cannot be found | |
238 | TypeError - If 'v' is of the wrong type | |
239 | """ | |
240 | if islong(v): | |
241 | return self.GetValueFromAddress(v, t) | |
242 | else: | |
243 | var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0] | |
244 | if not var: | |
245 | raise NameError("Failed to find global variable '{0}'".format(v)) | |
246 | if var.GetTypeName() != t: | |
247 | raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName())) | |
248 | return value(var) | |
249 | ||
250 | def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None): | |
251 | """ returns an iterator for a collection in kernel memory. | |
252 | params: | |
253 | iter_head_name - str : name of queue_head or list head variable. | |
254 | next_element_name - str : name of the element that leads to next element. | |
255 | for ex. in struct zone list 'next_zone' is the linking element. | |
256 | returns: | |
257 | iterable : typically used in conjunction with "for varname in iterable:" | |
258 | """ | |
259 | head_element = self.GetGlobalVariable(iter_head_name) | |
260 | return head_element.GetSBValue().linked_list_iter(next_element_name) | |
261 | ||
262 | def TruncPage(self, addr): | |
263 | return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1)) | |
264 | ||
265 | def RoundPage(self, addr): | |
266 | return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1) | |
267 | ||
268 | def StraddlesPage(self, addr, size): | |
269 | if size > unsigned(self.GetGlobalVariable("page_size")): | |
270 | return True | |
271 | return (((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1)) < size) | |
272 | ||
273 | def PhysToKernelVirt(self, addr): | |
274 | if self.arch == 'x86_64': | |
275 | return (addr + unsigned(self.GetGlobalVariable('physmap_base'))) | |
276 | elif self.arch == 'arm': | |
277 | return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase"))) | |
278 | else: | |
279 | raise ValueError("PhysToVirt does not support {0}".format(arch)) | |
280 | ||
281 | def __getattribute__(self, name): | |
282 | if name == 'zones' : | |
283 | self._zones_list = caching.GetDynamicCacheData("kern._zones_list", []) | |
284 | if len(self._zones_list) > 0: return self._zones_list | |
285 | first_zone = self.GetGlobalVariable('first_zone') | |
286 | for z in IterateLinkedList(first_zone, 'next_zone'): | |
287 | self._zones_list.append(z) | |
288 | caching.SaveDynamicCacheData("kern._zones_list", self._zones_list) | |
289 | return self._zones_list | |
290 | ||
291 | if name == 'threads' : | |
292 | self._threads_list = caching.GetDynamicCacheData("kern._threads_list", []) | |
293 | if len(self._threads_list) > 0 : return self._threads_list | |
294 | thread_queue_head = self.GetGlobalVariable('threads') | |
295 | thread_type = LazyTarget.GetTarget().FindFirstType('thread') | |
296 | thread_ptr_type = thread_type.GetPointerType() | |
297 | for th in IterateQueue(thread_queue_head, thread_ptr_type, 'threads'): | |
298 | self._threads_list.append(th) | |
299 | caching.SaveDynamicCacheData("kern._threads_list", self._threads_list) | |
300 | return self._threads_list | |
301 | ||
302 | if name == 'tasks' : | |
303 | self._tasks_list = caching.GetDynamicCacheData("kern._tasks_list", []) | |
304 | if len(self._tasks_list) > 0 : return self._tasks_list | |
305 | task_queue_head = self.GetGlobalVariable('tasks') | |
306 | task_type = LazyTarget.GetTarget().FindFirstType('task') | |
307 | task_ptr_type = task_type.GetPointerType() | |
308 | for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'): | |
309 | self._tasks_list.append(tsk) | |
310 | caching.SaveDynamicCacheData("kern._tasks_list", self._tasks_list) | |
311 | return self._tasks_list | |
312 | ||
313 | if name == 'terminated_tasks' : | |
314 | self._terminated_tasks_list = caching.GetDynamicCacheData("kern._terminated_tasks_list", []) | |
315 | if len(self._terminated_tasks_list) > 0 : return self._terminated_tasks_list | |
316 | task_queue_head = self.GetGlobalVariable('terminated_tasks') | |
317 | task_type = LazyTarget.GetTarget().FindFirstType('task') | |
318 | task_ptr_type = task_type.GetPointerType() | |
319 | for tsk in IterateQueue(task_queue_head, task_ptr_type, 'tasks'): | |
320 | self._terminated_tasks_list.append(tsk) | |
321 | caching.SaveDynamicCacheData("kern._terminated_tasks_list", self._terminated_tasks_list) | |
322 | return self._terminated_tasks_list | |
323 | ||
324 | if name == 'procs' : | |
325 | self._allproc = caching.GetDynamicCacheData("kern._allproc", []) | |
326 | if len(self._allproc) > 0 : return self._allproc | |
327 | all_proc_head = self.GetGlobalVariable('allproc') | |
328 | proc_val = cast(all_proc_head.lh_first, 'proc *') | |
329 | while proc_val != 0: | |
330 | self._allproc.append(proc_val) | |
331 | proc_val = cast(proc_val.p_list.le_next, 'proc *') | |
332 | caching.SaveDynamicCacheData("kern._allproc", self._allproc) | |
333 | return self._allproc | |
334 | ||
335 | if name == 'zombprocs' : | |
336 | self._zombproc_list = caching.GetDynamicCacheData("kern._zombproc_list", []) | |
337 | if len(self._zombproc_list) > 0 : return self._zombproc_list | |
338 | zproc_head = self.GetGlobalVariable('zombproc') | |
339 | proc_val = cast(zproc_head.lh_first, 'proc *') | |
340 | while proc_val != 0: | |
341 | self._zombproc_list.append(proc_val) | |
342 | proc_val = cast(proc_val.p_list.le_next, 'proc *') | |
343 | caching.SaveDynamicCacheData("kern._zombproc_list", self._zombproc_list) | |
344 | return self._zombproc_list | |
345 | ||
346 | if name == 'version' : | |
347 | self._version = caching.GetStaticCacheData("kern.version", None) | |
348 | if self._version != None : return self._version | |
349 | self._version = str(self.GetGlobalVariable('version')) | |
350 | caching.SaveStaticCacheData("kern.version", self._version) | |
351 | return self._version | |
352 | ||
353 | if name == 'arch' : | |
354 | self._arch = caching.GetStaticCacheData("kern.arch", None) | |
355 | if self._arch != None : return self._arch | |
356 | arch = LazyTarget.GetTarget().triple.split('-')[0] | |
357 | if arch in ('armv7', 'armv7s'): | |
358 | self._arch = 'arm' | |
359 | else: | |
360 | self._arch = arch | |
361 | caching.SaveStaticCacheData("kern.arch", self._arch) | |
362 | return self._arch | |
363 | ||
364 | if name == 'ptrsize' : | |
365 | self._ptrsize = caching.GetStaticCacheData("kern.ptrsize", None) | |
366 | if self._ptrsize != None : return self._ptrsize | |
367 | arch = LazyTarget.GetTarget().triple.split('-')[0] | |
368 | if arch in ('x86_64'): | |
369 | self._ptrsize = 8 | |
370 | else: | |
371 | self._ptrsize = 4 | |
372 | caching.SaveStaticCacheData("kern.ptrsize", self._ptrsize) | |
373 | return self._ptrsize | |
374 | ||
375 | return object.__getattribute__(self, name) | |
376 |