def __init__(self, mask, virtual_id=None, physical_id=None): super(Register, self).__init__() from peachpy.util import is_int assert is_int(mask), "Mask must be an integer" assert mask in Register._mask_size_map, "Unknown mask value: %X" % mask self.mask = int(mask) assert virtual_id is not None or physical_id is not None, "Virtual or physical ID must be specified" assert virtual_id is None or is_int(virtual_id) and virtual_id > 0, "Virtual ID must be a positive integer" assert ( physical_id is None or is_int(physical_id) and physical_id >= 0 ), "Physical ID must be a non-negative integer" self.virtual_id = None if virtual_id is None else int(virtual_id) self.physical_id = None if physical_id is None else int(physical_id)
def format(self, assembly_format, indent): from peachpy.x86_64.operand import format_operand text = "\t" * self._indent_level if indent else "" if assembly_format == "peachpy": return text + str(self) elif assembly_format == "nasm": return text + str(self) elif assembly_format == "gas": return text + str(self) elif assembly_format == "go": if self.go_name: from peachpy.util import is_int if self.name == "CMP" and is_int(self.operands[1]): # CMP instruction with an immediate operand has operands in normal (non-reversed) order return ( text + str(self.go_name) + " " + ", ".join(format_operand(op, assembly_format) for op in self.operands) ) elif self.operands: return ( text + str(self.go_name) + " " + ", ".join(format_operand(op, assembly_format) for op in reversed(self.operands)) ) else: return text + str(self.go_name) else: return text + "; ".join(map(lambda b: "BYTE $0x%02X" % b, self.encode())) + " // " + str(self)
def __init__(self, base=None, index=None, scale=None, displacement=0): from peachpy.x86_64.registers import GeneralPurposeRegister64, GeneralPurposeRegister32 from peachpy.util import is_int, is_sint32 # Check individual arguments if base is not None and not isinstance(base, (GeneralPurposeRegister32, GeneralPurposeRegister64)): raise TypeError("Base register must be a 32- or 64-bit general-purpose register") if index is not None and not isinstance(index, (GeneralPurposeRegister32, GeneralPurposeRegister64)): raise TypeError("Index register must be a 32- or 64-bit general-purpose register") if scale is not None and not is_int(scale): raise TypeError("Scale must be an integer") if scale is not None and int(scale) not in {1, 2, 4, 8}: raise TypeError("Scale must be 1, 2, 4, or 8") if not is_sint32(displacement): raise ValueError("Displacement value (%s) is not representable as a signed 32-bit integer" % str(displacement)) # Check relations of arguments if scale is not None and index is None or scale is None and index is not None: raise ValueError("Either both of neither of scale and index must be defined") if base is not None and index is not None and base.size != index.size: raise TypeError("Base (%s) and index (%s) registers have different size" % (str(base), str(index))) if index is None and base is None: raise ValueError("Either base or index * scale must be specified") self.base = base self.index = index self.scale = None if scale is None else int(scale) self.displacement = int(displacement)
def __add__(self, addend): from peachpy.x86_64.registers import GeneralPurposeRegister64, GeneralPurposeRegister32 from peachpy.util import is_int, is_sint32 if is_int(addend): if not is_sint32(addend): raise ValueError("The addend value (%d) is not representable as a signed 32-bit integer" % addend) return MemoryAddress(self.base, self.index, self.scale, self.displacement + addend) elif isinstance(addend, (GeneralPurposeRegister64, GeneralPurposeRegister32)): if self.base is not None: raise TypeError("Can not add a general-purpose register to a memory operand with existing base") if self.index.size != addend.size: raise TypeError("Index (%s) and addend (%s) registers have different size" % (str(self.index), str(addend))) return MemoryAddress(addend, self.index, self.scale, self.displacement) elif isinstance(addend, MemoryAddress): if self.base is not None and addend.base is not None: raise ValueError("Can not add memory address: both address expressions use base registers") if self.index is not None and addend.index is not None: raise ValueError("Can not add memory address: both address expressions use index registers") sum_base = self.base if self.base is not None else addend.base (sum_index, sum_scale) = (self.index, self.scale) \ if self.index is not None else (addend.index, addend.scale) return MemoryAddress(sum_base, sum_index, sum_scale, self.displacement + addend.displacement) else: raise TypeError("Can not add %s: unsupported addend type" % str(addend))
def __init__(self, base=None, index=None, scale=None, displacement=0): from peachpy.x86_64.registers import GeneralPurposeRegister64, \ XMMRegister, YMMRegister, ZMMRegister, MaskedRegister from peachpy.util import is_int, is_sint32 # Check individual arguments if base is not None and not isinstance(base, GeneralPurposeRegister64): raise TypeError("Base register must be a 64-bit general-purpose register") if index is not None and \ not isinstance(index, (GeneralPurposeRegister64, XMMRegister, YMMRegister, ZMMRegister)) and not \ (isinstance(index, MaskedRegister) and isinstance(index.register, (XMMRegister, YMMRegister, ZMMRegister)) and not index.mask.is_zeroing): raise TypeError("Index register must be a 64-bit general-purpose register or an XMM/YMM/ZMM register") if scale is not None and not is_int(scale): raise TypeError("Scale must be an integer") if scale is not None and int(scale) not in {1, 2, 4, 8}: raise TypeError("Scale must be 1, 2, 4, or 8") if not is_sint32(displacement): raise ValueError("Displacement value (%s) is not representable as a signed 32-bit integer" % str(displacement)) # Check relations of arguments if scale is not None and index is None or scale is None and index is not None: raise ValueError("Either both of neither of scale and index must be defined") if index is None and base is None: raise ValueError("Either base or index * scale must be specified") self.base = base self.index = index self.scale = None if scale is None else int(scale) self.displacement = int(displacement)
def check_operand(operand): """Validates operand object as an instruction operand and converts it to a standard form""" from peachpy.x86_64.registers import Register, MaskedRegister from peachpy.x86_64.pseudo import Label from peachpy.x86_64.function import LocalVariable from peachpy.literal import Constant from peachpy import Argument from peachpy.util import is_int, is_int64 if isinstance(operand, (Register, MaskedRegister, Constant, MemoryOperand, LocalVariable, Argument, RIPRelativeOffset, Label)): return operand elif is_int(operand): if not is_int64(operand): raise ValueError("The immediate operand %d is not representable as a 64-bit value") return operand elif isinstance(operand, list): if len(operand) != 1: raise ValueError("Memory operands must be represented by a list with only one element") return MemoryOperand(operand[0]) elif isinstance(operand, set): if len(operand) != 1: raise ValueError("Rounding control & suppress-all-errors operands must be represented by a set " "with only one element") return next(iter(operand)) else: raise TypeError("Unsupported operand: %s" % str(operand))
def __init__(self, address, size=None, mask=None, broadcast=None): from peachpy.x86_64.registers import GeneralPurposeRegister64, \ XMMRegister, YMMRegister, ZMMRegister, MaskedRegister assert isinstance(address, (GeneralPurposeRegister64, XMMRegister, YMMRegister, ZMMRegister, MemoryAddress)) or\ isinstance(address, MaskedRegister) and \ isinstance(address.register, (XMMRegister, YMMRegister, ZMMRegister)) and \ not address.mask.is_zeroing, \ "Only MemoryAddress, 64-bit general-purpose registers, XMM/YMM/ZMM registers, " \ "and merge-masked XMM/YMM/ZMM registers may be specified as an address" from peachpy.util import is_int assert size is None or is_int(size) and int(size) in SizeSpecification._size_name_map, \ "Unsupported size: %d" % size if isinstance(address, MemoryAddress): if isinstance(address.index, MaskedRegister): self.address = MemoryAddress(address.base, address.index.register, address.scale, address.displacement) assert mask is None, "Mask argument can't be used when address index is a masked XMM/YMM/ZMM register" mask = address.index.mask else: self.address = address elif isinstance(address, MaskedRegister): self.address = MemoryAddress(index=address.register, scale=1) assert mask is None, "Mask argument can't be used when address is a masked XMM/YMM/ZMM register" mask = address.mask else: # Convert register to memory address expression self.address = MemoryAddress(address) self.size = size self.mask = mask self.broadcast = broadcast
def __mul__(self, scale): from peachpy.x86_64.operand import MemoryAddress from peachpy.util import is_int if not is_int(scale): raise TypeError("Register can be scaled only by an integer number") if int(scale) not in {1, 2, 4, 8}: raise ValueError("Invalid scale value (%d): only scaling by 1, 2, 4, or 8 is supported" % scale) return MemoryAddress(index=self, scale=scale)
def __sub__(self, minuend): from peachpy.util import is_int, is_sint32 if is_int(minuend): if not is_sint32(-minuend): raise ValueError("The addend value (%d) is not representable as a signed 32-bit integer" % minuend) return MemoryAddress(self.base, self.index, self.scale, self.displacement - minuend) else: raise TypeError("Can not add %s: unsupported addend type" % str(minuend))
def _uint32xN(name, n, *args): from peachpy.util import is_int, is_int32 assert is_int(n) args = [arg for arg in args if arg is not None] if len(args) == 0: raise ValueError("At least one constant value must be specified") if len(args) != 1 and len(args) != n: raise ValueError("Either 1 or %d values must be specified" % n) for i, number in enumerate(args): if not is_int(number): raise TypeError("The value %s is not an integer" % str(number)) if not is_int32(number): raise ValueError("The number %d is not a 32-bit integer" % number) if number < 0: args[i] += 0x100000000 if len(args) == 1: args = [args[0]] * n return Constant(4 * n, n, tuple(args), uint32_t, name)
def __init__(self, offset, type, name=None, size=None): from peachpy.util import is_int if not is_int(offset): raise TypeError("Offset %s is not an integer" % str(offset)) if offset < 0: raise ValueError("Offset %d is negative" % offset) if not isinstance(type, SymbolType): raise TypeError("Symbol type %s is not in SymbolType enumeration" % str(type)) if name is not None and not isinstance(name, str): raise TypeError("Name %s is not a string" % str(name)) if size is not None: if not is_int(size): raise TypeError("Size %s is not an integer" % str(size)) if size < 0: raise ValueError("Size %d is negative" % size) self.offset = offset self.type = type self.name = name self.size = size
def __init__(self, offset, type, symbol=None, program_counter=None): from peachpy.util import is_int if not is_int(offset): raise TypeError("Offset %s is not an integer" % str(offset)) if offset < 0: raise ValueError("Offset %d is negative" % offset) if not isinstance(type, RelocationType): raise TypeError("Relocation type %s is not in RelocationType enumeration" % str(type)) if symbol is not None and not isinstance(symbol, Symbol): raise TypeError("Symbol %s is not an instance of Symbol type" % str(symbol)) if program_counter is not None: if not is_int(program_counter): raise TypeError("Program counter %s is not an integer" % str(program_counter)) if program_counter < 0: raise TypeError("Program counter %d is negative" % program_counter) self.offset = offset self.type = type self.symbol = symbol self.program_counter = program_counter
def alignment(self, alignment): from peachpy.util import is_int if not is_int(alignment): raise TypeError("Alignment %s is not an integer" % str(alignment)) if alignment < 0: raise ValueError("Alignment %d is not a positive integer" % alignment) if alignment & (alignment - 1) != 0: raise ValueError("Alignment %d is not a power of 2" % alignment) if alignment not in Section._alignment_flag_map: raise ValueError("Alignment %d exceeds maximum alignment (8192)" % alignment) self.flags = (self.flags & ~Section._alignment_mask) | Section._alignment_flag_map[alignment]
def __init__(self, offset, type, name=None, size=None): from peachpy.util import is_int if not is_int(offset): raise TypeError("Offset %s is not an integer" % str(offset)) if offset < 0: raise ValueError("Offset %d is negative" % offset) if not isinstance(type, SymbolType): raise TypeError("Symbol type %s is not in SymbolType enumeration" % str(type)) from peachpy.name import Name if name is not None and not (isinstance(name, tuple) and all(isinstance(part, Name) for part in name)): raise TypeError("Name %s must be a tuple of Name objects" % str(name)) if size is not None: if not is_int(size): raise TypeError("Size %s is not an integer" % str(size)) if size < 0: raise ValueError("Size %d is negative" % size) self.offset = offset self.type = type self.name = ".".join(map(str, name)) self.size = size
def as_bytearray(self): from peachpy.encoder import Encoder from peachpy.util import is_int if is_int(self.name): entry = Encoder.uint32le(0) + Encoder.uint32le(self.name) else: entry = Encoder.fixed_string(self.name, 8) return entry + \ Encoder.uint32le(self.value) + \ Encoder.uint16le(self.section_index) + \ Encoder.uint16le(self.symbol_type) + \ Encoder.uint8(self.storage_class) + \ Encoder.uint8(self.auxiliary_entries)
def alignment(self, alignment): from peachpy.util import is_int if not is_int(alignment): raise TypeError("Alignment %s is not an integer" % str(alignment)) if alignment < 0: raise ValueError("Alignment %d is not a positive integer" % alignment) if alignment & (alignment - 1) != 0: raise ValueError("Alignment %d is not a power of 2" % alignment) if alignment > Section.max_alignment: raise ValueError("Alignment %d exceeds maximum alignment (%d)" % (alignment, Section.max_alignment)) if alignment == 0: alignment = 1 self._alignment = alignment
def __init__(self, type, offset, symbol): from peachpy.util import is_int, is_uint32 if not isinstance(type, RelocationType): raise TypeError("Relocation type %s is not in RelocationType enumeration" % str(type)) if not is_int(offset): raise TypeError("Offset %s is not an integer" % str(offset)) if not is_uint32(offset): raise ValueError("Offset %d can not be represented as a 32-bit unsigned integer" % offset) if not isinstance(symbol, Symbol): raise TypeError("Symbol %s is not an instance of Symbol type" % str(symbol)) self.type = type self.offset = offset self.symbol = symbol
def alignment(self, alignment): from peachpy.util import is_int if not is_int(alignment): raise TypeError("Alignment %s is not an integer" % str(alignment)) if alignment < 0: raise ValueError("Alignment %d is not a positive integer" % alignment) if alignment & (alignment - 1) != 0: raise ValueError("Alignment %d is not a power of 2" % alignment) if alignment not in Section._alignment_flag_map: raise ValueError("Alignment %d exceeds maximum alignment (8192)" % alignment) self.flags = (self.flags & ~Section._alignment_mask ) | Section._alignment_flag_map[alignment]
def __init__(self, address, size=None): from peachpy.x86_64.registers import GeneralPurposeRegister64, GeneralPurposeRegister32 assert isinstance(address, (GeneralPurposeRegister64, GeneralPurposeRegister32, MemoryAddress)),\ "Only MemoryAddress, and 32- or 64-bit general-purpose registers may be specified as an address" from peachpy.util import is_int assert size is None or is_int(size) and \ int(size) in SizeSpecification._size_name_map, \ "Unsupported size: %d" % size if isinstance(address, MemoryAddress): self.address = address else: # Convert general-purpose register to memory address expression self.address = MemoryAddress(address) self.size = size
def __init__(self, address, size=None, mask=None, broadcast=None): from peachpy.x86_64.registers import GeneralPurposeRegister64, \ XMMRegister, YMMRegister, ZMMRegister, MaskedRegister from peachpy.x86_64.function import LocalVariable from peachpy.literal import Constant assert isinstance(address, (GeneralPurposeRegister64, XMMRegister, YMMRegister, ZMMRegister, MemoryAddress, Constant, LocalVariable, RIPRelativeOffset)) or \ isinstance(address, MaskedRegister) and \ isinstance(address.register, (XMMRegister, YMMRegister, ZMMRegister)) and \ not address.mask.is_zeroing, \ "Only MemoryAddress, 64-bit general-purpose registers, RIP-Relative addresses, XMM/YMM/ZMM registers, " \ "and merge-masked XMM/YMM/ZMM registers may be specified as an address" from peachpy.util import is_int assert size is None or is_int(size) and int(size) in SizeSpecification._size_name_map, \ "Unsupported size: %d" % size self.symbol = None self.size = size self.mask = mask self.broadcast = broadcast if isinstance(address, MemoryAddress): if isinstance(address.index, MaskedRegister): self.address = MemoryAddress(address.base, address.index.register, address.scale, address.displacement) assert mask is None, "Mask argument can't be used when address index is a masked XMM/YMM/ZMM register" self.mask = address.index.mask else: self.address = address elif isinstance(address, MaskedRegister): self.address = MemoryAddress(index=address.register, scale=1) assert mask is None, "Mask argument can't be used when address is a masked XMM/YMM/ZMM register" self.mask = address.mask elif isinstance(address, Constant): self.address = RIPRelativeOffset(0) self.symbol = address self.size = address.size elif isinstance(address, LocalVariable): from peachpy.x86_64.registers import rsp self.address = MemoryAddress(rsp, displacement=address.offset) self.symbol = address self.size = address.size elif isinstance(address, RIPRelativeOffset): self.address = address else: # Convert register to memory address expression self.address = MemoryAddress(address)
def check_operand(operand): """Validates operand object as an instruction operand and converts it to a standard form""" from peachpy.x86_64.registers import Register, MaskedRegister from peachpy.x86_64.pseudo import Label from peachpy.x86_64.function import LocalVariable from peachpy.literal import Constant from peachpy import Argument from peachpy.util import is_int, is_int64 from copy import copy, deepcopy if isinstance(operand, Register): return copy(operand) elif isinstance(operand, (MaskedRegister, MemoryOperand)): return deepcopy(operand) elif isinstance(operand, (Argument, RIPRelativeOffset, Label)): return operand elif is_int(operand): if not is_int64(operand): raise ValueError( "The immediate operand %d is not representable as a 64-bit value" ) return operand elif isinstance(operand, list): if len(operand) != 1: raise ValueError( "Memory operands must be represented by a list with only one element" ) return MemoryOperand(operand[0]) elif isinstance(operand, Constant): from copy import copy, deepcopy operand = copy(operand) import peachpy.common.function if peachpy.common.function.active_function: operand.name = deepcopy( operand.name, peachpy.common.function.active_function._names_memo) return MemoryOperand(operand) elif isinstance(operand, LocalVariable): return MemoryOperand(operand) elif isinstance(operand, set): if len(operand) != 1: raise ValueError( "Rounding control & suppress-all-errors operands must be represented by a set " "with only one element") return next(iter(operand)) else: raise TypeError("Unsupported operand: %s" % str(operand))
def as_bytearray(self): from peachpy.encoder import Encoder from peachpy.util import is_int if is_int(self.name): header = Encoder.fixed_string("/" + str(self.name), 8) else: header = Encoder.fixed_string(self.name, 8) return header + \ Encoder.uint32le(self.memory_size) + \ Encoder.uint32le(self.memory_address) + \ Encoder.uint32le(self.content_size) + \ Encoder.uint32le(self.content_offset) + \ Encoder.uint32le(self.relocations_offset or 0) + \ Encoder.uint32le(self.line_numbers_offset or 0) + \ Encoder.uint16le(self.relocations_count) + \ Encoder.uint16le(self.line_numbers_count) + \ Encoder.uint32le(self.flags)
def format(self, assembly_format, indent=False, line_number=None): from peachpy.x86_64.operand import format_operand text = "\t" * self._indent_level if indent else "" if assembly_format == "peachpy": return text + str(self) elif assembly_format == "nasm": return text + str(self) elif assembly_format == "gas": if self.operands: from peachpy.x86_64.pseudo import Label if line_number is not None and len( self.operands) == 1 and isinstance( self.operands[0], Label ) and self.operands[0].line_number is not None: label = self.operands[0] return "{indent}{mnemonic} {label_line}{direction} # {label_name}".format( indent=text, mnemonic=self.gas_name, label_line=self.operands[0].line_number, label_name=str(self.operands[0]), direction="b" if self.operands[0].line_number < line_number else "f") else: return text + self.gas_name + " " + ", ".join( format_operand(op, assembly_format) for op in reversed(self.operands)) else: return text + self.gas_name elif assembly_format == "go": if self.go_name: from peachpy.util import is_int if self.name == "CMP" and is_int(self.operands[1]): # CMP instruction with an immediate operand has operands in normal (non-reversed) order return text + str(self.go_name) + " " + \ ", ".join(format_operand(op, assembly_format) for op in self.operands) elif self.operands: return text + str(self.go_name) + " " + \ ", ".join(format_operand(op, assembly_format) for op in reversed(self.operands)) else: return text + str(self.go_name) else: return text + "; ".join( map(lambda b: "BYTE $0x%02X" % b, self.encode())) + " // " + str(self)
def __init__(self, type, offset, symbol): from peachpy.util import is_int, is_uint32 if not isinstance(type, RelocationType): raise TypeError( "Relocation type %s is not in RelocationType enumeration" % str(type)) if not is_int(offset): raise TypeError("Offset %s is not an integer" % str(offset)) if not is_uint32(offset): raise ValueError( "Offset %d can not be represented as a 32-bit unsigned integer" % offset) if not isinstance(symbol, Symbol): raise TypeError("Symbol %s is not an instance of Symbol type" % str(symbol)) self.type = type self.offset = offset self.symbol = symbol
def __add__(self, addend): from peachpy.x86_64.registers import GeneralPurposeRegister64, GeneralPurposeRegister32 from peachpy.util import is_int, is_sint32 if is_int(addend): if not is_sint32(addend): raise ValueError( "The addend value (%d) is not representable as a signed 32-bit integer" % addend) return MemoryAddress(self.base, self.index, self.scale, self.displacement + addend) elif isinstance(addend, (GeneralPurposeRegister64, GeneralPurposeRegister32)): if self.base is not None: raise TypeError( "Can not add a general-purpose register to a memory operand with existing base" ) if self.index.size != addend.size: raise TypeError( "Index (%s) and addend (%s) registers have different size" % (str(self.index), str(addend))) return MemoryAddress(addend, self.index, self.scale, self.displacement) elif isinstance(addend, MemoryAddress): if self.base is not None and addend.base is not None: raise ValueError( "Can not add memory address: both address expressions use base registers" ) if self.index is not None and addend.index is not None: raise ValueError( "Can not add memory address: both address expressions use index registers" ) sum_base = self.base if self.base is not None else addend.base (sum_index, sum_scale) = (self.index, self.scale) \ if self.index is not None else (addend.index, addend.scale) return MemoryAddress(sum_base, sum_index, sum_scale, self.displacement + addend.displacement) else: raise TypeError("Can not add %s: unsupported addend type" % str(addend))
def __init__(self, base=None, index=None, scale=None, displacement=0): from peachpy.x86_64.registers import GeneralPurposeRegister64, GeneralPurposeRegister32 from peachpy.util import is_int, is_sint32 # Check individual arguments if base is not None and not isinstance( base, (GeneralPurposeRegister32, GeneralPurposeRegister64)): raise TypeError( "Base register must be a 32- or 64-bit general-purpose register" ) if index is not None and not isinstance( index, (GeneralPurposeRegister32, GeneralPurposeRegister64)): raise TypeError( "Index register must be a 32- or 64-bit general-purpose register" ) if scale is not None and not is_int(scale): raise TypeError("Scale must be an integer") if scale is not None and int(scale) not in {1, 2, 4, 8}: raise TypeError("Scale must be 1, 2, 4, or 8") if not is_sint32(displacement): raise ValueError( "Displacement value (%s) is not representable as a signed 32-bit integer" % str(displacement)) # Check relations of arguments if scale is not None and index is None or scale is None and index is not None: raise ValueError( "Either both of neither of scale and index must be defined") if base is not None and index is not None and base.size != index.size: raise TypeError( "Base (%s) and index (%s) registers have different size" % (str(base), str(index))) if index is None and base is None: raise ValueError("Either base or index * scale must be specified") self.base = base self.index = index self.scale = None if scale is None else int(scale) self.displacement = int(displacement)
def format(self, assembly_format, indent): from peachpy.x86_64.operand import format_operand text = "\t" * self._indent_level if indent else "" if assembly_format == "peachpy": return text + str(self) elif assembly_format == "nasm": return text + str(self) elif assembly_format == "gas": return text + str(self) elif assembly_format == "go": if self.go_name: from peachpy.util import is_int if self.name == "CMP" and is_int(self.operands[1]): # CMP instruction with an immediate operand has operands in normal (non-reversed) order return text + str(self.go_name) + " " + \ ", ".join(format_operand(op, assembly_format) for op in self.operands) elif self.operands: return text + str(self.go_name) + " " + \ ", ".join(format_operand(op, assembly_format) for op in reversed(self.operands)) else: return text + str(self.go_name) else: return text + "; ".join(map(lambda b: "BYTE $0x%02X" % b, self.encode())) + " // " + str(self)
def __init__(self, base=None, index=None, scale=None, displacement=0): from peachpy.x86_64.registers import GeneralPurposeRegister64, \ XMMRegister, YMMRegister, ZMMRegister, MaskedRegister from peachpy.util import is_int, is_sint32 # Check individual arguments if base is not None and not isinstance(base, GeneralPurposeRegister64): raise TypeError( "Base register must be a 64-bit general-purpose register") if index is not None and \ not isinstance(index, (GeneralPurposeRegister64, XMMRegister, YMMRegister, ZMMRegister)) and not \ (isinstance(index, MaskedRegister) and isinstance(index.register, (XMMRegister, YMMRegister, ZMMRegister)) and not index.mask.is_zeroing): raise TypeError( "Index register must be a 64-bit general-purpose register or an XMM/YMM/ZMM register" ) if scale is not None and not is_int(scale): raise TypeError("Scale must be an integer") if scale is not None and int(scale) not in {1, 2, 4, 8}: raise TypeError("Scale must be 1, 2, 4, or 8") if not is_sint32(displacement): raise ValueError( "Displacement value (%s) is not representable as a signed 32-bit integer" % str(displacement)) # Check relations of arguments if scale is not None and index is None or scale is None and index is not None: raise ValueError( "Either both of neither of scale and index must be defined") if index is None and base is None: raise ValueError("Either base or index * scale must be specified") self.base = base self.index = index self.scale = None if scale is None else int(scale) self.displacement = int(displacement)
def __init__(self, code_size, data_size=0): from peachpy.util import is_int if not is_int(code_size): raise TypeError("code size must be an integer") if not is_int(data_size): raise TypeError("data size must be an integer") if code_size <= 0: raise ValueError("code size must be positive") if data_size < 0: raise ValueError("data size must be non-negative") import mmap self.allocation_granularity = max(mmap.ALLOCATIONGRANULARITY, mmap.PAGESIZE) self.code_address = None self.code_size = self.allocation_size(code_size) self.data_address = None self.data_size = self.allocation_size(data_size) self._release_memory = None osname = sys.platform.lower() if osname == "darwin" or osname.startswith("linux"): import ctypes if osname == "darwin": libc = ctypes.cdll.LoadLibrary("libc.dylib") else: libc = ctypes.cdll.LoadLibrary("libc.so.6") # void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) mmap_function = libc.mmap mmap_function.restype = ctypes.c_void_p mmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_size_t] # int munmap(void* addr, size_t len) munmap_function = libc.munmap munmap_function.restype = ctypes.c_int munmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t] def munmap(address, size): munmap_result = munmap_function(address, size) assert munmap_result == 0 self._release_memory = lambda address_size: munmap(address_size[0], address_size[1]) # Allocate code segment code_address = mmap_function(None, self.code_size, mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC, mmap.MAP_ANON | mmap.MAP_PRIVATE, -1, 0) if code_address == -1: raise OSError("Failed to allocate memory for code segment") self.code_address = code_address if self.data_size > 0: # Allocate data segment data_address = mmap_function(None, self.data_size, mmap.PROT_READ | mmap.PROT_WRITE, mmap.MAP_ANON | mmap.MAP_PRIVATE, -1, 0) if data_address == -1: raise OSError("Failed to allocate memory for data segment") self.data_address = data_address elif osname == "win32": import ctypes # From WinNT.h PAGE_READWRITE = 0x04 PAGE_EXECUTE_READWRITE = 0x40 MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 MEM_RELEASE = 0x8000 # LPVOID WINAPI VirtualAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect) VirtualAlloc_function = ctypes.windll.kernel32.VirtualAlloc VirtualAlloc_function.restype = ctypes.c_void_p VirtualAlloc_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong] # BOOL WINAPI VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) VirtualFree_function = ctypes.windll.kernel32.VirtualFree VirtualFree_function.restype = ctypes.c_int VirtualFree_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong] def VirtualFree(address, size): VirtualFree_result = VirtualFree_function(address, size, MEM_RELEASE) assert VirtualFree_result != 0 self._release_memory = lambda address_size: VirtualFree(address_size[0], address_size[1]) # Allocate code segment code_address = VirtualAlloc_function(None, self.code_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) if not code_address: raise OSError("Failed to allocate memory for code segment") self.code_address = code_address if self.data_size > 0: # Allocate data segment data_address = VirtualAlloc_function(None, self.data_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) if not data_address: raise OSError("Failed to allocate memory for data segment") self.data_address = data_address elif osname == "nacl": import dynacl # Allocate code segment self.allocation = dynacl.allocate(self.code_size, self.data_size) self.code_address = self.allocation.code_address self.data_address = self.allocation.data_address self.copy_code = self._nacl_copy_code else: raise ValueError("Unknown host OS: " + osname)
def __init__(self, size): from peachpy.util import is_int assert is_int(size) and int(size) in SizeSpecification._size_name_map, \ "Unsupported size: %d" % size self.size = size
def __init__(self, operand): super(Operand, self).__init__() import copy from peachpy import Constant from peachpy.arm.registers import Register, GeneralPurposeRegister, \ GeneralPurposeRegisterWriteback, ShiftedGeneralPurposeRegister, DRegisterLanes from peachpy.arm.function import LocalVariable from peachpy.arm.pseudo import Label from peachpy.util import is_int if isinstance(operand, GeneralPurposeRegisterWriteback): self.type = Operand.AddressRegisterType self.register = copy.deepcopy(operand.register) elif isinstance(operand, Register): self.type = Operand.RegisterType self.register = copy.deepcopy(operand) elif isinstance(operand, DRegisterLanes): self.type = Operand.RegisterLanesType self.lanes = copy.deepcopy(operand) elif isinstance(operand, ShiftedGeneralPurposeRegister): self.type = Operand.ShiftedRegisterType self.register = copy.deepcopy(operand) elif isinstance(operand, tuple): if all(isinstance(element, Register) for element in operand): if len( set((register.type, register.size) for register in operand)) == 1: self.type = Operand.RegisterListType self.register_list = copy.deepcopy(operand) else: raise TypeError( 'Register in the list {0} have different types'.format( ", ".join(operand))) elif all( isinstance(element, DRegisterLanes) for element in operand): self.type = Operand.RegisterLanesListType self.register_list = copy.deepcopy(operand) else: raise TypeError('Unknown tuple elements {0}'.format(operand)) elif is_int(operand): if -9223372036854775808 <= operand <= 18446744073709551615: self.type = Operand.ImmediateType self.immediate = operand else: raise ValueError( 'The immediate operand {0} is not a 64-bit value'.format( operand)) elif isinstance(operand, list): if len(operand) == 1 and (isinstance( operand[0], GeneralPurposeRegister) or isinstance( operand[0], GeneralPurposeRegisterWriteback)): self.type = Operand.MemoryType self.base = copy.deepcopy(operand[0]) self.offset = None elif len(operand) == 2 and isinstance( operand[0], GeneralPurposeRegister) and (isinstance( operand[1], int) or isinstance( operand[1], ShiftedGeneralPurposeRegister)): self.type = Operand.MemoryType self.base = copy.deepcopy(operand[0]) self.offset = operand[1] else: raise ValueError( 'Memory operand must be a list with only one or two elements' ) elif isinstance(operand, Constant): self.type = Operand.ConstantType self.constant = operand self.size = operand.size * operand.repeats elif isinstance(operand, LocalVariable): self.type = Operand.VariableType self.variable = operand self.size = operand.size * 8 elif isinstance(operand, str): self.type = Operand.LabelType self.label = operand elif isinstance(operand, Label): self.type = Operand.LabelType self.label = operand.name elif operand is None: self.type = Operand.NoneType else: raise TypeError( 'The operand {0} is not a valid assembly instruction operand'. format(operand))
def modrm_sib_disp(reg, rm, force_sib=False, min_disp=0, disp8xN=None): from peachpy.x86_64.operand import MemoryAddress from peachpy.x86_64.registers import rsp, rbp, r12, r13 from peachpy.util import is_int, is_sint8, ilog2 assert is_int(reg) and 0 <= reg <= 7, \ "Constant reg value expected, got " + str(reg) assert isinstance(rm, (MemoryAddress, int)) if disp8xN is None: disp8xN = 1 assert disp8xN in [1, 2, 4, 8, 16, 32, 64] # ModR/M byte # +----------------+---------------+--------------+ # | Bits 6-7: mode | Bits 3-5: reg | Bits 0-2: rm | # +----------------+---------------+--------------+ # # SIB byte # +-----------------+-----------------+----------------+ # | Bits 6-7: scale | Bits 3-5: index | Bits 0-2: base | # +-----------------+-----------------+----------------+ if isinstance(rm, MemoryAddress): # TODO: support global addresses, including rip-relative addresses assert rm.base is not None or rm.index is not None, \ "Global addressing is not yet supported" if not force_sib and rm.index is None and rm.base.lcode != 0b100: # No SIB byte if rm.displacement == 0 and rm.base != rbp and rm.base != r13 and min_disp <= 0: # ModRM.mode = 0 (no displacement) assert rm.base.lcode != 0b100, \ "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" assert rm.base.lcode != 0b101, \ "rbp/r13 is not encodable as a base register (interpreted as disp32 address)" return bytearray([(reg << 3) | rm.base.lcode]) elif (rm.displacement % disp8xN == 0) and is_sint8(rm.displacement // disp8xN) and min_disp <= 1: # ModRM.mode = 1 (8-bit displacement) assert rm.base.lcode != 0b100, \ "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" return bytearray([0x40 | (reg << 3) | rm.base.lcode, (rm.displacement // disp8xN) & 0xFF]) else: # ModRM.mode == 2 (32-bit displacement) assert rm.base.lcode != 0b100, \ "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" return bytearray([0x80 | (reg << 3) | rm.base.lcode, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF]) else: # All encodings below use ModRM.rm = 4 (0b100) to indicate the presence of SIB assert rsp != rm.index, "rsp is not encodable as an index register (interpreted as no index)" # Index = 4 (0b100) denotes no-index encoding index = 0x4 if rm.index is None else rm.index.lcode scale = 0 if rm.scale is None else ilog2(rm.scale) if rm.base is None: # SIB.base = 5 (0b101) and ModRM.mode = 0 indicates no-base encoding with disp32 return bytearray([(reg << 3) | 0x4, (scale << 6) | (index << 3) | 0x5, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF]) else: if rm.displacement == 0 and rm.base.lcode != 0b101 and min_disp <= 0: # ModRM.mode == 0 (no displacement) assert rm.base.lcode != 0b101, \ "rbp/r13 is not encodable as a base register (interpreted as disp32 address)" return bytearray([(reg << 3) | 0x4, (scale << 6) | (index << 3) | rm.base.lcode]) elif (rm.displacement % disp8xN == 0) and is_sint8(rm.displacement // disp8xN) and min_disp <= 1: # ModRM.mode == 1 (8-bit displacement) return bytearray([(reg << 3) | 0x44, (scale << 6) | (index << 3) | rm.base.lcode, (rm.displacement // disp8xN) & 0xFF]) else: # ModRM.mode == 2 (32-bit displacement) return bytearray([(reg << 3) | 0x84, (scale << 6) | (index << 3) | rm.base.lcode, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF]) else: # ModRM.mode == 0 and ModeRM.rm == 5 (0b101) indicates (rip + disp32) addressing return bytearray([0b00000101 | (reg << 3), rm & 0xFF, (rm >> 8) & 0xFF, (rm >> 16) & 0xFF, (rm >> 24) & 0xFF])
def modrm_sib_disp(reg, rm, force_sib=False, min_disp=0): from peachpy.x86_64.operand import MemoryAddress from peachpy.x86_64.registers import rsp, rbp, r12, r13 from peachpy.util import is_int, is_sint8, ilog2 assert is_int(reg) and 0 <= reg <= 7, \ "Constant reg value expected, got " + str(reg) assert isinstance(rm, MemoryAddress) # TODO: support global addresses, including rip-relative addresses assert rm.base is not None or rm.index is not None, \ "Global addressing is not yet supported" # ModR/M byte # +----------------+---------------+--------------+ # | Bits 6-7: mode | Bits 3-5: reg | Bits 0-2: rm | # +----------------+---------------+--------------+ # # SIB byte # +-----------------+-----------------+----------------+ # | Bits 6-7: scale | Bits 3-5: index | Bits 0-2: base | # +-----------------+-----------------+----------------+ if not force_sib and rm.index is None and rm.base.lcode != 0b100: # No SIB byte if rm.displacement == 0 and rm.base != rbp and rm.base != r13 and min_disp <= 0: # ModRM.mode = 0 (no displacement) assert rm.base.lcode != 0b100, "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" assert rm.base.lcode != 0b101, "rbp/r13 is not encodable as a base register (interpreted as disp32 address)" return bytearray([(reg << 3) | rm.base.lcode]) elif is_sint8(rm.displacement) and min_disp <= 1: # ModRM.mode = 1 (8-bit displacement) assert rm.base.lcode != 0b100, "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" return bytearray( [0x40 | (reg << 3) | rm.base.lcode, rm.displacement & 0xFF]) else: # ModRM.mode == 2 (32-bit displacement) assert rm.base.lcode != 0b100, "rsp/r12 are not encodable as a base register (interpreted as SIB indicator)" return bytearray([ 0x80 | (reg << 3) | rm.base.lcode, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF ]) else: # All encodings below use ModRM.rm = 4 (0b100) to indicate the presence of SIB assert rsp != rm.index, "rsp is not encodable as an index register (interpreted as no index)" # Index = 4 (0b100) denotes no-index encoding index = 0x4 if rm.index is None else rm.index.lcode scale = 0 if rm.scale is None else ilog2(rm.scale) if rm.base is None: # SIB.base = 5 (0b101) and ModRM.mode = 0 indicates no-base encoding with disp32 return bytearray([(reg << 3) | 0x4, (scale << 6) | (index << 3) | 0x5, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF]) else: if rm.displacement == 0 and rm.base.lcode != 0b101 and min_disp <= 0: # ModRM.mode == 0 (no displacement) assert rm.base.lcode != 0b101, \ "rbp/r13 is not encodable as a base register (interpreted as disp32 address)" return bytearray([(reg << 3) | 0x4, (scale << 6) | (index << 3) | rm.base.lcode]) elif is_sint8(rm.displacement) and min_disp <= 1: # ModRM.mode == 1 (8-bit displacement) return bytearray([(reg << 3) | 0x44, (scale << 6) | (index << 3) | rm.base.lcode, rm.displacement & 0xFF]) else: # ModRM.mode == 2 (32-bit displacement) return bytearray([(reg << 3) | 0x84, (scale << 6) | (index << 3) | rm.base.lcode, rm.displacement & 0xFF, (rm.displacement >> 8) & 0xFF, (rm.displacement >> 16) & 0xFF, (rm.displacement >> 24) & 0xFF])
def __init__(self, code_size, data_size=0): from peachpy.util import is_int if not is_int(code_size): raise TypeError("code size must be an integer") if not is_int(data_size): raise TypeError("data size must be an integer") if code_size <= 0: raise ValueError("code size must be positive") if data_size < 0: raise ValueError("data size must be non-negative") import mmap self.allocation_granularity = max(mmap.ALLOCATIONGRANULARITY, mmap.PAGESIZE) self.code_address = None self.code_size = self.allocation_size(code_size) self.data_address = None self.data_size = self.allocation_size(data_size) self._release_memory = None osname = sys.platform.lower() if osname == "darwin" or osname.startswith("linux"): import ctypes if osname == "darwin": libc = ctypes.cdll.LoadLibrary("libc.dylib") else: libc = ctypes.cdll.LoadLibrary("libc.so.6") # void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) mmap_function = libc.mmap mmap_function.restype = ctypes.c_void_p mmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_size_t] # int munmap(void* addr, size_t len) munmap_function = libc.munmap munmap_function.restype = ctypes.c_int munmap_function.argtype = [ctypes.c_void_p, ctypes.c_size_t] def munmap(address, size): munmap_result = munmap_function(ctypes.c_void_p(address), size) assert munmap_result == 0 self._release_memory = lambda address_size: munmap(address_size[0], address_size[1]) # Allocate code segment code_address = mmap_function(None, self.code_size, mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC, mmap.MAP_ANON | mmap.MAP_PRIVATE, -1, 0) if code_address == -1: raise OSError("Failed to allocate memory for code segment") self.code_address = code_address if self.data_size > 0: # Allocate data segment data_address = mmap_function(None, self.data_size, mmap.PROT_READ | mmap.PROT_WRITE, mmap.MAP_ANON | mmap.MAP_PRIVATE, -1, 0) if data_address == -1: raise OSError("Failed to allocate memory for data segment") self.data_address = data_address elif osname == "win32": import ctypes # From WinNT.h PAGE_READWRITE = 0x04 PAGE_EXECUTE_READWRITE = 0x40 MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 MEM_RELEASE = 0x8000 # LPVOID WINAPI VirtualAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect) VirtualAlloc_function = ctypes.windll.kernel32.VirtualAlloc VirtualAlloc_function.restype = ctypes.c_void_p VirtualAlloc_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong] # BOOL WINAPI VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) VirtualFree_function = ctypes.windll.kernel32.VirtualFree VirtualFree_function.restype = ctypes.c_int VirtualFree_function.argtype = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong] def VirtualFree(address, size): VirtualFree_result = VirtualFree_function(address, size, MEM_RELEASE) assert VirtualFree_result != 0 self._release_memory = lambda address_size: VirtualFree(address_size[0], address_size[1]) # Allocate code segment code_address = VirtualAlloc_function(None, self.code_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) if not code_address: raise OSError("Failed to allocate memory for code segment") self.code_address = code_address if self.data_size > 0: # Allocate data segment data_address = VirtualAlloc_function(None, self.data_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) if not data_address: raise OSError("Failed to allocate memory for data segment") self.data_address = data_address elif osname == "nacl": import dynacl # Allocate code segment self.allocation = dynacl.allocate(self.code_size, self.data_size) self.code_address = self.allocation.code_address self.data_address = self.allocation.data_address self.copy_code = self._nacl_copy_code else: raise ValueError("Unknown host OS: " + osname)