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

330 lines
12 KiB

## -*- coding: utf-8 -*-
##
## Jonathan Salwan - 2014-05-12 - ROPgadget tool
##
## http://twitter.com/JonathanSalwan
## http://shell-storm.org/project/ROPgadget/
##
from ctypes import *
from capstone import *
class MACH_HEADER_LE(LittleEndianStructure):
_fields_ = [
("magic", c_uint),
("cputype", c_uint),
("cpusubtype", c_uint),
("filetype", c_uint),
("ncmds", c_uint),
("sizeofcmds", c_uint),
("flags", c_uint),
]
class MACH_HEADER_BE(BigEndianStructure):
_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_LE(LittleEndianStructure):
_fields_ = [
("cmd", c_uint),
("cmdsize", c_uint),
]
class LOAD_COMMAND_BE(BigEndianStructure):
_fields_ = [
("cmd", c_uint),
("cmdsize", c_uint),
]
class SEGMENT_COMMAND_LE(LittleEndianStructure):
_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_COMMAND_BE(BigEndianStructure):
_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_LE(LittleEndianStructure):
_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 SEGMENT_COMMAND64_BE(BigEndianStructure):
_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_LE(LittleEndianStructure):
_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 SECTION_BE(BigEndianStructure):
_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_LE(LittleEndianStructure):
_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 SECTION64_BE(BigEndianStructure):
_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
class MACHO(object):
"""This class parses the Mach-O."""
def __init__(self, binary):
self.__binary = bytearray(binary)
self.__machHeader = None
self.__endianness = None
self.__rawLoadCmd = None
self.__sections_l = []
self.__setEndianness()
self.__setHeader()
self.__setLoadCmd()
def __setEndianness(self):
magic = self.__binary[0] << 24 | \
self.__binary[1] << 16 | \
self.__binary[2] << 8 | \
self.__binary[3]
if magic == 0xfeedface or magic == 0xfeedfacf:
self.__endianness = CS_MODE_BIG_ENDIAN
else:
self.__endianness = 0
def __setHeader(self):
if self.__endianness == CS_MODE_BIG_ENDIAN:
self.__machHeader = MACH_HEADER_BE.from_buffer_copy(self.__binary)
else:
self.__machHeader = MACH_HEADER_LE.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 _ in range(self.__machHeader.ncmds):
if self.__endianness == CS_MODE_BIG_ENDIAN:
command = LOAD_COMMAND_BE.from_buffer_copy(base)
else:
command = LOAD_COMMAND_LE.from_buffer_copy(base)
if command.cmd == MACHOFlags.LC_SEGMENT:
if self.__endianness == CS_MODE_BIG_ENDIAN:
segment = SEGMENT_COMMAND_BE.from_buffer_copy(base)
else:
segment = SEGMENT_COMMAND_LE.from_buffer_copy(base)
self.__setSections(segment, base[56:], 32)
elif command.cmd == MACHOFlags.LC_SEGMENT_64:
if self.__endianness == CS_MODE_BIG_ENDIAN:
segment = SEGMENT_COMMAND64_BE.from_buffer_copy(base)
else:
segment = SEGMENT_COMMAND64_LE.from_buffer_copy(base)
self.__setSections(segment, base[72:], 64)
base = base[command.cmdsize:]
def __setSections(self, segment, base, sizeHeader):
for _ in range(segment.nsects):
if sizeHeader == 32:
if self.__endianness == CS_MODE_BIG_ENDIAN:
section = SECTION_BE.from_buffer_copy(base)
else:
section = SECTION_LE.from_buffer_copy(base)
section.offset = segment.fileoff + section.addr - segment.vmaddr
base = base[68:]
self.__sections_l += [section]
elif sizeHeader == 64:
if self.__endianness == CS_MODE_BIG_ENDIAN:
section = SECTION64_BE.from_buffer_copy(base)
else:
section = SECTION64_LE.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
if self.__machHeader.cputype == MACHOFlags.CPU_TYPE_POWERPC or self.__machHeader.cputype == MACHOFlags.CPU_TYPE_POWERPC64:
return CS_ARCH_PPC
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
print("[Error] MACHO.getArchMode() - Bad Arch size")
return None
def getEndian(self):
if self.__endianness is None:
print("[Error] MACHO.getEndian() - Unable to determine endianness")
return self.__endianness
def getFormat(self):
return "Mach-O"