Repository: pwndbg/pwndbg

Issue:
search --asm: look for assembly instruction[s] bytes
The `search` command should have a way to search for a given assembly instruction[s] bytes.

We can assemble the given instructions through pwntools (its `pwnlib` import) similarly as we do in the `asm` command implementation: https://github.com/pwndbg/pwndbg/blob/c0d785565b499ba32d674c9e84a27e4967aee315/pwndbg/commands/asm.py#L69

This can probably be implemented as a `-a --asm` or `-t|--type=asm` option, where if provided, we would search for assebmled bytes.

E.g. in x86-64 programs `search --asm "xor rax, rax"` should assemble it through pwnlib to the following bytes:
  ```py
  In [4]: asm('xor rax, rax', arch='amd64')
  Out[4]: b'H1\xc0'
  ```
And then search the memory for those bytes (`b'H1\xc0'`). Ofc it should work with all the other options in `search` command like `--writable` or the ability to pass in a mapping name as the last argument of the search.



Discussion:
I'm working on this.

Resolving patch:
diff --git a/pwndbg/commands/search.py b/pwndbg/commands/search.py
--- a/pwndbg/commands/search.py
+++ b/pwndbg/commands/search.py
@@ -6,6 +6,8 @@
 import os
 import struct
 
+import pwnlib
+
 import pwndbg.color.memory as M
 import pwndbg.commands
 import pwndbg.enhance
@@ -56,7 +58,7 @@ def print_search_hit(address) -> None:
 parser.add_argument(
     "-t",
     "--type",
-    choices=["byte", "short", "word", "dword", "qword", "pointer", "string", "bytes"],
+    choices=["byte", "short", "word", "dword", "qword", "pointer", "string", "bytes", "asm"],
     help="Size of search target",
     default="bytes",
     type=str,
@@ -102,6 +104,19 @@ def print_search_hit(address) -> None:
     const="pointer",
     help="Search for a pointer-width integer",
 )
+parser.add_argument(
+    "--asm",
+    dest="type",
+    action="store_const",
+    const="asm",
+    help="Search for an assembly instruction",
+)
+parser.add_argument(
+    "--arch",
+    choices=pwnlib.context.context.architectures.keys(),
+    type=str,
+    help="Target architecture",
+)
 parser.add_argument(
     "-x", "--hex", action="store_true", help="Target is a hex-encoded (for bytes/strings)"
 )
@@ -158,6 +173,7 @@ def print_search_hit(address) -> None:
 @pwndbg.commands.OnlyWhenRunning
 def search(
     type,
+    arch,
     hex,
     executable,
     writable,
@@ -178,6 +194,9 @@ def search(
         next = False
         save = True
 
+    if not arch:
+        arch = pwnlib.context.context.arch
+
     # Adjust pointer sizes to the local architecture
     if type == "pointer":
         type = {4: "dword", 8: "qword"}[pwndbg.gdblib.arch.ptrsize]
@@ -201,7 +220,7 @@ def search(
     if limit:
         limit = pwndbg.commands.fix_int(limit)
     # Convert to an integer if needed, and pack to bytes
-    if type not in ("string", "bytes"):
+    if type not in ("string", "bytes", "asm"):
         value = pwndbg.commands.fix_int(value)
         value &= pwndbg.gdblib.arch.ptrmask
         fmt = {"little": "<", "big": ">"}[pwndbg.gdblib.arch.endian] + {
@@ -223,6 +242,10 @@ def search(
         value = value.encode()
         value += b"\x00"
 
+    elif type == "asm":
+        bits_for_arch = pwnlib.context.context.architectures.get(arch, {}).get("bits")
+        value = pwnlib.asm.asm(value, arch=arch, bits=bits_for_arch)
+
     # Find the mappings that we're looking for
     mappings = pwndbg.gdblib.vmmap.get()
 
@@ -234,7 +257,11 @@ def search(
         return
 
     # If next is passed, only perform a manual search over previously saved addresses
-    print("Searching for value: " + repr(value))
+    if type == "asm":
+        print("Searching for instruction (assembled value): " + repr(value))
+    else:
+        print("Searching for value: " + repr(value))
+
     if next:
         val_len = len(value)
         new_saved = set()


Test patch: