use super::*;
fn jpeg_style_indirect_spec() -> OffsetSpec {
OffsetSpec::Indirect {
base_offset: 2,
base_relative: false,
pointer_type: TypeKind::Short {
endian: Endianness::Big,
signed: true,
},
adjustment: 2,
adjustment_op: IndirectAdjustmentOp::Add,
result_relative: false,
endian: Endianness::Big,
}
}
#[test]
fn test_use_indirect_result_is_base_relative_jpeg_shape() {
let config = EvaluationConfig {
stop_at_first_match: false,
..EvaluationConfig::default()
};
let inner = vec![byte_eq_rule(0, 0xAA, "reached-150")];
let outer = vec![use_rule_indirect_at("inner", jpeg_style_indirect_spec())];
let table = build_name_table(vec![("outer", outer), ("inner", inner)]);
let env = std::sync::Arc::new(RuleEnvironment {
name_table: std::sync::Arc::new(table),
root_rules: std::sync::Arc::from(&[][..]),
});
let mut context = EvaluationContext::new(config).with_rule_env(env);
let mut buffer = vec![0x00u8; 160];
buffer[22] = 0x00;
buffer[23] = 0x80;
buffer[150] = 0xAA;
buffer[130] = 0xBB;
let rules = vec![use_rule_at("outer", 20)];
let matches = evaluate_rules(&rules, &buffer, &mut context).unwrap();
assert!(
matches.iter().any(|m| m.message.contains("reached-150")),
"a `use` rule's Indirect result must be rebased by the use-site base \
(20 + 130 = 150, GOTCHAS S3.10); a match at 130 means the base was \
dropped. got {matches:?}"
);
}
#[test]
fn test_use_indirect_result_at_zero_base_is_unrebased() {
let config = EvaluationConfig {
stop_at_first_match: false,
..EvaluationConfig::default()
};
let inner = vec![byte_eq_rule(0, 0xBB, "reached-130")];
let outer_rule = use_rule_indirect_at("inner", jpeg_style_indirect_spec());
let table = build_name_table(vec![("inner", inner)]);
let env = std::sync::Arc::new(RuleEnvironment {
name_table: std::sync::Arc::new(table),
root_rules: std::sync::Arc::from(&[][..]),
});
let mut context = EvaluationContext::new(config).with_rule_env(env);
let mut buffer = vec![0x00u8; 160];
buffer[2] = 0x00;
buffer[3] = 0x80;
buffer[130] = 0xBB;
buffer[150] = 0xAA;
let matches = evaluate_rules(&[outer_rule], &buffer, &mut context).unwrap();
assert!(
matches.iter().any(|m| m.message.contains("reached-130")),
"at a zero base the Indirect result is already absolute and must not \
move; got {matches:?}"
);
}
#[test]
fn test_indirect_result_inside_use_stays_absolute_macho_shape() {
let config = EvaluationConfig {
stop_at_first_match: false,
..EvaluationConfig::default()
};
let root_rules = vec![byte_eq_rule(0, 0xAA, "reentered-at-64")];
let subroutine = vec![indirect_rule_at_indirect_offset(
OffsetSpec::Indirect {
base_offset: 8,
base_relative: false,
pointer_type: TypeKind::Long {
endian: Endianness::Big,
signed: true,
},
adjustment: 0,
adjustment_op: IndirectAdjustmentOp::Add,
result_relative: false,
endian: Endianness::Big,
},
"",
)];
let table = build_name_table(vec![("macho", subroutine)]);
let env = std::sync::Arc::new(RuleEnvironment {
name_table: std::sync::Arc::new(table),
root_rules: std::sync::Arc::from(root_rules.as_slice()),
});
let mut context = EvaluationContext::new(config).with_rule_env(env);
let mut buffer = vec![0x00u8; 128];
buffer[16..20].copy_from_slice(&64u32.to_be_bytes());
buffer[64] = 0xAA;
buffer[72] = 0xBB;
let rules = vec![use_rule_at("macho", 8)];
let matches = evaluate_rules(&rules, &buffer, &mut context).unwrap();
assert!(
matches.iter().any(|m| {
crate::evaluator::strip_no_separator_marker(&m.message).unwrap_or(&m.message)
== "reentered-at-64"
}),
"an `indirect` rule's dereferenced result is an absolute file position \
and must not be biased by the subroutine base (GOTCHAS S3.10); a match \
at 72 would mean it was. got {matches:?}"
);
}