addressing review comments, removing copyright notices

This commit is contained in:
technikelly
2025-08-25 13:34:45 -04:00
parent a2223abb5a
commit 5ec4a3b57a
9 changed files with 43 additions and 97 deletions
+28 -19
View File
@@ -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 -7
View File
@@ -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 -7
View File
@@ -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
-7
View File
@@ -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 */
+1
View File
@@ -2,6 +2,7 @@
ram_size = 0xa00000
load_address = 0x80800000
entry_point = 0x80800000
heap_address = 0xa0000000
heap_size = 0x300000
-16
View File
@@ -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 -1
View File
@@ -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)
+1
View File
@@ -2,6 +2,7 @@
ram_size = 0xa00000
load_address = 0x80800000
entry_point = 0x80800000
heap_address = 0xa0000000
heap_size = 0x300000
+3 -40
View File
@@ -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__":