Skip to content

Silent feature corruption: registers parsed as numbers, u32-masked negatives on x64, duplicate stack-string and ASCII-string features #24

Description

@mnaza

Four extraction bugs on master (0.5.2) that silently emit wrong or duplicate features. PR with fixes + regression tests follows.

1. Registers ah/bh/ch/dh and hex-looking labels are parsed as numbers

src/extractor/smda.rs:1265 (parse_operand_to_number, case 2):

if let Some(stripped_operand) = operand.strip_suffix('h') {
    return i128::from_str_radix(stripped_operand, 16).ok();
}

"ah" → strip h"a"Some(10) — so mov al, ah emits Number(0xA) (same for bh→0xB, ch→0xC, dh→0xD). Case 5 (line 1292) has the same problem for bare hex without a suffix: labels like beef/face parse as numbers. Per the Intel convention an h-suffixed hex literal must start with a digit (0ABh); requiring a leading digit for both cases fixes the register/label collisions while keeping real literals working.

2. Negative immediates masked to 32 bits on x64

src/extractor/smda.rs:1320:

let masked_value = (s as u32) as i128;

On a 64-bit binary mov rax, -1 emits Number(0xFFFFFFFF) instead of Number(0xFFFFFFFFFFFFFFFF). The mask must follow the function's bitness.

3. stack string characteristic pushed once per remaining instruction

src/extractor/smda.rs:308-321: after the byte count crosses the threshold the feature is pushed inside the loop with no break, so a block with N instructions after the threshold gets N duplicate Characteristic("stack string") entries. Emit once per basic block.

4. Every ASCII string is emitted twice

extract_file_strings calls both extract_ascii_strings and extract_unicode_strings — but the latter also runs a UTF-8 pass (re_utf8, src/extractor/smda.rs:1776) whose pattern [\x20-\x7E]{4,} is the same printable-ASCII class. Result: each ASCII string appears twice in the file's string feature set (double matching work for every string rule, duplicate VAs in feature maps). The UTF-8 pass should be dropped from the Unicode extractor — ASCII coverage stays with extract_ascii_strings (whose class is even slightly wider: includes \t).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions