manticore/tests/auto/make_dump.py
JP Smith ca0bee2377 Rename test -> tests (#66)
* rename test -> tests

* re-add ignored tests
2017-03-13 14:06:36 -05:00

422 lines
15 KiB
Python

import copy
import traceback
import os
import sys
import time
import subprocess
from capstone import *
from capstone.x86 import *
from flags import flags
flags_maks={
'CF': 0x00001,
'PF': 0x00004,
'AF': 0x00010,
'ZF': 0x00040,
'SF': 0x00080,
'DF': 0x00400,
'OF': 0x00800,
'IF': 0x00200,
}
count = 0
#log = file('gdb.log','a')
class Gdb(subprocess.Popen):
def __init__(self, prg, prompt='(gdb) '):
"""Construct interactive Popen."""
self.prompt = prompt
subprocess.Popen.__init__(self, ['gdb', prg], stdin=subprocess.PIPE, stdout=subprocess.PIPE , stderr=subprocess.STDOUT)
def correspond(self, text):
"""Communicate with the child process without closing stdin."""
self.stdin.write(text)
self.stdin.flush()
str_buffer = ''
while not str_buffer.endswith(self.prompt):
str_buffer += self.stdout.read(1)
#log.write(">%s\n"%text)
#log.write("<%s\n"%str_buffer)
return str_buffer
def getR(self, reg):
reg = "$"+reg
if "XMM" in reg:
reg = reg+".uint128"
val = self.correspond('p %s\n'%reg.lower()).split("=")[-1].split("\n")[0]
if "0x" in val:
return int(val.split("0x")[-1],16)
else:
return int(val)
if "FLAG" in reg:
reg = "(unsigned) "+reg
if reg in ['$R%dB'%i for i in range(16)] :
reg = reg[:-1] + "&0xff"
if reg in ['$R%dW'%i for i in range(16)] :
reg = reg[:-1] + "&0xffff"
val = self.correspond('p /x %s\n'%reg.lower())
val = val.split("0x")[-1]
return long(val.split("\n")[0],16)
def setR(reg, value):
self.correspond('set $%s = %s\n'%(reg.lower(), int(value)))
def setByte(self, m, value):
self.correspond('set *(char*)(%s) = %s\n'%(m,value))
def stepi(self):
#print self.correspond("x/i $pc\n")
self.correspond("stepi\n")
def getM(self, m):
try:
return long(self.correspond('x/xg %s\n'%m).split("\t")[-1].split("0x")[-1].split("\n")[0],16)
except Exception,e:
raise e
return 0
def getPid(self):
return int(self.correspond('info proc\n').split("\n")[0].split(" ")[-1])
def getStack(self):
maps = file("/proc/%s/maps"%self.correspond('info proc\n').split("\n")[0].split(" ")[-1]).read().split("\n")
i,o = [ int(x,16) for x in maps[-3].split(" ")[0].split('-')]
def getByte(self, m):
arch = self.get_arch()
mask = {'i386': 0xffffffff, 'amd64': 0xffffffffffffffff}[arch]
return int(self.correspond("x/1bx %d\n"%(m&mask)).split("\t")[-1].split("\n")[0][2:],16)
def get_entry(self):
a=self.correspond('info target\n')
return int(a[a.find("Entry point:"):].split('\n')[0].split(' ')[-1][2:],16)
_arch = None
def get_arch(self):
if self._arch is not None:
return self._arch
infotarget = self.correspond('info target\n')
if 'elf32-i386' in infotarget:
self._arch = 'i386'
return 'i386'
elif 'elf64-x86-64' in infotarget:
self._arch = 'amd64'
return 'amd64'
else:
print infotarget
raise NotImplementedError()
gdb = Gdb(sys.argv[1])
gdb.correspond('')
#guess arch
arch = gdb.get_arch()
#guess architecture from file
entry = gdb.get_entry()
gdb.correspond("b *0\n")
gdb.correspond("run arg1 arg2 < /dev/urandom > /dev/null\n")
#gdb.correspond("run arg1 arg2 arg3 < input > /dev/null\n")
gdb.correspond("d 1\n")
'''
# Simulate no vdso (As when analized with symbemu)
found = 0
for i in range(75,120):
if gdb.getM('$sp+sizeof(void*)*%d'%i) ==0x19 and gdb.getM('$sp+%d'%(i+2))==0x1f:
found = i
if found !=0:
gdb.setByte('$sp+sizeof(void*)*%d'%found,1)
gdb.setByte('$sp+sizeof(void*)*%d'%(found+2),1)
vdso = gdb.getM('$sp+sizeof(void*)*%d'%(found+1))
for i in range(75,120):
val = gdb.getM('$sp+sizeof(void*)*%d'%i)
if val > vdso-0x10000 and val <= vdso+0x10000:
if (gdb.getM('$sp+sizeof(void*)*%d'%(i-1))) != 1:
gdb.setByte('$sp+sizeof(void*)*%d'%(i-1),1)
'''
def read_operand(o):
if o.type == X86_OP_IMM:
return o.value
elif o.type == X86_OP_REG:
reg_name = str(instruction.reg_name(o.reg).upper())
return gdb.getR(reg_name)
raise NotImplemented
STACK_INSTRUCTIONS = ['BOUND', 'CALL', 'CALLF', 'ENTER', 'INT', 'INT1', 'INTO', 'IRET', 'IRETD', 'LEAVE', 'POP', 'POPA', 'POPAD', 'POPF', 'POPFD', 'PUSH', 'PUSHA', 'PUSHAD', 'PUSHF', 'PUSHFD', 'RETF', 'RETN', 'RET']
while True:
try:
stepped = False
pc = gdb.getR({'i386': 'EIP', 'amd64': 'RIP'}[arch])
SP = {'i386': 'ESP', 'amd64': 'RSP'}[arch]
BP = {'i386': 'EBP', 'amd64': 'RBP'}[arch]
DI = {'i386': 'EDI', 'amd64': 'RDI'}[arch]
SI = {'i386': 'ESI', 'amd64': 'RSI'}[arch]
A = {'i386': 'EAX', 'amd64': 'RAX'}[arch]
D = {'i386': 'EDX', 'amd64': 'RDX'}[arch]
COUNTER = {'i386': 'ECX', 'amd64': 'RCX'}[arch]
wordsize = {'i386': 4, 'amd64': 8}[arch]
text = ''.join([chr(gdb.getByte(pc+i)) for i in range(16)])
cap_arch = {'i386': CS_ARCH_X86, 'amd64': CS_ARCH_X86}[arch]
cap_mode = {'i386': CS_MODE_32, 'amd64': CS_MODE_64}[arch]
md = Cs(cap_arch, cap_mode)
md.detail = True
md.syntax = 0
instruction = next(md.disasm(text, pc))
if instruction.insn_name().upper() in ['CPUID', 'RDTSC', 'NOP', 'SYSCALL', 'INT', 'SYSENTER']:
print "#Skiping:, ", instruction.insn_name().upper()
stepped=True
gdb.stepi()
continue
#print instruction
disassembly = "0x%x:\t%s\t%s" %(instruction.address, instruction.mnemonic, instruction.op_str)
print "#INSTRUCTION:", disassembly
groups = map(instruction.group_name, instruction.groups)
PC = {'i386': 'EIP', 'amd64': 'RIP'}[arch]
registers = {PC: gdb.getR(PC)}
memory = {}
#save the encoded instruction
for i in range(instruction.size):
memory[pc+i] = text[i]
if instruction.insn_name().upper() in ['MUL', 'IMUL']:
registers[A] = gdb.getR(A)
registers[D] = gdb.getR(D)
if instruction.insn_name().upper() in ['AAA', 'AAS', 'AAD', 'AAM']:
registers['AH'] = gdb.getR('AH')
registers['AL'] = gdb.getR('AL')
if instruction.insn_name().upper() in ['PUSHF', 'PUSHFD']:
registers['EFLAGS'] = gdb.getR('EFLAGS')
if instruction.insn_name().upper() in ['XLAT', 'XLATB']:
registers['AL'] = gdb.getR('AL')
registers[B] = gdb.getR(B)
address = registers[B]+registers['AL']
memory[address] = chr(gdb.getByte(address))
if instruction.insn_name().upper() in ['BTC', 'BTR', 'BTS', 'BT']:
if instruction.operands[0].type == X86_OP_MEM:
o = instruction.operands[0]
address = 0
address += o.mem.disp
if o.mem.base != 0:
base = str(instruction.reg_name(o.mem.base).upper())
registers[base] = gdb.getR(base)
address += registers[base]
if base == 'RIP':
address+=instruction.size
if o.mem.index != 0:
reg_name = str(instruction.reg_name(o.mem.index).upper())
registers[reg_name] = gdb.getR(reg_name)
address += o.mem.scale*registers[reg_name]
address = address&({'i386': 0xffffffff, 'amd64': 0xffffffffffffffff}[arch])
if instruction.operands[1].type == X86_OP_IMM:
address += instruction.operands.value
elif instruction.operands[1].type == X86_OP_REG:
reg_name = str(instruction.reg_name(o.reg).upper())
address + gdb.getR(reg_name)/8
memory[address] = chr(gdb.getByte(address))
if instruction.insn_name().upper() in STACK_INSTRUCTIONS:
registers[SP] = gdb.getR(SP)
registers[BP] = gdb.getR(BP)
#save a bunch of stack
pointer = registers[SP]
for i in range(-wordsize, wordsize+1):
memory[pointer+i] = chr(gdb.getByte(pointer+i))
if instruction.insn_name().upper() in ['ENTER', 'LEAVE']:
pointer = registers[BP]
for i in range(-wordsize, wordsize+1):
memory[pointer+i] = chr(gdb.getByte(pointer+i))
if instruction.mnemonic.startswith('rep'):
registers[DI] = gdb.getR(DI)
registers[SI] = gdb.getR(SI)
registers[COUNTER] = gdb.getR(COUNTER)
pointer = registers[DI]
for i in range(wordsize):
memory[pointer+i] = chr(gdb.getByte(pointer+i))
pointer = registers[SI]
for i in range(wordsize):
memory[pointer+i] = chr(gdb.getByte(pointer+i))
#implicit registers
#if not instruction.mnemonic.startswith('rep'):
# for ri in range(0xff):
# if instruction.reg_read(ri) or instruction.reg_write(ri):
# reg_name = str(instruction.reg_name(ri).upper())
# if 'FLAGS' in reg_name :
# continue
# registers[reg_name] = gdb.getR(reg_name)
reg_sizes = {
X86_REG_AH: X86_REG_AX,
X86_REG_AL: X86_REG_AX,
X86_REG_AX: X86_REG_EAX,
X86_REG_EAX: X86_REG_RAX,
X86_REG_RAX: X86_REG_INVALID,
X86_REG_BH: X86_REG_BX,
X86_REG_BL: X86_REG_BX,
X86_REG_BX: X86_REG_EBX,
X86_REG_EBX: X86_REG_RBX,
X86_REG_RBX: X86_REG_INVALID,
X86_REG_CH: X86_REG_CX,
X86_REG_CL: X86_REG_CX,
X86_REG_CX: X86_REG_ECX,
X86_REG_ECX: X86_REG_RCX,
X86_REG_RCX: X86_REG_INVALID,
X86_REG_DH: X86_REG_DX,
X86_REG_DL: X86_REG_DX,
X86_REG_DX: X86_REG_EDX,
X86_REG_EDX: X86_REG_RDX,
X86_REG_RDX: X86_REG_INVALID,
X86_REG_DIL: X86_REG_EDI,
X86_REG_DI: X86_REG_EDI,
X86_REG_EDI: X86_REG_RDI,
X86_REG_RDI: X86_REG_INVALID,
X86_REG_SIL: X86_REG_ESI,
X86_REG_SI: X86_REG_ESI,
X86_REG_ESI: X86_REG_RSI,
X86_REG_RSI: X86_REG_INVALID,
}
#There is a capstone branch that should fix all this annoyances .. soon
#https://github.com/aquynh/capstone/tree/next
used = set()
for ri in reg_sizes.keys():
if instruction.reg_read(ri) or instruction.reg_write(ri):
if not(instruction.reg_read(reg_sizes[ri]) or instruction.reg_write(reg_sizes[ri])):
if str(instruction.reg_name(reg_sizes[ri]).upper()) not in registers.keys():
used.add(ri)
for ri in used:
reg_name = str(instruction.reg_name(ri).upper())
registers[reg_name] = gdb.getR(reg_name)
#special case for flags...
if instruction.mnemonic.upper() in flags.keys():
EFLAGS = gdb.getR('EFLAGS')
for fl in flags[instruction.mnemonic.upper()]['tested']:
registers[fl] = (EFLAGS&flags_maks[fl]) != 0
for fl in flags[instruction.mnemonic.upper()]['defined']:
registers[fl] = (EFLAGS&flags_maks[fl]) != 0
if 'regs' in flags[instruction.mnemonic.upper()]:
for rg in flags[instruction.mnemonic.upper()]['regs']:
registers[rg] = gdb.getR(rg)
#operands
for o in instruction.operands:
if o.type == X86_OP_IMM:
pass #ignore, already encoded in instruction
elif o.type == X86_OP_REG:
reg_name = str(instruction.reg_name(o.reg).upper())
registers[reg_name] = gdb.getR(reg_name)
elif o.type == X86_OP_MEM:
#save register involved and memory values
address = 0
address += o.mem.disp
if o.mem.base != 0:
base = str(instruction.reg_name(o.mem.base).upper())
registers[base] = gdb.getR(base)
address += registers[base]
if base == 'RIP':
address+=instruction.size
if o.mem.index != 0:
reg_name = str(instruction.reg_name(o.mem.index).upper())
registers[reg_name] = gdb.getR(reg_name)
address += o.mem.scale*registers[reg_name]
address = address&({'i386': 0xffffffff, 'amd64': 0xffffffffffffffff}[arch])
for i in xrange(address, address+o.size):
memory[i] = chr(gdb.getByte(i))
# gather PRE info
test = {'mnemonic': instruction.insn_name().upper(), 'disassembly': disassembly, 'groups': groups, 'text':text[:instruction.size], 'arch':arch}
test['pre'] = {}
test['pre']['memory'] = memory
test['pre']['registers'] = registers
# STEP !
gdb.stepi()
stepped = True
# gather POS info
registers = dict(registers)
memory = dict(memory)
#special case for flags...
if instruction.mnemonic.upper() in flags:
for fl in flags[instruction.mnemonic.upper()]['tested']:
del registers[fl]
for fl in flags[instruction.mnemonic.upper()]['defined']:
del registers[fl]
#update registers
for i in registers.keys():
registers[i] = gdb.getR(i)
#special case for flags...
EFLAGS = gdb.getR('EFLAGS')
if instruction.mnemonic.upper() in flags:
for fl in flags[instruction.mnemonic.upper()]['defined']:
if 'OF' in registers and instruction.insn_name().upper() in ['ROL','RCL','ROR','RCR']:
print instruction.insn_name().upper(), read_operand(instruction.operands[1])
del registers['OF']
continue
registers[fl] = (EFLAGS&flags_maks[fl]) != 0
#update memory
for i in memory.keys():
memory[i] = chr(gdb.getByte(i))
test['pos'] = {}
test['pos']['memory'] = memory
test['pos']['registers'] = registers
if not 'int' in groups:
print test
count += 1
#check if exit
if instruction.insn_name().upper() in ['SYSCALL', 'INT', 'SYSENTER']:
if "The program has no registers now." in gdb.correspond("info registers \n"):
print "done"
break
except Exception,e:
if "The program has no registers now." in gdb.correspond("info registers\n"):
break
#print '-'*60
#traceback.print_exc(file=sys.stdout)
#print '-'*60
#import pdb
#pdb.set_trace()
print "# Exception", e
if not stepped:
gdb.stepi()
print "# Processed %d instructions." % count