From 81c2a39f1f39fb4117db20ad5d931bcb20d5ba47 Mon Sep 17 00:00:00 2001 From: technikelly <11539105+technikelly@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:55:37 -0400 Subject: [PATCH] minimizing blob changes --- examples/blob_raw.ql | 6 ++--- examples/hello_arm_blob_raw.py | 26 ++++++++++++++------- qiling/loader/blob.py | 42 +++++++++++++++++----------------- qiling/os/blob/blob.py | 7 ++---- tests/profiles/blob_raw.ql | 6 ++--- tests/test_blob.py | 11 ++++----- 6 files changed, 52 insertions(+), 46 deletions(-) diff --git a/examples/blob_raw.ql b/examples/blob_raw.ql index 164219a9..23390130 100644 --- a/examples/blob_raw.ql +++ b/examples/blob_raw.ql @@ -1,4 +1,4 @@ -[BLOB_RAW] +[CODE] load_address = 0x10000000 -image_size = 0xbc -image_name = example_raw.bin \ No newline at end of file +entry_point = 0x10000008 +ram_size = 0xa00000 \ No newline at end of file diff --git a/examples/hello_arm_blob_raw.py b/examples/hello_arm_blob_raw.py index 6e44d6bf..f260a4e0 100644 --- a/examples/hello_arm_blob_raw.py +++ b/examples/hello_arm_blob_raw.py @@ -6,6 +6,7 @@ from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.coverage import utils as cov_utils +from qiling.loader.loader import Image BASE_ADDRESS = 0x10000000 CHECKSUM_FUNC_ADDR = BASE_ADDRESS + 0x8 @@ -29,6 +30,7 @@ def checksum_function(input_data_buffer: bytes): for i in range(input_data_len): expected_checksum_python += input_data_buffer[i] expected_checksum_python &= 0xFF # Ensure it's a single byte + return expected_checksum_python def unmapped_handler(ql, type, addr, size, value): @@ -37,18 +39,24 @@ def unmapped_handler(ql, type, addr, size, value): def emulate_checksum_function(input_data_buffer: bytes): print(f"\n--- Testing with input: {input_data_buffer.hex()} ---") - ql = Qiling(archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="blob_raw.ql", verbose=QL_VERBOSE.DEBUG, thumb=True) + with open("rootfs/blob/example_raw.bin", "rb") as f: + raw_code = f.read() + + ql = Qiling(code=raw_code, archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="blob_raw.ql", verbose=QL_VERBOSE.DEBUG, thumb=True) + + # monkeypatch - Correcting the loader image name, used for coverage collection + # Remove all images with name 'blob_code' that were created by the blob loader + ql.loader.images = [img for img in ql.loader.images if img.path != 'blob_code'] + # Add image back with correct info + ql.loader.images.append(Image(ql.loader.load_address, ql.loader.load_address + ql.os.code_ram_size, 'example_raw.bin')) + input_data_len = len(input_data_buffer) - # Map memory for the binary, data and stack - ql.mem.map(BASE_ADDRESS, 0x10000) + # Map memory for the data and stack ql.mem.map(STACK_ADDR, 0x2000) ql.mem.map(DATA_ADDR, ql.mem.align_up(input_data_len + 0x100)) # Map enough space for data - # Write the binary into memory - ql.mem.write(BASE_ADDRESS, open("rootfs/blob/example_raw.bin", "rb").read()) - # Write input data ql.mem.write(DATA_ADDR, input_data_buffer) @@ -70,7 +78,8 @@ def emulate_checksum_function(input_data_buffer: bytes): # Start emulation print(f"Starting emulation at PC: {hex(ql.arch.regs.pc)}") try: - ql.run(begin=CHECKSUM_FUNC_ADDR, end=END_ADDRESS) + with cov_utils.collect_coverage(ql, 'drcov', 'output.cov'): + ql.run(begin=CHECKSUM_FUNC_ADDR, end=END_ADDRESS) except Exception as e: print(f"Emulation error: {e}") @@ -78,4 +87,5 @@ def emulate_checksum_function(input_data_buffer: bytes): if __name__ == "__main__": data = b"\x01\x02\x03\x04\x05" # Example input data - emulate_checksum_function(data) \ No newline at end of file + emulate_checksum_function(data) + print(hex(checksum_function(data))) \ No newline at end of file diff --git a/qiling/loader/blob.py b/qiling/loader/blob.py index 96168538..c4a69de8 100644 --- a/qiling/loader/blob.py +++ b/qiling/loader/blob.py @@ -9,38 +9,38 @@ from qiling import Qiling from qiling.loader.loader import QlLoader, Image from qiling.os.memory import QlMemoryHeap +import configparser class QlLoaderBLOB(QlLoader): def __init__(self, ql: Qiling): super().__init__(ql) + self.load_address = 0 + def run(self): - if self.ql.os.profile.has_section("BLOB_RAW"): - # For raw binary blobs, user will handle memory mapping - self.load_address = int(self.ql.os.profile.get("BLOB_RAW", "load_address"), 16) - image_size = int(self.ql.os.profile.get("BLOB_RAW", "image_size"), 16) - image_name = self.ql.os.profile.get("BLOB_RAW", "image_name", fallback="blob.raw") - self.images.append(Image(self.load_address, self.load_address+image_size, image_name)) # used to collect coverage - else: - self.load_address = self.ql.os.load_address - self.entry_point = self.ql.os.entry_point + self.load_address = self.ql.os.load_address + self.entry_point = self.ql.os.entry_point - code_begins = self.load_address - code_size = self.ql.os.code_ram_size - code_ends = code_begins + code_size + code_begins = self.load_address + code_size = self.ql.os.code_ram_size + code_ends = code_begins + code_size - self.ql.mem.map(code_begins, code_size, info="[code]") - self.ql.mem.write(code_begins, self.ql.code) + self.ql.mem.map(code_begins, code_size, info="[code]") + self.ql.mem.write(code_begins, self.ql.code) - # allow image-related functionalities - self.images.append(Image(code_begins, code_ends, 'blob_code')) + # allow image-related functionalities + self.images.append(Image(code_begins, code_ends, 'blob_code')) - # FIXME: heap starts above end of ram?? - # FIXME: heap should be allocated by OS, not loader - heap_base = code_ends + # FIXME: heap starts above end of ram?? + # FIXME: heap should be allocated by OS, not loader + heap_base = code_ends + # if heap_size is defined, create the heap + try: heap_size = int(self.ql.os.profile.get("CODE", "heap_size"), 16) self.ql.os.heap = QlMemoryHeap(self.ql, heap_base, heap_base + heap_size) + except (configparser.NoSectionError, configparser.NoOptionError): + pass # heap_size is not required - # FIXME: stack pointer should be a configurable profile setting - self.ql.arch.regs.arch_sp = code_ends - 0x1000 + # FIXME: stack pointer should be a configurable profile setting + self.ql.arch.regs.arch_sp = code_ends - 0x1000 diff --git a/qiling/os/blob/blob.py b/qiling/os/blob/blob.py index f2056e55..e4a02256 100644 --- a/qiling/os/blob/blob.py +++ b/qiling/os/blob/blob.py @@ -2,9 +2,6 @@ # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -# Added support for raw binary blob emulation -# Kelly Patterson - Cisco Talos -# Copyright (C) 2025 Cisco Systems Inc from qiling import Qiling from qiling.cc import QlCC, intel, arm, mips, riscv, ppc @@ -47,10 +44,10 @@ class QlOsBlob(QlOs): if self.ql.entry_point is not None: self.entry_point = self.ql.entry_point + self.exit_point = self.load_address + len(self.ql.code) + # if exit point was set explicitly, override the default one if self.ql.exit_point is not None: self.exit_point = self.ql.exit_point - elif self.ql.code is not None: # self.ql.code might not always be provided - self.exit_point = self.load_address + len(self.ql.code) self.ql.emu_start(self.entry_point, self.exit_point, self.ql.timeout, self.ql.count) diff --git a/tests/profiles/blob_raw.ql b/tests/profiles/blob_raw.ql index 164219a9..23390130 100644 --- a/tests/profiles/blob_raw.ql +++ b/tests/profiles/blob_raw.ql @@ -1,4 +1,4 @@ -[BLOB_RAW] +[CODE] load_address = 0x10000000 -image_size = 0xbc -image_name = example_raw.bin \ No newline at end of file +entry_point = 0x10000008 +ram_size = 0xa00000 \ No newline at end of file diff --git a/tests/test_blob.py b/tests/test_blob.py index d284bbac..bd0b9ecf 100644 --- a/tests/test_blob.py +++ b/tests/test_blob.py @@ -97,18 +97,17 @@ class BlobTest(unittest.TestCase): DATA_ADDR = 0xa0000000 STACK_ADDR = 0xb0000000 - ql = Qiling(archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="profiles/blob_raw.ql", verbose=QL_VERBOSE.DEBUG, thumb=True) + with open("../examples/rootfs/blob/example_raw.bin", "rb") as f: + raw_code = f.read() + + ql = Qiling(code=raw_code, archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="profiles/blob_raw.ql", verbose=QL_VERBOSE.DEBUG, thumb=True) input_data_len = len(input_data_buffer) - # Map memory for the binary, data and stack - ql.mem.map(BASE_ADDRESS, 0x10000) + # Map memory for data and stack ql.mem.map(STACK_ADDR, 0x2000) ql.mem.map(DATA_ADDR, ql.mem.align_up(input_data_len + 0x100)) - # Write the binary into memory - ql.mem.write(BASE_ADDRESS, open("../examples/rootfs/blob/example_raw.bin", "rb").read()) - # Write input data ql.mem.write(DATA_ADDR, input_data_buffer)