From 96996a191bdd9ba67cbe18f18512526dc645c3c5 Mon Sep 17 00:00:00 2001 From: Capo80 Date: Thu, 9 Oct 2025 16:33:36 +0200 Subject: [PATCH] Adding loading of decompressed sections --- firmware_config.json | 111 +++++++++++------------------------- src/breakpoints.rs | 130 ++++++++++++++++++++++++++++++++++++++++++- src/config.rs | 48 ++++++++++++++++ src/utils.rs | 24 +++++++- 4 files changed, 228 insertions(+), 85 deletions(-) diff --git a/firmware_config.json b/firmware_config.json index d15403b..63ed6a7 100644 --- a/firmware_config.json +++ b/firmware_config.json @@ -1,5 +1,5 @@ { - "fuzz": true, + "fuzz": false, "fuzz_target_address": "0xc04f1b88", "fuzz_target_return_address": "0xc04f2094", "qemu_args": [ @@ -7,107 +7,58 @@ "-monitor", "unix:qemu-monitor-socket,server,nowait", "-kernel", - "./qdsp6sw.mbn", + "../modems/srlabs_qdsp6sw.mbn", "-serial", "null", "-nographic", "-snapshot", - "-S", - "-s" + "-d", + "in_asm", + "-D", + "log_sr_labs.txt" + ], + "sections": [ + { + "name" : "clade1", + "section_type" : "clade", + "file" : "../patching/sr_labs_dec", + "format" : "raw", + "address" : "0xd8000000" + } ], "broker_port": "61337", "timeout_seconds": "1", "cores": "3", "breakpoints": [ { - "name": "qurt_println", - "address": "0xfe10f2b0", - "handler": "HandlePrintln" - }, - { - "name": "another_println", - "address": "0xc03c96cc", - "handler": "HandlePrintln" - }, - { - "name": "other_println", - "address": "0xc08460e4", - "handler": "HandlePrintln" - }, - { - "name": "diag_println", - "address": "0xbfe8a1f4", - "handler": "HandlePrintln" + "name": "clade_loading", + "address": "0xfe10a3ec", + "handler": "HandleCladeLoading" }, { - "name": "kernel_started", - "address": "0xfe10c028", + "name": "loop_1", + "address": "0xd8199fcc", "handler": "HandleNextPc" }, { - "name": "kernel_init", - "address": "0xfe10c0a8", + "name": "loop_2_1", + "address": "0xd8199fd0", "handler": "HandleNextPc" }, { - "name": "first_clade", - "address": "0xfe10a3ec", - "handler": "HandleJumpOver" - }, - { - "name": "second_clade", - "address": "0xfe10a744", - "handler": "HandleSecondClade" - }, - { - "name": "zeroeing", - "address": "0xc083b9f0", - "handler": "HandleJumpOver" - }, - { - "name": "app_init_done", - "address": "0xc0100064", - "handler": "HandlerEmpty" - }, - { - "name": "zeroing_yetanother", - "address": "0xfe1012b4", - "handler": "HandleZeroingYetAnother" - }, - { - "name": "fatal_error", - "address": "0xfe10ad10", - "handler": "HandleFatalError" - }, - { - "name": "read_loop_hardware", - "address": "0xfe115db0", - "handler": "HandleJumpOver" - }, - { - "name": "calling_fatal_error", - "address": "0xfe1021b4", - "handler": "HandleFatalError" - }, - { - "name": "time_ipc_task_started", - "address": "0xc04f1b96", - "handler": "HandlerEmpty" - }, - { - "name": "time_ipc_task_initialized", - "address": "0xc04f1bf4", - "handler": "HandlerEmpty" + "name": "loop_2_2", + "address": "0xd8199fd4", + "handler": "HandleNextPc" }, { - "name": "time_ipc_connecting_to_service", - "address": "0xc04f1c64", - "handler": "HandlerEmpty" + "name": "loop_2_3", + "address": "0xd8199fd8", + "handler": "HandleNextPc" }, { - "name": "time_ipc_qmi_client_initialized", - "address": "0xc04f1c70", - "handler": "HandlerEmpty" + "name": "skip_bad_calls", + "address" : "0xc0102044", + "handler": "HandleSkipCall" } ] } diff --git a/src/breakpoints.rs b/src/breakpoints.rs index d9ec3b1..9fed64d 100644 --- a/src/breakpoints.rs +++ b/src/breakpoints.rs @@ -1,10 +1,16 @@ use crate::config::Config; +use crate::config::Section; +use crate::config::SectionFormat; +use crate::config::SectionType; +use crate::utils::decode_hex; use libafl_qemu::ArchExtras; use libafl_qemu::CallingConvention; use libafl_qemu::Emulator; use libafl_qemu::Regs; use log::{debug, error, info}; use serde::Deserialize; +use std::fs; +use std::path::Path; use std::process; #[derive(Debug, Clone, Deserialize)] @@ -16,11 +22,14 @@ pub enum HandlerFunction { HandleFatalError, HandleZeroingYetAnother, HandlerEmpty, // Add other handlers here + HandleNextTwo, + HandleCladeLoading, + HandleSkipCall, } // Implement a method to call the actual handler function impl HandlerFunction { - pub fn call(&self, emu: &Emulator) { + pub fn call(&self, emu: &Emulator, sections: Vec
) { debug!("Executing handler function: {:?}", self); match self { HandlerFunction::HandlePrintln => handle_println(emu), @@ -30,6 +39,9 @@ impl HandlerFunction { HandlerFunction::HandleFatalError => handle_fatal_error(emu), HandlerFunction::HandleZeroingYetAnother => handle_zeroing_yet_another(emu), HandlerFunction::HandlerEmpty => handler_empty(emu), + HandlerFunction::HandleNextTwo => handle_next_two(emu), + HandlerFunction::HandleCladeLoading => handle_clade_loading(emu, sections), + HandlerFunction::HandleSkipCall => handle_skip_call(emu), // Add cases for other handlers } } @@ -99,7 +111,7 @@ pub fn handle_breakpoint(emu: &Emulator, config: Config) -> Result jumping to 0x{:x}", + current_pc, + current_pc + 8 + ); + emu.current_cpu() + .unwrap() + .write_reg(Regs::Pc, current_pc + 8) + .unwrap(); +} + fn handle_second_clade(emu: &Emulator) { let current_pc: u32 = emu.current_cpu().unwrap().read_reg(Regs::Pc).unwrap(); let old_r3: u32 = emu.current_cpu().unwrap().read_reg(Regs::R3).unwrap(); @@ -265,6 +292,105 @@ fn handle_zeroing_yet_another(emu: &Emulator) { .write_reg(Regs::Pc, 0xfe1012c0u32) .unwrap(); } + +fn handle_clade_loading(emu: &Emulator, sections: Vec
) { + for section in sections { + info!("Loading section {:?}", section.name); + // only load clade sections + if section.section_type == SectionType::Clade { + let file_path = Path::new(§ion.file); + + let raw_bytes: Vec = fs::read(file_path).expect(&format!( + "FATAL: Failed to load section '{}', unable to read file", + section.name + )); + + let data: Vec; + // if format is hex covert to raw bytes + match section.format { + SectionFormat::Hex => { + let hex_string = std::str::from_utf8(&raw_bytes) + .expect(&format!("FATAL: Failed to load section '{}', file contains non-UTF-8 characters for hex decoding", section.name)) + .trim(); + + data = decode_hex(hex_string).expect(&format!( + "FATAL: Failed to load section '{}', unable to convert from hex", + section.name + )); + } + SectionFormat::Raw => data = raw_bytes, + } + debug!("Bytes: {:?}", &data[..10]); + + unsafe { + emu.write_mem(section.address, &data); + } + info!("Section {:?} loaded at {:?}", section.name, section.address) + } + } + let current_pc: u32 = emu.current_cpu().unwrap().read_reg(Regs::Pc).unwrap(); + let return_address: u32 = emu.current_cpu().unwrap().read_return_address().unwrap(); + debug!( + "Jump over handler: current PC 0x{:x} -> jumping to 0x{:x}", + current_pc, return_address + ); + emu.current_cpu() + .unwrap() + .write_reg(Regs::Pc, return_address) + .unwrap(); + debug!("jumping over to: {return_address:#x}"); +} + +fn handle_skip_call(emu: &Emulator) { + let call_pointer: u32 = emu.current_cpu().unwrap().read_reg(Regs::R26).unwrap(); + debug!("call pointer {:x}", call_pointer); + let mut call_address: [u8; 4] = [0; 4]; + unsafe { emu.read_mem(call_pointer, &mut call_address) } + let call_address_int: u32 = u32::from_le_bytes(call_address); + debug!("call address {:x}", call_address_int); + if (call_address_int >= 0xdc000000 && call_address_int <= 0xdd000000) + || call_address_int == 0xd8ce2518 + { + let mut distance = 4; + let mut new_call_address_int: u32; + loop { + unsafe { emu.read_mem(call_pointer + distance, &mut call_address) } + new_call_address_int = u32::from_le_bytes(call_address); + + if !((new_call_address_int >= 0xdc000000 && new_call_address_int <= 0xdd000000) + || new_call_address_int == 0xd8ce2518) + { + break; + } + + distance += 4; + } + emu.current_cpu() + .unwrap() + .write_reg(Regs::R0, new_call_address_int) + .unwrap(); + emu.current_cpu() + .unwrap() + .write_reg(Regs::R26, call_pointer + distance) + .unwrap(); + debug!( + "changing bad call {:x} -> {:x}, dist: {:?}", + call_address_int, new_call_address_int, distance + ); + } else { + emu.current_cpu() + .unwrap() + .write_reg(Regs::R0, call_address_int) + .unwrap(); + debug!("allowed good call {:x}", call_address_int); + } + let current_pc: u32 = emu.current_cpu().unwrap().read_reg(Regs::Pc).unwrap(); + emu.current_cpu() + .unwrap() + .write_reg(Regs::Pc, current_pc + 4) + .unwrap(); +} + /* fn handle_skipping_hardware_init(emu: &Emulator) { unsafe { diff --git a/src/config.rs b/src/config.rs index 7cd8cf1..3feb258 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,17 @@ use std::fs; pub const CONFIG_PATH: &str = "firmware_config.json"; +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub enum SectionType { + Clade, + None, +} +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub enum SectionFormat { + Hex, + Raw, +} + #[derive(Deserialize, Debug, Clone)] pub struct FirmwareFunction { pub name: String, @@ -12,6 +23,17 @@ pub struct FirmwareFunction { pub address: u32, pub handler: HandlerFunction, } +#[derive(Deserialize, Debug, Clone)] +pub struct Section { + pub name: String, + #[serde(deserialize_with = "string_to_section_type")] + pub section_type: SectionType, + pub file: String, + #[serde(deserialize_with = "string_to_section_format")] + pub format: SectionFormat, + #[serde(deserialize_with = "hex_address_string_to_u32")] + pub address: u32, +} #[derive(Deserialize, Debug, Clone)] pub struct Config { @@ -21,6 +43,7 @@ pub struct Config { #[serde(deserialize_with = "hex_address_string_to_u32")] pub fuzz_target_return_address: u32, pub qemu_args: Vec, + pub sections: Vec
, #[serde(deserialize_with = "hex_string_to_u32")] pub broker_port: u32, #[serde(deserialize_with = "hex_string_to_u32")] @@ -47,6 +70,31 @@ where u32::from_str_radix(&s, 10).map_err(serde::de::Error::custom) } +fn string_to_section_type<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + match s.to_lowercase().as_str() { + "clade" => Ok(SectionType::Clade), + _ => Ok(SectionType::None), + } +} + +fn string_to_section_format<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + match s.to_lowercase().as_str() { + "hex" => Ok(SectionFormat::Hex), + "raw" => Ok(SectionFormat::Raw), + _ => Err(serde::de::Error::custom(format!( + "unknown section format: '{}'. Expected 'hex' or 'raw'.", + s + ))), + } +} pub fn parse_config(config_file_path: &str) -> Result> { let content = fs::read_to_string(config_file_path)?; let config: Config = serde_json::from_str(&content)?; diff --git a/src/utils.rs b/src/utils.rs index 7457ef7..eb95b5a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,7 +4,8 @@ use crate::{ }; use libafl_qemu::{Emulator, FastSnapshot, Regs}; use log::{debug, info}; -use std::env; +use std::fmt::Write; +use std::{env, num::ParseIntError}; /// Runs the emulator without fuzzing, continuously handling breakpoints. /// @@ -24,7 +25,6 @@ pub(crate) fn run_no_fuzzer(config: Config) -> ! { set_breakpoints(&emu, config.clone()); info!("Breakpoints set"); - unsafe { let _ = emu.run(); } @@ -94,7 +94,10 @@ pub(crate) fn boot_firmware(config: &Config, emu: &Emulator) -> Option Emulator { emu } + +pub fn decode_hex(s: &str) -> Result, ParseIntError> { + (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect() +} + +pub fn encode_hex(bytes: &[u8]) -> String { + let mut s = String::with_capacity(bytes.len() * 2); + for &b in bytes { + write!(&mut s, "{:02x}", b).unwrap(); + } + s +}