|
Packit |
8ea169 |
#!/usr/bin/python3
|
|
Packit |
8ea169 |
# This is a GDB plugin.
|
|
Packit |
8ea169 |
# Usage:
|
|
Packit |
8ea169 |
# gdb --batch -ex 'python exec(open("THIS_FILE").read())' -ex run -ex abrt-exploitable PROG
|
|
Packit |
8ea169 |
# or
|
|
Packit |
8ea169 |
# gdb --batch -ex 'python exec(open("THIS_FILE").read())' -ex 'core COREDUMP' -ex abrt-exploitable
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
import sys
|
|
Packit |
8ea169 |
import os
|
|
Packit |
8ea169 |
import signal
|
|
Packit |
8ea169 |
import re
|
|
Packit |
8ea169 |
import gettext
|
|
Packit |
8ea169 |
import locale
|
|
Packit |
8ea169 |
import gdb
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GETTEXT_PROGNAME = "abrt"
|
|
Packit |
8ea169 |
_ = gettext.gettext
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def init_gettext():
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
locale.setlocale(locale.LC_ALL, "")
|
|
Packit |
8ea169 |
except locale.Error:
|
|
Packit |
8ea169 |
os.environ['LC_ALL'] = 'C'
|
|
Packit |
8ea169 |
locale.setlocale(locale.LC_ALL, "")
|
|
Packit |
8ea169 |
# Defeat "AttributeError: 'module' object has no attribute 'nl_langinfo'"
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
gettext.bind_textdomain_codeset(GETTEXT_PROGNAME, locale.nl_langinfo(locale.CODESET))
|
|
Packit |
8ea169 |
except AttributeError:
|
|
Packit |
8ea169 |
pass
|
|
Packit |
8ea169 |
gettext.bindtextdomain(GETTEXT_PROGNAME, '/usr/share/locale')
|
|
Packit |
8ea169 |
gettext.textdomain(GETTEXT_PROGNAME)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_WRITES_ALWAYS = -1
|
|
Packit |
8ea169 |
_WRITES_IF_MEMREF = -2
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_x86_writing_instr = {
|
|
Packit |
8ea169 |
# insn:N, where N:
|
|
Packit |
8ea169 |
# -1: this insn always writes to memory
|
|
Packit |
8ea169 |
# -2: writes to memory if any operand is a memory operand
|
|
Packit |
8ea169 |
# 2: writes to memory if 2nd (or later) operand is a memory operand
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
# Two-operand insns
|
|
Packit |
8ea169 |
"add": 2,
|
|
Packit |
8ea169 |
"adc": 2,
|
|
Packit |
8ea169 |
"sub": 2,
|
|
Packit |
8ea169 |
"sbb": 2,
|
|
Packit |
8ea169 |
"and": 2,
|
|
Packit |
8ea169 |
"xor": 2,
|
|
Packit |
8ea169 |
"or": 2,
|
|
Packit |
8ea169 |
"xadd": 2,
|
|
Packit |
8ea169 |
"cmpxchg": 2,
|
|
Packit |
8ea169 |
# One-operand insns. Can use 1 or _WRITES_IF_MEMREF
|
|
Packit |
8ea169 |
"inc": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"dec": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"neg": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"not": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"pop": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# "Set byte on condition". One-operand insns.
|
|
Packit |
8ea169 |
"seta": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setae": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setb": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setbe": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setc": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sete": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setg": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setge": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setle": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setna": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnae": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnb": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnbe": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnc": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setne": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setng": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnge": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnle": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setno": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnp": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setns": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setnz": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"seto": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setp": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setpe": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setpo": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sets": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"setz": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# Shifts.
|
|
Packit |
8ea169 |
# sarl $2,(%rcx)
|
|
Packit |
8ea169 |
# sarl (%rax) - *implicit* operand (shift count) 1.
|
|
Packit |
8ea169 |
# shld 11,%ecx,(%rdi) - *third* operand is r/m.
|
|
Packit |
8ea169 |
# Luckily, any memory operand is a destination, can use _WRITES_IF_MEMREF.
|
|
Packit |
8ea169 |
"shl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"shr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sal": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sar": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"rol": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"ror": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"rcl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"rcr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"shld": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"shrd": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# Bit tests. Any memory operand is a destination, can use _WRITES_IF_MEMREF.
|
|
Packit |
8ea169 |
"bts": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"btr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"btc": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# One-operand (register pair is another, implicit operand).
|
|
Packit |
8ea169 |
"cmpxchg8b": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"cmpxchg16b": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Either mem operand indicates write to mem.
|
|
Packit |
8ea169 |
"xchg": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# String store insns.
|
|
Packit |
8ea169 |
# Look similar to widening signed move "movs[bwl][wlq]",
|
|
Packit |
8ea169 |
# but aliasing doesn't happen since widening move has two siffixes
|
|
Packit |
8ea169 |
"movs": _WRITES_ALWAYS,
|
|
Packit |
8ea169 |
"stos": _WRITES_ALWAYS,
|
|
Packit |
8ea169 |
# Widening moves never store to mem.
|
|
Packit |
8ea169 |
# May look like we need to list them because otherwise they get caught
|
|
Packit |
8ea169 |
# by "movXXX", but thankfully their 2nd operand is never a memory reference,
|
|
Packit |
8ea169 |
# which "movXXX" wildcard checks.
|
|
Packit |
8ea169 |
#"mov[sz][bwl][wlq]":0,
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# One-operand insn.
|
|
Packit |
8ea169 |
# These are system insns, but they do NOT cause exception in userspace.
|
|
Packit |
8ea169 |
"smsw": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sgdt": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sidt": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"sldt": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"str": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# FPU/SIMD madness follows.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# FPU store insns. One-operand.
|
|
Packit |
8ea169 |
"fsts": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fstl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
#"fstt" doesn't exist
|
|
Packit |
8ea169 |
"fstps": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fstpl": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fstpt": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# Saving state. One-operand insns.
|
|
Packit |
8ea169 |
"fstcw": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fnstcw": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fstsw": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fnstsw": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fstenv": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fnstenv": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fsave": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fnsave": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fxsave": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"xsave": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"xsaveopt": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fsave64": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fnsave64": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"fxsave64": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"xsave64": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"xsaveopt64": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"stmxcsr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vstmxcsr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# SIMD store insns.
|
|
Packit |
8ea169 |
# Three-operand insns. Any memory operand is a destination.
|
|
Packit |
8ea169 |
"vcvtps2ph": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"extractps": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vextractps": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
#[v]extractpd does not exist
|
|
Packit |
8ea169 |
"vextractf128": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vextracti128": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"pextr": _WRITES_IF_MEMREF, # covers pextr[bwq]
|
|
Packit |
8ea169 |
"pextrd": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vpextr": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vpextrd": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vmaskmovpd": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vmaskmovps": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vpmaskmovd": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
"vpmaskmovq": _WRITES_IF_MEMREF,
|
|
Packit |
8ea169 |
# These insns have implicit (%edi) dest operand:
|
|
Packit |
8ea169 |
"maskmovq": _WRITES_ALWAYS, # mmx version
|
|
Packit |
8ea169 |
"maskmovdqu": _WRITES_ALWAYS,
|
|
Packit |
8ea169 |
"vmaskmovdqu": _WRITES_ALWAYS,
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# check binutils/gas/testsuite/gas/i386/* for more weird insns
|
|
Packit |
8ea169 |
# Instruction Set Reference, A-M and N-Z:
|
|
Packit |
8ea169 |
# http://download.intel.com/products/processor/manual/253666.pdf
|
|
Packit |
8ea169 |
# http://download.intel.com/products/processor/manual/253667.pdf
|
|
Packit |
8ea169 |
# SSE4:
|
|
Packit |
8ea169 |
# http://software.intel.com/sites/default/files/m/0/3/c/d/4/18187-d9156103.pdf
|
|
Packit |
8ea169 |
# Instruction Set Extensions:
|
|
Packit |
8ea169 |
# http://download-software.intel.com/sites/default/files/319433-014.pdf
|
|
Packit |
8ea169 |
# Xeon Phi:
|
|
Packit |
8ea169 |
# http://download-software.intel.com/sites/default/files/forum/278102/327364001en.pdf
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#"[v]movXXX" - special-cased in the code
|
|
Packit |
8ea169 |
"mov": 2
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Note: stack-writing instructions are omitted
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_x86_pushing_instr = (
|
|
Packit |
8ea169 |
"push",
|
|
Packit |
8ea169 |
"pusha",
|
|
Packit |
8ea169 |
"pushf",
|
|
Packit |
8ea169 |
"enter",
|
|
Packit |
8ea169 |
"call",
|
|
Packit |
8ea169 |
"lcall"
|
|
Packit |
8ea169 |
)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_x86_intdiv_instr = ("div", "idiv")
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_x86_jumping_instr = (
|
|
Packit |
8ea169 |
"jmp", # indirect jumps/calls with garbage data
|
|
Packit |
8ea169 |
"call", # call: also possible that stack is exhausted (infinite recursion)
|
|
Packit |
8ea169 |
"ljmp",
|
|
Packit |
8ea169 |
"lcall",
|
|
Packit |
8ea169 |
# Yes, lret/iret isn't used in normal userspace code,
|
|
Packit |
8ea169 |
# but it does work (compile with "gcc -nostartfiles -nostdlib -m32"):
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
#_start: .globl _start
|
|
Packit |
8ea169 |
# pushf
|
|
Packit |
8ea169 |
# push %cs
|
|
Packit |
8ea169 |
# push $next
|
|
Packit |
8ea169 |
# iret # lret or ret would work too
|
|
Packit |
8ea169 |
#next:
|
|
Packit |
8ea169 |
# movl $42, %ebx
|
|
Packit |
8ea169 |
# movl $1, %eax
|
|
Packit |
8ea169 |
# int $0x80 # exit(42)
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
"iret",
|
|
Packit |
8ea169 |
"lret",
|
|
Packit |
8ea169 |
"ret"
|
|
Packit |
8ea169 |
)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# stack was smashed if we crash on one of these
|
|
Packit |
8ea169 |
_x86_return_instr = ("iret", "lret", "ret")
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
_x86_mem_op1_regex = re.compile("^((-?0x)|[(])")
|
|
Packit |
8ea169 |
_x86_mem_op2_regex = re.compile("[,:]((-?0x)|[(])")
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def _x86_fetch_insn_from_table(ins, table):
|
|
Packit |
8ea169 |
if not ins:
|
|
Packit |
8ea169 |
return None
|
|
Packit |
8ea169 |
if ins in table:
|
|
Packit |
8ea169 |
if type(table) == dict:
|
|
Packit |
8ea169 |
return table[ins]
|
|
Packit |
8ea169 |
return ins
|
|
Packit |
8ea169 |
# Drop common byte/word/long/quad suffix and try again
|
|
Packit |
8ea169 |
if ins[-1] in ("b", "w", "l", "q"):
|
|
Packit |
8ea169 |
ins = ins[:-1]
|
|
Packit |
8ea169 |
if ins in table:
|
|
Packit |
8ea169 |
if type(table) == dict:
|
|
Packit |
8ea169 |
return table[ins]
|
|
Packit |
8ea169 |
return ins
|
|
Packit |
8ea169 |
return None
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
class SignalAndInsn:
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def x86_instruction_is_store(self):
|
|
Packit |
8ea169 |
operand = _x86_fetch_insn_from_table(self.mnemonic, _x86_writing_instr)
|
|
Packit |
8ea169 |
if not operand:
|
|
Packit |
8ea169 |
if not self.mnemonic:
|
|
Packit |
8ea169 |
return False
|
|
Packit |
8ea169 |
# There are far too many SSE store instructions,
|
|
Packit |
8ea169 |
# don't want to pollute the table with them.
|
|
Packit |
8ea169 |
# Special-case the check for MOVxxx
|
|
Packit |
8ea169 |
# and its SIMD cousins VMOVxxx:
|
|
Packit |
8ea169 |
if self.mnemonic[:3] != "mov" and self.mnemonic[:4] != "vmov":
|
|
Packit |
8ea169 |
return False
|
|
Packit |
8ea169 |
operand = 2
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if operand == _WRITES_ALWAYS: # no need to check operands, it's a write
|
|
Packit |
8ea169 |
return True
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Memory operands look like this: [%seg:][[-]0xHEXNUM][(%reg[,...])]
|
|
Packit |
8ea169 |
# Careful with immediate operands which are $0xHEXNUM
|
|
Packit |
8ea169 |
# and FPU register references which are st(N).
|
|
Packit |
8ea169 |
if _x86_mem_op1_regex.search(self.operands):
|
|
Packit |
8ea169 |
mem_op_pos = 0
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
match = _x86_mem_op2_regex.search(self.operands)
|
|
Packit |
8ea169 |
if not match:
|
|
Packit |
8ea169 |
return False # no memory operands
|
|
Packit |
8ea169 |
mem_op_pos = match.start() + 1
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if operand == _WRITES_IF_MEMREF: # any mem operand indicates write
|
|
Packit |
8ea169 |
return True
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
comma = self.operands.find(",")
|
|
Packit |
8ea169 |
if mem_op_pos < comma:
|
|
Packit |
8ea169 |
# "%cs:0x0(%rax,%rax,1),foo" - 1st operand is memory
|
|
Packit |
8ea169 |
# "%cs:0x0(%rax),foo" - 1st operand is memory
|
|
Packit |
8ea169 |
memory_operand = 1
|
|
Packit |
8ea169 |
elif comma < 0:
|
|
Packit |
8ea169 |
# "%cs:0x0(%rax)" - 1st operand is memory
|
|
Packit |
8ea169 |
memory_operand = 1
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
# mem_op_pos is after comma
|
|
Packit |
8ea169 |
# "foo,%cs:0x0(%rax,%rax,1)" - 2nd operand is memory
|
|
Packit |
8ea169 |
# (It also can be a third, fourth etc operand)
|
|
Packit |
8ea169 |
memory_operand = 2
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if operand == memory_operand:
|
|
Packit |
8ea169 |
return True
|
|
Packit |
8ea169 |
return False
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def x86_get_instruction(self):
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
# just "disassemble $pc" won't work if $pc doesn't point
|
|
Packit |
8ea169 |
# inside a known function
|
|
Packit |
8ea169 |
raw_instructions = gdb.execute("disassemble $pc,$pc+32", to_string=True)
|
|
Packit |
8ea169 |
except gdb.error:
|
|
Packit |
8ea169 |
# For example, if tracee already exited normally.
|
|
Packit |
8ea169 |
# Another observed case is if $pc points to unmapped area.
|
|
Packit |
8ea169 |
# We get "Python Exception <class 'gdb.error'> No registers"
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
instructions = []
|
|
Packit |
8ea169 |
current = None
|
|
Packit |
8ea169 |
for line in raw_instructions.split("\n"):
|
|
Packit |
8ea169 |
# line can be:
|
|
Packit |
8ea169 |
# "Dump of assembler code from 0xAAAA to 0xBBBB:"
|
|
Packit |
8ea169 |
# "[=>] 0x00000000004004dc[ <+0>]: push %rbp"
|
|
Packit |
8ea169 |
# (" <+0>" part is present when we run on a live process,
|
|
Packit |
8ea169 |
# on coredump it is absent)
|
|
Packit |
8ea169 |
# "End of assembler dump."
|
|
Packit |
8ea169 |
# "" (empty line)
|
|
Packit |
8ea169 |
if line.startswith("=>"):
|
|
Packit |
8ea169 |
line = line[2:]
|
|
Packit |
8ea169 |
current = len(instructions)
|
|
Packit |
8ea169 |
line = line.split(":", 1)
|
|
Packit |
8ea169 |
if len(line) < 2: # no ":"?
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
line = line[1] # drop "foo:"
|
|
Packit |
8ea169 |
line = line.strip() # drop leading/trailing whitespace
|
|
Packit |
8ea169 |
if line:
|
|
Packit |
8ea169 |
instructions.append(line)
|
|
Packit |
8ea169 |
if current == None:
|
|
Packit |
8ea169 |
# we determined that $pc points to a bad address,
|
|
Packit |
8ea169 |
# which is an interesting fact.
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# There can be a disasm comment: "insn op,op,op # comment";
|
|
Packit |
8ea169 |
# strip it, and whitespace on both ends:
|
|
Packit |
8ea169 |
t = instructions[current].split("#", 1)[0].strip()
|
|
Packit |
8ea169 |
self.current_instruction = t
|
|
Packit |
8ea169 |
# Strip prefixes:
|
|
Packit |
8ea169 |
while True:
|
|
Packit |
8ea169 |
t = t.split(None, 1)
|
|
Packit |
8ea169 |
self.mnemonic = t[0]
|
|
Packit |
8ea169 |
if len(t) < 2:
|
|
Packit |
8ea169 |
break
|
|
Packit |
8ea169 |
if self.mnemonic.startswith("rex."):
|
|
Packit |
8ea169 |
t = t[1]
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
if self.mnemonic in (
|
|
Packit |
8ea169 |
"data32", "data16", "addr32", "addr16", "rex",
|
|
Packit |
8ea169 |
"cs", "ds", "es", "ss", "fs", "gs",
|
|
Packit |
8ea169 |
"lock", "rep", "repz", "repnz", "xacquire", "xrelease"
|
|
Packit |
8ea169 |
):
|
|
Packit |
8ea169 |
t = t[1]
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
# First word isn't a prefix -> we found the insn word
|
|
Packit |
8ea169 |
self.operands = t[1]
|
|
Packit |
8ea169 |
break
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
self.instruction_is_pushing = (_x86_fetch_insn_from_table(self.mnemonic, _x86_pushing_instr) is not None)
|
|
Packit |
8ea169 |
self.instruction_is_division = (_x86_fetch_insn_from_table(self.mnemonic, _x86_intdiv_instr) is not None)
|
|
Packit |
8ea169 |
self.instruction_is_branch = (_x86_fetch_insn_from_table(self.mnemonic, _x86_jumping_instr) is not None)
|
|
Packit |
8ea169 |
self.instruction_is_return = (_x86_fetch_insn_from_table(self.mnemonic, _x86_return_instr) is not None)
|
|
Packit |
8ea169 |
self.instruction_is_store = self.x86_instruction_is_store()
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def ppc_get_instruction(self):
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
# just "disassemble $pc" won't work if $pc doesn't point
|
|
Packit |
8ea169 |
# inside a known function
|
|
Packit |
8ea169 |
raw_instructions = gdb.execute("disassemble $pc,$pc+32", to_string=True)
|
|
Packit |
8ea169 |
except gdb.error:
|
|
Packit |
8ea169 |
# For example, if tracee already exited normally.
|
|
Packit |
8ea169 |
# Another observed case is if $pc points to unmapped area.
|
|
Packit |
8ea169 |
# We get "Python Exception <class 'gdb.error'> No registers"
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
instructions = []
|
|
Packit |
8ea169 |
current = None
|
|
Packit |
8ea169 |
for line in raw_instructions.split("\n"):
|
|
Packit |
8ea169 |
# line can be:
|
|
Packit |
8ea169 |
# "Dump of assembler code from 0xAAAA to 0xBBBB:"
|
|
Packit |
8ea169 |
# "[=>] 0x00000000004004dc[ <+0>]: push %rbp"
|
|
Packit |
8ea169 |
# (" <+0>" part is present when we run on a live process,
|
|
Packit |
8ea169 |
# on coredump it is absent)
|
|
Packit |
8ea169 |
# "End of assembler dump."
|
|
Packit |
8ea169 |
# "" (empty line)
|
|
Packit |
8ea169 |
if line.startswith("=>"):
|
|
Packit |
8ea169 |
line = line[2:]
|
|
Packit |
8ea169 |
current = len(instructions)
|
|
Packit |
8ea169 |
line = line.split(":", 1)
|
|
Packit |
8ea169 |
if len(line) < 2: # no ":"?
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
line = line[1] # drop "foo:"
|
|
Packit |
8ea169 |
line = line.strip() # drop leading/trailing whitespace
|
|
Packit |
8ea169 |
if line:
|
|
Packit |
8ea169 |
instructions.append(line)
|
|
Packit |
8ea169 |
if current is None:
|
|
Packit |
8ea169 |
# we determined that $pc points to a bad address,
|
|
Packit |
8ea169 |
# which is an interesting fact.
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# There can be a disasm comment: "insn op,op,op # comment";
|
|
Packit |
8ea169 |
# strip it, and whitespace on both ends:
|
|
Packit |
8ea169 |
t = instructions[current].split("#", 1)[0].strip()
|
|
Packit |
8ea169 |
self.current_instruction = t
|
|
Packit |
8ea169 |
# Split it into mnemonic and operands
|
|
Packit |
8ea169 |
t = t.split(None, 1)
|
|
Packit |
8ea169 |
self.mnemonic = t[0]
|
|
Packit |
8ea169 |
if len(t) > 1:
|
|
Packit |
8ea169 |
self.operands = t[1]
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
self.instruction_is_store = self.mnemonic.startswith("st")
|
|
Packit |
8ea169 |
self.instruction_is_branch = self.mnemonic.startswith("b")
|
|
Packit |
8ea169 |
self.instruction_is_pushing = (self.instruction_is_store and "(r1)" in self.operands)
|
|
Packit |
8ea169 |
# Looks like div[o] insns on ppc don't cause exceptions
|
|
Packit |
8ea169 |
# (need to check whether, and how, FPE is generated)
|
|
Packit |
8ea169 |
#self.instruction_is_division =
|
|
Packit |
8ea169 |
# On ppc, return insn is b[cond]lr. TODO: is cond form ever used by gcc?
|
|
Packit |
8ea169 |
self.instruction_is_return = (self.mnemonic == "blr")
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def get_instruction(self):
|
|
Packit |
8ea169 |
self.current_instruction = None
|
|
Packit |
8ea169 |
self.mnemonic = None
|
|
Packit |
8ea169 |
self.operands = ""
|
|
Packit |
8ea169 |
self.instruction_is_division = None
|
|
Packit |
8ea169 |
self.instruction_is_store = None
|
|
Packit |
8ea169 |
self.instruction_is_pushing = None
|
|
Packit |
8ea169 |
self.instruction_is_return = None
|
|
Packit |
8ea169 |
self.instruction_is_branch = None
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
arch = gdb.execute("show architecture", to_string=True)
|
|
Packit |
8ea169 |
# Examples of the string we get:
|
|
Packit |
8ea169 |
# The target architecture is set automatically (currently i386)
|
|
Packit |
8ea169 |
# The target architecture is set automatically (currently i386:x86-64)
|
|
Packit |
8ea169 |
# The target architecture is set automatically (currently powerpc:common64)
|
|
Packit |
8ea169 |
if " i386" in arch:
|
|
Packit |
8ea169 |
return self.x86_get_instruction()
|
|
Packit |
8ea169 |
if " powerpc" in arch:
|
|
Packit |
8ea169 |
return self.ppc_get_instruction()
|
|
Packit |
8ea169 |
except gdb.error:
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def get_signal(self):
|
|
Packit |
8ea169 |
self.signo = None
|
|
Packit |
8ea169 |
self.si_code = None
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
# Requires new kernels which record complete siginfo
|
|
Packit |
8ea169 |
# in coredumps (Linux 3.9 still don't have it),
|
|
Packit |
8ea169 |
# and new gdb:
|
|
Packit |
8ea169 |
sig = gdb.parse_and_eval("$_siginfo.si_signo")
|
|
Packit |
8ea169 |
code = gdb.parse_and_eval("$_siginfo.si_code")
|
|
Packit |
8ea169 |
# Requires patched gdb:
|
|
Packit |
8ea169 |
#sig = gdb.parse_and_eval("$_signo")
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
# type(sig) = <type 'gdb.Value'>, convert to plain int:
|
|
Packit |
8ea169 |
self.signo = int(sig)
|
|
Packit |
8ea169 |
self.si_code = int(code)
|
|
Packit |
8ea169 |
except gdb.error:
|
|
Packit |
8ea169 |
# "Python Exception <class 'gdb.error'>
|
|
Packit |
8ea169 |
# Attempt to extract a component of a value that is not a structure"
|
|
Packit |
8ea169 |
# Possible reasons why $_siginfo doesn't exist:
|
|
Packit |
8ea169 |
# program is still running, program exited normally,
|
|
Packit |
8ea169 |
# we work with a coredump from an old kernel.
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
# Lets see whether we are running from the abrt and it
|
|
Packit |
8ea169 |
# provided us with signal number. Horrible hack :(
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
self.signo = int(os.environ["SIGNO_OF_THE_COREDUMP"])
|
|
Packit |
8ea169 |
except KeyError:
|
|
Packit |
8ea169 |
return False
|
|
Packit |
8ea169 |
return True
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#Our initial set of testing will use the list Apple included in their
|
|
Packit |
8ea169 |
#CrashWrangler announcement:
|
|
Packit |
8ea169 |
#
|
|
Packit |
8ea169 |
#Exploitable if:
|
|
Packit |
8ea169 |
# Crash on write instruction
|
|
Packit |
8ea169 |
# Crash executing invalid address
|
|
Packit |
8ea169 |
# Crash calling an invalid address
|
|
Packit |
8ea169 |
# Crash accessing an uninitialized or freed pointer as indicated by
|
|
Packit |
8ea169 |
# using the MallocScribble environment variable
|
|
Packit |
8ea169 |
# Illegal instruction exception
|
|
Packit |
8ea169 |
# Abort due to -fstack-protector, _FORTIFY_SOURCE, heap corruption
|
|
Packit |
8ea169 |
# detected
|
|
Packit |
8ea169 |
# Stack trace of crashing thread contains certain functions such as
|
|
Packit |
8ea169 |
# malloc, free, szone_error, objc_MsgSend, etc.
|
|
Packit |
8ea169 |
def is_exploitable(self):
|
|
Packit |
8ea169 |
self.exploitable_rating = 3
|
|
Packit |
8ea169 |
self.exploitable_desc = ""
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# siginfo.si_code:
|
|
Packit |
8ea169 |
# If <= 0, then it's not a crash:
|
|
Packit |
8ea169 |
# SI_ASYNCNL = -60 /* asynch name lookup completion */
|
|
Packit |
8ea169 |
# SI_TKILL = -6 /* tkill (and tgkill?) */
|
|
Packit |
8ea169 |
# SI_SIGIO = -5 /* queued SIGIO */
|
|
Packit |
8ea169 |
# SI_ASYNCIO = -4 /* AIO completion */
|
|
Packit |
8ea169 |
# SI_MESGQ = -3 /* real time mesq state change */
|
|
Packit |
8ea169 |
# SI_TIMER = -2 /* timer expiration (timer_create() with SIGEV_SIGNAL) */
|
|
Packit |
8ea169 |
# SI_QUEUE = -1 /* sigqueue */
|
|
Packit |
8ea169 |
# SI_USER = 0 /* kill, sigsend */
|
|
Packit |
8ea169 |
# Crashes have si_code > 0:
|
|
Packit |
8ea169 |
# testDivideByZero: SIGFPE si_code=FPE_INTDIV(1), si_addr=0x40054a
|
|
Packit |
8ea169 |
# x86-64 opcode 0x62: SIGILL si_code=ILL_ILLOPN(2), si_addr=0x40053f
|
|
Packit |
8ea169 |
# x86-64 priv.insn.: SIGSEGV si_code=SI_KERNEL(128), si_addr=0
|
|
Packit |
8ea169 |
# testExecuteInvalid: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x1c2404000
|
|
Packit |
8ea169 |
# testStackBufferOverflow ("ret" to bad address):
|
|
Packit |
8ea169 |
# SIGSEGV si_code=SI_KERNEL(128), si_addr=0
|
|
Packit |
8ea169 |
# testStackRecursion: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x7fff4c216d28
|
|
Packit |
8ea169 |
# testWriteRandom: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x1eb004004
|
|
Packit |
8ea169 |
# However:
|
|
Packit |
8ea169 |
# Keyboard signals (^C INT, ^\ QUIT, ^Z TSTP) also have si_code=SI_KERNEL.
|
|
Packit |
8ea169 |
# SIGWINCH has si_code=SI_KERNEL.
|
|
Packit |
8ea169 |
# SIGALRM from alarm(N) has si_code=SI_KERNEL.
|
|
Packit |
8ea169 |
# Surprisingly, SIGPIPE has si_code=SI_USER!
|
|
Packit |
8ea169 |
if self.si_code is not None:
|
|
Packit |
8ea169 |
# Filter out user-generated signals:
|
|
Packit |
8ea169 |
if self.si_code == -6 or self.si_code == -1: # SI_TKILL/SI_QUEUE
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by userspace code")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.si_code < 0:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by timer/IO/async event")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# Unfortunately, this isn't reliable to flag user-sent signals:
|
|
Packit |
8ea169 |
# not only SIGPIPE, but some other kernel signals have SI_USER
|
|
Packit |
8ea169 |
# (grep kernel sources for "send_sig(sig, ..., 0)").
|
|
Packit |
8ea169 |
# At least we know it's not a crash.
|
|
Packit |
8ea169 |
if self.si_code == 0: # SI_USER
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal has siginfo.si_code = SI_USER")
|
|
Packit |
8ea169 |
# Special case (a kernel buglet?)
|
|
Packit |
8ea169 |
if self.signo == signal.SIGPIPE:
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal due to write to closed pipe")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# And kernel-generated ones:
|
|
Packit |
8ea169 |
if self.si_code == 0x80: # SI_KERNEL
|
|
Packit |
8ea169 |
if self.signo in (signal.SIGINT, signal.SIGQUIT, signal.SIGTSTP):
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by keyboard")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo in (signal.SIGTTIN, signal.SIGTTOU, signal.SIGHUP):
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Job control signal sent by kernel")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGWINCH:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by window resize")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGALRM:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by alarm(N) expiration")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# else: Can't rule out "crash" signal: may be FPE/ILL/BUS/SEGV.
|
|
Packit |
8ea169 |
# Fall through into signo/insn analysis.
|
|
Packit |
8ea169 |
# else: We are here if signal was not from user and not SI_KERNEL.
|
|
Packit |
8ea169 |
# Fall through into signo/insn analysis.
|
|
Packit |
8ea169 |
# else: We are here if si_code isn't known.
|
|
Packit |
8ea169 |
# Fall through into signo/insn analysis.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Guessing here... it might be kill(2) as well.
|
|
Packit |
8ea169 |
# Should I add "Likely ..." to the descriptions?
|
|
Packit |
8ea169 |
if self.signo in (signal.SIGINT, signal.SIGQUIT, signal.SIGTSTP):
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by keyboard")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo in (signal.SIGTTIN, signal.SIGTTOU, signal.SIGHUP):
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Job control signal sent by kernel")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGPIPE:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal due to write to broken pipe")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGWINCH:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by window resize")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGALRM:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Signal sent by alarm(N) expiration")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Which signals can coredump?
|
|
Packit |
8ea169 |
# SIGABRT Abort signal from abort(3)
|
|
Packit |
8ea169 |
# SIGQUIT Quit from keyboard
|
|
Packit |
8ea169 |
# SIGXCPU CPU time limit exceeded
|
|
Packit |
8ea169 |
# SIGXFSZ File size limit exceeded
|
|
Packit |
8ea169 |
# SIGTRAP Trace/breakpoint trap
|
|
Packit |
8ea169 |
# SIGSYS Bad argument to routine (SVr4)
|
|
Packit |
8ea169 |
# SIGFPE Floating point exception
|
|
Packit |
8ea169 |
# SIGILL Illegal Instruction
|
|
Packit |
8ea169 |
# SIGSEGV Invalid memory reference
|
|
Packit |
8ea169 |
# SIGBUS Bus error (bad memory access)
|
|
Packit |
8ea169 |
if self.signo == signal.SIGABRT:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("ABRT signal (abort() was called?)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# Already handled above:
|
|
Packit |
8ea169 |
#if self.signo == signal.SIGQUIT:
|
|
Packit |
8ea169 |
# self.exploitable_rating = 0
|
|
Packit |
8ea169 |
# self.exploitable_desc = _("QUIT signal (Ctrl-\\ pressed?)")
|
|
Packit |
8ea169 |
# return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGXCPU:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("XCPU signal (over CPU time limit)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGXFSZ:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("XFSZ signal (over file size limit)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGTRAP:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("TRAP signal (can be a bug in a debugger/tracer)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGSYS:
|
|
Packit |
8ea169 |
self.exploitable_rating = 1
|
|
Packit |
8ea169 |
self.exploitable_desc = _("SYS signal (unknown syscall was called?)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if self.signo == signal.SIGFPE:
|
|
Packit |
8ea169 |
self.exploitable_rating = 1
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Arithmetic exception")
|
|
Packit |
8ea169 |
# 1 is FPE_INTDIV
|
|
Packit |
8ea169 |
if self.si_code == 1 or self.instruction_is_division:
|
|
Packit |
8ea169 |
self.exploitable_rating = 0
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Division by zero")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGILL:
|
|
Packit |
8ea169 |
self.exploitable_rating = 5
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Illegal instruction (jump to a random address?)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if self.signo != signal.SIGSEGV and self.signo != signal.SIGBUS:
|
|
Packit |
8ea169 |
self.exploitable_rating = 2
|
|
Packit |
8ea169 |
# Pity that we can't give a more descriptive explanation
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Non-crash related signal")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if self.instruction_is_pushing:
|
|
Packit |
8ea169 |
self.exploitable_rating = 4
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Stack overflow")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.instruction_is_store:
|
|
Packit |
8ea169 |
self.exploitable_rating = 6
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Write to an invalid address")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.instruction_is_return:
|
|
Packit |
8ea169 |
self.exploitable_rating = 7
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Subroutine return to an invalid address (corrupted stack?)")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# Note: we check "ret" first, _then_ jumps.
|
|
Packit |
8ea169 |
# Corrupted stack is different from corrupted data.
|
|
Packit |
8ea169 |
if self.instruction_is_branch:
|
|
Packit |
8ea169 |
self.exploitable_rating = 6
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Jump to an invalid address")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if not self.current_instruction:
|
|
Packit |
8ea169 |
self.exploitable_rating = 6
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Jump to an invalid address")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
if self.signo == signal.SIGBUS:
|
|
Packit |
8ea169 |
self.exploitable_rating = 5
|
|
Packit |
8ea169 |
self.exploitable_desc = _("Access past the end of mapped file, invalid address, unaligned access, etc")
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
# default values remain (e.g. description is "")
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
class AbrtExploitable(gdb.Command):
|
|
Packit |
8ea169 |
"Analyze a crash to determine exploitability"
|
|
Packit |
8ea169 |
def __init__(self):
|
|
Packit |
8ea169 |
super(AbrtExploitable, self).__init__(
|
|
Packit |
8ea169 |
"abrt-exploitable",
|
|
Packit |
8ea169 |
gdb.COMMAND_SUPPORT, # command class
|
|
Packit |
8ea169 |
gdb.COMPLETE_NONE, # completion method
|
|
Packit |
8ea169 |
False # => it's not a prefix command
|
|
Packit |
8ea169 |
)
|
|
Packit |
8ea169 |
init_gettext()
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Called when the command is invoked from GDB
|
|
Packit |
8ea169 |
def invoke(self, args, from_tty):
|
|
Packit |
8ea169 |
si = SignalAndInsn()
|
|
Packit |
8ea169 |
if not si.get_signal():
|
|
Packit |
8ea169 |
sys.stderr.write(_("Can't get signal no and do exploitability analysis\n"))
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
si.get_instruction()
|
|
Packit |
8ea169 |
min_rating = 0
|
|
Packit |
8ea169 |
if args:
|
|
Packit |
8ea169 |
args = args.split(None, 1)
|
|
Packit |
8ea169 |
min_rating = int(args[0])
|
|
Packit |
8ea169 |
si.is_exploitable()
|
|
Packit |
8ea169 |
if si.exploitable_desc:
|
|
Packit |
8ea169 |
if si.exploitable_rating >= min_rating:
|
|
Packit |
8ea169 |
f = sys.stdout
|
|
Packit |
8ea169 |
if args and len(args) > 1:
|
|
Packit |
8ea169 |
f = open(args[1], 'w')
|
|
Packit |
8ea169 |
f.write(_("Likely crash reason: ") + si.exploitable_desc + "\n")
|
|
Packit |
8ea169 |
f.write(_("Exploitable rating (0-9 scale): ") + str(si.exploitable_rating) + "\n")
|
|
Packit |
8ea169 |
if si.current_instruction:
|
|
Packit |
8ea169 |
f.write(_("Current instruction: ") + si.current_instruction + "\n")
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
sys.stderr.write(_("Exploitability analysis came up empty\n"))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
AbrtExploitable()
|