mirror of https://github.com/pwndbg/pwndbg
Lots of changes for automatically showing various function arguments etc.
This commit is contained in:
parent
8f36054413
commit
68529bcb5c
|
@ -16,13 +16,19 @@ Best supported on Ubuntu 14.04 with default `gdb` or `gdb-multiarch` (e.g. with
|
|||
|
||||
### Prerequisites
|
||||
|
||||
As of recent versions, you need Capstone 4.0.
|
||||
#### Capstone 4.0
|
||||
|
||||
Currently this is only available via a source build.
|
||||
|
||||
1. Clone the repo: `git clone https://github.com/aquynh/capstone`
|
||||
2. Select the `next` branch: `git checkout -t origin/next`
|
||||
3. Build and install libcapstone: `sudo make.sh install`
|
||||
4. Build and install Python bindings: `cd bindings/python && python setup.py install`
|
||||
|
||||
#### pycparser
|
||||
|
||||
`pip install pycparser`
|
||||
|
||||
## Features
|
||||
|
||||
Does most things that PEDA does. Doesn't do things that PEDA does that [pwntools](https://github.com/Gallopsled/pwntools) or [binjitsu](https://binjit.su) (my fork of pwntools) do better.
|
||||
|
|
|
@ -35,7 +35,12 @@ def wrap(f):
|
|||
try:
|
||||
rv = []
|
||||
def work(): rv.append(f(*a,**kw))
|
||||
with mutex: idaapi.execute_sync(work, idaapi.MFF_WRITE)
|
||||
with mutex:
|
||||
flags = idaapi.MFF_WRITE
|
||||
if f == idc.SetColor:
|
||||
flags |= idaapi.MFF_NOWAIT
|
||||
rv.append(None)
|
||||
idaapi.execute_sync(work, flags)
|
||||
return rv[0]
|
||||
except:
|
||||
import traceback
|
||||
|
|
|
@ -102,6 +102,7 @@ print(pwndbg.color.red(msg))
|
|||
|
||||
@pwndbg.memoize.reset_on_stop
|
||||
def prompt_hook(*a):
|
||||
pwndbg.commands.context.context()
|
||||
with pwndbg.stdio.stdio:
|
||||
pwndbg.commands.context.context()
|
||||
|
||||
gdb.prompt_hook = prompt_hook
|
||||
|
|
|
@ -7,36 +7,124 @@ may be passed in a combination of registers and stack values.
|
|||
import gdb
|
||||
import pwndbg.arch
|
||||
import pwndbg.disasm
|
||||
import pwndbg.functions
|
||||
import pwndbg.funcparser
|
||||
import pwndbg.ida
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
import pwndbg.typeinfo
|
||||
import pwndbg.functions
|
||||
import pwndbg.symbol
|
||||
import pwndbg.typeinfo
|
||||
|
||||
def arguments():
|
||||
from capstone import CS_GRP_CALL
|
||||
|
||||
ida_replacements = {
|
||||
'__int64': 'signed long long int',
|
||||
'__int32': 'signed int',
|
||||
'__int16': 'signed short',
|
||||
'__int8': 'signed char',
|
||||
'__uint64': 'unsigned long long int',
|
||||
'__uint32': 'unsigned int',
|
||||
'__uint16': 'unsigned short',
|
||||
'__uint8': 'unsigned char',
|
||||
'_BOOL_1': 'unsigned char',
|
||||
'_BOOL_2': 'unsigned short',
|
||||
'_BOOL_4': 'unsigned int',
|
||||
'_BYTE': 'unsigned char',
|
||||
'_WORD': 'unsigned short',
|
||||
'_DWORD': 'unsigned int',
|
||||
'_QWORD': 'unsigned long long',
|
||||
'__pure': '',
|
||||
'__hidden': '',
|
||||
'__return_ptr': '',
|
||||
'__struct_ptr': '',
|
||||
'__array_ptr': '',
|
||||
'__fastcall': '',
|
||||
'__cdecl': '',
|
||||
'__thiscall': '',
|
||||
'__userpurge': '',
|
||||
}
|
||||
|
||||
|
||||
def arguments(instruction):
|
||||
"""
|
||||
Returns an array containing the arguments to the current function,
|
||||
if $pc is a 'call' or 'bl' type instruction.
|
||||
|
||||
Otherwise, returns None.
|
||||
"""
|
||||
pwndbg.disasm.calls
|
||||
if instruction.address != pwndbg.regs.pc:
|
||||
return []
|
||||
|
||||
if not instruction.target:
|
||||
return []
|
||||
|
||||
if CS_GRP_CALL not in instruction.groups:
|
||||
return []
|
||||
|
||||
sym = pwndbg.symbol.get(instruction.target)
|
||||
if not sym:
|
||||
return []
|
||||
|
||||
sym = sym.strip().lstrip('_') # _malloc
|
||||
sym = sym.replace('isoc99_', '') # __isoc99_sscanf
|
||||
sym = sym.replace('@plt', '') # getpwiod@plt
|
||||
sym = sym.replace('_chk', '') # __printf_chk
|
||||
func = pwndbg.functions.functions.get(sym, None)
|
||||
|
||||
result = []
|
||||
args = []
|
||||
|
||||
# Try to grab the data out of IDA
|
||||
if not func and instruction.target:
|
||||
typename = pwndbg.ida.GetType(instruction.target)
|
||||
|
||||
if typename:
|
||||
typename += ';'
|
||||
|
||||
# GetType() does not include the name.
|
||||
typename = typename.replace('(', ' function_name(', 1)
|
||||
|
||||
for k,v in ida_replacements.items():
|
||||
typename = typename.replace(k,v)
|
||||
|
||||
func = pwndbg.funcparser.ExtractFuncDeclFromSource(typename + ';')
|
||||
|
||||
if func:
|
||||
args = func.args
|
||||
else:
|
||||
args = [pwndbg.functions.Argument('int',0,argname(i)) for i in range(4)]
|
||||
|
||||
print(repr(args))
|
||||
|
||||
for i,arg in enumerate(args):
|
||||
result.append((arg, argument(i)))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
REGS = {
|
||||
'x86-64': ['rdi','rsi','rdx','rcx','r8','r9'],
|
||||
'arm': ['r%i' % i for i in range(0, 4)],
|
||||
'aarch64': ['x%i' % i for i in range(0, 4)],
|
||||
'powerpc': ['r%i' % i for i in range(3, 10+1)],
|
||||
'mips': ['r%i' % i for i in range(4, 7+1)],
|
||||
'sparc': ['i%i' % i for i in range(0,8)],
|
||||
}
|
||||
|
||||
def argname(n):
|
||||
regs = REGS[pwndbg.arch.current]
|
||||
|
||||
if n < len(regs):
|
||||
return regs[n]
|
||||
|
||||
return 'arg[%i]' % n
|
||||
|
||||
def argument(n):
|
||||
"""
|
||||
Returns the nth argument, as if $pc were a 'call' or 'bl' type
|
||||
instruction.
|
||||
"""
|
||||
arch = pwndbg.arch.current
|
||||
|
||||
regs = {
|
||||
'x86-64': ['rdi','rsi','rdx','rcx','r8','r9'],
|
||||
'arm': ['r%i' % i for i in range(0, 4)],
|
||||
'aarch64': ['x%i' % i for i in range(0, 4)],
|
||||
'powerpc': ['r%i' % i for i in range(3, 10+1)],
|
||||
'mips': ['r%i' % i for i in range(4, 7+1)],
|
||||
'sparc': ['i%i' % i for i in range(0,8)],
|
||||
}[arch]
|
||||
regs = REGS[pwndbg.arch.current]
|
||||
|
||||
if n < len(regs):
|
||||
return getattr(pwndbg.regs, regs[n])
|
||||
|
|
|
@ -8,7 +8,7 @@ import pwndbg.vmmap
|
|||
|
||||
LIMIT = 5
|
||||
|
||||
def get(address, limit=5):
|
||||
def get(address, limit=LIMIT):
|
||||
"""
|
||||
Recursively dereferences an address.
|
||||
|
||||
|
@ -30,32 +30,32 @@ def get(address, limit=5):
|
|||
return result
|
||||
|
||||
|
||||
def format(value):
|
||||
chain = get(value, LIMIT)
|
||||
def format(value, limit=LIMIT, code=True):
|
||||
chain = get(value, limit)
|
||||
|
||||
# Enhance the last entry
|
||||
# If there are no pointers (e.g. eax = 0x41414141), then enhance
|
||||
# the only element there is.
|
||||
if len(chain) == 1:
|
||||
enhanced = pwndbg.enhance.enhance(chain[-1])
|
||||
# Enhance the last entry
|
||||
# If there are no pointers (e.g. eax = 0x41414141), then enhance
|
||||
# the only element there is.
|
||||
if len(chain) == 1:
|
||||
enhanced = pwndbg.enhance.enhance(chain[-1], code=code)
|
||||
|
||||
# Otherwise, the last element in the chain is the non-pointer value.
|
||||
# We want to enhance the last pointer value.
|
||||
elif len(chain) < LIMIT:
|
||||
enhanced = pwndbg.enhance.enhance(chain[-2])
|
||||
# Otherwise, the last element in the chain is the non-pointer value.
|
||||
# We want to enhance the last pointer value.
|
||||
elif len(chain) < limit:
|
||||
enhanced = pwndbg.enhance.enhance(chain[-2], code=code)
|
||||
|
||||
else:
|
||||
enhanced = '...'
|
||||
else:
|
||||
enhanced = '...'
|
||||
|
||||
# Colorize the rest
|
||||
rest = []
|
||||
for link in chain[:-1]:
|
||||
symbol = pwndbg.symbol.get(link) or None
|
||||
if symbol:
|
||||
symbol = '%#x (%s)' % (link, symbol)
|
||||
rest.append(pwndbg.color.get(link, symbol))
|
||||
# Colorize the rest
|
||||
rest = []
|
||||
for link in chain[:-1]:
|
||||
symbol = pwndbg.symbol.get(link) or None
|
||||
if symbol:
|
||||
symbol = '%#x (%s)' % (link, symbol)
|
||||
rest.append(pwndbg.color.get(link, symbol))
|
||||
|
||||
if len(chain) == 1:
|
||||
return enhanced
|
||||
if len(chain) == 1:
|
||||
return enhanced
|
||||
|
||||
return ' --> '.join(rest) + ' <-- ' + enhanced
|
||||
return ' --> '.join(rest) + ' <-- ' + enhanced
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import gdb
|
||||
import pwndbg.enhance
|
||||
import pwndbg.vmmap
|
||||
|
||||
NORMAL = "\x1b[0m"
|
||||
|
@ -65,3 +66,4 @@ def legend():
|
|||
UNDERLINE + 'RWX' + NORMAL,
|
||||
'RODATA'
|
||||
))
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import functools
|
||||
import traceback
|
||||
import gdb
|
||||
|
||||
import pwndbg.regs
|
||||
import pwndbg.memory
|
||||
import pwndbg.hexdump
|
||||
import pwndbg.color
|
||||
import pwndbg.chain
|
||||
import pwndbg.color
|
||||
import pwndbg.enhance
|
||||
import pwndbg.hexdump
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
import pwndbg.stdio
|
||||
import pwndbg.symbol
|
||||
import pwndbg.ui
|
||||
|
||||
|
@ -41,7 +44,8 @@ class _Command(gdb.Command):
|
|||
raise
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.function(*args, **kwargs)
|
||||
with pwndbg.stdio.stdio:
|
||||
return self.function(*args, **kwargs)
|
||||
|
||||
|
||||
class _ParsedCommand(_Command):
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import gdb
|
||||
import pwndbg.arguments
|
||||
import pwndbg.chain
|
||||
import pwndbg.color
|
||||
import pwndbg.commands
|
||||
|
@ -12,15 +15,15 @@ import pwndbg.symbol
|
|||
import pwndbg.ui
|
||||
import pwndbg.vmmap
|
||||
|
||||
@pwndbg.events.stop
|
||||
@pwndbg.commands.ParsedCommand
|
||||
# @pwndbg.events.stop
|
||||
@pwndbg.commands.Command
|
||||
@pwndbg.commands.OnlyWhenRunning
|
||||
def context(*args):
|
||||
"""
|
||||
Print out the current register, instruction, and stack context.
|
||||
"""
|
||||
if len(args) == 0:
|
||||
args = ['reg','code','stack','backtrace']
|
||||
args = ['reg','code','stack','backtrace','args']
|
||||
|
||||
args = [a[0] for a in args]
|
||||
|
||||
|
@ -29,11 +32,13 @@ def context(*args):
|
|||
result.append(pwndbg.color.legend())
|
||||
if 'r' in args: result.extend(context_regs())
|
||||
if 'c' in args: result.extend(context_code())
|
||||
if 'a' in args: result.extend(context_args())
|
||||
if 's' in args: result.extend(context_stack())
|
||||
if 'b' in args: result.extend(context_backtrace())
|
||||
result.extend(context_signal())
|
||||
|
||||
print('\n'.join(map(str, result)))
|
||||
for line in result:
|
||||
print(line.encode('utf-8'))
|
||||
|
||||
def context_regs():
|
||||
result = []
|
||||
|
@ -140,6 +145,23 @@ def context_backtrace(frame_count=10, with_banner=True):
|
|||
i += 1
|
||||
return result
|
||||
|
||||
def context_args():
|
||||
result = []
|
||||
|
||||
##################################################
|
||||
# DISABLED FOR NOW, I LIKE INLINE DISPLAY BETTER
|
||||
##################################################
|
||||
# # For call instructions, attempt to resolve the target and
|
||||
# # determine the number of arguments.
|
||||
# for arg, value in pwndbg.arguments.arguments(pwndbg.disasm.one()):
|
||||
# code = False if arg.type == 'char' else True
|
||||
# pretty = pwndbg.chain.format(value, code=code)
|
||||
# result.append('%-10s %s' % (arg.name+':', pretty))
|
||||
# if not result:
|
||||
# return []
|
||||
# result.insert(0, pwndbg.color.blue(pwndbg.ui.banner("arguments")))
|
||||
return result
|
||||
|
||||
last_signal = []
|
||||
|
||||
def save_signal(signal):
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import pwndbg.commands
|
||||
import pwndbg.hexdump
|
||||
import pwndbg.memory
|
||||
|
@ -26,7 +28,7 @@ def hexdump(address=None, count=64):
|
|||
# if address is None:
|
||||
# address =
|
||||
|
||||
data = pwndbg.memory.read(address, count)
|
||||
data = pwndbg.memory.read(address, count, partial=True)
|
||||
|
||||
for line in pwndbg.hexdump.hexdump(data, address=address):
|
||||
print(line)
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from capstone import *
|
||||
|
||||
import pwndbg.arguments
|
||||
import pwndbg.color
|
||||
import pwndbg.disasm
|
||||
import pwndbg.disasm.color
|
||||
import pwndbg.functions
|
||||
import pwndbg.ida
|
||||
import pwndbg.regs
|
||||
import pwndbg.strings
|
||||
import pwndbg.symbol
|
||||
import pwndbg.ui
|
||||
import pwndbg.vmmap
|
||||
|
@ -54,17 +62,38 @@ def nearpc(pc=None, lines=None, to_string=False):
|
|||
symbols[i] = s.ljust(longest_sym)
|
||||
|
||||
# Print out each instruction
|
||||
prev = None
|
||||
for i,s in zip(instructions, symbols):
|
||||
asm = pwndbg.disasm.color(i)
|
||||
asm = pwndbg.disasm.color.instruction(i)
|
||||
prefix = ' =>' if i.address == pc else ' '
|
||||
|
||||
pre = pwndbg.ida.Anterior(i.address)
|
||||
if pre:
|
||||
result.append(pwndbg.color.bold(pre))
|
||||
|
||||
line = ' '.join((prefix, s or hex(i.address), asm))
|
||||
line = ' '.join((prefix, "%#x" % i.address, s or '', asm))
|
||||
|
||||
old, prev = prev, i
|
||||
|
||||
# Put an ellipsis between discontiguous code groups
|
||||
if not old:
|
||||
pass
|
||||
elif old.address + old.size != i.address:
|
||||
result.append('...')
|
||||
# Put an empty line after fall-through basic blocks
|
||||
elif any(g in old.groups for g in (CS_GRP_CALL, CS_GRP_JUMP, CS_GRP_RET)):
|
||||
result.append('')
|
||||
|
||||
result.append(line)
|
||||
|
||||
# For call instructions, attempt to resolve the target and
|
||||
# determine the number of arguments.
|
||||
for arg, value in pwndbg.arguments.arguments(i):
|
||||
code = False if arg.type == 'char' else True
|
||||
pretty = pwndbg.chain.format(value, code=code)
|
||||
result.append('%8s%-10s %s' % ('',arg.name+':', pretty))
|
||||
|
||||
|
||||
if not to_string:
|
||||
print('\n'.join(result))
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ def dX(size, address, count, to_string=False):
|
|||
lines = []
|
||||
|
||||
for i, row in enumerate(rows):
|
||||
if not row:
|
||||
continue
|
||||
|
||||
line = [enhex(pwndbg.arch.ptrsize, address + i+16)]
|
||||
for value in row:
|
||||
line.append(enhex(size, value))
|
||||
|
|
|
@ -10,3 +10,6 @@ import sys
|
|||
# Quickly determine which version is running
|
||||
python2 = sys.version_info.major == 2
|
||||
python3 = sys.version_info.major == 3
|
||||
|
||||
if python3:
|
||||
globals()['basestring'] = str
|
172
pwndbg/disasm.py
172
pwndbg/disasm.py
|
@ -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)
|
|
@ -12,7 +12,6 @@ import string
|
|||
|
||||
import gdb
|
||||
import pwndbg.arch
|
||||
import pwndbg.color
|
||||
import pwndbg.disasm
|
||||
import pwndbg.memoize
|
||||
import pwndbg.memory
|
||||
|
@ -33,7 +32,7 @@ def good_instr(i):
|
|||
return not any(bad in i for bad in bad_instrs)
|
||||
|
||||
# @pwndbg.memoize.reset_on_stop
|
||||
def enhance(value):
|
||||
def enhance(value, code = True):
|
||||
"""
|
||||
Given the last pointer in a chain, attempt to characterize
|
||||
|
||||
|
@ -43,6 +42,10 @@ def enhance(value):
|
|||
'value'. For example, if it is set to RWX, we try to get information on whether
|
||||
it resides on the stack, or in a RW section that *happens* to be RWX, to
|
||||
determine which order to print the fields.
|
||||
|
||||
Arguments:
|
||||
value(obj): Value to enhance
|
||||
code(bool): Hint that indicates the value may be an instruction
|
||||
"""
|
||||
value = int(value)
|
||||
|
||||
|
@ -56,7 +59,7 @@ def enhance(value):
|
|||
can_read = False
|
||||
|
||||
if not can_read:
|
||||
retval = hex(int(value))
|
||||
retval = '%#x' % int(value)
|
||||
|
||||
# Try to unpack the value as a string
|
||||
packed = pwndbg.arch.pack(int(value))
|
||||
|
@ -82,11 +85,9 @@ def enhance(value):
|
|||
rwx = exe = False
|
||||
|
||||
if exe:
|
||||
instr = pwndbg.disasm.get(value, 1)[0].asm
|
||||
|
||||
# However, if it contains bad instructions, bail
|
||||
if not good_instr(instr):
|
||||
instr = None
|
||||
instr = pwndbg.disasm.one(value)
|
||||
if instr:
|
||||
instr = "%-6s %s" % (instr.mnemonic, instr.op_str)
|
||||
|
||||
szval = pwndbg.strings.get(value) or None
|
||||
szval0 = szval
|
||||
|
@ -98,11 +99,13 @@ def enhance(value):
|
|||
if 0 <= intval < 10:
|
||||
intval = str(intval)
|
||||
else:
|
||||
intval = hex(int(intval & pwndbg.arch.ptrmask))
|
||||
intval = '%#x' % int(intval & pwndbg.arch.ptrmask)
|
||||
|
||||
retval = []
|
||||
|
||||
# print([instr,intval0,szval])
|
||||
if not code:
|
||||
instr = None
|
||||
|
||||
# If it's on the stack, don't display it as code in a chain.
|
||||
if instr and 'stack' in page.objfile:
|
||||
|
|
|
@ -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
|
@ -111,6 +111,13 @@ def GetFuncOffset(addr):
|
|||
rv = _ida.GetFuncOffset(addr)
|
||||
return rv
|
||||
|
||||
@withIDA
|
||||
@takes_address
|
||||
@pwndbg.memoize.reset_on_objfile
|
||||
def GetType(addr):
|
||||
rv = _ida.GetType(addr)
|
||||
return rv
|
||||
|
||||
@withIDA
|
||||
@returns_address
|
||||
def here():
|
||||
|
@ -186,20 +193,20 @@ def SetColor(pc, color):
|
|||
|
||||
colored_pc = None
|
||||
|
||||
# @pwndbg.events.stop
|
||||
# @withIDA
|
||||
# def Auto_Color_PC():
|
||||
# global colored_pc
|
||||
# colored_pc = pwndbg.regs.pc
|
||||
# SetColor(colored_pc, 0x7f7fff)c
|
||||
@pwndbg.events.stop
|
||||
@withIDA
|
||||
def Auto_Color_PC():
|
||||
global colored_pc
|
||||
colored_pc = pwndbg.regs.pc
|
||||
SetColor(colored_pc, 0x7f7fff)
|
||||
|
||||
# @pwndbg.events.cont
|
||||
# @withIDA
|
||||
# def Auto_UnColor_PC():
|
||||
# global colored_pc
|
||||
# if colored_pc:
|
||||
# SetColor(colored_pc, 0xffffff)
|
||||
# colored_pc = None
|
||||
@pwndbg.events.cont
|
||||
@withIDA
|
||||
def Auto_UnColor_PC():
|
||||
global colored_pc
|
||||
if colored_pc:
|
||||
SetColor(colored_pc, 0xffffff)
|
||||
colored_pc = None
|
||||
|
||||
@withIDA
|
||||
@returns_address
|
||||
|
@ -237,3 +244,9 @@ def GetFlags(addr):
|
|||
@pwndbg.memoize.reset_on_objfile
|
||||
def isASCII(flags):
|
||||
return _ida.isASCII(flags)
|
||||
|
||||
@withIDA
|
||||
@takes_address
|
||||
@pwndbg.memoize.reset_on_objfile
|
||||
def ArgCount(address):
|
||||
pass
|
|
@ -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()
|
|
@ -10,8 +10,15 @@ import pwndbg.typeinfo
|
|||
PAGE_SIZE = 0x1000
|
||||
MMAP_MIN_ADDR = 0x10000
|
||||
|
||||
def read(addr, count):
|
||||
result = gdb.selected_inferior().read_memory(addr, count)
|
||||
def read(addr, count, partial=False):
|
||||
try:
|
||||
result = gdb.selected_inferior().read_memory(addr, count)
|
||||
except gdb.error as e:
|
||||
if not partial:
|
||||
raise
|
||||
|
||||
stop_addr = int(e.message.split()[-1], 0)
|
||||
return read(addr, stop_addr-addr)
|
||||
|
||||
if pwndbg.compat.python3:
|
||||
result = result.tobytes()
|
||||
|
@ -42,15 +49,23 @@ def ushort(addr): return readtype(pwndbg.typeinfo.ushort, addr)
|
|||
def uint(addr): return readtype(pwndbg.typeinfo.uint, addr)
|
||||
def pvoid(addr): return readtype(pwndbg.typeinfo.pvoid, addr)
|
||||
|
||||
def u8(addr): return readtype(pwndbg.typeinfo.uint8_t, addr)
|
||||
def u16(addr): return readtype(pwndbg.typeinfo.uint16_t, addr)
|
||||
def u32(addr): return readtype(pwndbg.typeinfo.uint32_t, addr)
|
||||
def u64(addr): return readtype(pwndbg.typeinfo.uint64_t, addr)
|
||||
def u8(addr): return readtype(pwndbg.typeinfo.uint8, addr)
|
||||
def u16(addr): return readtype(pwndbg.typeinfo.uint16, addr)
|
||||
def u32(addr): return readtype(pwndbg.typeinfo.uint32, addr)
|
||||
def u64(addr): return readtype(pwndbg.typeinfo.uint64, addr)
|
||||
|
||||
def s8(addr): return readtype(pwndbg.typeinfo.int8_t, addr)
|
||||
def s16(addr): return readtype(pwndbg.typeinfo.int16_t, addr)
|
||||
def s32(addr): return readtype(pwndbg.typeinfo.int32_t, addr)
|
||||
def s64(addr): return readtype(pwndbg.typeinfo.int64_t, addr)
|
||||
def u(addr, size):
|
||||
return {
|
||||
8: u8,
|
||||
16: u16,
|
||||
32: u32,
|
||||
64: u64
|
||||
}[size](addr)
|
||||
|
||||
def s8(addr): return readtype(pwndbg.typeinfo.int8, addr)
|
||||
def s16(addr): return readtype(pwndbg.typeinfo.int16, addr)
|
||||
def s32(addr): return readtype(pwndbg.typeinfo.int32, addr)
|
||||
def s64(addr): return readtype(pwndbg.typeinfo.int64, addr)
|
||||
|
||||
def write(addr, data):
|
||||
gdb.selected_inferior().write_memory(addr, data)
|
||||
|
|
|
@ -189,7 +189,13 @@ class module(ModuleType):
|
|||
|
||||
def __getattr__(self, attr):
|
||||
try:
|
||||
value = int(gdb.parse_and_eval('$' + attr.lstrip('$')))
|
||||
value = gdb.parse_and_eval('$' + attr.lstrip('$'))
|
||||
if 'eflags' not in attr:
|
||||
value = value.cast(pwndbg.typeinfo.ptrdiff)
|
||||
else:
|
||||
# Seriously, gdb? Only accepts uint32.
|
||||
value = value.cast(pwndbg.typeinfo.uint32)
|
||||
value = int(value)
|
||||
return value & pwndbg.arch.ptrmask
|
||||
except gdb.error:
|
||||
return None
|
||||
|
@ -198,7 +204,12 @@ class module(ModuleType):
|
|||
if isinstance(item, int):
|
||||
return arch_to_regs[pwndbg.arch.current][item]
|
||||
|
||||
assert isinstance(item, str), "Unknown type %r" % item
|
||||
if not isinstance(item, basestring):
|
||||
print "Unknown register type: %r" % (item)
|
||||
import pdb, traceback
|
||||
traceback.print_stack()
|
||||
pdb.set_trace()
|
||||
return None
|
||||
|
||||
# e.g. if we're looking for register "$rax", turn it into "rax"
|
||||
item = item.lstrip('$')
|
||||
|
|
|
@ -13,18 +13,28 @@ import gdb
|
|||
import pwndbg.compat
|
||||
|
||||
def get(fd, mode):
|
||||
file = io.open(fd, mode=mode, buffering=0, closefd=False)
|
||||
return io.TextIOWrapper(file, write_through=True)
|
||||
if pwndbg.compat.python3:
|
||||
file = io.open(fd, mode=mode, buffering=0)
|
||||
|
||||
kw ={}
|
||||
if pwndbg.compat.python3:
|
||||
kw['write_through']=True
|
||||
|
||||
return io.TextIOWrapper(file, **kw)
|
||||
else:
|
||||
return open(fd, mode=mode)
|
||||
|
||||
|
||||
class Stdio(object):
|
||||
queue = []
|
||||
|
||||
def __enter__(self, *a, **kw):
|
||||
self.queue.append((sys.stdin, sys.stdout, sys.stderr))
|
||||
if pwndbg.compat.python3:
|
||||
sys.stdin = get(0, 'rb')
|
||||
sys.stdout = get(1, 'wb')
|
||||
sys.stderr = get(2, 'wb')
|
||||
|
||||
if pwndbg.compat.python3 or True:
|
||||
sys.stdin = get('/dev/stdin', 'rb')
|
||||
sys.stdout = get('/dev/stdout', 'wb')
|
||||
sys.stderr = get('/dev/stderr', 'wb')
|
||||
|
||||
def __exit__(self, *a, **kw):
|
||||
sys.stdin, sys.stdout, sys.stderr = self.queue.pop()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
capstone>=4.0
|
||||
pycparser
|
Loading…
Reference in New Issue