mirror of https://github.com/pwndbg/pwndbg.git
Add these files so I don't lose them again
parent
1bafd35211
commit
5703cc1acb
@ -0,0 +1,2 @@
|
||||
#Embedded file name: /usr/local/google/home/riggle/pwndbg/pwndbg/emu/__init__.py
|
||||
import pwndbg.emu.x86
|
||||
@ -0,0 +1,174 @@
|
||||
#Embedded file name: /usr/local/google/home/riggle/pwndbg/pwndbg/emu/emulator.py
|
||||
"""
|
||||
Emulation assistance from Unicorn.
|
||||
"""
|
||||
import gdb
|
||||
import unicorn as U
|
||||
import capstone as C
|
||||
import pwndbg.arch
|
||||
import pwndbg.disasm
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
import pwndbg.emu.emulator
|
||||
arch_to_UC = {'i386': U.UC_ARCH_X86,
|
||||
'x86-64': U.UC_ARCH_X86,
|
||||
'mips': U.UC_ARCH_MIPS,
|
||||
'sparc': U.UC_ARCH_SPARC,
|
||||
'arm': U.UC_ARCH_ARM,
|
||||
'aarch64': U.UC_ARCH_ARM64}
|
||||
arch_to_UC_consts = {'i386': U.x86_const,
|
||||
'x86-64': U.x86_const,
|
||||
'mips': U.mips_const,
|
||||
'sparc': U.sparc_const,
|
||||
'arm': U.arm_const,
|
||||
'aarch64': U.arm64_const}
|
||||
arch_to_CS = {'i386': C.CS_ARCH_X86,
|
||||
'x86-64': C.CS_ARCH_X86,
|
||||
'mips': C.CS_ARCH_MIPS,
|
||||
'sparc': C.CS_ARCH_SPARC,
|
||||
'arm': C.CS_ARCH_ARM,
|
||||
'aarch64': C.CS_ARCH_ARM64}
|
||||
arch_to_SYSCALL = {U.UC_ARCH_X86: [C.x86_const.X86_INS_SYSCALL,
|
||||
C.x86_const.X86_INS_SYSENTER,
|
||||
C.x86_const.X86_INS_SYSEXIT,
|
||||
C.x86_const.X86_INS_SYSRET,
|
||||
C.x86_const.X86_INS_IRET,
|
||||
C.x86_const.X86_INS_IRETD,
|
||||
C.x86_const.X86_INS_IRETQ,
|
||||
C.x86_const.X86_INS_INT,
|
||||
C.x86_const.X86_INS_INT1,
|
||||
C.x86_const.X86_INS_INT3],
|
||||
U.UC_ARCH_MIPS: [C.mips_const.MIPS_INS_SYSCALL],
|
||||
U.UC_ARCH_SPARC: [C.sparc_const.SPARC_INS_T],
|
||||
U.UC_ARCH_ARM: [C.arm_const.ARM_INS_SVC],
|
||||
U.UC_ARCH_ARM64: [C.arm64_const.ARM64_INS_SVC],
|
||||
U.UC_ARCH_PPC: [C.ppc_const.PPC_INS_SC]}
|
||||
|
||||
class Emulator(object):
|
||||
|
||||
def __init__(self):
|
||||
self.arch = pwndbg.arch.current
|
||||
if self.arch not in arch_to_UC:
|
||||
raise NotImplementedError('Cannot emulate code for %s' % self.arch)
|
||||
self.consts = arch_to_UC_consts[self.arch]
|
||||
self.mode = self.get_mode()
|
||||
self.cs = C.Cs(arch_to_CS[self.arch], self.mode)
|
||||
self.uc = U.Uc(arch_to_UC[self.arch], self.mode)
|
||||
self.regs = pwndbg.regs.current
|
||||
for reg in list(self.regs.misc) + list(self.regs.common) + list(self.regs.flags):
|
||||
enum = self.get_reg_enum(reg)
|
||||
if not reg:
|
||||
print 'Could not set register %r' % reg
|
||||
continue
|
||||
value = getattr(pwndbg.regs, reg)
|
||||
if value is None:
|
||||
print '# Could not set register %r' % reg
|
||||
continue
|
||||
else:
|
||||
name = 'U.x86_const.UC_X86_REG_%s' % reg.upper()
|
||||
print 'uc.reg_write(%(name)s, %(value)r)' % locals()
|
||||
self.uc.reg_write(enum, value)
|
||||
|
||||
self.uc.hook_add(U.UC_HOOK_MEM_READ_UNMAPPED | U.UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
|
||||
self.uc.hook_add(U.UC_HOOK_INTR, self.hook_intr)
|
||||
self.map_page(self.pc)
|
||||
|
||||
def __getattr__(self, name):
|
||||
reg = self.get_reg_enum(name)
|
||||
if reg:
|
||||
return self.uc.reg_read(reg)
|
||||
raise AttributeError('AttributeError: %r object has no attribute %r' % (self, name))
|
||||
|
||||
def get_mode(self):
|
||||
"""
|
||||
Retrieve the mode used by Capstone and Unicorn for the current
|
||||
architecture.
|
||||
|
||||
This relies on the enums being the same.
|
||||
"""
|
||||
arch = pwndbg.arch.current
|
||||
if arch in ('arm', 'aarch64'):
|
||||
return {0: C.CS_MODE_ARM,
|
||||
32: C.CS_MODE_THUMB}[pwndbg.regs.cpsr & 32]
|
||||
else:
|
||||
return {4: C.CS_MODE_32,
|
||||
8: C.CS_MODE_64}[pwndbg.arch.ptrsize]
|
||||
|
||||
def map_page(self, page):
|
||||
page = pwndbg.memory.page_align(page)
|
||||
try:
|
||||
data = pwndbg.memory.read(page, pwndbg.memory.PAGE_SIZE)
|
||||
except gdb.MemoryError:
|
||||
return False
|
||||
|
||||
if not data:
|
||||
return False
|
||||
self.uc.mem_map(page, pwndbg.memory.PAGE_SIZE)
|
||||
self.uc.mem_write(page, str(data))
|
||||
return True
|
||||
|
||||
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
|
||||
start = pwndbg.memory.page_align(address)
|
||||
size = pwndbg.memory.page_size_align(address + size - start)
|
||||
stop = start + size
|
||||
print 'Mapping %x -> %x' % (start, stop)
|
||||
for page in range(start, stop, pwndbg.memory.PAGE_SIZE):
|
||||
if not self.map_page(page):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def hook_intr(self, uc, intno, user_data):
|
||||
"""
|
||||
We never want to emulate through an interrupt. Just stop.
|
||||
"""
|
||||
self.uc.emu_stop()
|
||||
|
||||
def get_reg_enum(self, reg):
|
||||
"""
|
||||
Returns the Unicorn Emulator enum code for the named register.
|
||||
|
||||
Also supports general registers like 'sp' and 'pc'.
|
||||
"""
|
||||
if not self.regs:
|
||||
return None
|
||||
if reg == 'sp':
|
||||
return self.get_reg_enum(self.regs.stack)
|
||||
if hasattr(self.regs, reg):
|
||||
return self.get_reg_enum(getattr(self.regs, reg))
|
||||
if reg in self.regs.all:
|
||||
for reg_enum in (c for c in dir(self.consts) if c.endswith(reg.upper())):
|
||||
return getattr(self.consts, reg_enum)
|
||||
|
||||
def until_jump(self, pc = None):
|
||||
"""
|
||||
Emulates instructions starting at the specified address until the
|
||||
program counter is set to an address which does not linearly follow
|
||||
the previously-emulated instruction.
|
||||
"""
|
||||
self.until_jump_prev = None
|
||||
self.until_jump_prevsize = None
|
||||
self.until_jump_target = None
|
||||
self.uc.hook_add(U.UC_HOOK_CODE, self.until_jump_hook_code)
|
||||
self.uc.emu_start(self.pc, 0)
|
||||
self.uc.hook_del(U.UC_HOOK_CODE)
|
||||
return (self.until_jump_prev, self.until_jump_target)
|
||||
|
||||
def until_jump_hook_code(self, uc, address, size, user_data):
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
print hex(address)
|
||||
print pwndbg.disasm.one(address)
|
||||
if self.until_jump_prev is not None and self.until_jump_prev + self.until_jump_prevsize != address:
|
||||
self.until_jump_target = address
|
||||
self.uc.emu_stop()
|
||||
return
|
||||
self.until_jump_prev = address
|
||||
self.until_jump_prevsize = size
|
||||
|
||||
def until_syscall(self, pc = None):
|
||||
"""
|
||||
Emulates instructions starting at the specified address until the program
|
||||
counter points at a syscall instruction (int 0x80, svc, etc.).
|
||||
"""
|
||||
pass
|
||||
@ -0,0 +1,251 @@
|
||||
"""
|
||||
Emulation assistance from Unicorn.
|
||||
"""
|
||||
import gdb
|
||||
import unicorn as U
|
||||
import capstone as C
|
||||
import pwndbg.arch
|
||||
import pwndbg.disasm
|
||||
import pwndbg.memory
|
||||
import pwndbg.regs
|
||||
import pwndbg.emu.emulator
|
||||
|
||||
# Map our internal architecture names onto Unicorn Engine's architecture types.
|
||||
arch_to_UC = {
|
||||
'i386': U.UC_ARCH_X86,
|
||||
'x86-64': U.UC_ARCH_X86,
|
||||
'mips': U.UC_ARCH_MIPS,
|
||||
'sparc': U.UC_ARCH_SPARC,
|
||||
'arm': U.UC_ARCH_ARM,
|
||||
'aarch64': U.UC_ARCH_ARM64,
|
||||
# 'powerpc': U.UC_ARCH_PPC,
|
||||
}
|
||||
|
||||
arch_to_UC_consts = {
|
||||
'i386': U.x86_const,
|
||||
'x86-64': U.x86_const,
|
||||
'mips': U.mips_const,
|
||||
'sparc': U.sparc_const,
|
||||
'arm': U.arm_const,
|
||||
'aarch64': U.arm64_const,
|
||||
}
|
||||
|
||||
# Map our internal architecture names onto Unicorn Engine's architecture types.
|
||||
arch_to_CS = {
|
||||
'i386': C.CS_ARCH_X86,
|
||||
'x86-64': C.CS_ARCH_X86,
|
||||
'mips': C.CS_ARCH_MIPS,
|
||||
'sparc': C.CS_ARCH_SPARC,
|
||||
'arm': C.CS_ARCH_ARM,
|
||||
'aarch64': C.CS_ARCH_ARM64,
|
||||
# 'powerpc': C.CS_ARCH_PPC,
|
||||
}
|
||||
|
||||
|
||||
# Until Unicorn Engine provides full information about the specific instruction
|
||||
# being executed for all architectures, we must rely on Capstone to provide
|
||||
# that information.
|
||||
arch_to_SYSCALL = {
|
||||
U.UC_ARCH_X86: [
|
||||
C.x86_const.X86_INS_SYSCALL,
|
||||
C.x86_const.X86_INS_SYSENTER,
|
||||
C.x86_const.X86_INS_SYSEXIT,
|
||||
C.x86_const.X86_INS_SYSRET,
|
||||
C.x86_const.X86_INS_IRET,
|
||||
C.x86_const.X86_INS_IRETD,
|
||||
C.x86_const.X86_INS_IRETQ,
|
||||
C.x86_const.X86_INS_INT,
|
||||
C.x86_const.X86_INS_INT1,
|
||||
C.x86_const.X86_INS_INT3,
|
||||
],
|
||||
U.UC_ARCH_MIPS: [
|
||||
C.mips_const.MIPS_INS_SYSCALL
|
||||
],
|
||||
U.UC_ARCH_SPARC: [
|
||||
C.sparc_const.SPARC_INS_T
|
||||
],
|
||||
U.UC_ARCH_ARM: [
|
||||
C.arm_const.ARM_INS_SVC
|
||||
],
|
||||
U.UC_ARCH_ARM64: [
|
||||
C.arm64_const.ARM64_INS_SVC
|
||||
],
|
||||
U.UC_ARCH_PPC: [
|
||||
C.ppc_const.PPC_INS_SC
|
||||
],
|
||||
}
|
||||
|
||||
class Emulator(object):
|
||||
def __init__(self):
|
||||
self.arch = pwndbg.arch.current
|
||||
|
||||
if self.arch not in arch_to_UC:
|
||||
raise NotImplementedError("Cannot emulate code for %s" % self.arch)
|
||||
|
||||
self.consts = arch_to_UC_consts[self.arch]
|
||||
self.mode = self.get_mode()
|
||||
self.cs = C.Cs(arch_to_CS[self.arch], self.mode)
|
||||
self.uc = U.Uc(arch_to_UC[self.arch], self.mode)
|
||||
self.regs = pwndbg.regs.current
|
||||
|
||||
# Initialize the register state
|
||||
for reg in list(self.regs.misc) + list(self.regs.common) + list(self.regs.flags):
|
||||
enum = self.get_reg_enum(reg)
|
||||
|
||||
if not reg:
|
||||
print "Could not set register %r" % reg
|
||||
continue
|
||||
|
||||
value = getattr(pwndbg.regs, reg)
|
||||
if value is None:
|
||||
print "# Could not set register %r" % reg
|
||||
continue
|
||||
else:
|
||||
name = 'U.x86_const.UC_X86_REG_%s' % reg.upper()
|
||||
print "uc.reg_write(%(name)s, %(value)r)" % locals()
|
||||
self.uc.reg_write(enum, value)
|
||||
|
||||
# Add a hook for unmapped memory
|
||||
self.uc.hook_add(U.UC_HOOK_MEM_READ_UNMAPPED | U.UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
|
||||
|
||||
# Always stop executing as soon as there's an interrupt.
|
||||
self.uc.hook_add(U.UC_HOOK_INTR, self.hook_intr)
|
||||
|
||||
# Map in the page that $pc is on
|
||||
self.map_page(self.pc)
|
||||
|
||||
def __getattr__(self, name):
|
||||
reg = self.get_reg_enum(name)
|
||||
|
||||
if reg:
|
||||
return self.uc.reg_read(reg)
|
||||
|
||||
raise AttributeError("AttributeError: %r object has no attribute %r" % (self, name))
|
||||
|
||||
def get_mode(self):
|
||||
"""
|
||||
Retrieve the mode used by Capstone and Unicorn for the current
|
||||
architecture.
|
||||
|
||||
This relies on the enums being the same.
|
||||
"""
|
||||
arch = pwndbg.arch.current
|
||||
|
||||
if arch in ('arm', 'aarch64'):
|
||||
return {0:C.CS_MODE_ARM,0x20:C.CS_MODE_THUMB}[pwndbg.regs.cpsr & 0x20]
|
||||
else:
|
||||
return {4:C.CS_MODE_32, 8:C.CS_MODE_64}[pwndbg.arch.ptrsize]
|
||||
|
||||
def map_page(self, page):
|
||||
page = pwndbg.memory.page_align(page)
|
||||
|
||||
try:
|
||||
data = pwndbg.memory.read(page, pwndbg.memory.PAGE_SIZE)
|
||||
except gdb.MemoryError:
|
||||
return False
|
||||
|
||||
if not data:
|
||||
return False
|
||||
|
||||
self.uc.mem_map(page, pwndbg.memory.PAGE_SIZE)
|
||||
self.uc.mem_write(page, str(data))
|
||||
|
||||
return True
|
||||
|
||||
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
|
||||
|
||||
# Page-align the start address
|
||||
start = pwndbg.memory.page_align(address)
|
||||
size = pwndbg.memory.page_size_align(address + size - start)
|
||||
stop = start + size
|
||||
|
||||
print "Mapping %x -> %x" % (start, stop)
|
||||
|
||||
# Map each page with the permissions that we think it has.
|
||||
for page in range(start, stop, pwndbg.memory.PAGE_SIZE):
|
||||
if not self.map_page(page):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def hook_intr(self, uc, intno, user_data):
|
||||
"""
|
||||
We never want to emulate through an interrupt. Just stop.
|
||||
"""
|
||||
self.uc.emu_stop()
|
||||
|
||||
def get_reg_enum(self, reg):
|
||||
"""
|
||||
Returns the Unicorn Emulator enum code for the named register.
|
||||
|
||||
Also supports general registers like 'sp' and 'pc'.
|
||||
"""
|
||||
if not self.regs:
|
||||
return None
|
||||
|
||||
# If we're looking for an abstract register which does not exist on
|
||||
# the RegisterSet objects, we need to do an indirect lookup.
|
||||
#
|
||||
# 'sp' ==> 'stack' ==> 'esp' ==> enum
|
||||
#
|
||||
if reg == 'sp':
|
||||
return self.get_reg_enum(self.regs.stack)
|
||||
|
||||
# If we're looking for an abstract register which *is* accounted for,
|
||||
# we can also do an indirect lookup.
|
||||
#
|
||||
# 'pc' ==> 'eip' ==> enum
|
||||
#
|
||||
if hasattr(self.regs, reg):
|
||||
return self.get_reg_enum(getattr(self.regs, reg))
|
||||
|
||||
# If we're looking for an exact register ('eax', 'ebp', 'r0') then
|
||||
# we can look those up easily.
|
||||
#
|
||||
# 'eax' ==> enum
|
||||
#
|
||||
if reg in self.regs.all:
|
||||
for reg_enum in (c for c in dir(self.consts) if c.endswith(reg.upper())):
|
||||
return getattr(self.consts, reg_enum)
|
||||
|
||||
return None
|
||||
|
||||
def until_jump(self, pc=None):
|
||||
"""
|
||||
Emulates instructions starting at the specified address until the
|
||||
program counter is set to an address which does not linearly follow
|
||||
the previously-emulated instruction.
|
||||
"""
|
||||
self.until_jump_prev = None
|
||||
self.until_jump_prevsize = None
|
||||
self.until_jump_target = None
|
||||
|
||||
# Add the single-step hook, start emulating, and remove the hook.
|
||||
self.uc.hook_add(U.UC_HOOK_CODE, self.until_jump_hook_code)
|
||||
self.uc.emu_start(self.pc, 0)
|
||||
self.uc.hook_del(U.UC_HOOK_CODE)
|
||||
|
||||
# We're done emulating
|
||||
return self.until_jump_prev, self.until_jump_target
|
||||
|
||||
def until_jump_hook_code(self, uc, address, size, user_data):
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
print hex(address)
|
||||
print pwndbg.disasm.one(address)
|
||||
|
||||
if self.until_jump_prev is not None \
|
||||
and self.until_jump_prev + self.until_jump_prevsize != address:
|
||||
self.until_jump_target = address
|
||||
self.uc.emu_stop()
|
||||
return
|
||||
|
||||
self.until_jump_prev = address
|
||||
self.until_jump_prevsize = size
|
||||
|
||||
def until_syscall(self, pc=None):
|
||||
"""
|
||||
Emulates instructions starting at the specified address until the program
|
||||
counter points at a syscall instruction (int 0x80, svc, etc.).
|
||||
"""
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
#Embedded file name: /usr/local/google/home/riggle/pwndbg/pwndbg/emu/x86.py
|
||||
pass
|
||||
Loading…
Reference in new issue