Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Periodic maintenance PR #1488

Merged
merged 15 commits into from
Oct 11, 2024
4 changes: 2 additions & 2 deletions qiling/extensions/idaplugin/qilingida.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ def start(self, *args, **kwargs):
elif self.ql.arch.bits == 64:
self.baseaddr = int(self.ql.os.profile.get("OS64", "load_address"), 16)
else:
self.baseaddr = 0x0
self.baseaddr = get_imagebase()

def run(self, begin=None, end=None):
self.ql.run(begin, end)
Expand Down Expand Up @@ -1115,7 +1115,7 @@ def ql_run_selection(self):

def ql_set_pc(self):
if self.qlinit:
ea = IDA.get_current_address()
ea = self.qlemu.ql_addr_from_ida(IDA.get_current_address())
self.qlemu.ql.arch.regs.arch_pc = ea
logging.info(f"QIling PC set to {hex(ea)}")
self.qlemu.status = self.qlemu.ql.save()
Expand Down
90 changes: 70 additions & 20 deletions qiling/loader/pe_uefi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from pefile import PE
import os
from typing import Any, Mapping, Optional, Sequence
from pefile import PE

from unicorn.unicorn_const import UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC

from qiling import Qiling
from qiling.const import QL_ARCH
Expand Down Expand Up @@ -96,17 +99,64 @@ def map_and_load(self, path: str, context: UefiContext, exec_now: bool=False):
# use image base only if it does not point to NULL
image_base = pe.OPTIONAL_HEADER.ImageBase or context.next_image_base
image_size = ql.mem.align_up(pe.OPTIONAL_HEADER.SizeOfImage)
image_name = os.path.basename(path)

assert (image_base % ql.mem.pagesize) == 0, 'image base is expected to be page-aligned'

if image_base != pe.OPTIONAL_HEADER.ImageBase:
pe.relocate_image(image_base)

pe.parse_data_directories()
data = bytes(pe.get_memory_mapped_image())
# pe.parse_data_directories()

sec_alignment = pe.OPTIONAL_HEADER.SectionAlignment

def __map_sections():
"""Load file sections to memory, each in its own memory region protected by
its defined permissions. That allows separation of code and data, which makes
it easier to detect abnomal behavior or memory corruptions.
"""

# load the header
hdr_data = bytes(pe.header)
hdr_base = image_base
hdr_size = ql.mem.align_up(len(hdr_data), sec_alignment)
hdr_perm = UC_PROT_READ

ql.mem.map(hdr_base, hdr_size, hdr_perm, image_name)
ql.mem.write(hdr_base, hdr_data)

# load sections
for section in pe.sections:
if not section.IMAGE_SCN_MEM_DISCARDABLE:
sec_name = section.Name.rstrip(b'\x00').decode()
sec_data = bytes(section.get_data(ignore_padding=True))
sec_base = image_base + section.get_VirtualAddress_adj()
sec_size = ql.mem.align_up(len(sec_data), sec_alignment)

sec_perm = sum((
section.IMAGE_SCN_MEM_READ * UC_PROT_READ,
section.IMAGE_SCN_MEM_WRITE * UC_PROT_WRITE,
section.IMAGE_SCN_MEM_EXECUTE * UC_PROT_EXEC
))

ql.mem.map(sec_base, sec_size, sec_perm, f'{image_name} ({sec_name})')
ql.mem.write(sec_base, sec_data)

def __map_all():
"""Load the entire file to memory as a single memory region.
"""

data = bytes(pe.get_memory_mapped_image())

ql.mem.map(image_base, image_size, info=image_name)
ql.mem.write(image_base, data)

# if sections are aligned to page, we can map them separately
if (sec_alignment % ql.mem.pagesize) == 0:
__map_sections()
else:
__map_all()

ql.mem.map(image_base, image_size, info="[module]")
ql.mem.write(image_base, data)
ql.log.info(f'Module {path} loaded to {image_base:#x}')

entry_point = image_base + pe.OPTIONAL_HEADER.AddressOfEntryPoint
Expand All @@ -120,7 +170,7 @@ def map_and_load(self, path: str, context: UefiContext, exec_now: bool=False):
self.install_loaded_image_protocol(image_base, image_size)

# this would be used later be loader.find_containing_image
self.images.append(Image(image_base, image_base + image_size, path))
self.images.append(Image(image_base, image_base + image_size, os.path.abspath(path)))

# update next memory slot to allow sequencial loading. its availability
# is unknown though
Expand Down Expand Up @@ -160,7 +210,7 @@ def unload_modules(self, context: UefiContext) -> bool:

for handle in context.loaded_image_protocol_modules:
struct_addr = context.protocols[handle][self.loaded_image_protocol_guid]
loaded_image_protocol = EfiLoadedImageProtocol.EFI_LOADED_IMAGE_PROTOCOL.loadFrom(self.ql, struct_addr)
loaded_image_protocol = EfiLoadedImageProtocol.EFI_LOADED_IMAGE_PROTOCOL.load_from(self.ql.mem, struct_addr)

unload_ptr = loaded_image_protocol.Unload.value

Expand Down Expand Up @@ -223,19 +273,19 @@ def __init_dxe_environment(self, ql: Qiling) -> DxeContext:
context = DxeContext(ql)

