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).
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/dhand hex-looking labels are parsed as numberssrc/extractor/smda.rs:1265(parse_operand_to_number, case 2):"ah"→ striph→"a"→Some(10)— somov al, ahemitsNumber(0xA)(same forbh→0xB,ch→0xC,dh→0xD). Case 5 (line 1292) has the same problem for bare hex without a suffix: labels likebeef/faceparse as numbers. Per the Intel convention anh-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:On a 64-bit binary
mov rax, -1emitsNumber(0xFFFFFFFF)instead ofNumber(0xFFFFFFFFFFFFFFFF). The mask must follow the function's bitness.3.
stack stringcharacteristic pushed once per remaining instructionsrc/extractor/smda.rs:308-321: after the byte count crosses the threshold the feature is pushed inside the loop with nobreak, so a block with N instructions after the threshold gets N duplicateCharacteristic("stack string")entries. Emit once per basic block.4. Every ASCII string is emitted twice
extract_file_stringscalls bothextract_ascii_stringsandextract_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 withextract_ascii_strings(whose class is even slightly wider: includes\t).