lldb: add support for attaching via `attach <pid|name>` (#2705)

* lldb: add support for attaching via `attach <pid|name>`

This commit adds support for the `attach <pid|name>` command so that it
can attach to a pid or full process name similarly to as in LLDB.

Please note that this LLDB command is a bit confusing.
It may seem it is an alias for `process attach` as in here:

```
(lldb) attach -n -w htop
error: 'process attach' doesn't take any arguments.
```

However, in practice it is not. It is an alias for `_regexp-attach`:

```
(lldb) help attach
Attach to process by ID or name.  Expects 'raw' input (see 'help raw-input'.)

Syntax: _regexp-attach <pid> | <process-name>

'attach' is an abbreviation for '_regexp-attach'
```

...which has its own problems:
1) it does not perform any regexp match in practice. passing `hto.*`,
   `hto?` or `hto[p]` will not attach to `htop`. One must pass the full process name like `htop`.
2) it can work without arguments, which is stupid and we should not
   support it?:

```
(lldb) _regexp-attach
There is a running process, detach from it and attach?: [Y/n] y
Process 56358 detached
Process 56358 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007ff8118bedd2 libsystem_kernel.dylib`__select + 10
libsystem_kernel.dylib`__select:
->  0x7ff8118bedd2 <+10>: jae    0x7ff8118beddc ; <+20>
    0x7ff8118bedd4 <+12>: movq   %rax, %rdi
    0x7ff8118bedd7 <+15>: jmp    0x7ff8118b737b ; cerror
    0x7ff8118beddc <+20>: retq
```

* fixup

* Update pwndbg/dbg/lldb/repl/__init__.py

Co-authored-by: Matt. <dark.ryu.550@gmail.com>

---------

Co-authored-by: Matt. <dark.ryu.550@gmail.com>
pull/2661/head
Disconnect3d 11 months ago committed by GitHub
parent 1f9ec9631e
commit 02faa58513
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -316,6 +316,14 @@ def run(startup: List[str] | None = None, debug: bool = False) -> None:
continue
# We don't care about other process commands..
if (bits[0].startswith("at") and "attach".startswith(bits[0])) or (
bits[0].startswith("_regexp-a") and "_regexp-attach".startswith(bits[0])
):
# `attach` is an alias for `_regexp-attach`
# (it is NOT an alias for `process attach` even if it may seem so!)
attach(driver, relay, bits[1:], dbg)
continue
if bits[0].startswith("ta") and "target".startswith(bits[0]):
if len(bits) > 1 and bits[1].startswith("c") and "create".startswith(bits[1]):
# This is `target create`
@ -679,43 +687,30 @@ process_attach_unsupported = [
]
def process_attach(driver: ProcessDriver, relay: EventRelay, args: List[str], dbg: LLDB) -> None:
def _attach_with_info(
driver: ProcessDriver, relay: EventRelay, dbg: LLDB, info: lldb.SBAttachInfo, cont=False
):
"""
Attaches to a process with the given arguments.
Attaches to a process based on SBAttachInfo information
"""
args = parse(args, process_attach_ap, process_attach_unsupported)
if not args:
return
targets = dbg.debugger.GetNumTargets()
assert targets < 2
if targets == 0:
print(message.error("error: no target, create one using the 'target create' command"))
return
# TODO/FIXME: This should ask:
# 'There is a running process, detach from it and attach?: [Y/n]'
if driver.has_process():
print(message.error("error: a process is already being debugged"))
return
# The first two arguments - executable name and wait_for_launch - don't
# matter, we set them later. The third one is required, as it tells LLDB the
# attach should be asynchronous.
params = lldb.SBAttachInfo(None, False, True)
if args.name is not None:
params.SetExecutable(args.name)
if args.pid is not None:
params.SetProcessID(args.pid)
params.SetWaitForLaunch(args.waitfor)
if getattr(args, "continue"):
params.SetResumeCount(1)
params.SetIgnoreExisting(not args.include_existing)
io_driver = get_io_driver()
result = driver.attach(
dbg.debugger.GetTargetAtIndex(0),
io_driver,
params,
info,
)
if not result.success:
@ -723,16 +718,67 @@ def process_attach(driver: ProcessDriver, relay: EventRelay, args: List[str], db
return
# Continue execution if the user has requested it.
if getattr(args, "continue"):
if cont:
# Same logic applies here as in `process_launch`.
relay._set_ignore_resumed(1)
driver.cont()
else:
# Same logic applies here as in `process_launch`.
dbg._trigger_event(EventType.STOP)
def process_attach(driver: ProcessDriver, relay: EventRelay, args: List[str], dbg: LLDB) -> None:
"""
Attaches to a process with the given arguments.
"""
args = parse(args, process_attach_ap, process_attach_unsupported)
if not args:
return
# The first two arguments - executable name and wait_for_launch - don't
# matter, we set them later. The third one is required, as it tells LLDB the
# attach should be asynchronous.
info = lldb.SBAttachInfo(None, False, True)
if args.name is not None:
info.SetExecutable(args.name)
if args.pid is not None:
info.SetProcessID(args.pid)
info.SetWaitForLaunch(args.waitfor)
do_continue = getattr(args, "continue", False)
if do_continue:
info.SetResumeCount(1)
info.SetIgnoreExisting(not args.include_existing)
_attach_with_info(driver, relay, dbg, info, cont=do_continue)
def attach(driver: ProcessDriver, relay: EventRelay, args: List[str], dbg: LLDB) -> None:
"""
Attaches to a process with the given name or pid based on regex match.
Used for `_regexp-attach <pid|name>` (alias for `attach <pid|name>`)
Note: for some reason, `attach` does not really take a regex for process name.
"""
if len(args) != 1:
print(message.error("Expected 1 argument: <pid> or <name>"))
return
arg = args[0]
# exec name - None, we set it later (or pid)
# wait_for_launch - False, since we don't wait
# third arg - tell LLDB to attach asynchronously
info = lldb.SBAttachInfo(None, False, True)
# Argument is pid
if arg.isdigit():
info.SetProcessID(int(arg))
else:
info.SetExecutable(arg)
_attach_with_info(driver, relay, dbg, info)
process_connect_ap = argparse.ArgumentParser(add_help=False)
process_connect_ap.add_argument("-p", "--plugin")
process_connect_ap.add_argument("remoteurl")

Loading…
Cancel
Save