mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
addressing review comments, removing copyright notices
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
##############################################################################
|
||||
# Added example for raw binary blob
|
||||
# Kelly Patterson - Cisco Talos
|
||||
# Copyright (C) 2025 Cisco Systems Inc
|
||||
# Licensed under the GNU General Public License v2.0 or later
|
||||
# This example is meant to demonstrate the modifications necessary
|
||||
# to enable code coverage when emulating small code snippets or bare-metal
|
||||
# code.
|
||||
##############################################################################
|
||||
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
|
||||
import os
|
||||
|
||||
BASE_ADDRESS = 0x10000000
|
||||
CHECKSUM_FUNC_ADDR = BASE_ADDRESS + 0x8
|
||||
@@ -16,6 +16,8 @@ DATA_ADDR = 0xa0000000 # Arbitrary address for data
|
||||
STACK_ADDR = 0xb0000000 # Arbitrary address for stack
|
||||
|
||||
# Python implementation of the checksum function being emulated
|
||||
# This checksum function is intended to have different code paths based on the input
|
||||
# which is useful for observing code coverage
|
||||
def checksum_function(input_data_buffer: bytes):
|
||||
expected_checksum_python = 0
|
||||
input_data_len = len(input_data_buffer)
|
||||
@@ -33,26 +35,34 @@ def checksum_function(input_data_buffer: bytes):
|
||||
expected_checksum_python &= 0xFF # Ensure it's a single byte
|
||||
return expected_checksum_python
|
||||
|
||||
def unmapped_handler(ql, type, addr, size, value):
|
||||
def unmapped_handler(ql: Qiling, type: int, addr: int, size: int, value: int) -> None:
|
||||
print(f"Unmapped Memory R/W, trying to access {size:d} bytes at {addr:#010x} from {ql.arch.regs.pc:#010x}")
|
||||
|
||||
print(f"Unmapped Memory R/W, trying to access {hex(size)} bytes at {hex(addr)} from {hex(ql.arch.regs.pc)}")
|
||||
|
||||
def emulate_checksum_function(input_data_buffer: bytes):
|
||||
def emulate_checksum_function(input_data_buffer: bytes) -> None:
|
||||
print(f"\n--- Testing with input: {input_data_buffer.hex()} ---")
|
||||
|
||||
with open("rootfs/blob/example_raw.bin", "rb") as f:
|
||||
raw_code = f.read()
|
||||
test_file = "rootfs/blob/example_raw.bin"
|
||||
|
||||
ql = Qiling(code=raw_code, archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="blob_raw.ql", verbose=QL_VERBOSE.DEBUG, thumb=True)
|
||||
with open(test_file, "rb") as f:
|
||||
raw_code: bytes = f.read()
|
||||
|
||||
# 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: Qiling = 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
|
||||
removing all images with name 'blob_code' that were created by the blob loader.
|
||||
This is necessary because some code coverage visualization tools require the
|
||||
module name to match that of the input file '''
|
||||
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'))
|
||||
ql.loader.images.append(Image(ql.loader.load_address, ql.loader.load_address + ql.os.code_ram_size, os.path.basename(test_file)))
|
||||
|
||||
|
||||
input_data_len = len(input_data_buffer)
|
||||
input_data_len: int = len(input_data_buffer)
|
||||
|
||||
# Map memory for the data and stack
|
||||
ql.mem.map(STACK_ADDR, 0x2000)
|
||||
@@ -88,5 +98,4 @@ 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)
|
||||
print(hex(checksum_function(data)))
|
||||
emulate_checksum_function(data)
|
||||
@@ -1,10 +1,4 @@
|
||||
##############################################################################
|
||||
# Added example for raw binary blob
|
||||
# Kelly Patterson - Cisco Talos
|
||||
# Copyright (C) 2025 Cisco Systems Inc
|
||||
# Licensed under the GNU General Public License v2.0 or later
|
||||
##############################################################################
|
||||
# Makefile for Bare-Metal ARM Hash Calculator
|
||||
# Makefile for Bare-Metal ARM Checksum Calculator
|
||||
|
||||
# --- Toolchain Definitions ---
|
||||
TOOLCHAIN_PREFIX = arm-none-eabi
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
/*
|
||||
* Added example for raw binary blob
|
||||
* Kelly Patterson - Cisco Talos
|
||||
* Copyright (C) 2025 Cisco Systems Inc
|
||||
* Licensed under the GNU General Public License v2.0 or later
|
||||
*
|
||||
*/
|
||||
// example checksum algorithm to demonstrate raw binary code coverage in qiling
|
||||
// example_raw.c
|
||||
|
||||
// Define some magic values
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
/* linker.ld */
|
||||
/*
|
||||
* Added example for raw binary blob
|
||||
* Kelly Patterson - Cisco Talos
|
||||
* Copyright (C) 2025 Cisco Systems Inc
|
||||
* Licensed under the GNU General Public License v2.0 or later
|
||||
*
|
||||
*/
|
||||
|
||||
ENTRY(_start) /* Define the entry point of our program */
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
ram_size = 0xa00000
|
||||
load_address = 0x80800000
|
||||
entry_point = 0x80800000
|
||||
heap_address = 0xa0000000
|
||||
heap_size = 0x300000
|
||||
|
||||
|
||||
|
||||
@@ -2,15 +2,9 @@
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
# Heaps are optional for blobs
|
||||
# Kelly Patterson - Cisco Talos
|
||||
# Copyright (C) 2025 Cisco Systems Inc
|
||||
# Licensed under the GNU General Public License v2.0 or later
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.loader.loader import QlLoader, Image
|
||||
from qiling.os.memory import QlMemoryHeap
|
||||
import configparser
|
||||
|
||||
|
||||
class QlLoaderBLOB(QlLoader):
|
||||
@@ -33,15 +27,5 @@ class QlLoaderBLOB(QlLoader):
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@@ -8,6 +8,7 @@ from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
|
||||
from qiling.const import QL_ARCH, QL_OS
|
||||
from qiling.os.fcall import QlFunctionCall
|
||||
from qiling.os.os import QlOs
|
||||
from qiling.os.memory import QlMemoryHeap
|
||||
|
||||
|
||||
class QlOsBlob(QlOs):
|
||||
@@ -49,5 +50,11 @@ class QlOsBlob(QlOs):
|
||||
# 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
|
||||
|
||||
|
||||
# if heap info is provided in profile, create heap
|
||||
heap_base = self.profile.getint('CODE', 'heap_address', fallback=None)
|
||||
heap_size = self.profile.getint('CODE', 'heap_size', fallback=None)
|
||||
if heap_base is not None and heap_size is not None:
|
||||
self.heap = QlMemoryHeap(self.ql, heap_base, heap_base + heap_size)
|
||||
|
||||
self.ql.emu_start(self.entry_point, self.exit_point, self.ql.timeout, self.ql.count)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
ram_size = 0xa00000
|
||||
load_address = 0x80800000
|
||||
entry_point = 0x80800000
|
||||
heap_address = 0xa0000000
|
||||
heap_size = 0x300000
|
||||
|
||||
|
||||
|
||||
+3
-40
@@ -2,10 +2,6 @@
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
# Added test for raw binary blob emulation
|
||||
# Kelly Patterson - Cisco Talos
|
||||
# Copyright (C) 2025 Cisco Systems Inc
|
||||
# Licensed under the GNU General Public License v2.0 or later
|
||||
|
||||
import unittest
|
||||
|
||||
@@ -87,7 +83,7 @@ class BlobTest(unittest.TestCase):
|
||||
del ql
|
||||
|
||||
@unittest.skip("Temporarily disabled")
|
||||
def test_blob_checksum_calculations(self):
|
||||
def test_blob_raw(self):
|
||||
def run_checksum_emu(input_data_buffer: bytes) -> int:
|
||||
"""
|
||||
Callable function that takes input data buffer and returns the checksum.
|
||||
@@ -145,41 +141,8 @@ class BlobTest(unittest.TestCase):
|
||||
|
||||
return expected_checksum & 0xFF
|
||||
|
||||
# Test cases with descriptions
|
||||
test_cases = {
|
||||
"default_path": {
|
||||
"data": b"\x01\x02\x03\x04\x05",
|
||||
"description": "Default path - simple sum of all bytes"
|
||||
},
|
||||
"magic_value_1": {
|
||||
"data": b"\xDE\x01\x02\x03\x04\x05",
|
||||
"description": "Magic Value 1 path (0xDE at data[0]) - sum first 4 bytes + 0x10"
|
||||
},
|
||||
"magic_value_2": {
|
||||
"data": b"\x01\xAD\x02\x03\x04\x05",
|
||||
"description": "Magic Value 2 path (0xAD at data[1]) - XOR all bytes + 0x20"
|
||||
},
|
||||
"edge_magic1_short": {
|
||||
"data": b"\xDE\x01",
|
||||
"description": "Edge case: Magic Value 1, but short data (only 2 bytes)"
|
||||
},
|
||||
"edge_magic2_too_short": {
|
||||
"data": b"\xAD",
|
||||
"description": "Edge case: Magic Value 2, but too short (fallback to default)"
|
||||
},
|
||||
"both_magic_values": {
|
||||
"data": b"\xDE\xAD\x01\x02",
|
||||
"description": "Both magic values present, DE at [0] takes precedence"
|
||||
}
|
||||
}
|
||||
|
||||
# Assertions with descriptions - directly call functions with test data
|
||||
self.assertEqual(run_checksum_emu(test_cases["default_path"]["data"]), calculate_expected_checksum(test_cases["default_path"]["data"])) # Default path
|
||||
self.assertEqual(run_checksum_emu(test_cases["magic_value_1"]["data"]), calculate_expected_checksum(test_cases["magic_value_1"]["data"])) # Magic Value 1 path
|
||||
self.assertEqual(run_checksum_emu(test_cases["magic_value_2"]["data"]), calculate_expected_checksum(test_cases["magic_value_2"]["data"])) # Magic Value 2 path
|
||||
self.assertEqual(run_checksum_emu(test_cases["edge_magic1_short"]["data"]), calculate_expected_checksum(test_cases["edge_magic1_short"]["data"])) # Edge case: Magic Value 1, short data
|
||||
self.assertEqual(run_checksum_emu(test_cases["edge_magic2_too_short"]["data"]), calculate_expected_checksum(test_cases["edge_magic2_too_short"]["data"])) # Edge case: Magic Value 2, too short
|
||||
self.assertEqual(run_checksum_emu(test_cases["both_magic_values"]["data"]), calculate_expected_checksum(test_cases["both_magic_values"]["data"])) # Both magic values, DE takes precedence
|
||||
test_input = b"\x01\x02\x03\x04\x05"
|
||||
self.assertEqual(run_checksum_emu(test_input), calculate_expected_checksum(test_input))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user