# initialize and locate heap
heap_base = int(profile['heap_address'], 0)
heap_size = int(profile['heap_size'], 0)
heap_base = profile.getint('heap_address')
heap_size = profile.getint('heap_size')
context.init_heap(heap_base, heap_size)
ql.log.info(f'DXE heap at {heap_base:#010x}')

# initialize and locate stack
stack_base = int(profile['stack_address'], 0)
stack_size = int(profile['stack_size'], 0)
stack_base = profile.getint('stack_address')
stack_size = profile.getint('stack_size')
context.init_stack(stack_base, stack_size)
ql.log.info(f'DXE stack at {context.top_of_stack:#010x}')

# base address for next image
context.next_image_base = int(profile['image_address'], 0)
context.next_image_base = profile.getint('image_address')

# statically allocating 4 KiB for ST, RT, BS, DS and about 100 configuration table entries.
# the actual size needed was rounded up to the nearest page boundary.
Expand Down Expand Up @@ -272,23 +322,23 @@ def __init_smm_environment(self, ql: Qiling) -> SmmContext:
context = SmmContext(ql)

# set smram boundaries
context.smram_base = int(profile["smram_base"], 0)
context.smram_size = int(profile["smram_size"], 0)
context.smram_base = profile.getint('smram_base')
context.smram_size = profile.getint('smram_size')

# initialize and locate heap
heap_base = int(profile["heap_address"], 0)
heap_size = int(profile["heap_size"], 0)
heap_base = profile.getint('heap_address')
heap_size = profile.getint('heap_size')
context.init_heap(heap_base, heap_size)
ql.log.info(f"SMM heap at {heap_base:#010x}")

# initialize and locate stack
stack_base = int(profile['stack_address'], 0)
stack_size = int(profile['stack_size'], 0)
stack_base = profile.getint('stack_address')
stack_size = profile.getint('stack_size')
context.init_stack(stack_base, stack_size)
ql.log.info(f'SMM stack at {context.top_of_stack:#010x}')

# base address for next image
context.next_image_base = int(profile['image_address'], 0)
context.next_image_base = profile.getint('image_address')

# statically allocating 4 KiB for SMM ST and about 100 configuration table entries
# the actual size needed was rounded up to the nearest page boundary.
Expand Down Expand Up @@ -325,7 +375,7 @@ def run(self):
raise QlErrorArch("Unsupported architecture")

# x86-64 arch only
if ql.arch.type != QL_ARCH.X8664:
if ql.arch.type is not QL_ARCH.X8664:
raise QlErrorArch("Only 64-bit modules are supported at the moment")

self.loaded_image_protocol_guid = ql.os.profile["LOADED_IMAGE_PROTOCOL"]["Guid"]
Expand Down
45 changes: 31 additions & 14 deletions qiling/os/uefi/PiMultiPhase.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
#!/usr/bin/env python3
#
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from .UefiBaseType import *
from .ProcessorBind import *
from .UefiBaseType import EFI_PHYSICAL_ADDRESS
from .ProcessorBind import UINT64, STRUCT, ENUM_UC


EFI_SMRAM_OPEN = 0x00000001
EFI_SMRAM_CLOSED = 0x00000002
EFI_SMRAM_LOCKED = 0x00000004
EFI_CACHEABLE = 0x00000008
EFI_ALLOCATED = 0x00000010
EFI_NEEDS_TESTING = 0x00000020
EFI_NEEDS_ECC_INITIALIZATION = 0x00000040

EFI_SMRAM_OPEN = 0x00000001
EFI_SMRAM_CLOSED = 0x00000002
EFI_SMRAM_LOCKED = 0x00000004
EFI_CACHEABLE = 0x00000008
EFI_ALLOCATED = 0x00000010
EFI_NEEDS_TESTING = 0x00000020
EFI_NEEDS_ECC_INITIALIZATION = 0x00000040

class EFI_SMRAM_DESCRIPTOR(STRUCT):
_fields_ = [
('PhysicalStart', EFI_PHYSICAL_ADDRESS),
('CpuStart', EFI_PHYSICAL_ADDRESS),
('PhysicalSize', UINT64),
('RegionState', UINT64)
('PhysicalStart', EFI_PHYSICAL_ADDRESS),
('CpuStart', EFI_PHYSICAL_ADDRESS),
('PhysicalSize', UINT64),
('RegionState', UINT64)
]


class EFI_VARIABLE(ENUM_UC):
_members_ = {
'NON_VOLATILE': 0x00000001,
'BOOTSERVICE_ACCESS': 0x00000002,
'RUNTIME_ACCESS': 0x00000004,
'HARDWARE_ERROR_RECORD': 0x00000008,
'AUTHENTICATED_WRITE_ACCESS': 0x00000010,
'TIME_BASED_AUTHENTICATED_WRITE_ACCESS': 0x00000020,
'APPEND_WRITE': 0x00000040,
'ENHANCED_AUTHENTICATED_ACCESS': 0x00000080
}


__all__ = [
'EFI_SMRAM_DESCRIPTOR',
'EFI_VARIABLE',
'EFI_SMRAM_OPEN',
'EFI_SMRAM_CLOSED',
'EFI_SMRAM_LOCKED',
Expand Down
Loading
Loading