mirror of https://github.com/pwndbg/pwndbg
Call like instructions (#2261)
* Move syscall number evaluation into instruction.py. This allows us to determine and display future syscalls * Move string manipulation to color.disasm.py * lint * fix padding * Fix x86 syscall * disable debug mode * @override decorator added to methods * comments * lint * Fix x86/x86_64 edge cases with syscall register reading, and add test for emulation off for syscalls * Tests depend on width of context banner * Fix strange rebasing error * Call like instructions * Add IRET to jump groups, and remove multiple places in codebase where jumps groups are defined (non uniformly) * remove duplicate test (rebase stuff) * lint
This commit is contained in:
parent
41f335bec8
commit
83cc8c57cf
|
@ -9,7 +9,6 @@ from typing import List
|
|||
from typing import Tuple
|
||||
|
||||
import gdb
|
||||
from capstone import CS_GRP_CALL
|
||||
from capstone import CS_GRP_INT
|
||||
|
||||
import pwndbg.chain
|
||||
|
@ -73,7 +72,7 @@ def get(instruction: PwndbgInstruction) -> List[Tuple[pwndbg.lib.functions.Argum
|
|||
if instruction.address != pwndbg.gdblib.regs.pc:
|
||||
return []
|
||||
|
||||
if CS_GRP_CALL in instruction.groups:
|
||||
if instruction.call_like:
|
||||
try:
|
||||
abi = pwndbg.lib.abi.ABI.default()
|
||||
except KeyError:
|
||||
|
|
|
@ -2,8 +2,6 @@ from __future__ import annotations
|
|||
|
||||
from typing import List
|
||||
|
||||
import capstone
|
||||
|
||||
import pwndbg.chain
|
||||
import pwndbg.color.context as C
|
||||
from pwndbg.color import ColorConfig
|
||||
|
@ -11,11 +9,10 @@ from pwndbg.color import ColorParamSpec
|
|||
from pwndbg.color import ljust_colored
|
||||
from pwndbg.color import strip
|
||||
from pwndbg.color.message import on
|
||||
from pwndbg.gdblib.disasm.instruction import ALL_JUMP_GROUPS
|
||||
from pwndbg.gdblib.disasm.instruction import InstructionCondition
|
||||
from pwndbg.gdblib.disasm.instruction import PwndbgInstruction
|
||||
|
||||
capstone_branch_groups = {capstone.CS_GRP_CALL, capstone.CS_GRP_JUMP}
|
||||
|
||||
c = ColorConfig(
|
||||
"disasm",
|
||||
[
|
||||
|
@ -33,7 +30,7 @@ def one_instruction(ins: PwndbgInstruction) -> str:
|
|||
if pwndbg.config.highlight_pc and ins.address == pwndbg.gdblib.regs.pc:
|
||||
asm = C.highlight(asm)
|
||||
|
||||
is_call_or_jump = ins.groups_set & capstone_branch_groups
|
||||
is_call_or_jump = ins.groups_set & ALL_JUMP_GROUPS
|
||||
|
||||
# Style the instruction mnemonic if it's a call/jump instruction.
|
||||
if is_call_or_jump:
|
||||
|
|
|
@ -676,8 +676,6 @@ class Emulator:
|
|||
debug(DEBUG_MEM_READ, "uc.mem_read(*%r, **%r)", (a, kw))
|
||||
return self.uc.mem_read(*a, **kw)
|
||||
|
||||
jump_types = {C.CS_GRP_CALL, C.CS_GRP_JUMP, C.CS_GRP_RET}
|
||||
|
||||
def until_jump(self, pc=None):
|
||||
"""
|
||||
Emulates instructions starting at the specified address until the
|
||||
|
@ -739,7 +737,7 @@ class Emulator:
|
|||
def until_call(self, pc=None):
|
||||
addr, target = self.until_jump(pc)
|
||||
|
||||
while target and C.CS_GRP_CALL not in pwndbg.gdblib.disasm.one_raw(addr).groups:
|
||||
while target and not pwndbg.gdblib.disasm.one_raw(addr).call_like:
|
||||
addr, target = self.until_jump(target)
|
||||
|
||||
return addr, target
|
||||
|
|
|
@ -17,6 +17,7 @@ import pwndbg.gdblib.typeinfo
|
|||
import pwndbg.gdblib.vmmap
|
||||
import pwndbg.lib.config
|
||||
from pwndbg.emu.emulator import Emulator
|
||||
from pwndbg.gdblib.disasm.instruction import FORWARD_JUMP_GROUP
|
||||
from pwndbg.gdblib.disasm.instruction import EnhancedOperand
|
||||
from pwndbg.gdblib.disasm.instruction import InstructionCondition
|
||||
from pwndbg.gdblib.disasm.instruction import PwndbgInstruction
|
||||
|
@ -222,7 +223,7 @@ class DisassemblyAssistant:
|
|||
|
||||
# Disable emulation after CALL instructions. We do it after enhancement, as we can use emulation
|
||||
# to determine the call's target address.
|
||||
if jump_emu and CS_GRP_CALL in set(instruction.groups):
|
||||
if jump_emu and instruction.call_like:
|
||||
jump_emu.valid = False
|
||||
jump_emu = None
|
||||
emu = None
|
||||
|
@ -600,10 +601,7 @@ class DisassemblyAssistant:
|
|||
# Use emulator to determine the next address:
|
||||
# 1. Only use it to determine non-call's (`nexti` should step over calls)
|
||||
# 2. Make sure we haven't manually set .condition to False (which should override the emulators prediction)
|
||||
if (
|
||||
CS_GRP_CALL not in instruction.groups_set
|
||||
and instruction.condition != InstructionCondition.FALSE
|
||||
):
|
||||
if not instruction.call_like and instruction.condition != InstructionCondition.FALSE:
|
||||
next_addr = jump_emu.pc
|
||||
|
||||
# All else fails, take the next instruction in memory
|
||||
|
@ -641,10 +639,10 @@ class DisassemblyAssistant:
|
|||
"call" specifies if we allow this to resolve call instruction targets
|
||||
"""
|
||||
|
||||
if CS_GRP_CALL in instruction.groups:
|
||||
if instruction.call_like:
|
||||
if not call:
|
||||
return None
|
||||
elif CS_GRP_JUMP not in instruction.groups:
|
||||
elif not bool(instruction.groups_set & FORWARD_JUMP_GROUP):
|
||||
return None
|
||||
|
||||
addr = None
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
from collections import defaultdict
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
|
@ -28,6 +29,7 @@ from capstone.arm64 import ARM64_INS_BLR
|
|||
from capstone.arm64 import ARM64_INS_BR
|
||||
from capstone.mips import MIPS_INS_B
|
||||
from capstone.mips import MIPS_INS_BAL
|
||||
from capstone.mips import MIPS_INS_BLTZAL
|
||||
from capstone.mips import MIPS_INS_J
|
||||
from capstone.mips import MIPS_INS_JAL
|
||||
from capstone.mips import MIPS_INS_JALR
|
||||
|
@ -64,13 +66,24 @@ UNCONDITIONAL_JUMP_INSTRUCTIONS: Dict[int, Set[int]] = {
|
|||
CS_ARCH_PPC: {PPC_INS_B, PPC_INS_BA, PPC_INS_BL, PPC_INS_BLA},
|
||||
}
|
||||
|
||||
BRANCH_AND_LINK_INSTRUCTIONS: Dict[int, Set[int]] = defaultdict(set)
|
||||
BRANCH_AND_LINK_INSTRUCTIONS[CS_ARCH_MIPS] = {
|
||||
MIPS_INS_BAL,
|
||||
MIPS_INS_BLTZAL,
|
||||
MIPS_INS_JAL,
|
||||
MIPS_INS_JALR,
|
||||
}
|
||||
|
||||
# Everything that is a CALL or a RET is a unconditional jump
|
||||
GENERIC_UNCONDITIONAL_JUMP_GROUPS = {CS_GRP_CALL, CS_GRP_RET}
|
||||
GENERIC_UNCONDITIONAL_JUMP_GROUPS = {CS_GRP_CALL, CS_GRP_RET, CS_GRP_IRET}
|
||||
# All branch-like instructions - jumps thats are non-call and non-ret - should have one of these two groups in Capstone
|
||||
GENERIC_JUMP_GROUPS = {CS_GRP_JUMP, CS_GRP_BRANCH_RELATIVE}
|
||||
# All Capstone jumps should have at least one of these groups
|
||||
ALL_JUMP_GROUPS = GENERIC_JUMP_GROUPS | GENERIC_UNCONDITIONAL_JUMP_GROUPS
|
||||
|
||||
# All non-ret jumps
|
||||
FORWARD_JUMP_GROUP = {CS_GRP_CALL} | GENERIC_JUMP_GROUPS
|
||||
|
||||
|
||||
class InstructionCondition(Enum):
|
||||
# Conditional instruction, and action is taken
|
||||
|
@ -244,6 +257,18 @@ class PwndbgInstruction:
|
|||
If the enhancement successfully used emulation for this instruction
|
||||
"""
|
||||
|
||||
@property
|
||||
def call_like(self) -> bool:
|
||||
"""
|
||||
True if this is a call-like instruction, meaning either it's a CALL or a branch and link.
|
||||
|
||||
Checking for the CS_GRP_CALL is insufficient, as there are many "branch and link" instructions that are not labeled as a call
|
||||
"""
|
||||
return (
|
||||
CS_GRP_CALL in self.groups_set
|
||||
or self.id in BRANCH_AND_LINK_INSTRUCTIONS[self.cs_insn._cs.arch]
|
||||
)
|
||||
|
||||
@property
|
||||
def can_change_instruction_pointer(self) -> bool:
|
||||
"""
|
||||
|
|
|
@ -16,8 +16,7 @@ import pwndbg.gdblib.events
|
|||
import pwndbg.gdblib.proc
|
||||
import pwndbg.gdblib.regs
|
||||
from pwndbg.color import message
|
||||
|
||||
jumps = {capstone.CS_GRP_CALL, capstone.CS_GRP_JUMP, capstone.CS_GRP_RET, capstone.CS_GRP_IRET}
|
||||
from pwndbg.gdblib.disasm.instruction import ALL_JUMP_GROUPS
|
||||
|
||||
interrupts = {capstone.CS_GRP_INT}
|
||||
|
||||
|
@ -45,10 +44,9 @@ def next_int(address=None):
|
|||
|
||||
ins = pwndbg.gdblib.disasm.one(address)
|
||||
while ins:
|
||||
ins_groups = set(ins.groups)
|
||||
if ins_groups & jumps:
|
||||
if ins.groups_set & ALL_JUMP_GROUPS:
|
||||
return None
|
||||
elif ins_groups & interrupts:
|
||||
elif ins.groups_set & interrupts:
|
||||
return ins
|
||||
ins = pwndbg.gdblib.disasm.one(ins.next)
|
||||
|
||||
|
@ -64,7 +62,7 @@ def next_branch(address=None):
|
|||
|
||||
ins = pwndbg.gdblib.disasm.one(address)
|
||||
while ins:
|
||||
if set(ins.groups) & jumps:
|
||||
if ins.groups_set & ALL_JUMP_GROUPS:
|
||||
return ins
|
||||
ins = pwndbg.gdblib.disasm.one(ins.next)
|
||||
|
||||
|
@ -104,7 +102,7 @@ def next_matching_until_branch(address=None, mnemonic=None, op_str=None):
|
|||
if mnemonic_match and op_str_match:
|
||||
return ins
|
||||
|
||||
if set(ins.groups) & jumps:
|
||||
if ins.groups_set & ALL_JUMP_GROUPS:
|
||||
# No matching instruction until the next branch, and we're
|
||||
# not trying to match the branch instruction itself.
|
||||
return None
|
||||
|
@ -145,7 +143,7 @@ def break_next_call(symbol_regex=None):
|
|||
break
|
||||
|
||||
# continue if not a call
|
||||
if capstone.CS_GRP_CALL not in ins.groups:
|
||||
if not ins.call_like:
|
||||
continue
|
||||
|
||||
# return call if we:
|
||||
|
|
Loading…
Reference in New Issue