import archinfo
from cle.errors import CLEError
from .backend import Backend, register_backend
from .region import Section, Segment
try:
from xbe import Xbe
except ImportError:
Xbe = None
[docs]class XBESection(Section):
[docs] def __init__(self, name, file_offset, file_size, virtual_addr, virtual_size, xbe_sec):
super().__init__(name, file_offset, virtual_addr, virtual_size)
self.filesize = file_size
self._xbe_sec = xbe_sec
@property
def is_readable(self):
"""
Whether this section has read permissions
"""
return True
@property
def is_writable(self):
"""
Whether this section has write permissions
"""
return (self._xbe_sec.header.flags & self._xbe_sec.header.FLAG_WRITEABLE) != 0
@property
def is_executable(self):
"""
Whether this section has execute permissions
"""
return (self._xbe_sec.header.flags & self._xbe_sec.header.FLAG_EXECUTABLE) != 0
@property
def only_contains_uninitialized_data(self):
"""
We load every section in, they're all initialized
"""
return False
[docs]class XBE(Backend):
"""
The main loader class for statically loading XBE executables.
"""
is_default = True
[docs] def __init__(self, *args, **kwargs):
if Xbe is None:
raise CLEError("Run `pip install pyxbe==0.0.2` to support loading XBE files")
super().__init__(*args, **kwargs)
self.set_arch(archinfo.arch_from_id("x86"))
self.os = "xbox"
if self.binary is None:
self._xbe = Xbe(data=self._binary_stream.read())
else:
self._xbe = Xbe.from_file(self.binary)
self._entry = self._xbe.entry_addr
self._image_vmem = bytearray(self._xbe.header.image_size)
self._min_addr = self._xbe.header.base_addr
self._max_addr = self._xbe.header.base_addr + self._xbe.header.image_size - 1
# Add header
self._add_xbe_section(
0,
self._xbe.header.image_header_size,
self._xbe.header.base_addr,
self._xbe.header.image_header_size,
self._xbe.header_data,
)
# Add each section
for _, sec in self._xbe.sections.items():
self._add_xbe_section(
sec.header.raw_addr,
sec.header.raw_size,
sec.header.virtual_addr,
sec.header.virtual_size,
sec.data,
sec,
)
self.memory.add_backer(0, bytes(self._image_vmem))
self.mapped_base = self.linked_base = self._xbe.header.base_addr
def _add_xbe_section(self, file_offset, file_size, virtual_addr, virtual_size, backer, sec=None):
# Copy in section contents
start = virtual_addr - self._xbe.header.base_addr
end = start + file_size
self._image_vmem[start:end] = backer
# Create a segment and a section
seg = Segment(file_offset, virtual_addr, file_size, virtual_size)
self.segments.append(seg)
if sec is not None:
sec = XBESection(sec.name, file_offset, file_size, virtual_addr, virtual_size, sec)
self.sections.append(sec)
[docs] def close(self):
super().close()
del self._xbe
[docs] @staticmethod
def is_compatible(stream):
stream.seek(0)
identstring = stream.read(4)
stream.seek(0)
return identstring.startswith(b"XBEH")
@property
def min_addr(self):
return self._min_addr
@property
def max_addr(self):
return self._max_addr
[docs] @classmethod
def check_compatibility(cls, spec, obj): # pylint: disable=unused-argument
assert False
return True
register_backend("xbe", XBE)