diff --git a/pwndbg/arguments.py b/pwndbg/arguments.py index 198dfea40..1fd8e774f 100644 --- 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 ` 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 "" % syscall_number + return pwndbg.constants.syscall(syscall_number, syscall_arch) or "" % syscall_number def get(instruction): diff --git a/pwndbg/constants/__init__.py b/pwndbg/constants/__init__.py index 90eb280d6..51ae5f1e6 100644 --- a/pwndbg/constants/__init__.py +++ b/pwndbg/constants/__init__.py @@ -10,20 +10,20 @@ from . import thumb 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): diff --git a/tests/gdb-tests/tests/binaries/syscalls-x64.asm b/tests/gdb-tests/tests/binaries/syscalls-x64.asm new file mode 100644 index 000000000..fd47a91e2 --- /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 index 69816c057..cd74c96f1 100644 --- a/tests/gdb-tests/tests/test_context_commands.py +++ b/tests/gdb-tests/tests/test_context_commands.py @@ -8,6 +8,7 @@ import tests 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 \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 \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" + )