212 lines
7.7 KiB
Python
212 lines
7.7 KiB
Python
## -*- coding: utf-8 -*-
|
|
##
|
|
## Jonathan Salwan - 2014-05-12 - ROPgadget tool
|
|
##
|
|
## http://twitter.com/JonathanSalwan
|
|
## http://shell-storm.org/project/ROPgadget/
|
|
##
|
|
|
|
from capstone import *
|
|
from ctypes import *
|
|
|
|
class MACH_HEADER(Structure):
|
|
_fields_ = [
|
|
("magic", c_uint),
|
|
("cputype", c_uint),
|
|
("cpusubtype", c_uint),
|
|
("filetype", c_uint),
|
|
("ncmds", c_uint),
|
|
("sizeofcmds", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class LOAD_COMMAND(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint)
|
|
]
|
|
|
|
class SEGMENT_COMMAND(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint),
|
|
("segname", c_ubyte * 16),
|
|
("vmaddr", c_uint),
|
|
("vmsize", c_uint),
|
|
("fileoff", c_uint),
|
|
("filesize", c_uint),
|
|
("maxprot", c_uint),
|
|
("initprot", c_uint),
|
|
("nsects", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class SEGMENT_COMMAND64(Structure):
|
|
_fields_ = [
|
|
("cmd", c_uint),
|
|
("cmdsize", c_uint),
|
|
("segname", c_ubyte * 16),
|
|
("vmaddr", c_ulonglong),
|
|
("vmsize", c_ulonglong),
|
|
("fileoff", c_ulonglong),
|
|
("filesize", c_ulonglong),
|
|
("maxprot", c_uint),
|
|
("initprot", c_uint),
|
|
("nsects", c_uint),
|
|
("flags", c_uint)
|
|
]
|
|
|
|
class SECTION(Structure):
|
|
_fields_ = [
|
|
("sectname", c_ubyte * 16),
|
|
("segname", c_ubyte * 16),
|
|
("addr", c_uint),
|
|
("size", c_uint),
|
|
("offset", c_uint),
|
|
("align", c_uint),
|
|
("reloff", c_uint),
|
|
("nreloc", c_uint),
|
|
("flags", c_uint),
|
|
("reserved1", c_uint),
|
|
("reserved2", c_uint)
|
|
]
|
|
|
|
class SECTION64(Structure):
|
|
_fields_ = [
|
|
("sectname", c_ubyte * 16),
|
|
("segname", c_ubyte * 16),
|
|
("addr", c_ulonglong),
|
|
("size", c_ulonglong),
|
|
("offset", c_uint),
|
|
("align", c_uint),
|
|
("reloff", c_uint),
|
|
("nreloc", c_uint),
|
|
("flags", c_uint),
|
|
("reserved1", c_uint),
|
|
("reserved2", c_uint)
|
|
]
|
|
|
|
class MACHOFlags(object):
|
|
CPU_TYPE_I386 = 0x7
|
|
CPU_TYPE_X86_64 = (CPU_TYPE_I386 | 0x1000000)
|
|
CPU_TYPE_MIPS = 0x8
|
|
CPU_TYPE_ARM = 12
|
|
CPU_TYPE_ARM64 = (CPU_TYPE_ARM | 0x1000000)
|
|
CPU_TYPE_SPARC = 14
|
|
CPU_TYPE_POWERPC = 18
|
|
CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | 0x1000000)
|
|
LC_SEGMENT = 0x1
|
|
LC_SEGMENT_64 = 0x19
|
|
S_ATTR_SOME_INSTRUCTIONS = 0x00000400
|
|
S_ATTR_PURE_INSTRUCTIONS = 0x80000000
|
|
|
|
""" This class parses the Mach-O """
|
|
class MACHO(object):
|
|
def __init__(self, binary):
|
|
self.__binary = bytearray(binary)
|
|
|
|
self.__machHeader = None
|
|
self.__rawLoadCmd = None
|
|
self.__sections_l = []
|
|
|
|
self.__setHeader()
|
|
self.__setLoadCmd()
|
|
|
|
def __setHeader(self):
|
|
self.__machHeader = MACH_HEADER.from_buffer_copy(self.__binary)
|
|
|
|
if self.getArchMode() == CS_MODE_32:
|
|
self.__rawLoadCmd = self.__binary[28:28+self.__machHeader.sizeofcmds]
|
|
|
|
elif self.getArchMode() == CS_MODE_64:
|
|
self.__rawLoadCmd = self.__binary[32:32+self.__machHeader.sizeofcmds]
|
|
|
|
def __setLoadCmd(self):
|
|
base = self.__rawLoadCmd
|
|
for i in range(self.__machHeader.ncmds):
|
|
command = LOAD_COMMAND.from_buffer_copy(base)
|
|
|
|
if command.cmd == MACHOFlags.LC_SEGMENT:
|
|
segment = SEGMENT_COMMAND.from_buffer_copy(base)
|
|
self.__setSections(segment, base[56:], 32)
|
|
|
|
elif command.cmd == MACHOFlags.LC_SEGMENT_64:
|
|
segment = SEGMENT_COMMAND64.from_buffer_copy(base)
|
|
self.__setSections(segment, base[72:], 64)
|
|
|
|
base = base[command.cmdsize:]
|
|
|
|
def __setSections(self, segment, base, sizeHeader):
|
|
for i in range(segment.nsects):
|
|
if sizeHeader == 32:
|
|
section = SECTION.from_buffer_copy(base)
|
|
section.offset = segment.fileoff + section.addr - segment.vmaddr
|
|
base = base[68:]
|
|
self.__sections_l += [section]
|
|
elif sizeHeader == 64:
|
|
section = SECTION64.from_buffer_copy(base)
|
|
section.offset = segment.fileoff + section.addr - segment.vmaddr
|
|
base = base[80:]
|
|
self.__sections_l += [section]
|
|
|
|
def getEntryPoint(self):
|
|
for section in self.__sections_l:
|
|
if section.sectname[0:6] == "__text":
|
|
return section.addr
|
|
|
|
def getExecSections(self):
|
|
ret = []
|
|
for section in self.__sections_l:
|
|
if section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS or section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
|
|
ret += [{
|
|
"name" : section.sectname,
|
|
"offset" : section.offset,
|
|
"size" : section.size,
|
|
"vaddr" : section.addr,
|
|
"opcodes" : bytes(self.__binary[section.offset:section.offset+section.size])
|
|
}]
|
|
return ret
|
|
|
|
def getDataSections(self):
|
|
ret = []
|
|
for section in self.__sections_l:
|
|
if not section.flags & MACHOFlags.S_ATTR_SOME_INSTRUCTIONS and not section.flags & MACHOFlags.S_ATTR_PURE_INSTRUCTIONS:
|
|
ret += [{
|
|
"name" : section.sectname,
|
|
"offset" : section.offset,
|
|
"size" : section.size,
|
|
"vaddr" : section.addr,
|
|
"opcodes" : bytes(self.__binary[section.offset:section.offset+section.size])
|
|
}]
|
|
return ret
|
|
|
|
def getArch(self):
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_I386 or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_X86_64:
|
|
return CS_ARCH_X86
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM:
|
|
return CS_ARCH_ARM
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_ARM64:
|
|
return CS_ARCH_ARM64
|
|
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_MIPS:
|
|
return CS_ARCH_MIPS
|
|
else:
|
|
print("[Error] MACHO.getArch() - Architecture not supported")
|
|
return None
|
|
|
|
def getArchMode(self):
|
|
if self.__machHeader.magic == 0xfeedface:
|
|
return CS_MODE_32
|
|
elif self.__machHeader.magic == 0xfeedfacf:
|
|
return CS_MODE_64
|
|
else:
|
|
print("[Error] MACHO.getArchMode() - Bad Arch size")
|
|
return None
|
|
pass
|
|
|
|
def getEndian(self):
|
|
# TODO: Support other endianness
|
|
return 0
|
|
|
|
def getFormat(self):
|
|
return "Mach-O"
|