Repository: pwndbg/pwndbg

Issue:
System calls display is wrong for x86 syscalls on x86-64
When we step on `int 0x80` in a 64-bit (x86-64) program, we display wrong syscall number and arguments.
System calls display is wrong for x86 syscalls on x86-64
When we step on `int 0x80` in a 64-bit (x86-64) program, we display wrong syscall number and arguments.


Discussion:

Resolving patch:
diff --git a/pwndbg/arguments.py b/pwndbg/arguments.py
--- a/pwndbg/arguments.py
+++ b/pwndbg/arguments.py
@@ -53,15 +53,26 @@ def get_syscall_name(instruction):
         return None
 
     syscall_register = pwndbg.lib.abi.ABI.syscall().syscall_register
+    syscall_arch = pwndbg.gdblib.arch.current
 
-    # If we are on x86/x64, return no syscall name for other instructions than syscall and int 0x80
+    # On x86/x64 `syscall` and `int <value>` instructions are in CS_GRP_INT
+    # but only `syscall` and `int 0x80` actually execute syscalls on Linux.
+    # So here, we return no syscall name for other instructions and we also
+    # handle a case when 32-bit syscalls are executed on x64
     if syscall_register in ("eax", "rax"):
         mnemonic = instruction.mnemonic
-        if not (mnemonic == "syscall" or (mnemonic == "int" and instruction.op_str == "0x80")):
+
+        is_32bit = mnemonic == "int" and instruction.op_str == "0x80"
+        if not (mnemonic == "syscall" or is_32bit):
             return None
 
+        # On x64 the int 0x80 instruction executes 32-bit syscalls from i386
+        # On x86, the syscall_arch is already i386, so its all fine
+        if is_32bit:
+            syscall_arch = "i386"
+
     syscall_number = getattr(pwndbg.gdblib.regs, syscall_register)
-    return pwndbg.constants.syscall(syscall_number) or "<unk_%d>" % syscall_number
+    return pwndbg.constants.syscall(syscall_number, syscall_arch) or "<unk_%d>" % syscall_number
 
 
 def get(instruction):
diff --git a/pwndbg/constants/__init__.py b/pwndbg/constants/__init__.py
--- a/pwndbg/constants/__init__.py
+++ b/pwndbg/constants/__init__.py
@@ -10,20 +10,20 @@
 arches = {"arm": arm, "armcm": arm, "i386": i386, "mips": mips, "x86-64": amd64, "aarch64": aarch64}
 
 
-def syscall(value):
+def syscall(number, arch):
     """
-    Given a value for a syscall number (e.g. execve == 11), return
-    the *name* of the syscall.
+    Given a syscall number and architecture, returns the name of the syscall.
+    E.g. execve == 59 on x86-64
     """
-    arch = arches.get(pwndbg.gdblib.arch.current, None)
+    arch = arches.get(arch, None)
 
-    if not arch:
+    if arch is None:
         return None
 
     prefix = "__NR_"
 
     for k, v in arch.__dict__.items():
-        if v != value:
+        if v != number:
             continue
 
         if not k.startswith(prefix):


Test patch:
diff --git a/tests/gdb-tests/tests/binaries/syscalls-x64.asm b/tests/gdb-tests/tests/binaries/syscalls-x64.asm
new file mode 100644
--- /dev/null
+++ b/tests/gdb-tests/tests/binaries/syscalls-x64.asm
@@ -0,0 +1,14 @@
+global _start
+
+; This binary is there to test syscall arguments display on x64
+; along with 32-bit syscalls executed on x64
+; Motivated by https://github.com/pwndbg/pwndbg/issues/1188
+
+_start:
+mov rax, 0
+mov rdi, 0x1337
+mov rsi, 0xdeadbeef
+mov rcx, 0x10
+syscall
+mov eax, 10
+int 0x80
diff --git a/tests/gdb-tests/tests/test_context_commands.py b/tests/gdb-tests/tests/test_context_commands.py
--- a/tests/gdb-tests/tests/test_context_commands.py
+++ b/tests/gdb-tests/tests/test_context_commands.py
@@ -8,6 +8,7 @@
 
 USE_FDS_BINARY = tests.binaries.get("use-fds.out")
 TABSTOP_BINARY = tests.binaries.get("tabstop.out")
+SYSCALLS_BINARY = tests.binaries.get("syscalls-x64.out")
 
 
 def test_context_disasm_show_fd_filepath(start_binary):
@@ -139,3 +140,48 @@ def test_source_code_tabstop(start_binary):
     assert """ 8         return 0;\n""" in src
     assert """ 9 }\n""" in src
     assert """10 \n""" in src
+
+
+def test_context_disasm_syscalls_args_display(start_binary):
+    start_binary(SYSCALLS_BINARY)
+    gdb.execute("nextsyscall")
+    dis = gdb.execute("context disasm", to_string=True)
+    assert dis == (
+        "LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA\n"
+        "──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────\n"
+        "   0x400080 <_start>       mov    eax, 0\n"
+        "   0x400085 <_start+5>     mov    edi, 0x1337\n"
+        "   0x40008a <_start+10>    mov    esi, 0xdeadbeef\n"
+        "   0x40008f <_start+15>    mov    ecx, 0x10\n"
+        " ► 0x400094 <_start+20>    syscall  <SYS_read>\n"
+        "        fd:        0x1337\n"
+        "        buf:       0xdeadbeef\n"
+        "        nbytes:    0x0\n"
+        "   0x400096 <_start+22>    mov    eax, 0xa\n"
+        "   0x40009b <_start+27>    int    0x80\n"
+        "   0x40009d                add    byte ptr [rax], al\n"
+        "   0x40009f                add    byte ptr [rax], al\n"
+        "   0x4000a1                add    byte ptr [rax], al\n"
+        "   0x4000a3                add    byte ptr [rax], al\n"
+        "────────────────────────────────────────────────────────────────────────────────\n"
+    )
+
+    gdb.execute("nextsyscall")
+    dis = gdb.execute("context disasm", to_string=True)
+    assert dis == (
+        "LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA\n"
+        "──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────\n"
+        "   0x400085 <_start+5>     mov    edi, 0x1337\n"
+        "   0x40008a <_start+10>    mov    esi, 0xdeadbeef\n"
+        "   0x40008f <_start+15>    mov    ecx, 0x10\n"
+        "   0x400094 <_start+20>    syscall \n"
+        "   0x400096 <_start+22>    mov    eax, 0xa\n"
+        " ► 0x40009b <_start+27>    int    0x80 <SYS_unlink>\n"
+        "        name:      0x1337\n"
+        "   0x40009d                add    byte ptr [rax], al\n"
+        "   0x40009f                add    byte ptr [rax], al\n"
+        "   0x4000a1                add    byte ptr [rax], al\n"
+        "   0x4000a3                add    byte ptr [rax], al\n"
+        "   0x4000a5                add    byte ptr [rax], al\n"
+        "────────────────────────────────────────────────────────────────────────────────\n"
+    )