You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pwndbg/gef/old.py

2546 lines
70 KiB
Python

################################################################################
# GEF - GDB Enhanced Features for Exploiters & Reverse-Engineers
#
# by @_hugsy_
#
# GEF provides additional functions to GDB using its powerful Python API. Some
# functions were inspired by PEDA (https://github.com/longld/peda) which is totally
# awesome *but* is x86 (32/64bits) specific, whereas GEF supports almost all archs
# supported by GDB.
#
# Notes:
# * Since GEF relies on /proc for mapping addresses in memory or other features, it
# cannot work on hardened configurations (such as GrSec)
# * GEF supports kernel debugging in a limit way (please report crashes & bugs)
#
# Tested on
# * x86-32/x86-64 (even though you should totally use `gdb-peda` (https://github.com/longld/peda) instead)
# * arm-32/arm-64
# * mips
# * powerpc
# * sparc/sparc64
#
#
# Tested on gdb 7.x / python 2.6 & 2.7 & 3.x
#
# To start: in gdb, type `source /path/to/gef.py`
#
#
# ToDo:
# - add explicit actions for flags (jumps/overflow/negative/etc)
#
# ToDo commands:
# - finish FormatStringSearchCommand
#
#
#
from __future__ import print_function
import math
import struct
import subprocess
import functools
import sys
import re
import tempfile
import os
import binascii
import gdb
# Relative imports
import gef.vmmap
import gef.dt
import gef.memory
import gef.elf
'''
if sys.version_info.major == 2:
import HTMLParser
import itertools
from cStringIO import StringIO
# Compat Py2/3 hacks
range = xrange
elif sys.version_info.major == 3:
from html.parser import HTMLParser
from io import StringIO
# Compat Py2/3 hack
long = int
FileNotFoundError = IOError
else:
raise Exception("WTF is this Python version??")
__aliases__ = {}
__config__ = {}
NO_COLOR = False
# Quickly determine which version is running
python2 = sys.version_info.major == 2
python3 = sys.version_info.major == 3
# Access to various types
ul = gdb.lookup_type('unsigned long')
sz = gdb.lookup_type('char').pointer()
class GefGenericException(Exception):
def __init__(self, value):
self.message = value
return
def __str__(self):
return repr(self.message)
class GefMissingDependencyException(GefGenericException): pass
class GefUnsupportedMode(GefGenericException): pass
class GefUnsupportedOS(GefGenericException): pass
# let's get fancy
class Color:
NORMAL = "\x1b[0m"
RED = "\x1b[31m"
GREEN = "\x1b[32m"
YELLOW = "\x1b[33m"
BLUE = "\x1b[34m"
BOLD = "\x1b[1m"
UNDERLINE = "\x1b[4m"
@staticmethod
def redify(msg): return Color.RED + msg + Color.NORMAL if not NO_COLOR else ""
@staticmethod
def greenify(msg): return Color.GREEN + msg + Color.NORMAL if not NO_COLOR else ""
@staticmethod
def blueify(msg): return Color.BLUE + msg + Color.NORMAL if not NO_COLOR else ""
@staticmethod
def yellowify(msg): return Color.YELLOW + msg + Color.NORMAL if not NO_COLOR else ""
@staticmethod
def boldify(msg): return Color.BOLD + msg + Color.NORMAL if not NO_COLOR else ""
# helpers
class Address:
pass
class Permission:
READ = 4
WRITE = 2
EXECUTE = 1
def __init__(self, *args, **kwargs):
self.value = 0
return
def __str__(self):
perm_str = ""
perm_str += "r" if self.value & Permission.READ else "-"
perm_str += "w" if self.value & Permission.WRITE else "-"
perm_str += "x" if self.value & Permission.EXECUTE else "-"
return perm_str
@staticmethod
def from_info_sections(*args):
p = Permission()
for arg in args:
if "READONLY" in arg:
p.value += Permission.READ
if "DATA" in arg:
p.value += Permission.WRITE
if "CODE" in arg:
p.value += Permission.EXECUTE
return p
@staticmethod
def from_process_maps(perm_str):
p = Permission()
if perm_str[0] == "r":
p.value += Permission.READ
if perm_str[1] == "w":
p.value += Permission.WRITE
if perm_str[2] == "x":
p.value += Permission.EXECUTE
return p
class Section:
page_start = None
page_end = None
offset = None
permission = None
inode = None
path = None
def __init__(self, *args, **kwargs):
attrs = ["page_start", "page_end", "offset", "permission", "inode", "path"]
for attr in attrs:
value = kwargs[attr] if attr in kwargs else None
setattr(self, attr, value)
return
class Zone:
name = None
zone_start = None
zone_end = None
filename = None
class Elf:
e_magic = None
e_class = None
e_endianness = None
e_eiversion = None
e_osabi = None
e_abiversion = None
e_pad = None
e_type = None
e_machine = None
e_version = None
e_entry = None
e_phoff = None
e_shoff = None
e_flags = None
e_ehsize = None
e_phentsize = None
e_phnum = None
e_shentsize = None
e_shnum = None
e_shstrndx = None
def titlify(msg):
return "{0}[{1} {3} {2}]{0}".format('='*20, Color.RED, Color.NORMAL, msg)
def ok(msg):
print((Color.BOLD+Color.GREEN+"[+]"+Color.NORMAL+" "+msg))
return
def warn(msg):
print((Color.BOLD+Color.YELLOW+"[+]"+Color.NORMAL+" "+msg))
return
def err(msg):
print((Color.BOLD+Color.RED+"[+]"+Color.NORMAL+" "+msg))
return
def info(msg):
print((Color.BOLD+Color.BLUE+"[+]"+Color.NORMAL+" "+msg))
return
def hexdump(src, l=0x10, sep='.', show_raw=False):
res = []
for i in range(0, len(src), l):
s = src[i:i+l]
hexa = ''
isMiddle = False
for h in range(0,len(s)):
if h == l/2:
hexa += ' '
h = s[h]
if not isinstance(h, int):
h = ord(h)
h = hex(h).replace('0x','')
if len(h) == 1:
h = '0'+h
hexa += h + ' '
hexa = hexa.strip(' ')
text = ''
for c in s:
if not isinstance(c, int):
c = ord(c)
if 0x20 <= c < 0x7F:
text += chr(c)
else:
text += sep
if show_raw:
res.append(('%-'+str(l*(2+1)+1)+'s') % (hexa))
else:
res.append(('%08X: %-'+str(l*(2+1)+1)+'s |%s|') % (i, hexa, text))
return '\n'.join(res)
def gef_obsolete_function(func):
def new_func(*args, **kwargs):
warn("Call to deprecated function {}.".format(func.__name__), category=DeprecationWarning)
return func(*args, **kwargs)
new_func.__name__ = func.__name__
new_func.__doc__ = func.__doc__
new_func.__dict__.update(func.__dict__)
return new_func
def gef_execute(command, as_list = False):
output = []
fd, fname = tempfile.mkstemp()
os.close(fd)
gdb.execute("set logging file " + fname)
gdb.execute("set logging overwrite on")
gdb.execute("set logging redirect on")
gdb.execute("set logging on")
try :
lines = gdb.execute(command, to_string=True)
lines = data.splitlines()
for line in lines:
address, content = x.split(" ", 1)
address = long(address.strip()[:-1], 16)
content = content.strip()
output.append( (address, content) )
except:
pass
finally:
gdb.execute("set logging off")
gdb.execute("set logging redirect off")
os.unlink(fname)
return output
def gef_execute_external(command, as_list=False, stdin=''):
process = subprocess.Popen(command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True)
stdout, stderr = process.communicate(stdin)
return_code = process.wait()
if return_code != 0:
raise Exception("%r exited with return code %r\n" + \
"It had this on stdout: %r" % (command, return_code, stdout))
if as_list:
stdout = stdout.splitlines()
return stdout
def disassemble_parse(name, filter_opcode=None):
lines = [x.split(":", 1) for x in gdb_exec("disassemble %s" % name).split('\n') if "0x" in x]
dis = []
for address, opcode in lines:
try:
address = address.replace("=>", " ").strip()
address = long(address.split(" ")[0], 16)
i = opcode.find("#")
if i != -1:
opcode = opcode[:i]
i = opcode.find("<")
if i != -1:
opcode = opcode[:i]
opcode = opcode.strip()
if filter_opcode is None or filter_opcode in opcode:
dis.append( (address, opcode) )
except:
continue
return dis
def get_frame():
return gdb.selected_inferior()
def get_arch():
return gdb.execute("show architecture", to_string=True).strip().split(" ")[7][:-1]
def arm_registers():
return ["$r0 ", "$r1 ", "$r2 ", "$r3 ", "$r4 ", "$r5 ", "$r6 ",
"$r7 ", "$r8 ", "$r9 ", "$r10 ", "$r11 ", "$r12 ", "$sp ",
"$lr ", "$pc ", "$cpsr", ]
def x86_64_registers():
return [ "$rax ", "$rcx ", "$rdx ", "$rbx ", "$rsp ", "$rbp ", "$rsi ",
"$rdi ", "$rip ", "$r8 ", "$r9 ", "$r10 ", "$r11 ", "$r12 ",
"$r13 ", "$r14 ", "$r15 ",
"$cs ", "$ss ", "$ds ", "$es ", "$fs ", "$gs ", "$eflags", ]
def x86_32_registers():
return [ "$eax ", "$ecx ", "$edx ", "$ebx ", "$esp ", "$ebp ", "$esi ",
"$edi ", "$eip ", "$cs ", "$ss ", "$ds ", "$es ",
"$fs ", "$gs ", "$eflags", ]
def powerpc_registers():
return ["$r0 ", "$r1 ", "$r2 ", "$r3 ", "$r4 ", "$r5 ", "$r6 ", "$r7 ",
"$r8 ", "$r9 ", "$r10 ", "$r11 ", "$r12 ", "$r13 ", "$r14 ", "$r15 ",
"$r16 ", "$r17 ", "$r18 ", "$r19 ", "$r20 ", "$r21 ", "$r22 ", "$r23 ",
"$r24 ", "$r25 ", "$r26 ", "$r27 ", "$r28 ", "$r29 ", "$r30 ", "$r31 ",
"$pc ", "$msr ", "$cr ", "$lr ", "$ctr ", "$xer ", "$trap" ]
def sparc_registers():
return ["$g0 ", "$g1 ", "$g2 ", "$g3 ", "$g4 ", "$g5 ", "$g6 ", "$g7 ",
"$o0 ", "$o1 ", "$o2 ", "$o3 ", "$o4 ", "$o5 ",
"$l0 ", "$l1 ", "$l2 ", "$l3 ", "$l4 ", "$l5 ", "$l6 ", "$l7 ",
"$i0 ", "$i1 ", "$i2 ", "$i3 ", "$i4 ", "$i5 ",
"$pc ", "$sp ", "$fp ", "$psr", ]
def all_registers():
if is_arm():
return arm_registers()
elif is_x86_32():
return x86_32_registers()
elif is_x86_64():
return x86_64_registers()
elif is_powerpc():
return powerpc_registers()
elif is_sparc() or is_sparc64():
return sparc_registers()
else:
raise GefUnsupportedOS("OS type is currently not supported: %s" % get_arch())
def write_memory(address, buffer, length=0x10):
return gdb.selected_inferior().write_memory(address, buffer, length)
def read_memory(addr, length=0x10):
result = gdb.selected_inferior().read_memory(addr, length)
if python3:
result = result.tobytes()
return bytearray(result)
def read_memory_until_null(address):
i = 0
if sys.version_info.major == 2:
buf = ''
while True:
c = read_memory(address + i, 1)[0]
if c == '\x00': break
buf += c
i += 1
return buf
else:
buf = []
while True:
c = read_memory(address + i, 1)[0]
if c == 0x00: break
buf.append( c )
i += 1
return bytes(buf)
def is_readable_string(address):
"""
Here we will assume that a readable string is
a consecutive byte array whose
* last element is 0x00
* and values for each byte is [0x07, 0x7F]
"""
buffer = read_memory_until_null(address)
if len(buffer) == 0:
return False
if sys.version_info.major == 2:
for c in buffer:
if not (0x07 <= ord(c) < 0x0e) and not (0x20 <= ord(c) < 0x7f):
return False
else:
for c in buffer:
if not (0x07 <= c < 0x0e) and not (0x20 <= c < 0x7f):
return False
return True
def read_string(address):
if not is_readable_string(address):
raise ValueError("Content at address `%#x` is not a string" % address)
buf = read_memory_until_null(address)
replaced_chars = [ (b"\n",b"\\n"), (b"\r",b"\\r"), (b"\t",b"\\t"), (b"\"",b"\\\"")]
for f,t in replaced_chars:
buf = buf.replace(f, t)
return buf
def is_alive():
try:
pid = get_frame().pid
return pid > 0
except gdb.error as e:
return False
return False
def get_register(regname):
"""
Get register value. Exception will be raised if expression cannot be parse.
This function won't catch on purpose.
@param regname : expected register
@return register value
"""
return int(gdb.parse_and_eval(regname))
class Registers():
def __getattr__(self, attr):
if is_alive():
return get_register('$' + attr.rstrip('$'))
return 0
registers = Registers()
@memoize
def get_pid():
return get_frame().pid
@memoize
def get_filename():
return gdb.current_progspace().filename
@memoize
def get_process_maps():
pid = get_pid()
sections = []
mapping = gdb.execute('info proc mapping', to_string=True)
# Format looks like this:
# gef> info proc mapping
# process 47863
# Mapped address spaces:
#
# Start Addr End Addr Size Offset objfile
# 0x400000 0x4ef000 0xef000 0x0 /bin/bash
# 0x6ef000 0x6f0000 0x1000 0xef000 /bin/bash
# 0x6f0000 0x6f9000 0x9000 0xf0000 /bin/bash
# 0x6f9000 0x6ff000 0x6000 0x0 [heap]
# 0x7ffff75e7000 0x7ffff77a2000 0x1bb000 0x0 /lib/x86_64-linux-gnu/libc-2.19.so
# FreeBSD does not support 'info proc mapping', or indeed
# even have /proc/$pid/map.
if "Not supported on this target." == mapping:
return get_info_sections() + get_info_sharedlibrary()
# Skip the first four lines
for line in mapping.splitlines()[3:]:
while True:
line = f.readline()
if len(line) == 0:
break
line = line.strip()
addr, perm, off, dev, rest = line.split(" ", 4)
rest = rest.split(" ", 1)
if len(rest) == 1:
inode = rest[0]
pathname = ""
else:
inode = rest[0]
pathname = rest[1].replace(' ', '')
addr_start, addr_end = addr.split("-")
addr_start, addr_end = long(addr_start, 16), long(addr_end, 16)
off = long(off, 16)
perm = Permission.from_process_maps(perm)
section = Section(page_start = addr_start,
page_end = addr_end,
offset = off,
permission = perm,
inode = inode,
path = pathname)
sections.append( section )
return sections
@memoize
def get_info_sections():
sections = []
stream = StringIO(gdb.execute("maintenance info sections", to_string=True))
while True:
line = stream.readline()
if len(line) == 0:
break
line = re.sub('\s+',' ', line.strip())
try:
blobs = [x.strip() for x in line.split(' ')]
index = blobs[0][1:-1]
addr_start, addr_end = [ long(x, 16) for x in blobs[1].split("->") ]
at = blobs[2]
off = long(blobs[3][:-1], 16)
path = blobs[4]
inode = ""
perm = Permission.from_info_sections(blobs[5:])
section = Section(page_start = addr_start,
page_end = addr_end,
offset = off,
permission = perm,
inode = inode,
path = path)
sections.append( section )
except IndexError:
continue
except ValueError:
continue
return sections
@memoize
def get_info_files():
infos = []
stream = StringIO(gdb.execute("info files", to_string=True))
while True:
line = stream.readline()
if len(line) == 0:
break
try:
blobs = [x.strip() for x in line.split(' ')]
addr_start = long(blobs[0], 16)
addr_end = long(blobs[2], 16)
section_name = blobs[4]
if len(blobs) == 7:
filename = blobs[6]
else:
filename = get_filename()
except ValueError:
continue
except IndexError:
continue
info = Zone()
info.name = section_name
info.zone_start = addr_start
info.zone_end = addr_end
info.filename = filename
infos.append( info )
stream.close()
return infos
def process_lookup_address(address):
if not is_alive():
err("Process is not running")
return None
if is_x86_64() or is_x86_32() :
if is_in_x86_kernel(address):
return None
for sect in get_process_maps():
if sect.page_start <= address <= sect.page_end:
return sect
return None
def file_lookup_address(address):
for info in get_info_files():
if info.zone_start <= address < info.zone_end:
return info
return None
def lookup_address(address):
addr = Address()
for attr in ["value", "section", "info"]:
setattr(addr, attr, None)
addr.value = address
sect = process_lookup_address(address)
info = file_lookup_address(address)
if sect is None and info is None:
# i.e. there is no info on this address
return None
if sect:
addr.section = sect
if info:
addr.info = info
return addr
def XOR(data, key):
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(data, itertools.cycle(key)))
# dirty hack, from https://github.com/longld/peda
def define_user_command(cmd, code):
if sys.version_info.major == 3:
commands = bytes( "define {0}\n{1}\nend".format(cmd, code), "UTF-8" )
else:
commands = "define {0}\n{1}\nend".format(cmd, code)
fd, fname = tempfile.mkstemp()
os.write(fd, commands)
os.close(fd)
gdb.execute("source %s" % fname)
os.unlink(fname)
return
@memoize
def get_elf_headers(filename=None):
if filename is None:
filename = get_filename()
try:
f = open(filename, "rb")
except IOError:
err("'{0}' not found/readable".format(filename))
return None
elf = Elf()
# off 0x0
elf.e_magic, elf.e_class, elf.e_endianness, elf.e_eiversion = struct.unpack(">IBBB", f.read(7))
# adjust endianness in bin reading
if elf.e_endianness == 0x01:
endian = "<" # LE
else:
endian = ">" # BE
# off 0x7
elf.e_osabi, elf.e_abiversion = struct.unpack(endian + "BB", f.read(2))
# off 0x9
elf.e_pad = f.read(7)
# off 0x10
elf.e_type, elf.e_machine, elf.e_version = struct.unpack(endian + "HHI", f.read(8))
# off 0x18
if elf.e_class == 0x02: # arch 64bits
elf.e_entry, elf.e_phoff, elf.e_shoff = struct.unpack(endian + "QQQ", f.read(24))
else: # arch 32bits
elf.e_entry, elf.e_phoff, elf.e_shoff = struct.unpack(endian + "III", f.read(12))
elf.e_flags, elf.e_ehsize, elf.e_phentsize, elf.e_phnum = struct.unpack(endian + "HHHH", f.read(8))
elf.e_shentsize, elf.e_shnum, elf.e_shstrndx = struct.unpack(endian + "HHH", f.read(6))
f.close()
return elf
@memoize
def is_elf64():
elf = get_elf_headers()
return elf.e_class == 0x02
@memoize
def is_elf32():
elf = get_elf_headers()
return elf.e_class == 0x01
@memoize
def is_x86_64():
elf = get_elf_headers()
return elf.e_machine==0x3e
@memoize
def is_x86_32():
elf = get_elf_headers()
return elf.e_machine==0x03
@memoize
def is_arm():
elf = get_elf_headers()
return elf.e_machine==0x28
@memoize
def is_mips():
elf = get_elf_headers()
return elf.e_machine==0x08
@memoize
def is_powerpc():
elf = get_elf_headers()
return elf.e_machine==0x14 # http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
@memoize
def is_sparc():
elf = get_elf_headers()
return elf.e_machine==0x02
@memoize
def is_sparc():
elf = get_elf_headers()
return elf.e_machine==0x12
def get_memory_alignment():
if is_elf32():
return 32
elif is_elf64():
return 64
else:
raise GefUnsupportedMode("GEF is running under an unsupported mode, functions will not work")
def format_address(addr):
memalign_size = get_memory_alignment()
if memalign_size == 32:
return "%#.8x" % (addr & 0xFFFFFFFF)
elif memalign_size == 64:
return "%#.16x" % (addr & 0xFFFFFFFFFFFFFFFF)
def clear_screen():
gdb.execute("shell clear")
return
def align_address(address):
if get_memory_alignment()== 32:
return address & 0xFFFFFFFF
else:
return address & 0xFFFFFFFFFFFFFFFF
def is_in_x86_kernel(address):
address = align_address(address)
memalign = get_memory_alignment()-1
return (address >> memalign) == 0xF
#
# breakpoints
#
class FormatStringBreakpoint(gdb.Breakpoint):
""" Inspect stack for format string """
def __init__(self, spec, num_args):
super(FormatStringBreakpoint, self).__init__(spec, gdb.BP_BREAKPOINT, internal=False)
self.num_args = num_args
self.enabled = True
return
def stop(self):
if is_arm():
regs = ['$r0','$r1','$r2','$3']
ref = regs[self.num_args]
else :
raise NotImplementedError()
value = gdb.parse_and_eval(ref)
address = long(value)
pid = get_pid()
addr = lookup_address(address)
if 'w' in addr.permissions:
print((titlify("Format String Detection")))
info(">>> Possible writable format string %#x (%s): %s" % (addr, ref, content))
print((gdb.execute("backtrace")))
return True
return False
#
# Functions
#
# credits: http://tromey.com/blog/?p=515
class CallerIs (gdb.Function):
"""Return True if the calling function's name is equal to a string.
This function takes one or two arguments."""
def __init__ (self):
super (CallerIs, self).__init__ ("caller_is")
return
def invoke (self, name, nframes = 1):
frame = gdb.get_current_frame ()
while nframes > 0:
frame = frame.get_prev ()
nframes = nframes - 1
return frame.get_name () == name.string ()
CallerIs()
#
# Commands
#
class GenericCommand(gdb.Command):
"""Generic class for invoking commands"""
def __init__(self, *args, **kwargs):
self.pre_load()
required_attrs = ["do_invoke", "_cmdline_", "_syntax_"]
for attr in required_attrs:
if not hasattr(self, attr):
raise NotImplemented("Invalid class: missing '%s'" % attr)
self.__doc__ += "\n" + "Syntax: " + self._syntax_
command_type = kwargs["command"] if "command" in kwargs else gdb.COMMAND_OBSCURE
complete_type = kwargs["complete"] if "complete" in kwargs else gdb.COMPLETE_NONE
super(GenericCommand, self).__init__(self._cmdline_, command_type, complete_type, True)
self.post_load()
return
def invoke(self, args, from_tty):
argv = gdb.string_to_argv(args)
self.do_invoke(argv)
return
def usage(self):
err("Syntax\n" + self._syntax_ )
return
def pre_load(self):
return
def post_load(self):
return
def add_setting(self, name, value):
key = "%s.%s" % (self.__class__._cmdline_, name)
__config__[ key ] = (value, type(value))
return
def get_setting(self, name):
key = "%s.%s" % (self.__class__._cmdline_, name)
return __config__[ key ][0]
def del_setting(self, name):
key = "%s.%s" % (self.__class__._cmdline_, name)
del ( __config__[ key ] )
return
class DumpMemoryCommand(GenericCommand):
"""Dump chunks of memory into raw file on the filesystem. Dump file
name template can be defined in GEF runtime config"""
_cmdline_ = "dump-memory"
_syntax_ = "%s LOCATION [SIZE]" % _cmdline_
def __init__(self):
super(DumpMemoryCommand, self).__init__(complete=gdb.COMPLETE_LOCATION)
self.add_setting("dumpfile_prefix", "./dumpmem-")
self.add_setting("dumpfile_suffix", "raw")
return
def do_invoke(self, argv):
argc = len(argv)
if argc not in (1, 2):
err("Invalid arguments number")
self.usage()
return
prefix = self.get_setting("dumpfile_prefix")
suffix = self.get_setting("dumpfile_suffix")
start_addr = align_address( long(gdb.parse_and_eval( argv[0] )) )
filename = "%s%#x.%s" % (prefix, start_addr, suffix)
size = long(argv[1]) if argc==2 and argv[1].isdigit() else 0x100
with open(filename, "wb") as f:
mem = read_memory( start_addr, size )
f.write( mem )
info("Dumped %d bytes from %#x in '%s'" % (size, start_addr, filename))
return
class AliasCommand(GenericCommand):
"""GEF defined aliases"""
_cmdline_ = "gef-alias"
_syntax_ = "%s (set|show|do|unset)" % _cmdline_
def do_invoke(self, argv):
argc = len(argv)
if argc == 0:
err("Missing action")
self.usage()
return
class AliasSetCommand(GenericCommand):
"""GEF add alias command"""
_cmdline_ = "gef-alias set"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
argc = len(argv)
if argc < 2:
err("'%s set' requires at least 2 params")
return
alias_name = argv[0]
alias_cmds = " ".join(argv[1:]).split(";")
if alias_name in list( __aliases__.keys() ):
warn("Replacing alias '%s'" % alias_name)
__aliases__[ alias_name ] = alias_cmds
ok("'%s': '%s'" % (alias_name, "; ".join(alias_cmds)))
return
class AliasUnsetCommand(GenericCommand):
"""GEF remove alias command"""
_cmdline_ = "gef-alias unset"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
if len(argv) != 1:
err("'%s' requires 1 param" % self._cmdline_)
return
if argv[1] in __aliases__:
del __aliases__[ argv[1] ]
else:
err("'%s' not an alias" % argv[1])
return
class AliasShowCommand(GenericCommand):
"""GEF show alias command"""
_cmdline_ = "gef-alias show"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
for alias_name in list( __aliases__.keys() ):
print(("'%s'\t'%s'" % (alias_name, ";".join(__aliases__[alias_name]))))
return
class AliasDoCommand(GenericCommand):
"""GEF do alias command"""
_cmdline_ = "gef-alias do"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
argc = len(argv)
if argc != 1:
err("'%s do' requires 1 param")
return
alias_name = argv[0]
if alias_name not in list( __aliases__.keys() ):
err("No alias '%s'" % alias_name)
return
alias_cmds = __aliases__[alias_name]
for cmd in alias_cmds:
try:
if " >> " in cmd:
cmd, outfile = cmd.split(" >> ")
cmd = cmd.strip()
outfile = outfile.strip()
with open(outfile, "a") as f:
lines_out = gdb.execute(cmd, to_string=True)
f.write(lines_out)
elif " > " in cmd:
cmd, outfile = cmd.split(" > ")
cmd = cmd.strip()
outfile = outfile.strip()
with open(outfile, "w") as f:
lines_out = gdb.execute(cmd, to_string=True)
f.write(lines_out)
else:
gdb.execute(cmd)
except:
continue
return
class SolveKernelSymbolCommand(GenericCommand):
"""Get kernel address"""
_cmdline_ = "ksymaddr"
_syntax_ = "%s SymbolToSearch" % _cmdline_
def do_invoke(self, argv):
if len(argv) != 1:
self.usage()
return
found = False
sym = argv[0]
with open("/proc/kallsyms", "r") as f:
for line in f:
try:
symaddr, symtype, symname = line.strip().split(" ", 3)
symaddr = long(symaddr, 16)
if symname == sym:
ok("Found matching symbol for '%s' at %#x (type=%s)" % (sym, symaddr, symtype))
found = True
if sym in symname:
warn("Found partial match for '%s' at %#x (type=%s): %s" % (sym, symaddr, symtype, symname))
found = True
except ValueError:
pass
if not found:
err("No match for '%s'" % sym)
return
class DetailRegistersCommand(GenericCommand):
"""Display full details on one, many or all registers value from current architecture."""
_cmdline_ = "reg"
_syntax_ = "%s [Register1] [Register2] ... [RegisterN]" % _cmdline_
def do_invoke(self, argv):
regs = []
if not is_alive():
warn("No debugging session active")
return
if len(argv) > 0:
regs = [ reg for reg in all_registers() if reg.strip() in argv ]
else:
regs = all_registers()
for regname in regs:
reg = gdb.parse_and_eval(regname)
line = Color.boldify(Color.redify(regname)) + ": "
if str(reg.type) == 'builtin_type_sparc_psr': # ugly but more explicit
line+= "%s" % reg
elif reg.type.code == gdb.TYPE_CODE_FLAGS:
line+= "%s" % (Color.boldify(str(reg)))
else:
addr = align_address( long(reg) )
line+= Color.boldify(Color.blueify(format_address(addr)))
addrs = DereferenceCommand.dereference_from(addr)
if len(addrs) > 1:
line+= " -> " + " -> ".join(addrs[1:])
print(line)
return
class ShellcodeCommand(GenericCommand):
"""ShellcodeCommand uses @JonathanSalwan simple-yet-awesome shellcode API to
download shellcodes"""
_cmdline_ = "shellcode"
_syntax_ = "%s (search|get)" % _cmdline_
def pre_load(self):
try:
import requests
except ImportError:
raise GefMissingDependencyException("Missing Python `requests` package")
return
def do_invoke(self, argv):
self.usage()
return
class ShellcodeSearchCommand(GenericCommand):
"""Search patthern in shellcodes database."""
_cmdline_ = "shellcode search"
_syntax_ = "%s <pattern1> <pattern2>" % _cmdline_
api_base = "http://shell-storm.org"
search_url = api_base + "/api/?s="
def do_invoke(self, argv):
if len(argv) == 0:
err("Missing pattern to search")
self.usage()
else:
self.search_shellcode(argv)
return
def search_shellcode(self, search_options):
requests = sys.modules['requests']
# API : http://shell-storm.org/shellcode/
args = "*".join(search_options)
http = requests.get(self.search_url + args)
if http.status_code != 200:
err("Could not query search page: got %d" % http.status_code)
return
# format: [author, OS/arch, cmd, id, link]
lines = http.text.split("\n")
refs = [ line.split("::::") for line in lines ]
info("Showing matching shellcodes")
for ref in refs:
try:
auth, arch, cmd, sid, link = ref
print(("\t".join([sid, arch, cmd])))
except ValueError:
continue
info("Use `%s get <id>` to fetch shellcode" % self._cmdline_)
return
class ShellcodeGetCommand(GenericCommand):
"""Download shellcode from shellcodes database"""
_cmdline_ = "shellcode get"
_syntax_ = "%s <shellcode_id>" % _cmdline_
api_base = "http://shell-storm.org"
get_url = api_base + "/shellcode/files/shellcode-%d.php"
def do_invoke(self, argv):
if len(argv) != 1:
err("Missing pattern to search")
self.usage()
return
if not argv[0].isdigit():
err("ID is not a digit")
self.usage()
return
self.get_shellcode(long(argv[0]))
return
def get_shellcode(self, sid):
requests = sys.modules['requests']
http = requests.get(self.get_url % sid)
if http.status_code != 200:
err("Could not query search page: got %d" % http.status_code)
return
info("Downloading shellcode id=%d" % sid)
fd, fname = tempfile.mkstemp(suffix=".txt", prefix="sc-", text=True, dir='/tmp')
data = http.text.split("\n")[7:-11]
buf = "\n".join(data)
unesc_buf = HTMLParser().unescape( buf )
os.write(fd, bytes(unesc_buf, "UTF-8"))
os.close(fd)
info("Shellcode written as '%s'" % fname)
return
class FileDescriptorCommand(GenericCommand):
"""Enumerate file descriptors opened by process."""
_cmdline_ = "fd"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
if not is_alive():
warn("No debugging session active")
return
pid = get_pid()
path = "/proc/%s/fd" % pid
for fname in os.listdir(path):
fullpath = path+"/"+fname
if os.path.islink(fullpath):
info("- %s -> %s" % (fullpath, os.readlink(fullpath)))
return
class AssembleCommand(GenericCommand):
"""AssembleCommand: using radare2 to assemble code (requires r2 Python bindings)
Architecture can be set in GEF runtime config (default is x86).
Use `list' subcommand to list architectures supported"""
_cmdline_ = "assemble"
_syntax_ = "%s (list|instruction1;[instruction2;]...[instructionN;])" % _cmdline_
def __init__(self, *args, **kwargs):
super(AssembleCommand, self).__init__()
return
def pre_load(self):
try:
gef_execute_external('asm --help')
except:
raise GefMissingDependencyException("pwntools is not installed")
def do_invoke(self, argv):
return gef_execute_external('asm -c %s' % self.get_setting("arch"), stdin=" ".join(argv))
# def assemble(self, mode, instructions):
# r2 = sys.modules['r2']
# asm = r2.r_asm.RAsm()
# asm.use(mode)
# opcode = asm.massemble( instructions )
# return None if opcode is None else opcode.buf_hex
class InvokeCommand(GenericCommand):
"""InvokeCommand: invoke an external command and display result."""
_cmdline_ = "invoke"
_syntax_ = "%s [COMMAND]" % _cmdline_
def do_invoke(self, argv):
print(( "%s" % gef_execute_external(" ".join(argv)) ))
return
class ProcessListingCommand(GenericCommand):
"""List and filter process."""
_cmdline_ = "ps"
_syntax_ = "%s [PATTERN]" % _cmdline_
def __init__(self):
super(ProcessListingCommand, self).__init__(complete=gdb.COMPLETE_LOCATION)
self.add_setting("ps_command", "/bin/ps auxww")
return
def do_invoke(self, argv):
processes = self.ps()
if len(argv) == 0:
pattern = re.compile("^.*$")
else:
pattern = re.compile(argv[0])
for process in processes:
command = process['command']
if not re.search(pattern, command):
continue
line = ""
line += "%s " % process["user"]
line += "%d " % process["pid"]
line += "%.f " % process["percentage_cpu"]
line += "%.f " % process["percentage_mem"]
line += "%s " % process["tty"]
line += "%d " % process["vsz"]
line += "%s " % process["stat"]
line += "%s " % process["time"]
line += "%s " % process["command"]
print (line)
return None
def ps(self):
processes = list()
output = gef_execute_external(self.get_setting("ps_command"), True)[1:]
for line in output:
field = re.compile('\s+').split(line)
processes.append({ 'user': field[0],
'pid': long(field[1]),
'percentage_cpu': eval(field[2]),
'percentage_mem': eval(field[3]),
'vsz': long(field[4]),
'rss': long(field[5]),
'tty': field[6],
'stat': field[7],
'start': field[8],
'time': field[9],
'command': field[10],
'args': field[11:] if len(field) > 11 else ''
})
return processes
class ElfInfoCommand(GenericCommand):
"""Display ELF header informations."""
_cmdline_ = "elf-info"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
# http://www.sco.com/developers/gabi/latest/ch4.eheader.html
classes = { 0x01: "32-bit",
0x02: "64-bit",
}
endianness = { 0x01: "Little-Endian",
0x02: "Big-Endian",
}
osabi = { 0x00: "System V",
0x01: "HP-UX",
0x02: "NetBSD",
0x03: "Linux",
0x06: "Solaris",
0x07: "AIX",
0x08: "IRIX",
0x09: "FreeBSD",
0x0C: "OpenBSD",
}
types = { 0x01: "Relocatable",
0x02: "Executable",
0x03: "Shared",
0x04: "Core"
}
machines = { 0x02: "SPARC",
0x03: "x86",
0x08: "MIPS",
0x12: "SPARC64",
0x14: "PowerPC",
0x15: "PowerPC64",
0x28: "ARM",
0x32: "IA-64",
0x3E: "x86-64",
0xB7: "AArch64",
}
filename = argv[0] if len(argv) else get_filename()
elf = get_elf_headers(filename)
if elf is None:
return
data = [("Magic", "{0!s}".format( hexdump(struct.pack(">I",elf.e_magic), show_raw=True))),
("Class", "{0:#x} - {1}".format(elf.e_class, classes[elf.e_class])),
("Endianness", "{0:#x} - {1}".format(elf.e_endianness, endianness[ elf.e_endianness ])),
("Version", "{:#x}".format(elf.e_eiversion)),
("OS ABI", "{0:#x} - {1}".format(elf.e_osabi, osabi[ elf.e_osabi])),
("ABI Version", "{:#x}".format(elf.e_abiversion)),
("Type", "{0:#x} - {1}".format(elf.e_type, types[elf.e_type]) ),
("Machine", "{0:#x} - {1}".format(elf.e_machine, machines[elf.e_machine])),
("Program Header Table" , "{}".format(format_address(elf.e_phoff))),
("Section Header Table" , "{}".format( format_address(elf.e_shoff) )),
("Header Table" , "{}".format( format_address(elf.e_phoff))),
("ELF Version", "{:#x}".format( elf.e_version)),
("Header size" , "{0} ({0:#x})".format(elf.e_ehsize)),
("Entry point", "{}".format( format_address(elf.e_entry) )),
# todo finish
]
for title, content in data:
print(("{:<30}: {}".format(Color.boldify(title), content)))
# todo finish
return
class EntryPointBreakCommand(GenericCommand):
"""Tries to find best entry point and sets a temporary breakpoint on it."""
_cmdline_ = "entry-break"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
# has main() ?
try:
value = gdb.parse_and_eval("main")
info("Breaking at '%s'" % value)
gdb.execute("tbreak main")
info("Starting execution")
gdb.execute("run")
return
except gdb.error:
info("Could not solve `main` symbol")
# has __libc_start_main() ?
try:
value = gdb.parse_and_eval("__libc_start_main")
info("Breaking at '%s'" % value)
gdb.execute("tbreak __libc_start_main")
info("Starting execution")
gdb.execute("run")
return
except gdb.error:
info("Could not solve `__libc_start_main` symbol")
## TODO : add more tests
# break at entry point - never fail
elf = get_elf_headers()
if elf is None:
return
value = elf.e_entry
if value:
info("Breaking at entry-point: %#x" % value)
gdb.execute("tbreak *%x" % value)
info("Starting execution")
gdb.execute("run")
return
return
class ContextCommand(GenericCommand):
"""Display execution context."""
_cmdline_ = "context"
_syntax_ = "%s" % _cmdline_
old_registers = {}
def __init__(self):
super(ContextCommand, self).__init__(complete=gdb.COMPLETE_LOCATION)
self.add_setting("show_stack_raw", False)
self.add_setting("nb_registers_per_line", 4)
self.add_setting("nb_lines_stack", 5)
self.add_setting("nb_lines_backtrace", 5)
self.add_setting("nb_lines_code", 6)
return
def do_invoke(self, argv):
if not is_alive():
warn("No debugging session active")
return
# clear_screen()
self.context_regs()
self.context_stack()
self.context_code()
self.context_trace()
self.update_registers()
return
def context_regs(self):
print((Color.boldify( Color.blueify("-"*80 + "[regs]") )))
i = 0
l = ""
for reg in all_registers():
new_value = gdb.parse_and_eval(reg)
old_value = self.old_registers[reg] if reg in self.old_registers else 0x00
l += "%s " % (Color.greenify(reg))
if new_value.type.code == gdb.TYPE_CODE_FLAGS:
l += "%s " % (new_value)
else:
new_value = align_address( long(new_value) )
old_value = align_address( long(old_value) )
if new_value == old_value:
l += "%s " % (format_address(new_value))
else:
l += "%s " % Color.redify(format_address(new_value))
i+=1
if (i > 0) and (i % self.get_setting("nb_registers_per_line")==0) :
print(l)
l = ""
print("")
return
def context_stack(self):
print (Color.boldify( Color.blueify("-"*80 + "[stack]")))
show_raw = self.get_setting("show_stack_raw")
try:
if show_raw == True:
mem = read_memory(get_register("$sp"), 0x10 * self.get_setting("nb_lines_stack"))
print (( hexdump(mem) ))
else:
InspectStackCommand.inspect_stack(get_register("$sp"), 10)
except gdb.MemoryError:
err("Cannot read memory from $SP (corrupted stack pointer?)")
return
def context_code(self):
print(( Color.boldify( Color.blueify("-"*80 + "[code]")) ))
try:
gdb.execute("x/%di $pc" % self.get_setting("nb_lines_code"))
except gdb.MemoryError:
err("Cannot disassemble from $PC")
return
def context_trace(self):
print(( Color.boldify( Color.blueify("-"*80 + "[trace]")) ))
try:
gdb.execute("backtrace %d" % self.get_setting("nb_lines_backtrace"))
except gdb.MemoryError:
err("Cannot backtrace (corrupted frames?)")
return
def update_registers(self):
for reg in all_registers():
self.old_registers[reg] = gdb.parse_and_eval(reg)
return
class HexdumpCommand(GenericCommand):
"""Display arranged hexdump (according to architecture endianness) of memory range."""
_cmdline_ = "xd"
_syntax_ = "%s (q|d|w|b) LOCATION [SIZE]" % _cmdline_
def do_invoke(self, argv):
argc = len(argv)
if not is_alive():
warn("No debugging session active")
return
if argc < 2:
self.usage()
return
if argv[0] not in ("q", "d", "w", "b"):
self.usage()
return
fmt = argv[0]
read_from = align_address( long(gdb.parse_and_eval(argv[1])) )
read_len = long(argv[2]) if argc>=3 and argv[2].isdigit() else 10
self._hexdump ( read_from, read_len, fmt )
return
def _hexdump(self, start_addr, length, arrange_as):
elf = get_elf_headers()
if elf is None:
return
endianness = "<" if elf.e_endianness == 0x01 else ">"
i = 0
formats = { 'q': ('Q', 8),
'd': ('I', 4),
'w': ('H', 2),
'b': ('B', 1),
}
r, l = formats[arrange_as]
fmt_str = "<%#x+%x> %#."+str(l*2)+"x"
fmt_pack = endianness + r
while i < length:
cur_addr = start_addr + i*l
mem = read_memory(cur_addr, l)
val = struct.unpack(fmt_pack, mem)[0]
print (fmt_str % (start_addr, i*l, val))
i += 1
return
class DereferenceCommand(GenericCommand):
"""Dereference recursively an address and display information"""
_cmdline_ = "deref"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
if not is_alive():
warn("No debugging session active")
return
if len(argv) != 1:
err("Missing argument (register/address)")
return
pointer = align_address( long(gdb.parse_and_eval(argv[0])) )
addrs = DereferenceCommand.dereference_from(pointer)
print(("Following pointers from `%s`:\n%s: %s" % (argv[0],
format_address(pointer),
" -> ".join(addrs))))
return
@staticmethod
def dereference(addr):
p_long = gdb.lookup_type('unsigned long').pointer()
return gdb.Value(addr).cast(p_long).dereference()
@staticmethod
def dereference_from(addr):
old_deref = None
deref = addr
msg = []
while True:
try:
value = align_address( long(deref) )
infos = lookup_address(value)
if infos is None or infos.section is None:
msg.append( "%#x" % ( long(deref) ))
break
section = infos.section
msg.append( "%s" % format_address( long(deref) ))
if section.permission.value & Permission.EXECUTE:
cmd = gdb.execute("x/i %x" % value, to_string=True).replace("=>", '')
cmd = re.sub('\s+',' ', cmd.strip())
msg.append( "%s" % cmd )
break
elif section.permission.value & Permission.READ:
if is_readable_string(value):
msg.append( '"%s"' % read_string(value) )
break
old_deref = deref
deref = DereferenceCommand.dereference(value)
except Exception as e:
print(((e)))
break
return msg
class ASLRCommand(GenericCommand):
"""View/modify GDB ASLR behavior."""
_cmdline_ = "aslr"
_syntax_ = "%s (on|off)" % _cmdline_
def do_invoke(self, argv):
argc = len(argv)
if argc == 0:
ret = gdb.execute("show disable-randomization", to_string=True)
i = ret.find("virtual address space is ")
if i < 0:
return
msg = "ASLR is currently "
if ret[i+25:].strip() == "on.":
msg+= Color.redify( "disabled" )
else:
msg+= Color.green( "enabled" )
print(("%s" % msg))
return
elif argc == 1:
if argv[0] == "on":
info("Enabling ASLR")
gdb.execute("set disable-randomization off")
return
elif argv[0] == "off":
info("Disabling ASLR")
gdb.execute("set disable-randomization on")
return
warn("Invalid command")
self.usage()
return
class ResetCacheCommand(GenericCommand):
"""Reset cache of all stored data."""
_cmdline_ = "reset-cache"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
reset_all_caches()
return
class VMMapCommand(GenericCommand):
"""Display virtual memory mapping"""
_cmdline_ = "vmmap"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
if not is_alive():
warn("No debugging session active")
return
vmmap = get_process_maps()
if vmmap is None or len(vmmap)==0:
err("No address mapping information found")
return
if is_elf64():
print(("%18s %18s %18s %4s %s" % ("Start", "End", "Offset", "Perm", "Path")))
else:
print(("%10s %10s %10s %4s %s" % ("Start", "End", "Offset", "Perm", "Path")))
for entry in vmmap:
l = []
l.append( format_address( entry.page_start ))
l.append( format_address( entry.page_end ))
l.append( format_address( entry.offset ))
if entry.permission.value == (Permission.READ|Permission.WRITE|Permission.EXECUTE) :
l.append( Color.boldify(Color.redify(str(entry.permission))) )
else:
l.append( str(entry.permission) )
l.append( entry.path )
print((" ".join(l)))
return
class XFilesCommand(GenericCommand):
"""Shows all libraries (and sections) loaded by binary (Truth is out there)."""
_cmdline_ = "xfiles"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
if not is_alive():
warn("Debugging session is not active")
warn("Result may be incomplete (shared libs, etc.)")
return
print(("%10s %10s %20s %s" % ("Start", "End", "Name", "File")))
for xfile in get_info_files():
l= ""
l+= "%s %s" % (format_address(xfile.zone_start),
format_address(xfile.zone_end))
l+= "%20s " % xfile.name
l+= "%s" % xfile.filename
print (l)
return
class XAddressInfoCommand(GenericCommand):
"""Get virtual section information for specific address"""
_cmdline_ = "xinfo"
_syntax_ = "%s LOCATION" % _cmdline_
def __init__(self):
super(XAddressInfoCommand, self).__init__(complete=gdb.COMPLETE_LOCATION)
return
def do_invoke (self, argv):
if len(argv) < 1:
err ("At least one valid address must be specified")
return
for sym in argv:
try:
addr = align_address( long(gdb.parse_and_eval(sym)) )
print(( titlify("xinfo: %#x" % addr )))
self.infos(addr)
except gdb.error as gdb_err:
err("Exception raised: %s" % gdb_err)
continue
return
def infos(self, address):
addr = lookup_address(address)
if addr is None:
warn("Cannot reach %#x in memory space" % address)
return
sect = addr.section
info = addr.info
if sect:
print(("Found %s" % format_address(addr.value)))
print(("Page: %s->%s (size=%#x)" % (format_address(sect.page_start),
format_address(sect.page_end),
sect.page_end-sect.page_start)))
print(("Permissions: %s" % sect.permission))
print(("Pathname: %s" % sect.path))
print(("Offset (from page): +%#x" % (address-sect.page_start)))
print(("Inode: %s" % sect.inode))
if info:
print(("Section: %s (%s-%s)" % (info.name,
format_address(info.zone_start),
format_address(info.zone_end))))
return
class XorMemoryCommand(GenericCommand):
"""XOR a block of memory."""
_cmdline_ = "xor-memory"
_syntax_ = "%s (display|patch) <address> <size_to_read> <xor_key> " % _cmdline_
def do_invoke(self, argv):
if len(argv) == 0:
err("Missing subcommand (display|patch)")
self.usage()
return
class XorMemoryDisplayCommand(GenericCommand):
"""Display a block of memory by XOR-ing each key with a key."""
_cmdline_ = "xor-memory display"
_syntax_ = "%s <address> <size_to_read> <xor_key> " % _cmdline_
def do_invoke(self, argv):
if len(argv) != 3:
self.usage()
return
address = long(gdb.parse_and_eval(argv[0]))
length, key = long(argv[1]), argv[2]
block = read_memory(address, length)
info("Displaying XOR-ing %#x-%#x with '%s'" % (address, address+len(block), key))
print(( titlify("Original block") ))
print(( hexdump( block ) ))
print(( titlify("XOR-ed block") ))
print(( hexdump( XOR(block, key) )))
return
class XorMemoryPatchCommand(GenericCommand):
"""Patch a block of memory by XOR-ing each key with a key."""
_cmdline_ = "xor-memory patch"
_syntax_ = "%s <address> <size_to_read> <xor_key> " % _cmdline_
def do_invoke(self, argv):
if len(argv) != 3:
self.usage()
return
address = long(gdb.parse_and_eval(argv[0]))
length, key = long(argv[1]), argv[2]
block = read_memory(address, length)
info("Patching XOR-ing %#x-%#x with '%s'" % (address, address+len(block), key))
xored_block = XOR(block, key)
write_memory(address, xored_block, length)
return
class TraceRunCommand(GenericCommand):
"""Create a runtime trace of all instructions executed from $pc to LOCATION specified."""
_cmdline_ = "trace-run"
_syntax_ = "%s LOCATION [MAX_CALL_DEPTH]" % _cmdline_
def __init__(self):
super(TraceRunCommand, self).__init__(self._cmdline_, complete=gdb.COMPLETE_LOCATION)
self.add_setting("max_tracing_recursion", 1)
self.add_setting("tracefile_prefix", "./gef-trace-")
return
def do_invoke(self, argv):
if len(argv) > 2:
self.usage()
return
if not is_alive():
warn("Debugging session is not active")
return
depth = long(argv[1]) if len(argv)==2 and argv[1].isdigit() else 1
try:
loc_start = long(gdb.parse_and_eval("$pc"))
loc_end = long(gdb.parse_and_eval(argv[0]).address)
except gdb.error as e:
err("Invalid location: %s" % e)
return
self.trace(loc_start, loc_end)
return
def trace(self, loc_start, loc_end):
info("Tracing from %#x to %#x" % (loc_start, loc_end))
logfile = "%s-%#x-%#x.txt" % (self.get_setting("tracefile_prefix"), loc_start, loc_end)
gdb.execute( "set logging overwrite" )
gdb.execute( "set logging file %s" % logfile)
gdb.execute( "set logging redirect on" )
gdb.execute( "set logging on" )
self._do_trace(loc_start, loc_end)
gdb.execute( "set logging redirect off" )
gdb.execute( "set logging off" )
info("Formatting output")
gdb.execute("shell sed -i -e '/^[^0x]/d' -e '/^$/d' %s" % logfile)
ok("Done, logfile stored as '%s'" % logfile)
info("Hint: import logfile with `ida_color_gdb_trace.py` script in IDA to visualize path")
return
def _do_trace(self, loc_start, loc_end):
# todo: add max_depth
loc_old = 0
loc_cur = loc_start
page_mask = 0xFFFF0000
frame_old = 0
frame_cur = gdb.selected_frame()
while is_alive() and loc_cur != loc_end:
gdb.execute( "nexti" )
return
class PatternCommand(GenericCommand):
"""Metasploit-like pattern generation/search"""
_cmdline_ = "pattern"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
self.usage()
return
class PatternCreateCommand(GenericCommand):
"""Metasploit-like pattern generation"""
_cmdline_ = "pattern create"
_syntax_ = "%s SIZE" % _cmdline_
def do_invoke(self, argv):
if len(argv) != 1:
self.usage()
return
if not argv[0].isdigit():
err("Invalid size")
return
size = long(argv[0])
info("Generating a pattern of %d bytes" % size)
print(( PatternCreateCommand.generate(size) ))
return
@staticmethod
def generate(limit):
pattern = ""
for mj in range(ord('A'), ord('Z')+1) : # from A to Z
for mn in range(ord('a'), ord('z')+1) : # from a to z
for dg in range(ord('0'), ord('9')+1) : # from 0 to 9
for extra in "~!@#$%&*()-_+={}[]|;:<>?/": # adding extra chars
for c in (chr(mj), chr(mn), chr(dg), extra):
if len(pattern) == limit :
return pattern
else:
pattern += "%s" % c
# Should never be here, just for clarity
return ""
class PatternSearchCommand(GenericCommand):
"""Metasploit-like pattern search"""
_cmdline_ = "pattern search"
_syntax_ = "%s SIZE PATTERN" % _cmdline_
def do_invoke(self, argv):
if len(argv) != 2:
self.usage()
return
if not argv[0].isdigit():
err("Invalid size")
return
size, pattern = long(argv[0]), argv[1]
info("Searching in '%s'" % pattern)
offset = self.search(pattern, size)
if offset < 0:
print(("Not found"))
return
def search(self, pattern, size):
try:
addr = long( gdb.parse_and_eval(pattern) )
if get_memory_alignment() == 32:
pattern_be = struct.pack(">I", addr)
pattern_le = struct.pack("<I", addr)
else:
pattern_be = struct.pack(">Q", addr)
pattern_le = struct.pack("<Q", addr)
except gdb.error:
err("Incorrect pattern")
return -1
buffer = PatternCreateCommand.generate(size)
found = False
off = buffer.find(pattern_le)
if off >= 0:
ok("Found at offset %d (little-endian search)" % off)
found = True
off = buffer.find(pattern_be)
if off >= 0:
ok("Found at offset %d (big-endian search)" % off)
found = True
return -1 if not found else 0
class InspectStackCommand(GenericCommand):
"""Exploiter-friendly top-down stack inspection command (peda-like)"""
_cmdline_ = "inspect-stack"
_syntax_ = "%s [NbStackEntry]" % _cmdline_
def do_invoke(self, argv):
if not is_alive():
warn("No debugging session active")
return
nb_stack_block = 10
argc = len(argv)
if argc >= 1:
try:
nb_stack_block = long(argv[0])
except ValueError:
pass
top_stack = get_register("$sp")
self.inspect_stack(top_stack, nb_stack_block)
return
@staticmethod
def inspect_stack(sp, nb_stack_block):
memalign = get_memory_alignment() >> 3
for i in range(nb_stack_block):
cur_addr = align_address( long(sp) + i*memalign )
addrs = DereferenceCommand.dereference_from(cur_addr)
msg = Color.boldify(Color.blueify( format_address(cur_addr) ))
msg += ": "
msg += " -> ".join(addrs)
print((msg))
return
class ChecksecCommand(GenericCommand):
"""Checksec.sh (http://www.trapkit.de/tools/checksec.html) port."""
_cmdline_ = "checksec"
_syntax_ = "%s (filename)" % _cmdline_
def __init__(self):
super(ChecksecCommand, self).__init__(complete=gdb.COMPLETE_FILENAME)
self.add_setting("readelf_path", "/usr/bin/readelf")
return
def do_invoke(self, argv):
argc = len(argv)
if argc == 0:
filename = get_filename()
elif argc == 1:
filename = argv[0]
else:
self.usage()
return
if not os.access(self.get_setting("readelf_path"), os.X_OK):
err("Could not access readelf")
info("%s for '%s'" % (self._cmdline_, filename))
self.checksec(filename)
return
def do_check(self, title, opt, filename, pattern, is_match):
options = opt.split(" ")
buf = "%-50s" % (title+":")
cmd = [self.get_setting("readelf_path"), ]
cmd+= options
cmd+= [filename, ]
lines = subprocess.check_output( cmd ).split("\n")
found = False
for line in lines:
if re.search(pattern, line):
buf += Color.GREEN
if is_match:
buf += Color.greenify("Yes")
else:
buf += Color.redify("No")
found = True
break
if not found:
if is_match:
buf+= Color.redify("No")
else:
buf+= Color.greenify("Yes")
print(("%s" % buf))
return
def checksec(self, filename):
# check for canary
self.do_check("Canary", "-s", filename, r'__stack_chk_fail', is_match=True)
# check for NX
self.do_check("NX Support", "-W -l", filename, r'GNU_STACK.*RWE', is_match=False)
# check for PIE support
self.do_check("PIE Support", "-h", filename, r'Type:.*EXEC', is_match=False)
# todo : add check for (DEBUG) if .so
# check for RPATH
self.do_check("RPATH", "-d -l", filename, r'rpath', is_match=True)
# check for RUNPATH
self.do_check("RUNPATH", "-d -l", filename, r'runpath', is_match=True)
return
class FormatStringSearchCommand(GenericCommand):
"""Exploitable format-string helper (experimental)"""
_cmdline_ = "fmtstr-helper"
_syntax_ = "%s" % _cmdline_
def do_invoke(self, argv):
dangerous_functions = {
'printf': 0,
'sprintf': 1,
'vfprintf': 1,
'vsprintf': 1,
'fprintf': 1,
'snprintf': 2,
'vsnprintf': 2,
}
for func_name, num_arg in dangerous_functions.iteritems():
FormatStringBreakpoint(func_name, num_arg)
return
class GEFCommand(gdb.Command):
"""GEF Control Center"""
_cmdline_ = "gef"
_syntax_ = "%s (load/help)" % _cmdline_
def __init__(self):
super(GEFCommand, self).__init__(GEFCommand._cmdline_,
gdb.COMMAND_SUPPORT)
self.classes = [ResetCacheCommand,
XAddressInfoCommand,
XorMemoryCommand, XorMemoryDisplayCommand, XorMemoryPatchCommand,
FormatStringSearchCommand,
TraceRunCommand,
PatternCommand, PatternSearchCommand, PatternCreateCommand,
ChecksecCommand,
VMMapCommand,
XFilesCommand,
ASLRCommand,
DereferenceCommand,
HexdumpCommand,
ContextCommand,
EntryPointBreakCommand,
ElfInfoCommand,
ProcessListingCommand,
InvokeCommand,
AssembleCommand,
FileDescriptorCommand,
InspectStackCommand,
ShellcodeCommand, ShellcodeSearchCommand, ShellcodeGetCommand,
DetailRegistersCommand,
SolveKernelSymbolCommand,
AliasCommand, AliasShowCommand, AliasSetCommand, AliasUnsetCommand, AliasDoCommand,
DumpMemoryCommand,
# add new commands here
]
self.__cmds = [ (x._cmdline_, x) for x in self.classes ]
self.__loaded_cmds = []
self.load()
return
@property
def loaded_command_names(self):
return [ x[0] for x in self.__loaded_cmds ]
def invoke(self, args, from_tty):
argv = gdb.string_to_argv(args)
if len(argv) < 1 :
err("Missing command for gef -- `gef help` for help -- `gef config` for configuring")
return
cmd = argv[0]
if cmd == "help":
self.help()
elif cmd == "config":
self.config(*argv[1:])
else:
err("Invalid command '%s' for gef -- type `gef help' for help" % ' '.join(argv))
return
def load(self, mod=None):
for (cmd, class_name) in self.__cmds:
try:
class_name()
self.__loaded_cmds.append( (cmd, class_name) )
except Exception as e:
err("Failed to load `%s`: %s" % (cmd, e))
print(("%s, `%s' to start, `%s' to configure" % (Color.greenify("gef loaded"),
Color.redify("gef help"),
Color.redify("gef config"))))
ver = "%d.%d" % (sys.version_info.major, sys.version_info.minor)
nb_cmds = sum([1 for x in self.loaded_command_names if " " not in x])
nb_sub_cmds = sum([1 for x in self.loaded_command_names if " " in x])
print(("%s commands loaded (%s sub-commands), using Python engine %s" % (Color.greenify(str(nb_cmds)),
Color.greenify(str(nb_sub_cmds)),
Color.redify(ver))))
return
def help(self):
print((titlify("GEF - GDB Enhanced Features") ))
for (cmd, class_name) in self.__loaded_cmds:
if " " in cmd:
# do not print out subcommands in main help
continue
doc = class_name.__doc__ if hasattr(class_name, "__doc__") else ""
msg = "%-25s -- %s" % (cmd, Color.greenify( doc ))
print(("%s" % msg))
return
def config(self, *args):
argc = len(args)
if not (0 <= argc <= 2):
err("Invalid number of arguments")
return
if argc==0 or argc==1:
config_items = sorted( __config__ )
plugin_name = args[0] if argc==1 and args[0] in self.loaded_command_names else ""
print(( titlify("GEF configuration settings %s" % plugin_name) ))
for key in config_items:
if plugin_name not in key:
continue
value, type = __config__.get(key, None)
print( ("%-40s (%s) = %s" % (key, type.__name__, value)) )
return
if "." not in args[0]:
err("Invalid command format")
return
plugin_name, setting_name = args[0].split(".", 1)
if plugin_name not in self.loaded_command_names:
err("Unknown plugin '%s'" % plugin_name)
return
_curval, _type = __config__.get( args[0], (None, None) )
if _type == None:
err("Failed to get '%s' config setting" % (args[0], ))
return
try:
if _type == bool:
_newval = True if args[1]=="True" else False
else:
_newval = args[1]
_type( _newval )
except:
err("%s expects type '%s'" % (args[0], _type.__name__))
return
__config__[ args[0] ] = (_newval, _type)
return
def main():
GEF_PROMPT = Color.boldify(Color.redify("gef> "))
# setup config
gdb.execute("set confirm off")
gdb.execute("set verbose off")
gdb.execute("set output-radix 0x10")
gdb.execute("set input-radix 0x10")
gdb.execute("set height 0")
gdb.execute("set width 0")
gdb.execute("set prompt %s" % GEF_PROMPT)
gdb.execute("set follow-fork-mode child")
# gdb history
gdb.execute("set history filename ~/.gdb_history")
gdb.execute("set history save")
# aliases
# WinDBG-like aliases (I like them)
# breakpoints
gdb.execute("alias -a bl = info breakpoints")
gdb.execute("alias -a bp = break")
gdb.execute("alias -a be = enable breakpoints")
gdb.execute("alias -a bd = disable breakpoints")
gdb.execute("alias -a bc = delete breakpoints")
gdb.execute("alias -a tbp = tbreak")
gdb.execute("alias -a tba = thbreak")
# runtime
gdb.execute("alias -a g = run")
# memory access
gdb.execute("alias -a uf = disassemble")
# context
gdb.execute("alias -a argv = show args")
gdb.execute("alias -a kp = info stack")
try:
# this will raise a gdb.error unless we're on x86
# we can safely ignore this
gdb.execute("set disassembly-flavor intel")
except gdb.error:
pass
# load GEF
GEFCommand()
# post-loading stuff
define_user_command("hook-stop", "context")
'''
def main(): pass
if __name__ == "__main__":
main()