|
1 | 1 | from abc import ABCMeta, abstractmethod
|
2 | 2 |
|
3 | 3 | import lldb
|
| 4 | +import json, struct, signal |
4 | 5 |
|
5 | 6 | class ScriptedProcess(metaclass=ABCMeta):
|
6 | 7 |
|
@@ -225,7 +226,6 @@ def create_breakpoint(self, addr, error):
|
225 | 226 | % self.__class__.__name__)
|
226 | 227 | return False
|
227 | 228 |
|
228 |
| - |
229 | 229 | class ScriptedThread(metaclass=ABCMeta):
|
230 | 230 |
|
231 | 231 | """
|
@@ -376,6 +376,162 @@ def get_extended_info(self):
|
376 | 376 | """
|
377 | 377 | return self.extended_info
|
378 | 378 |
|
| 379 | + |
| 380 | +class PassthroughScriptedProcess(ScriptedProcess): |
| 381 | + driving_target = None |
| 382 | + driving_process = None |
| 383 | + |
| 384 | + def __init__(self, exe_ctx, args, launched_driving_process=True): |
| 385 | + super().__init__(exe_ctx, args) |
| 386 | + |
| 387 | + self.driving_target = None |
| 388 | + self.driving_process = None |
| 389 | + |
| 390 | + self.driving_target_idx = args.GetValueForKey("driving_target_idx") |
| 391 | + if self.driving_target_idx and self.driving_target_idx.IsValid(): |
| 392 | + idx = self.driving_target_idx.GetUnsignedIntegerValue(42) |
| 393 | + self.driving_target = self.target.GetDebugger().GetTargetAtIndex(idx) |
| 394 | + |
| 395 | + if launched_driving_process: |
| 396 | + self.driving_process = self.driving_target.GetProcess() |
| 397 | + for driving_thread in self.driving_process: |
| 398 | + structured_data = lldb.SBStructuredData() |
| 399 | + structured_data.SetFromJSON( |
| 400 | + json.dumps( |
| 401 | + { |
| 402 | + "driving_target_idx": idx, |
| 403 | + "thread_idx": driving_thread.GetIndexID(), |
| 404 | + } |
| 405 | + ) |
| 406 | + ) |
| 407 | + |
| 408 | + self.threads[ |
| 409 | + driving_thread.GetThreadID() |
| 410 | + ] = PassthroughScriptedThread(self, structured_data) |
| 411 | + |
| 412 | + for module in self.driving_target.modules: |
| 413 | + path = module.file.fullpath |
| 414 | + load_addr = module.GetObjectFileHeaderAddress().GetLoadAddress( |
| 415 | + self.driving_target |
| 416 | + ) |
| 417 | + self.loaded_images.append({"path": path, "load_addr": load_addr}) |
| 418 | + |
| 419 | + def get_memory_region_containing_address(self, addr): |
| 420 | + mem_region = lldb.SBMemoryRegionInfo() |
| 421 | + error = self.driving_process.GetMemoryRegionInfo(addr, mem_region) |
| 422 | + if error.Fail(): |
| 423 | + return None |
| 424 | + return mem_region |
| 425 | + |
| 426 | + def read_memory_at_address(self, addr, size, error): |
| 427 | + data = lldb.SBData() |
| 428 | + bytes_read = self.driving_process.ReadMemory(addr, size, error) |
| 429 | + |
| 430 | + if error.Fail(): |
| 431 | + return data |
| 432 | + |
| 433 | + data.SetDataWithOwnership( |
| 434 | + error, |
| 435 | + bytes_read, |
| 436 | + self.driving_target.GetByteOrder(), |
| 437 | + self.driving_target.GetAddressByteSize(), |
| 438 | + ) |
| 439 | + |
| 440 | + return data |
| 441 | + |
| 442 | + def write_memory_at_address(self, addr, data, error): |
| 443 | + return self.driving_process.WriteMemory( |
| 444 | + addr, bytearray(data.uint8.all()), error |
| 445 | + ) |
| 446 | + |
| 447 | + def get_process_id(self): |
| 448 | + return self.driving_process.GetProcessID() |
| 449 | + |
| 450 | + def is_alive(self): |
| 451 | + return True |
| 452 | + |
| 453 | + def get_scripted_thread_plugin(self): |
| 454 | + return f"{PassthroughScriptedThread.__module__}.{PassthroughScriptedThread.__name__}" |
| 455 | + |
| 456 | + |
| 457 | +class PassthroughScriptedThread(ScriptedThread): |
| 458 | + def __init__(self, process, args): |
| 459 | + super().__init__(process, args) |
| 460 | + driving_target_idx = args.GetValueForKey("driving_target_idx") |
| 461 | + thread_idx = args.GetValueForKey("thread_idx") |
| 462 | + |
| 463 | + # TODO: Change to Walrus operator (:=) with oneline if assignment |
| 464 | + # Requires python 3.8 |
| 465 | + val = thread_idx.GetUnsignedIntegerValue() |
| 466 | + if val is not None: |
| 467 | + self.idx = val |
| 468 | + |
| 469 | + self.driving_target = None |
| 470 | + self.driving_process = None |
| 471 | + self.driving_thread = None |
| 472 | + |
| 473 | + # TODO: Change to Walrus operator (:=) with oneline if assignment |
| 474 | + # Requires python 3.8 |
| 475 | + val = driving_target_idx.GetUnsignedIntegerValue() |
| 476 | + if val is not None: |
| 477 | + self.driving_target = self.target.GetDebugger().GetTargetAtIndex(val) |
| 478 | + self.driving_process = self.driving_target.GetProcess() |
| 479 | + self.driving_thread = self.driving_process.GetThreadByIndexID(self.idx) |
| 480 | + |
| 481 | + if self.driving_thread: |
| 482 | + self.id = self.driving_thread.GetThreadID() |
| 483 | + |
| 484 | + def get_thread_id(self): |
| 485 | + return self.id |
| 486 | + |
| 487 | + def get_name(self): |
| 488 | + return f"{PassthroughScriptedThread.__name__}.thread-{self.idx}" |
| 489 | + |
| 490 | + def get_stop_reason(self): |
| 491 | + stop_reason = {"type": lldb.eStopReasonInvalid, "data": {}} |
| 492 | + |
| 493 | + if ( |
| 494 | + self.driving_thread |
| 495 | + and self.driving_thread.IsValid() |
| 496 | + and self.get_thread_id() == self.driving_thread.GetThreadID() |
| 497 | + ): |
| 498 | + stop_reason["type"] = lldb.eStopReasonNone |
| 499 | + |
| 500 | + # TODO: Passthrough stop reason from driving process |
| 501 | + if self.driving_thread.GetStopReason() != lldb.eStopReasonNone: |
| 502 | + if "arm64" in self.scripted_process.arch: |
| 503 | + stop_reason["type"] = lldb.eStopReasonException |
| 504 | + stop_reason["data"][ |
| 505 | + "desc" |
| 506 | + ] = self.driving_thread.GetStopDescription(100) |
| 507 | + elif self.scripted_process.arch == "x86_64": |
| 508 | + stop_reason["type"] = lldb.eStopReasonSignal |
| 509 | + stop_reason["data"]["signal"] = signal.SIGTRAP |
| 510 | + else: |
| 511 | + stop_reason["type"] = self.driving_thread.GetStopReason() |
| 512 | + |
| 513 | + return stop_reason |
| 514 | + |
| 515 | + def get_register_context(self): |
| 516 | + if not self.driving_thread or self.driving_thread.GetNumFrames() == 0: |
| 517 | + return None |
| 518 | + frame = self.driving_thread.GetFrameAtIndex(0) |
| 519 | + |
| 520 | + GPRs = None |
| 521 | + registerSet = frame.registers # Returns an SBValueList. |
| 522 | + for regs in registerSet: |
| 523 | + if "general purpose" in regs.name.lower(): |
| 524 | + GPRs = regs |
| 525 | + break |
| 526 | + |
| 527 | + if not GPRs: |
| 528 | + return None |
| 529 | + |
| 530 | + for reg in GPRs: |
| 531 | + self.register_ctx[reg.name] = int(reg.value, base=16) |
| 532 | + |
| 533 | + return struct.pack(f"{len(self.register_ctx)}Q", *self.register_ctx.values()) |
| 534 | + |
379 | 535 | ARM64_GPR = [ {'name': 'x0', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0, 'generic': 'arg0', 'alt-name': 'arg0'},
|
380 | 536 | {'name': 'x1', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 1, 'dwarf': 1, 'generic': 'arg1', 'alt-name': 'arg1'},
|
381 | 537 | {'name': 'x2', 'bitsize': 64, 'offset': 16, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 2, 'dwarf': 2, 'generic': 'arg2', 'alt-name': 'arg2'},
|
|
0 commit comments