mirror of https://github.com/pwndbg/pwndbg.git
Lots of changes for automatically showing various function arguments etc.
parent
8f36054413
commit
68529bcb5c
@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Functionality for disassmebling code at an address, or at an
|
||||
address +/- a few instructions.
|
||||
"""
|
||||
import collections
|
||||
|
||||
import gdb
|
||||
import pwndbg.arch
|
||||
import pwndbg.color
|
||||
import pwndbg.disasm_powerpc
|
||||
import pwndbg.ida
|
||||
import pwndbg.memory
|
||||
import pwndbg.symbol
|
||||
import pwndbg.memoize
|
||||
import pwndbg.jump
|
||||
|
||||
from capstone import *
|
||||
|
||||
Instruction = collections.namedtuple('Instruction', ['address', 'length', 'asm', 'target'])
|
||||
|
||||
disassembler = None
|
||||
last_arch = None
|
||||
|
||||
CapstoneArch = {
|
||||
'arm': Cs(CS_ARCH_ARM, CS_MODE_ARM),
|
||||
'aarch64': Cs(CS_ARCH_ARM64, CS_MODE_ARM),
|
||||
'i386': Cs(CS_ARCH_X86, CS_MODE_32),
|
||||
'x86-64': Cs(CS_ARCH_X86, CS_MODE_64),
|
||||
'powerpc': Cs(CS_ARCH_PPC, CS_MODE_32),
|
||||
'mips': Cs(CS_ARCH_MIPS, CS_MODE_32),
|
||||
'sparc': Cs(CS_ARCH_SPARC, 0),
|
||||
}
|
||||
|
||||
InstructionMaxSize = {
|
||||
'arm': 4,
|
||||
'aarch64': 4,
|
||||
'i386': 16,
|
||||
'x86-64': 16
|
||||
}
|
||||
|
||||
def get_disassembler(pc):
|
||||
arch = pwndbg.arch.current
|
||||
d = CapstoneArch[arch]
|
||||
if arch in ('i386', 'x86-64', 'powerpc', 'mips'):
|
||||
d.mode = {4:CS_MODE_32, 8:CS_MODE_64}[pwndbg.arch.ptrsize]
|
||||
if arch in ('arm', 'aarch64'):
|
||||
d.mode = {0:CS_MODE_ARM,1:CS_MODE_THUMB}[pc & 1]
|
||||
return d
|
||||
|
||||
def get_one_instruction(pc):
|
||||
pass
|
||||
|
||||
def get(address, instructions=1):
|
||||
address = int(address)
|
||||
|
||||
# Dont disassemble if there's no memory
|
||||
if not pwndbg.memory.peek(address):
|
||||
return []
|
||||
|
||||
raw = pwndbg.arch.disasm(address, address+0xffffffff, instructions)
|
||||
|
||||
retval = []
|
||||
for insn in raw:
|
||||
addr = int(insn['addr'])
|
||||
length = insn['length']
|
||||
asm = insn['asm']
|
||||
target = 0
|
||||
split = asm.split()
|
||||
|
||||
if len(split) == 2:
|
||||
try:
|
||||
target = split[1]
|
||||
name = pwndbg.symbol.get(int(target, 0))
|
||||
if name:
|
||||
asm = asm + ' <%s>' % name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
retval.append(Instruction(addr,length,asm,target))
|
||||
return retval
|
||||
|
||||
def near(address, instructions=1):
|
||||
# If we have IDA, we can just use it to find out where the various
|
||||
# isntructions are.
|
||||
if pwndbg.ida.available():
|
||||
head = address
|
||||
for i in range(instructions):
|
||||
head = pwndbg.ida.PrevHead(head)
|
||||
|
||||
retval = []
|
||||
for i in range(2*instructions + 1):
|
||||
retval.append(get(head))
|
||||
head = pwndbg.ida.NextHead(head)
|
||||
|
||||
|
||||
# Find out how far back we can go without having a page fault
|
||||
distance = instructions * 8
|
||||
for start in range(address-distance, address):
|
||||
if pwndbg.memory.peek(start):
|
||||
break
|
||||
|
||||
# Disassemble more than we expect to need, move forward until we have
|
||||
# enough instructions and we start on the correct spot
|
||||
insns = []
|
||||
while start < address:
|
||||
insns = get(start, instructions)
|
||||
if not insns:
|
||||
return []
|
||||
|
||||
last = insns[-1]
|
||||
|
||||
if last.address + last.length == address:
|
||||
break
|
||||
|
||||
start += 1
|
||||
|
||||
return insns[-instructions:] + get(address, instructions + 1)
|
||||
|
||||
|
||||
calls = set([
|
||||
'call', 'callq',
|
||||
'bl','blx',
|
||||
'jal'
|
||||
])
|
||||
|
||||
returns = set([
|
||||
'ret','retn','return',
|
||||
'jr'
|
||||
])
|
||||
|
||||
branches = calls | returns | set([
|
||||
# Unconditional x86 branches
|
||||
'call', 'callq',
|
||||
'jmp',
|
||||
# Conditional x86 branches
|
||||
'ja', 'jna',
|
||||
'jae', 'jnae',
|
||||
'jb', 'jnb',
|
||||
'jbe', 'jnbe',
|
||||
'jc', 'jnc',
|
||||
'je', 'jne',
|
||||
'jg', 'jng',
|
||||
'jge', 'jnge',
|
||||
'jl', 'jnl',
|
||||
'jle', 'jnle',
|
||||
'jo', 'jno',
|
||||
'jp', 'jnp',
|
||||
'jpe', 'jpo',
|
||||
'js', 'jns',
|
||||
'jz', 'jnz',
|
||||
# ARM branches
|
||||
'b', 'bl', 'bx', 'blx', 'bxj', 'b.w',
|
||||
'beq', 'beq.w', 'bne', 'bmi', 'bpl', 'blt',
|
||||
'ble', 'bgt', 'bge', 'bxne',
|
||||
# MIPS branches
|
||||
'j', 'jal',
|
||||
# SPARC
|
||||
'ba', 'bne', 'be', 'bg', 'ble', 'bge', 'bl', 'bgu', 'bleu',
|
||||
'jmpl'
|
||||
])
|
||||
|
||||
branches = branches | pwndbg.disasm_powerpc.branches
|
||||
|
||||
def color(ins):
|
||||
asm = ins.asm
|
||||
mnem = asm.split()[0].strip().rstrip('+-')
|
||||
if mnem in branches:
|
||||
asm = pwndbg.color.bold(asm)
|
||||
asm += '\n'
|
||||
return asm
|
||||
@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Functionality for disassmebling code at an address, or at an
|
||||
address +/- a few instructions.
|
||||
"""
|
||||
import collections
|
||||
|
||||
import gdb
|
||||
import pwndbg.arch
|
||||
import pwndbg.disasm.mips
|
||||
import pwndbg.disasm.arm
|
||||
import pwndbg.disasm.ppc
|
||||
import pwndbg.disasm.x86
|
||||
import pwndbg.disasm.jump
|
||||
import pwndbg.disasm.sparc
|
||||
import pwndbg.ida
|
||||
import pwndbg.memory
|
||||
import pwndbg.symbol
|
||||
import pwndbg.memoize
|
||||
import pwndbg.jump
|
||||
|
||||
import capstone
|
||||
from capstone import *
|
||||
|
||||
|
||||
Instruction = collections.namedtuple('Instruction', ['address', 'length', 'asm', 'target'])
|
||||
|
||||
disassembler = None
|
||||
last_arch = None
|
||||
|
||||
|
||||
CapstoneArch = {
|
||||
'arm': Cs(CS_ARCH_ARM, CS_MODE_ARM),
|
||||
'aarch64': Cs(CS_ARCH_ARM64, CS_MODE_ARM),
|
||||
'i386': Cs(CS_ARCH_X86, CS_MODE_32),
|
||||
'x86-64': Cs(CS_ARCH_X86, CS_MODE_64),
|
||||
'powerpc': Cs(CS_ARCH_PPC, CS_MODE_32),
|
||||
'mips': Cs(CS_ARCH_MIPS, CS_MODE_32),
|
||||
'sparc': Cs(CS_ARCH_SPARC, 0),
|
||||
}
|
||||
|
||||
for cs in CapstoneArch.values():
|
||||
cs.detail = True
|
||||
|
||||
# For variable-instruction-width architectures
|
||||
# (x86 and amd64), we keep a cache of instruction
|
||||
# sizes, and where the end of the instruction falls.
|
||||
#
|
||||
# This allows us to consistently disassemble backward.
|
||||
VariableInstructionSizeMax = {
|
||||
'i386': 16,
|
||||
'x86-64': 16,
|
||||
}
|
||||
|
||||
backward_cache = {}
|
||||
|
||||
|
||||
def get_target(instruction):
|
||||
"""
|
||||
Make a best effort to determine what value or memory address
|
||||
is important in a given instruction. For example:
|
||||
|
||||
- Any single-operand instruction ==> that value
|
||||
- push rax ==> evaluate rax
|
||||
- Jump or call ==> target address
|
||||
- jmp rax ==> evaluate rax
|
||||
- jmp 0xdeadbeef ==> deadbeef
|
||||
- Memory load or store ==> target address
|
||||
- mov [eax], ebx ==> evaluate eax
|
||||
- Register move ==> source value
|
||||
- mov eax, ebx ==> evaluate ebx
|
||||
- Register manipulation ==> value after execution*
|
||||
- lea eax, [ebx*4] ==> evaluate ebx*4
|
||||
|
||||
Register arguments are only evaluated for the next instruction.
|
||||
|
||||
Returns:
|
||||
A tuple containing the resolved value (or None) and
|
||||
a boolean indicating whether the value is a constant.
|
||||
"""
|
||||
return {
|
||||
'i386': pwndbg.disasm.x86.resolve,
|
||||
'x86-64': pwndbg.disasm.x86.resolve
|
||||
}.get(pwndbg.arch.current, lambda *a: None)(instruction)
|
||||
|
||||
|
||||
def get_disassembler(pc):
|
||||
arch = pwndbg.arch.current
|
||||
d = CapstoneArch[arch]
|
||||
if arch in ('arm', 'aarch64'):
|
||||
d.mode = {0:CS_MODE_ARM,1:CS_MODE_THUMB}[pc & 1]
|
||||
else:
|
||||
d.mode = {4:CS_MODE_32, 8:CS_MODE_64}[pwndbg.arch.ptrsize]
|
||||
return d
|
||||
|
||||
def get_one_instruction(address):
|
||||
md = get_disassembler(address)
|
||||
size = VariableInstructionSizeMax.get(pwndbg.arch.current, 4)
|
||||
data = pwndbg.memory.read(address, size, partial=True)
|
||||
for ins in md.disasm(bytes(data), address, 1):
|
||||
ins.target, ins.target_constant = get_target(ins)
|
||||
return ins
|
||||
|
||||
def one(address=None):
|
||||
if address is None:
|
||||
address = pwndbg.regs.pc
|
||||
for insn in get(address, 1):
|
||||
return insn
|
||||
|
||||
def fix(i):
|
||||
for op in i.operands:
|
||||
if op.type == CS_OP_IMM and op.va:
|
||||
i.op_str = i.op_str.replace()
|
||||
|
||||
return i
|
||||
|
||||
def get(address, instructions=1):
|
||||
address = int(address)
|
||||
|
||||
# Dont disassemble if there's no memory
|
||||
if not pwndbg.memory.peek(address):
|
||||
return []
|
||||
|
||||
retval = []
|
||||
for _ in range(instructions):
|
||||
i = get_one_instruction(address)
|
||||
if i is None:
|
||||
break
|
||||
backward_cache[address+i.size] = address
|
||||
address += i.size
|
||||
retval.append(i)
|
||||
|
||||
return retval
|
||||
|
||||
def near(address, instructions=1):
|
||||
# # If we have IDA, we can just use it to find out where the various
|
||||
# # isntructions are.
|
||||
# if pwndbg.ida.available():
|
||||
# head = address
|
||||
# for i in range(instructions):
|
||||
# head = pwndbg.ida.PrevHead(head)
|
||||
|
||||
# retval = []
|
||||
# for i in range(2*instructions + 1):
|
||||
# retval.append(get(head))
|
||||
# head = pwndbg.ida.NextHead(head)
|
||||
|
||||
# See if we can satisfy the request based on the instruction
|
||||
# length cache.
|
||||
needle = address
|
||||
insns = []
|
||||
while len(insns) < instructions and needle in backward_cache:
|
||||
needle = backward_cache[needle]
|
||||
insn = one(needle)
|
||||
if not insn:
|
||||
return insns
|
||||
insns.insert(0, insn)
|
||||
|
||||
current = one(address)
|
||||
|
||||
if not current:
|
||||
return insns
|
||||
|
||||
target = current.target
|
||||
|
||||
if not pwndbg.disasm.jump.is_jump_taken(current):
|
||||
target = current.address + current.size
|
||||
|
||||
backward_cache[target] = address
|
||||
|
||||
insns.append(current)
|
||||
insns.extend(get(target, instructions))
|
||||
|
||||
return insns
|
||||
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import capstone
|
||||
import pwndbg.chain
|
||||
import pwndbg.color
|
||||
import pwndbg.disasm.jump
|
||||
|
||||
capstone_branch_groups = [
|
||||
capstone.arm.ARM_GRP_CALL,
|
||||
capstone.arm.ARM_GRP_JUMP,
|
||||
capstone.arm64.ARM64_GRP_JUMP,
|
||||
capstone.mips.MIPS_GRP_JUMP,
|
||||
capstone.ppc.PPC_GRP_JUMP,
|
||||
capstone.sparc.SPARC_GRP_JUMP,
|
||||
capstone.x86.X86_GRP_CALL,
|
||||
capstone.x86.X86_GRP_JUMP,
|
||||
]
|
||||
|
||||
def instruction(ins):
|
||||
asm = u'%-06s %s' % (ins.mnemonic, ins.op_str)
|
||||
|
||||
branch = any(g in capstone_branch_groups for g in ins.groups)
|
||||
taken = pwndbg.disasm.jump.is_jump_taken(ins)
|
||||
|
||||
if branch:
|
||||
asm = pwndbg.color.bold(asm)
|
||||
|
||||
if ins.target is not None:
|
||||
sym = pwndbg.symbol.get(ins.target)
|
||||
target = pwndbg.color.get(ins.target)
|
||||
const = ins.target_constant
|
||||
|
||||
# If it's a constant expression, color it directly in the asm.
|
||||
if const:
|
||||
asm = asm.replace(hex(ins.target), target)
|
||||
|
||||
if sym:
|
||||
asm = '%-36s <%s>' % (asm, sym)
|
||||
elif sym:
|
||||
asm = '%-36s <%s; %s>' % (asm, target, sym)
|
||||
else:
|
||||
asm = '%-36s <%s>' % (asm, target)
|
||||
|
||||
if taken:
|
||||
asm = pwndbg.color.green(u'✔ ') + asm
|
||||
else:
|
||||
asm = ' ' + asm
|
||||
|
||||
return asm
|
||||
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import pwndbg.arch
|
||||
import pwndbg.disasm.x86
|
||||
|
||||
from capstone import CS_GRP_JUMP
|
||||
|
||||
def is_jump_taken(instruction):
|
||||
"""
|
||||
Attempt to determine if a conditional instruction is executed.
|
||||
Only valid for the current instruction.
|
||||
|
||||
Returns:
|
||||
Returns True IFF the current instruction is a conditional
|
||||
*or* jump instruction, and it is taken.
|
||||
|
||||
Returns False in all other cases.
|
||||
"""
|
||||
if CS_GRP_JUMP not in instruction.groups:
|
||||
return False
|
||||
if pwndbg.regs.pc != instruction.address:
|
||||
return False
|
||||
|
||||
return {
|
||||
'i386': pwndbg.disasm.x86.is_jump_taken,
|
||||
'x86-64': pwndbg.disasm.x86.is_jump_taken,
|
||||
}.get(pwndbg.arch.current, lambda *a: False)(instruction)
|
||||
@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import pwndbg.arch
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
|
||||
from capstone import *
|
||||
from capstone.x86 import *
|
||||
|
||||
groups = {v:k for k,v in globals().items() if k.startswith('X86_GRP_')}
|
||||
ops = {v:k for k,v in globals().items() if k.startswith('X86_OP_')}
|
||||
regs = {v:k for k,v in globals().items() if k.startswith('X86_REG_')}
|
||||
access = {v:k for k,v in globals().items() if k.startswith('CS_AC_')}
|
||||
|
||||
def is_memory_op(op):
|
||||
return op.type == X86_OP_MEM
|
||||
|
||||
def get_access(ac):
|
||||
rv = []
|
||||
for k,v in access.items():
|
||||
if ac & k: rv.append(v)
|
||||
return ' | '.join(rv)
|
||||
|
||||
def dump(instruction):
|
||||
ins = instruction
|
||||
rv = []
|
||||
rv.append('%s %s' % (ins.mnemonic,ins.op_str))
|
||||
for i, group in enumerate(ins.groups):
|
||||
rv.append(' groups[%i] = %s' % (i, groups[group]))
|
||||
for i, op in enumerate(ins.operands):
|
||||
rv.append(' operands[%i] = %s' % (i, ops[op.type]))
|
||||
rv.append(' access = %s' % (get_access(op.access)))
|
||||
return '\n'.join(rv)
|
||||
|
||||
def resolve(instruction):
|
||||
ops = list(instruction.operands)
|
||||
|
||||
if instruction.mnemonic == 'nop' or not ops:
|
||||
return (None,None)
|
||||
|
||||
# 'ret', 'syscall'
|
||||
if not ops:
|
||||
return
|
||||
|
||||
# 'jmp rax', 'call 0xdeadbeef'
|
||||
if len(ops) == 1:
|
||||
return get_operand_target(instruction, ops[0])
|
||||
|
||||
# 'mov eax, ebx' ==> ebx
|
||||
# 'mov [eax], ebx' ==> [eax]
|
||||
# 'mov eax, 0xdeadbeef' ==> 0xdeadbeef
|
||||
if len(ops) == 2:
|
||||
# If there are any memory operands, prefer those
|
||||
for op in filter(is_memory_op, ops):
|
||||
return get_operand_target(instruction, op)
|
||||
|
||||
# Otherwise, prefer the 'source' operand
|
||||
return get_operand_target(instruction, ops[1])
|
||||
|
||||
|
||||
print("Weird number of operands!!!!!")
|
||||
print(dump(instruction))
|
||||
|
||||
def get_operand_target(instruction, op):
|
||||
current = (instruction.address == pwndbg.regs.pc)
|
||||
|
||||
# EB/E8/E9 or similar "call $+offset"
|
||||
# Capstone handles the instruction + instruction size.
|
||||
if op.type == X86_OP_IMM:
|
||||
return (op.value.imm, True)
|
||||
|
||||
# jmp/call REG
|
||||
if op.type == X86_OP_REG:
|
||||
if not current:
|
||||
return (None, False)
|
||||
|
||||
regname = instruction.reg_name(op.value.reg)
|
||||
return (pwndbg.regs[regname], False)
|
||||
|
||||
# base + disp + scale * offset
|
||||
assert op.type == X86_OP_MEM, "Invalid operand type %i (%s)" % (op.type, ops[op.type])
|
||||
|
||||
target = 0
|
||||
|
||||
# Don't resolve registers
|
||||
constant = bool(op.mem.base == 0 and op.mem.index == 0)
|
||||
if not current and not constant:
|
||||
return (None, False)
|
||||
|
||||
if op.mem.segment != 0:
|
||||
return (None, False)
|
||||
|
||||
if op.mem.base != 0:
|
||||
regname = instruction.reg_name(op.mem.base)
|
||||
target += pwndbg.regs[regname]
|
||||
|
||||
if op.mem.disp != 0:
|
||||
target += op.value.mem.disp
|
||||
|
||||
if op.mem.index != 0:
|
||||
scale = op.mem.scale
|
||||
index = pwndbg.regs[instruction.reg_name(op.mem.index)]
|
||||
target += (scale * index)
|
||||
|
||||
# for source operands, resolve
|
||||
if op.access == CS_AC_READ:
|
||||
try:
|
||||
target = pwndbg.memory.u(target, op.size * 8)
|
||||
except:
|
||||
return (None, False)
|
||||
|
||||
return (target, constant)
|
||||
|
||||
|
||||
def is_jump_taken(instruction):
|
||||
efl = pwndbg.regs.eflags
|
||||
|
||||
cf = efl & (1<<0)
|
||||
pf = efl & (1<<2)
|
||||
af = efl & (1<<4)
|
||||
zf = efl & (1<<6)
|
||||
sf = efl & (1<<7)
|
||||
of = efl & (1<<11)
|
||||
|
||||
return {
|
||||
X86_INS_JO: of,
|
||||
X86_INS_JNO: not of,
|
||||
X86_INS_JS: sf,
|
||||
X86_INS_JNS: not sf,
|
||||
X86_INS_JE: zf,
|
||||
X86_INS_JNE: not zf,
|
||||
X86_INS_JB: cf,
|
||||
X86_INS_JAE: not cf,
|
||||
X86_INS_JBE: cf or zf,
|
||||
X86_INS_JA: not (cf or zf),
|
||||
X86_INS_JL: sf != of,
|
||||
X86_INS_JGE: sf == of,
|
||||
X86_INS_JLE: zf or (sf != of),
|
||||
X86_INS_JP: pf,
|
||||
X86_INS_JNP: not pf,
|
||||
X86_INS_JMP: True,
|
||||
}.get(instruction.id, None)
|
||||
@ -0,0 +1,84 @@
|
||||
import collections
|
||||
from pycparser import c_ast, CParser
|
||||
|
||||
def extractTypeAndName(n, defaultName=None):
|
||||
if isinstance(n, c_ast.EllipsisParam):
|
||||
return ('int', 0, 'vararg')
|
||||
|
||||
t = n.type
|
||||
d = 0
|
||||
|
||||
while isinstance(t, c_ast.PtrDecl) or isinstance(t, c_ast.ArrayDecl):
|
||||
d += 1
|
||||
children = dict(t.children())
|
||||
t = children['type']
|
||||
|
||||
if isinstance(t, c_ast.FuncDecl):
|
||||
return extractTypeAndName(t)
|
||||
|
||||
if isinstance(t.type, c_ast.Struct) \
|
||||
or isinstance(t.type, c_ast.Union) \
|
||||
or isinstance(t.type, c_ast.Enum):
|
||||
typename = t.type.name
|
||||
else:
|
||||
typename = t.type.names[0]
|
||||
|
||||
if typename == 'void' and d == 0 and not t.declname:
|
||||
return None
|
||||
|
||||
name = t.declname or defaultName or ''
|
||||
return typename.lstrip('_'),d,name.lstrip('_')
|
||||
|
||||
Function = collections.namedtuple('Function', ('type', 'derefcnt', 'name', 'args'))
|
||||
Argument = collections.namedtuple('Argument', ('type', 'derefcnt', 'name'))
|
||||
|
||||
def Stringify(X):
|
||||
return '%s %s %s' % (X.type, X.derefcnt * '*', X.name)
|
||||
|
||||
def ExtractFuncDecl(node, verbose=False):
|
||||
# The function name needs to be dereferenced.
|
||||
ftype, fderef, fname = extractTypeAndName(node)
|
||||
|
||||
if not fname:
|
||||
print "Skipping function without a name!"
|
||||
print node.show()
|
||||
return
|
||||
|
||||
fargs = []
|
||||
for i, (argName, arg) in enumerate(node.args.children()):
|
||||
defname = 'arg%i' % i
|
||||
argdata = extractTypeAndName(arg, defname)
|
||||
if argdata is not None:
|
||||
a = Argument(*argdata)
|
||||
fargs.append(a)
|
||||
|
||||
Func = Function(ftype, fderef, fname, fargs)
|
||||
|
||||
if verbose:
|
||||
print Stringify(Func) + '(' + ','.join(Stringify(a) for a in Func.args) + ');'
|
||||
|
||||
return Func
|
||||
|
||||
def ExtractAllFuncDecls(ast, verbose=False):
|
||||
Functions = {}
|
||||
|
||||
class FuncDefVisitor(c_ast.NodeVisitor):
|
||||
def visit_FuncDecl(self, node, *a):
|
||||
f = ExtractFuncDecl(node, verbose)
|
||||
Functions[f.name] = f
|
||||
|
||||
FuncDefVisitor().visit(ast)
|
||||
|
||||
return Functions
|
||||
|
||||
def ExtractFuncDeclFromSource(source):
|
||||
try:
|
||||
p = CParser()
|
||||
ast = p.parse(source + ';')
|
||||
funcs = ExtractAllFuncDecls(ast)
|
||||
for name, func in funcs.items():
|
||||
return func
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# eat it
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,18 @@
|
||||
import pwndbg.arch
|
||||
import pwndbg.jump.mips
|
||||
import pwndbg.jump.arm
|
||||
import pwndbg.jump.ppc
|
||||
import pwndbg.jump.x86
|
||||
import pwndbg.jump.sparc
|
||||
# import pwndbg.arch
|
||||
# import pwndbg.jump.mips
|
||||
# import pwndbg.jump.arm
|
||||
# import pwndbg.jump.ppc
|
||||
# import pwndbg.jump.x86
|
||||
# import pwndbg.jump.sparc
|
||||
|
||||
def get_target(pc):
|
||||
return {
|
||||
'i386': pwndbg.jump.x86.resolver,
|
||||
'x86-64': pwndbg.jump.x86.resolver
|
||||
}.get(pwndbg.arch.current, lambda *a: None)(pc)
|
||||
# def get_target(pc):
|
||||
# return {
|
||||
# 'i386': pwndbg.jump.x86.resolver,
|
||||
# 'x86-64': pwndbg.jump.x86.resolver
|
||||
# }.get(pwndbg.arch.current, lambda *a: None)(pc)
|
||||
|
||||
class Foo(object):
|
||||
@property
|
||||
def foobar(self):
|
||||
return self._foobar
|
||||
# class Foo(object):
|
||||
# @property
|
||||
# def foobar(self):
|
||||
# return self._foobar
|
||||
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
import pwndbg.arch
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
|
||||
from capstone import *
|
||||
from capstone.x86 import *
|
||||
|
||||
md = Cs(CS_ARCH_X86, CS_MODE_32)
|
||||
md.detail = True
|
||||
|
||||
class TargetResolver(object):
|
||||
groups = {v:k for k,v in globals().items() if k.startswith('X86_GRP_')}
|
||||
ops = {v:k for k,v in globals().items() if k.startswith('X86_OP_')}
|
||||
regs = {v:k for k,v in globals().items() if k.startswith('X86_REG_')}
|
||||
|
||||
def __init__(self):
|
||||
self.classes = {
|
||||
X86_GRP_CALL: self.call_or_jump,
|
||||
X86_GRP_JUMP: self.call_or_jump,
|
||||
X86_GRP_RET: self.ret
|
||||
}
|
||||
|
||||
def resolve(self, address):
|
||||
code = bytes(pwndbg.memory.read(address, 16))
|
||||
|
||||
md.mode = CS_MODE_32 if pwndbg.arch.ptrsize == 4 else CS_MODE_64
|
||||
|
||||
instruction = next(md.disasm(code, address, 1))
|
||||
|
||||
for group in instruction.groups:
|
||||
function = self.classes.get(group, None)
|
||||
print(self.groups[group])
|
||||
if function:
|
||||
return function(instruction)
|
||||
|
||||
def get_operand_target(self, op):
|
||||
# EB/E8/E9 or similar "call $+offset"
|
||||
# Capstone handles the instruction + instruction size.
|
||||
if op.type == X86_OP_IMM:
|
||||
return op.value.imm
|
||||
|
||||
# jmp/call REG
|
||||
if op.type == X86_OP_REG:
|
||||
regname = instruction.reg_name(op.value.reg)
|
||||
return pwndbg.regs[regname]
|
||||
|
||||
# base + disp + scale * offset
|
||||
assert op.type == X86_OP_MEM, "Invalid operand type %i" % op.type
|
||||
|
||||
target = 0
|
||||
|
||||
if op.mem.base != 0:
|
||||
regname = instruction.reg_name(op.value.reg)
|
||||
target += pwndbg.regs[regname]
|
||||
|
||||
if op.mem.disp != 0:
|
||||
target += op.value.mem.disp
|
||||
|
||||
if op.mem.index != 0:
|
||||
scale = op.mem.scale
|
||||
index = pwndbg.regs[instruction.reg_name(op.mem.index)]
|
||||
target += (scale * index)
|
||||
|
||||
return target
|
||||
|
||||
|
||||
def call_or_jump(self, instruction):
|
||||
ops = instruction.operands
|
||||
assert len(ops) == 1, "Too many operands (%i)" % len(ops)
|
||||
|
||||
return self.get_operand_target(ops[0])
|
||||
|
||||
def ret(self, instruction):
|
||||
target = pwndbg.regs.sp
|
||||
|
||||
for op in instruction.operands:
|
||||
assert op.type == X86_OP_IMM, "Unknown RET operand type"
|
||||
target += op.value.imm
|
||||
|
||||
return pwndbg.memory.pvoid(target)
|
||||
|
||||
resolver = TargetResolver()
|
||||
@ -0,0 +1,2 @@
|
||||
capstone>=4.0
|
||||
pycparser
|
||||
Loading…
Reference in new issue