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