use anyhow::Result;
use std::collections::HashMap;
use crate::types::{
Address, Disassembly, Function, InstructionGroup, PseudoCode,
Variable, VariableScope, VariableType
};
pub fn generate_pseudocode(functions: &[Function], disasm: &Disassembly) -> Result<HashMap<Address, PseudoCode>> {
let mut pseudocode_map = HashMap::new();
for function in functions {
let pseudocode = lift_function_to_pseudocode(function, disasm)?;
pseudocode_map.insert(function.address, pseudocode);
}
Ok(pseudocode_map)
}
pub fn lift_function_to_pseudocode(function: &Function, disasm: &Disassembly) -> Result<PseudoCode> {
let mut code_lines = Vec::new();
let mut variables = Vec::new();
let mut comments = HashMap::new();
let mut register_map = HashMap::new();
for i in 0..8 {
register_map.insert(format!("r{}", i), format!("var_{}", i));
}
register_map.insert("sp".to_string(), "stack_ptr".to_string());
register_map.insert("lr".to_string(), "return_addr".to_string());
register_map.insert("pc".to_string(), "program_counter".to_string());
let default_name = format!("func_{:x}", function.address);
let func_name = function.name.as_deref().unwrap_or(&default_name);
code_lines.push(format!("int {}() {{", func_name));
for i in 0..8 {
let var_name = format!("var_{}", i);
variables.push(Variable {
name: var_name.clone(),
var_type: VariableType::Integer(32),
first_use: function.address,
scope: VariableScope::Local,
});
code_lines.push(format!(" int {};", var_name));
}
code_lines.push("".to_string());
for &addr in &function.instructions {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let pseudocode_line = lift_instruction_to_pseudocode(instruction, ®ister_map);
comments.insert(addr, format!("0x{:x}: {} {}", addr, instruction.mnemonic, instruction.operands));
if !pseudocode_line.is_empty() {
code_lines.push(format!(" {}; // 0x{:x}", pseudocode_line, addr));
}
match instruction.group {
InstructionGroup::Jump => {
if is_conditional_jump(instruction) {
code_lines.push(" // Conditional branch".to_string());
} else {
code_lines.push(" goto label; // Unconditional jump".to_string());
}
}
InstructionGroup::Call => {
code_lines.push(" // Function call".to_string());
}
InstructionGroup::Return => {
code_lines.push(" return var_0; // Return statement".to_string());
}
_ => {}
}
}
}
code_lines.push("}".to_string());
let confidence = calculate_confidence(&function.instructions, disasm);
Ok(PseudoCode {
function_address: function.address,
function_name: function.name.clone(),
code: code_lines.join("\n"),
variables,
comments,
confidence,
})
}
fn lift_instruction_to_pseudocode(instruction: &crate::types::Instruction, register_map: &HashMap<String, String>) -> String {
let mnemonic = instruction.mnemonic.to_lowercase();
let operands = &instruction.operands;
match mnemonic.as_str() {
"mov" => lift_move_instruction(operands, register_map),
"ldr" => lift_load_instruction(operands, register_map),
"str" => lift_store_instruction(operands, register_map),
"add" => lift_arithmetic_instruction("add", operands, register_map),
"sub" => lift_arithmetic_instruction("sub", operands, register_map),
"mul" => lift_arithmetic_instruction("mul", operands, register_map),
"and" => lift_logical_instruction("and", operands, register_map),
"orr" => lift_logical_instruction("or", operands, register_map),
"eor" => lift_logical_instruction("xor", operands, register_map),
"cmp" => lift_compare_instruction(operands, register_map),
"tst" => lift_test_instruction(operands, register_map),
"b" | "bl" => lift_branch_instruction(&mnemonic, operands),
"push" => lift_push_instruction(operands, register_map),
"pop" => lift_pop_instruction(operands, register_map),
"nop" => "/* no operation */".to_string(),
_ => format!("/* {} {} */", mnemonic, operands),
}
}
fn lift_move_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() == 2 {
let dest = translate_operand(parts[0], register_map);
let src = translate_operand(parts[1], register_map);
format!("{} = {}", dest, src)
} else {
format!("/* mov {} */", operands)
}
}
fn lift_load_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() == 2 {
let dest = translate_operand(parts[0], register_map);
let src = translate_memory_operand(parts[1], register_map);
format!("{} = {}", dest, src)
} else {
format!("/* ldr {} */", operands)
}
}
fn lift_store_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() == 2 {
let src = translate_operand(parts[0], register_map);
let dest = translate_memory_operand(parts[1], register_map);
format!("{} = {}", dest, src)
} else {
format!("/* str {} */", operands)
}
}
fn lift_arithmetic_instruction(op: &str, operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
match parts.len() {
2 => {
let dest = translate_operand(parts[0], register_map);
let src = translate_operand(parts[1], register_map);
format!("{} {}= {}", dest, op_to_assignment(op), src)
}
3 => {
let dest = translate_operand(parts[0], register_map);
let src1 = translate_operand(parts[1], register_map);
let src2 = translate_operand(parts[2], register_map);
format!("{} = {} {} {}", dest, src1, op_to_operator(op), src2)
}
_ => format!("/* {} {} */", op, operands),
}
}
fn lift_logical_instruction(op: &str, operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() >= 2 {
let dest = translate_operand(parts[0], register_map);
let src1 = translate_operand(parts[1], register_map);
if parts.len() == 3 {
let src2 = translate_operand(parts[2], register_map);
format!("{} = {} {} {}", dest, src1, op, src2)
} else {
format!("{} {}= {}", dest, op, src1)
}
} else {
format!("/* {} {} */", op, operands)
}
}
fn lift_compare_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() == 2 {
let op1 = translate_operand(parts[0], register_map);
let op2 = translate_operand(parts[1], register_map);
format!("flags = compare({}, {})", op1, op2)
} else {
format!("/* cmp {} */", operands)
}
}
fn lift_test_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let parts: Vec<&str> = operands.split(',').map(|s| s.trim()).collect();
if parts.len() == 2 {
let op1 = translate_operand(parts[0], register_map);
let op2 = translate_operand(parts[1], register_map);
format!("flags = test({} & {})", op1, op2)
} else {
format!("/* tst {} */", operands)
}
}
fn lift_branch_instruction(mnemonic: &str, operands: &str) -> String {
if mnemonic == "bl" {
format!("call({})", operands.trim())
} else if mnemonic.len() > 1 {
let condition = &mnemonic[1..];
format!("if (condition_{}) goto {}", condition, operands.trim())
} else {
format!("goto {}", operands.trim())
}
}
fn lift_push_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let regs = parse_register_list(operands);
let translated_regs: Vec<String> = regs.iter()
.map(|reg| translate_operand(reg, register_map))
.collect();
format!("push({})", translated_regs.join(", "))
}
fn lift_pop_instruction(operands: &str, register_map: &HashMap<String, String>) -> String {
let regs = parse_register_list(operands);
let translated_regs: Vec<String> = regs.iter()
.map(|reg| translate_operand(reg, register_map))
.collect();
format!("pop({})", translated_regs.join(", "))
}
fn translate_operand(operand: &str, register_map: &HashMap<String, String>) -> String {
let operand = operand.trim();
if operand.starts_with('#') {
return operand[1..].to_string();
}
if operand.starts_with("0x") {
return operand.to_string();
}
if let Some(var_name) = register_map.get(operand) {
return var_name.clone();
}
operand.to_string()
}
fn translate_memory_operand(operand: &str, register_map: &HashMap<String, String>) -> String {
let operand = operand.trim();
if operand.starts_with('[') && operand.ends_with(']') {
let inner = &operand[1..operand.len()-1];
let base_reg = translate_operand(inner, register_map);
format!("*{}", base_reg)
} else {
translate_operand(operand, register_map)
}
}
fn parse_register_list(operands: &str) -> Vec<String> {
if operands.starts_with('{') && operands.ends_with('}') {
let inner = &operands[1..operands.len()-1];
inner.split(',').map(|s| s.trim().to_string()).collect()
} else {
vec![operands.trim().to_string()]
}
}
fn op_to_assignment(op: &str) -> &str {
match op {
"add" => "+",
"sub" => "-",
"mul" => "*",
_ => "=",
}
}
fn op_to_operator(op: &str) -> &str {
match op {
"add" => "+",
"sub" => "-",
"mul" => "*",
_ => op,
}
}
fn is_conditional_jump(instruction: &crate::types::Instruction) -> bool {
let mnemonic = instruction.mnemonic.to_lowercase();
mnemonic.starts_with("b") && mnemonic.len() > 1 && mnemonic != "bl" && mnemonic != "blx"
}
fn calculate_confidence(instructions: &[Address], disasm: &Disassembly) -> f32 {
if instructions.is_empty() {
return 0.0;
}
let mut confidence_factors = ConfidenceFactors::new();
analyze_instruction_coverage(instructions, disasm, &mut confidence_factors);
analyze_control_flow_patterns(instructions, disasm, &mut confidence_factors);
analyze_data_flow_patterns(instructions, disasm, &mut confidence_factors);
analyze_architecture_support(instructions, disasm, &mut confidence_factors);
analyze_semantic_validity(instructions, disasm, &mut confidence_factors);
confidence_factors.calculate_final_score()
}
#[derive(Debug)]
struct ConfidenceFactors {
instruction_coverage: f32, instruction_complexity: f32, control_flow_clarity: f32, data_flow_consistency: f32, architecture_support: f32, semantic_validity: f32, }
impl ConfidenceFactors {
fn new() -> Self {
Self {
instruction_coverage: 0.0,
instruction_complexity: 0.5, control_flow_clarity: 0.5,
data_flow_consistency: 0.5,
architecture_support: 0.7, semantic_validity: 0.5,
}
}
fn calculate_final_score(&self) -> f32 {
let weights = [
(self.instruction_coverage, 0.25), (self.instruction_complexity, 0.15), (self.control_flow_clarity, 0.20), (self.data_flow_consistency, 0.15), (self.architecture_support, 0.10), (self.semantic_validity, 0.15), ];
let weighted_sum: f32 = weights.iter().map(|(score, weight)| score * weight).sum();
weighted_sum.min(1.0).max(0.0)
}
}
fn analyze_instruction_coverage(instructions: &[Address], disasm: &Disassembly, factors: &mut ConfidenceFactors) {
let mut known_instructions = 0;
let mut complex_instructions = 0;
let total_instructions = instructions.len();
for &addr in instructions {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let mnemonic = instruction.mnemonic.to_lowercase();
let support_level = get_instruction_support_level(&mnemonic);
match support_level {
InstructionSupport::FullSupport => {
known_instructions += 1;
}
InstructionSupport::PartialSupport => {
known_instructions += 1;
complex_instructions += 1;
}
InstructionSupport::BasicSupport => {
}
InstructionSupport::NoSupport => {
}
}
}
}
factors.instruction_coverage = known_instructions as f32 / total_instructions as f32;
if complex_instructions > 0 {
let complexity_ratio = complex_instructions as f32 / total_instructions as f32;
factors.instruction_complexity = 1.0 - (complexity_ratio * 0.5); }
}
fn analyze_control_flow_patterns(instructions: &[Address], disasm: &Disassembly, factors: &mut ConfidenceFactors) {
let mut jump_count = 0;
let mut _conditional_jumps = 0;
let mut call_count = 0;
let mut return_count = 0;
let mut unclear_branches = 0;
for &addr in instructions {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
match instruction.group {
InstructionGroup::Jump => {
jump_count += 1;
if is_conditional_jump(instruction) {
_conditional_jumps += 1;
if !has_clear_condition(instruction) {
unclear_branches += 1;
}
}
}
InstructionGroup::Call => call_count += 1,
InstructionGroup::Return => return_count += 1,
_ => {}
}
}
}
let total_control_flow = jump_count + call_count + return_count;
if total_control_flow == 0 {
factors.control_flow_clarity = 1.0; } else {
let complexity_penalty = (unclear_branches as f32 / total_control_flow as f32) * 0.5;
let branch_density = total_control_flow as f32 / instructions.len() as f32;
let density_penalty = if branch_density > 0.3 { 0.2 } else { 0.0 };
factors.control_flow_clarity = (1.0 - complexity_penalty - density_penalty).max(0.1);
}
}
fn analyze_data_flow_patterns(instructions: &[Address], disasm: &Disassembly, factors: &mut ConfidenceFactors) {
let mut register_usage = HashMap::new();
let mut _memory_accesses = 0;
let mut undefined_operations = 0;
for &addr in instructions {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let registers = extract_registers_from_operands(&instruction.operands);
for reg in registers {
*register_usage.entry(reg).or_insert(0) += 1;
}
if matches!(instruction.group, InstructionGroup::Load | InstructionGroup::Store) {
_memory_accesses += 1;
}
if instruction.group == InstructionGroup::Other {
undefined_operations += 1;
}
}
}
let total_ops = instructions.len();
let undefined_ratio = undefined_operations as f32 / total_ops as f32;
factors.data_flow_consistency = (1.0 - undefined_ratio).max(0.0);
if !register_usage.is_empty() {
let avg_usage = register_usage.values().sum::<usize>() as f32 / register_usage.len() as f32;
let usage_variance = register_usage.values()
.map(|&count| (count as f32 - avg_usage).powi(2))
.sum::<f32>() / register_usage.len() as f32;
let consistency_bonus = if usage_variance < avg_usage { 0.1 } else { 0.0 };
factors.data_flow_consistency = (factors.data_flow_consistency + consistency_bonus).min(1.0);
}
}
#[derive(Debug, PartialEq)]
enum InstructionSupport {
FullSupport, PartialSupport, BasicSupport, NoSupport, }
fn get_instruction_support_level(mnemonic: &str) -> InstructionSupport {
match mnemonic {
"mov" | "ldr" | "str" | "add" | "sub" | "mul" | "div" |
"and" | "orr" | "eor" | "cmp" | "tst" | "push" | "pop" |
"nop" => InstructionSupport::FullSupport,
"b" | "bl" | "bx" | "blx" | "beq" | "bne" | "blt" | "bgt" |
"ble" | "bge" | "bhi" | "bls" | "bcc" | "bcs" |
"lsl" | "lsr" | "asr" | "ror" | "rrx" |
"adc" | "sbc" | "rsb" | "rsc" |
"ldm" | "stm" | "ldmia" | "stmia" | "ldmdb" | "stmdb" => InstructionSupport::PartialSupport,
"swi" | "svc" | "bkpt" | "wfi" | "wfe" | "sev" |
"msr" | "mrs" | "mcr" | "mrc" | "cdp" |
"vmov" | "vadd" | "vsub" | "vmul" | "vdiv" |
"ldrex" | "strex" | "clrex" | "dmb" | "dsb" | "isb" => InstructionSupport::BasicSupport,
_ => InstructionSupport::NoSupport,
}
}
fn has_clear_condition(instruction: &crate::types::Instruction) -> bool {
let mnemonic = instruction.mnemonic.to_lowercase();
matches!(mnemonic.as_str(),
"beq" | "bne" | "blt" | "bgt" | "ble" | "bge" |
"bhi" | "bls" | "bcc" | "bcs" | "bmi" | "bpl" |
"bvs" | "bvc" | "bal"
)
}
fn extract_registers_from_operands(operands: &str) -> Vec<String> {
let mut registers = Vec::new();
for part in operands.split(&[',', ' ', '[', ']', '{', '}']) {
let part = part.trim();
if part.is_empty() {
continue;
}
if part.starts_with('r') && part.len() <= 3 {
if let Ok(_) = part[1..].parse::<u8>() {
registers.push(part.to_string());
}
} else if matches!(part, "sp" | "lr" | "pc" | "fp") {
registers.push(part.to_string());
}
else if matches!(part, "eax" | "ebx" | "ecx" | "edx" | "esi" | "edi" | "esp" | "ebp" |
"rax" | "rbx" | "rcx" | "rdx" | "rsi" | "rdi" | "rsp" | "rbp" |
"r8" | "r9" | "r10" | "r11" | "r12" | "r13" | "r14" | "r15") {
registers.push(part.to_string());
}
}
registers
}
fn analyze_architecture_support(instructions: &[Address], disasm: &Disassembly, factors: &mut ConfidenceFactors) {
let mut arch_indicators = HashMap::new();
for &addr in instructions {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let mnemonic = instruction.mnemonic.to_lowercase();
if matches!(mnemonic.as_str(), "mov" | "ldr" | "str" | "add" | "sub" | "b" | "bl" | "push" | "pop") {
*arch_indicators.entry("arm").or_insert(0) += 1;
} else if matches!(mnemonic.as_str(), "mov" | "add" | "sub" | "jmp" | "call" | "ret" | "push" | "pop") {
*arch_indicators.entry("x86").or_insert(0) += 1;
} else if mnemonic.starts_with("v") {
*arch_indicators.entry("vector").or_insert(0) += 1;
}
}
}
if let Some((dominant_arch, count)) = arch_indicators.iter().max_by_key(|(_, &count)| count) {
let total_instructions = instructions.len();
let coverage_ratio = *count as f32 / total_instructions as f32;
factors.architecture_support = match *dominant_arch {
"arm" => (0.9 * coverage_ratio).min(0.9), "x86" => (0.8 * coverage_ratio).min(0.8), "vector" => (0.6 * coverage_ratio).min(0.6), _ => 0.5,
};
}
}
fn analyze_semantic_validity(instructions: &[Address], disasm: &Disassembly, factors: &mut ConfidenceFactors) {
let mut semantic_issues = 0;
let mut valid_patterns = 0;
for window in instructions.windows(2) {
if let (Some(instr1), Some(instr2)) = (
disasm.instructions.iter().find(|i| i.address == window[0]),
disasm.instructions.iter().find(|i| i.address == window[1])
) {
if is_valid_instruction_sequence(instr1, instr2) {
valid_patterns += 1;
} else if is_problematic_sequence(instr1, instr2) {
semantic_issues += 1;
}
}
}
if has_valid_function_structure(instructions, disasm) {
valid_patterns += 2; }
let total_patterns = valid_patterns + semantic_issues;
if total_patterns > 0 {
factors.semantic_validity = valid_patterns as f32 / total_patterns as f32;
} else {
factors.semantic_validity = 0.7; }
}
fn is_valid_instruction_sequence(instr1: &crate::types::Instruction, instr2: &crate::types::Instruction) -> bool {
let mnemonic1 = instr1.mnemonic.to_lowercase();
let mnemonic2 = instr2.mnemonic.to_lowercase();
if mnemonic1 == "cmp" && mnemonic2.starts_with("b") && mnemonic2.len() > 1 {
return true;
}
matches!(
(mnemonic1.as_str(), mnemonic2.as_str()),
("ldr", "add") | ("ldr", "sub") | ("ldr", "cmp") | ("ldr", "str") |
("push", "mov") | ("push", "sub") |
("bl", "cmp") | ("bl", "mov") |
("sub", "str")
)
}
fn is_problematic_sequence(instr1: &crate::types::Instruction, instr2: &crate::types::Instruction) -> bool {
let mnemonic1 = instr1.mnemonic.to_lowercase();
let mnemonic2 = instr2.mnemonic.to_lowercase();
let mnemonics = (mnemonic1.as_str(), mnemonic2.as_str());
if mnemonics == ("mov", "mov") && instr1.operands == instr2.operands {
return true;
}
if mnemonics == ("push", "pop") && instr1.operands == instr2.operands {
return true;
}
if mnemonics.0 == "ret" && mnemonics.1 != "nop" { return true;
}
false
}
fn has_valid_function_structure(instructions: &[Address], disasm: &Disassembly) -> bool {
if instructions.len() < 3 {
return false;
}
let has_prologue = instructions.iter().take(3).any(|&addr| {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let mnemonic = instruction.mnemonic.to_lowercase();
matches!(mnemonic.as_str(), "push" | "sub" | "mov") &&
(instruction.operands.contains("sp") || instruction.operands.contains("lr"))
} else {
false
}
});
let has_epilogue = instructions.iter().rev().take(3).any(|&addr| {
if let Some(instruction) = disasm.instructions.iter().find(|i| i.address == addr) {
let mnemonic = instruction.mnemonic.to_lowercase();
matches!(mnemonic.as_str(), "pop" | "add" | "bx" | "ret") &&
(instruction.operands.contains("sp") || instruction.operands.contains("lr") || instruction.operands.contains("pc"))
} else {
false
}
});
has_prologue && has_epilogue
}