Skip to content

Implement UTF-16 string extraction with confidence scoring and minimum length filtering - #109

Merged
unclesp1d3r merged 2 commits into
mainfrom
implement-utf-16le-string-extraction-with-confidence-scoring-and-minimum-length-filtering
Nov 13, 2025
Merged

unclesp1d3r merged 2 commits into
mainfrom
implement-utf-16le-string-extraction-with-confidence-scoring-and-minimum-length-filtering

Conversation

@unclesp1d3r

Copy link
Copy Markdown
Member

This pull request introduces comprehensive UTF-16 string extraction support to the codebase, including both UTF-16LE and UTF-16BE encodings, with advanced confidence scoring and noise filtering. The documentation and code are updated to reflect new configuration options, usage examples, and integration with the main string extraction pipeline. The changes also improve configurability for encoding selection and extraction thresholds, and add new tests to verify encoding-specific extraction logic.

Addresses issues #11 and #12

UTF-16 Extraction Support and Configuration

  • Added full UTF-16LE and UTF-16BE extraction, including automatic byte order detection, confidence scoring, and false positive prevention. Extraction is configurable via the new Utf16ExtractionConfig and integrated into the main ExtractionConfig struct. [1] [2] [3] [4] [5]
  • Updated documentation to describe UTF-16 extraction algorithms, configuration, confidence scoring, usage examples, and recommendations for different binary formats. [1] [2] [3]

API and Codebase Integration

  • Exposed new UTF-16 extraction entry points in src/extraction/mod.rs and updated public API to include extract_utf16_strings, extract_utf16le_strings, and related types. [1] [2]
  • Modified the main extraction pipeline to conditionally run ASCII, UTF-8, and UTF-16 extractors based on enabled encodings, supporting flexible extraction strategies per configuration. [1] [2] [3]

Testing and Validation

  • Added tests to verify encoding-specific extraction, including disabling ASCII and ensuring UTF-8 extraction still functions as expected.

Documentation Improvements

  • Clarified in PE resource extraction docs that general UTF-16 string extraction is now handled by the dedicated utf16 module.

These changes collectively provide robust and configurable extraction of UTF-16 strings from binary data, improve accuracy through confidence scoring, and enhance the flexibility of the string extraction framework.

- Added UTF-16LE extraction capabilities to the string extraction framework, enabling the extraction of UTF-16LE encoded strings from Windows PE binaries.
- Introduced `extract_utf16le_strings` and `Utf16ExtractionConfig` for customizable extraction parameters, including minimum character length and confidence thresholds.
- Implemented confidence scoring to enhance the accuracy of string detection, incorporating factors such as printable character percentage and null termination.
- Updated documentation to include detailed usage examples and configuration options for UTF-16LE extraction.
- Added unit and integration tests to validate the functionality and performance of the new extraction features.

This enhancement significantly improves the library's ability to handle UTF-16LE encoded strings, facilitating better analysis of binary data.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
- Updated the UTF-16 extraction module to support both UTF-16LE and UTF-16BE encodings, including automatic byte order detection.
- Introduced advanced confidence scoring mechanisms to improve the accuracy of string detection, incorporating multiple heuristics for filtering false positives.
- Enhanced the `Utf16ExtractionConfig` to allow customizable parameters such as byte order selection and confidence thresholds.
- Updated documentation to reflect the new features, including detailed usage examples for both extraction methods.
- Added unit and integration tests to validate the functionality and performance of the updated extraction capabilities.

This enhancement significantly improves the library's ability to handle UTF-16 encoded strings, facilitating better analysis of binary data.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Nov 13, 2025
@coderabbitai

coderabbitai Bot commented Nov 13, 2025 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

Failed to post review comments

Summary by CodeRabbit

Release Notes

  • New Features

    • Added UTF-16 string extraction support with automatic byte order detection (little-endian and big-endian variants).
    • Enhanced extraction configuration with UTF-16-specific settings including confidence thresholds and byte order control.
  • Documentation

    • Expanded documentation with comprehensive UTF-16 extraction usage examples, configuration patterns, and performance considerations.
  • Tests

    • Added extensive test coverage for UTF-16 extraction across multiple scenarios including encoding filtering, byte order handling, and edge cases.

Walkthrough

This PR introduces comprehensive UTF-16 string extraction support to the codebase, adding a new utf16 module with LE/BE/auto-detection capabilities, confidence scoring, and heuristic-based validation. UTF-16 extraction is now integrated into the core extraction pipeline, wired into ExtractionConfig with three new fields, and exposed via public API with extensive documentation and test coverage.

Changes

Cohort / File(s) Summary
UTF-16 Extraction Module
src/extraction/utf16.rs
Introduces comprehensive UTF-16 extraction with ByteOrder enum (LE/BE/Auto), Utf16ExtractionConfig struct, confidence scoring heuristics, null-pattern detection, and section-aware extraction with noise filtering. Includes extract_utf16_strings, extract_utf16le_strings, and extract_from_section functions.
Extraction Pipeline Integration
src/extraction/mod.rs
Extends ExtractionConfig with utf16_min_confidence, utf16_byte_order, and utf16_confidence_threshold fields. Wires UTF-16 extraction into the core pipeline with conditional execution based on enabled encodings. Updates ASCII/UTF-8 extraction to respect enablement flags. Exports public UTF-16 API.
Utility Functions
src/extraction/util.rs
Adds decode_utf16le_bytes(bytes, trim_nulls) helper to decode UTF-16LE byte sequences into Rust Strings with optional null trimming.
PE Resources Update
src/extraction/pe_resources.rs
Delegates internal UTF-16LE decoding to shared util::decode_utf16le_bytes, centralizing decoding logic.
Public API Exports
src/lib.rs
Re-exports Utf16ExtractionConfig alongside existing extraction configs. Updates module documentation to reference UTF-16LE extraction capabilities.
Documentation
docs/src/string-extraction.md
Expands Encoding Support section with UTF-16LE/UTF-16BE details, byte order auto-detection, confidence scoring mechanics, heuristics, and configuration examples. Integrates UTF-16 usage patterns across extraction and noise-filter sections.
Test Suite
tests/test_utf16_extraction.rs
Comprehensive unit tests covering LE/BE extraction, auto-detection, confidence thresholds, null patterns, surrogate pairs, section metadata, noise filtering, boundary conditions, and alignment handling.
Integration Tests
tests/integration_extraction.rs
Adds scenario-based tests validating UTF-16 extraction from PE/.rdata sections, encoding filtering, mixed-encoding scenarios, byte order handling, and metadata propagation.

Sequence Diagram

sequenceDiagram
    participant User
    participant Extractor as Extraction Pipeline
    participant ASCII as ASCII/UTF-8
    participant UTF16 as UTF-16 Module
    participant Noise as Noise Filter
    participant Result

    User->>Extractor: extract_strings(data, config)
    
    alt ASCII Enabled
        Extractor->>ASCII: extract (if enabled)
        ASCII-->>Extractor: ASCII results
    end
    
    alt UTF-16 Enabled
        Extractor->>UTF16: extract_utf16_strings(data, config)
        
        alt ByteOrder = Auto
            UTF16->>UTF16: extract_utf16le_strings
            UTF16->>UTF16: extract_utf16be_strings
            UTF16->>UTF16: merge & deduplicate by offset
        else ByteOrder = LE
            UTF16->>UTF16: extract_utf16le_strings
        else ByteOrder = BE
            UTF16->>UTF16: extract_utf16be_strings
        end
        
        UTF16->>UTF16: compute confidence scores<br/>(heuristics: unicode range,<br/>printable ratio, null patterns)
        UTF16->>UTF16: filter by confidence_threshold
        UTF16-->>Extractor: UTF-16 results
    end
    
    Extractor->>Extractor: merge ASCII + UTF-16<br/>(deduplicate ASCII overlaps)
    
    alt Noise Filtering Enabled
        Extractor->>Noise: apply noise filter
        Noise-->>Extractor: filtered results
    end
    
    Extractor-->>Result: final string list
    Result-->>User: return
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • src/extraction/utf16.rs: Dense heuristic logic with confidence scoring across multiple components (Unicode validation, printable ratios, null-pattern detection, byte-order consistency), surrogate pair handling, and section-aware extraction with noise filter integration. Requires careful validation of confidence formulas and edge cases.
  • src/extraction/mod.rs: Integration point touching core extraction flow, conditional encoding execution paths, and ExtractionConfig validation—requires verification of encoding precedence and deduplication logic.
  • Test suites (test_utf16_extraction.rs + integration_extraction.rs): Extensive coverage (~150+ test cases) that exercises complex scenarios (auto-detection, confidence thresholds, mixed encodings, surrogate pairs, section metadata)—ensure test assumptions align with intended behavior.
  • Public API surface: New exports and struct fields across extraction module, util module, and lib.rs require consistency review.

Possibly related issues

  • Issue #12: Directly implements the ByteOrder enum and UTF-16BE extraction with confidence scoring requirements outlined in the issue.
  • Issue #11: Directly addresses UTF-16 extraction functionality request, adding the exact module structure, config API, and confidence heuristics specified.
  • Issue #62: Related through shared pattern of introducing a new extraction module and integrating it into src/extraction/mod.rs.

Possibly related PRs

  • PR #66: Related through shared modifications to PE resource extraction and UTF-16 decoding code paths (src/extraction/pe_resources.rs).
  • PR #108: This PR extends the extraction framework introduced in #108 by adding the utf16 module and wiring it into the same ExtractionConfig and noise-filtering pipeline.

Suggested labels

enhancement, lang:rust, area:analyzer, documentation

Suggested reviewers

  • Copilot

Poem

🐰 Hop, skip, and UTF-16 now!

Byte orders dance (LE and BE),
Auto-detect with confidence glee,
Heuristics catch what's true and clear,
While null-byte patterns disappear! ✨

— The CodeRabbit's extraction cheer 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and clearly summarizes the main change: implementing UTF-16 string extraction with confidence scoring and minimum length filtering, which aligns with the substantial code additions and documentation updates.
Description check ✅ Passed The PR description is well-organized, directly related to the changeset, and covers the major components: UTF-16 extraction support with configuration, API integration, testing, and documentation improvements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch implement-utf-16le-string-extraction-with-confidence-scoring-and-minimum-length-filtering

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (2)
  • UTF-16: Entity not found: Issue - Could not find referenced Issue.
  • UTF-8: Entity not found: Issue - Could not find referenced Issue.

Comment @coderabbitai help to get the list of available commands and usage tips.

@unclesp1d3r

Copy link
Copy Markdown
Member Author

@coderabbitai fix the PR title

@unclesp1d3r unclesp1d3r self-assigned this Nov 13, 2025
@dosubot

dosubot Bot commented Nov 13, 2025

Copy link
Copy Markdown

Related Documentation

Checked 14 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

@unclesp1d3r
unclesp1d3r requested a review from Copilot November 13, 2025 05:32
@dosubot dosubot Bot added area:analyzer Binary analyzer functionality enhancement New feature or request labels Nov 13, 2025
@coderabbitai coderabbitai Bot changed the title Implement utf 16le string extraction with confidence scoring and minimum length filtering Implement UTF-16 string extraction with confidence scoring and minimum length filtering Nov 13, 2025
@coderabbitai

coderabbitai Bot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the current PR title
gh pr view 109 --json title,url --jq '.title'

Length of output: 89


🏁 Script executed:

#!/bin/bash
# Update the PR title to reflect that it implements both UTF-16LE and UTF-16BE
gh pr edit 109 --title "Implement UTF-16 string extraction with confidence scoring and minimum length filtering"

Length of output: 60


@unclesp1d3r: I've updated the PR title from "Implement utf 16le string extraction..." to "Implement UTF-16 string extraction with confidence scoring and minimum length filtering".

The change better reflects that this PR implements both UTF-16LE and UTF-16BE string extraction (not just LE), and uses proper capitalization for "UTF-16".

@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation lang:rust Rust implementation labels Nov 13, 2025
@unclesp1d3r
unclesp1d3r merged commit 185cd81 into main Nov 13, 2025
20 of 22 checks passed
@unclesp1d3r
unclesp1d3r deleted the implement-utf-16le-string-extraction-with-confidence-scoring-and-minimum-length-filtering branch November 13, 2025 05:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request implements comprehensive UTF-16 string extraction support for the StringyMcStringFace library, adding both UTF-16LE and UTF-16BE encoding detection with advanced confidence scoring, noise filtering, and automatic byte order detection. The implementation follows the established ASCII extraction pattern and integrates cleanly with the existing extraction pipeline.

Key changes:

  • Adds full UTF-16 extraction module with confidence-based false positive prevention
  • Introduces two-threshold filtering system combining UTF-16-specific and general noise filtering confidence scores
  • Updates extraction configuration with UTF-16-specific settings and byte order selection

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
tests/test_utf16_extraction.rs Comprehensive unit tests for UTF-16LE/BE extraction covering edge cases, confidence scoring, and various string formats
tests/integration_extraction.rs Integration tests verifying UTF-16 extraction through BasicExtractor with encoding filtering and configuration
src/extraction/utf16.rs Core UTF-16 extraction implementation with LE/BE support, confidence scoring, surrogate pair handling, and section-aware extraction
src/extraction/util.rs Shared UTF-16LE decoding utility function for cross-module reuse
src/extraction/mod.rs Integration of UTF-16 extractors into main pipeline with conditional execution based on enabled encodings
src/extraction/pe_resources.rs Refactored to use shared utility function for UTF-16LE decoding
src/lib.rs Updated public API exports to include UTF-16 extraction configuration
docs/src/string-extraction.md Comprehensive documentation of UTF-16 extraction algorithms, configuration, confidence scoring, and usage recommendations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +923 to +927
// Should find string at even offset (index 0), but may miss the one at odd offset
// Actually, with the current implementation, it should find "Hello" starting at index 1
// because the section starts at 0, so index 1 is odd relative to section start
// But wait - the section data slice starts at index 0, so index 1 is odd within that slice
// Let's verify the behavior

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The test comment (lines 923-927) contains confusing and incorrect explanations about what the test is verifying. The comment says:

"Should find string at even offset (index 0), but may miss the one at odd offset. Actually, with the current implementation, it should find 'Hello' starting at index 1 because the section starts at 0, so index 1 is odd relative to section start. But wait - the section data slice starts at index 0, so index 1 is odd within that slice. Let's verify the behavior"

This rambling commentary suggests uncertainty about the expected behavior and should be removed or clarified. The test should have clear assertions about expected behavior rather than speculative comments about implementation details.

Suggested change
// Should find string at even offset (index 0), but may miss the one at odd offset
// Actually, with the current implementation, it should find "Hello" starting at index 1
// because the section starts at 0, so index 1 is odd relative to section start
// But wait - the section data slice starts at index 0, so index 1 is odd within that slice
// Let's verify the behavior
// Verify that extraction only finds the UTF-16LE string at odd index when scan_both_alignments is enabled.

Copilot uses AI. Check for mistakes.
Comment on lines +921 to +927
let _strings_disabled =
extract_from_section(&section, &data, &config_disabled, None, false, 0.5);
// Should find string at even offset (index 0), but may miss the one at odd offset
// Actually, with the current implementation, it should find "Hello" starting at index 1
// because the section starts at 0, so index 1 is odd relative to section start
// But wait - the section data slice starts at index 0, so index 1 is odd within that slice
// Let's verify the behavior

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable: The _strings_disabled variable (line 921) is assigned but never used. This variable should either be removed, or used in assertions to verify the expected behavior when scan_both_alignments is disabled.

Either remove the assignment or add assertions like:

let strings_disabled = extract_from_section(&section, &data, &config_disabled, None, false, 0.5);
// Add assertions here to verify expected behavior
Suggested change
let _strings_disabled =
extract_from_section(&section, &data, &config_disabled, None, false, 0.5);
// Should find string at even offset (index 0), but may miss the one at odd offset
// Actually, with the current implementation, it should find "Hello" starting at index 1
// because the section starts at 0, so index 1 is odd relative to section start
// But wait - the section data slice starts at index 0, so index 1 is odd within that slice
// Let's verify the behavior
let strings_disabled =
extract_from_section(&section, &data, &config_disabled, None, false, 0.5);
// Should not find string at odd offset (index 1) when scan_both_alignments is disabled
let hello_string_disabled = strings_disabled.iter().find(|s| s.text == "Hello");
assert!(
hello_string_disabled.is_none(),
"Should NOT find 'Hello' string when scan_both_alignments is disabled"
);

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +295
#### UTF-16LE (Little-Endian) ✅

Most common on Windows platforms.
Most common on Windows platforms. Default 3 character minimum.

**Detection heuristics**:

- Even-length sequences
- Even-length sequences (2-byte alignment required)
- Low byte printable, high byte mostly zero
- Null termination patterns (0x00 0x00)
- Advanced confidence scoring with multiple heuristics

#### UTF-16BE (Big Endian)
#### UTF-16BE (Big-Endian) ✅

Less common but found in some formats.
Found in Java .class files, network protocols, some cross-platform binaries.

**Detection heuristics**:

- Even-length sequences
- High byte printable, low byte mostly zero
- Reverse byte order from UTF-16LE
- Same advanced confidence scoring as UTF-16LE

#### Automatic Byte Order Detection ✅

The `ByteOrder::Auto` mode automatically detects and extracts both UTF-16LE and UTF-16BE strings from the same data, avoiding duplicates and correctly identifying the encoding of each string.

#### Implementation

#### Implementation Strategy
UTF-16 extraction is implemented in `src/extraction/utf16.rs` following the pattern established in the ASCII extractor. The implementation provides:

- `extract_utf16_strings()`: Main extraction function supporting both byte orders
- `extract_utf16le_strings()`: UTF-16LE-specific extraction (backward compatibility)
- `extract_from_section()`: Section-aware extraction with proper metadata population
- `Utf16ExtractionConfig`: Configuration for minimum/maximum character count, byte order selection, and confidence thresholds
- `ByteOrder` enum: Control which byte order(s) to scan (LE, BE, Auto)

**Usage Example**:

```rust
fn extract_utf16_strings(data: &[u8], endian: Endianness, min_len: usize) -> Vec<RawString> {
let mut strings = Vec::new();
let mut i = 0;

while i + 1 < data.len() {
let mut char_count = 0;
let start = i;

// Scan for UTF-16 sequence
while i + 1 < data.len() {
let (low, high) = match endian {
Endianness::Little => (data[i], data[i + 1]),
Endianness::Big => (data[i + 1], data[i]),
};

if is_printable_utf16_char(low, high) {
char_count += 1;
i += 2;
} else if low == 0 && high == 0 && char_count >= min_len {
// Null terminator found
break;
} else {
break;
}
}
use stringy::extraction::utf16::{extract_utf16_strings, Utf16ExtractionConfig, ByteOrder};

// Extract UTF-16LE strings from Windows PE binary
let config = Utf16ExtractionConfig {
byte_order: ByteOrder::LE,
min_length: 3,
confidence_threshold: 0.6,
..Default::default()
};
let strings = extract_utf16_strings(data, &config);

if char_count >= min_len {
strings.push(extract_utf16_string(&data[start..i], endian));
}
// Extract both UTF-16LE and UTF-16BE with auto-detection
let config = Utf16ExtractionConfig {
byte_order: ByteOrder::Auto,
..Default::default()
};
let strings = extract_utf16_strings(data, &config);
```

i += 2;
}
**Configuration**:

strings
}
```rust
use stringy::extraction::utf16::{Utf16ExtractionConfig, ByteOrder};

// Default configuration (min_length: 3, byte_order: Auto, confidence_threshold: 0.5)
let config = Utf16ExtractionConfig::default();

// Custom minimum character length
let config = Utf16ExtractionConfig::new(5);

// Custom configuration
let mut config = Utf16ExtractionConfig::default();
config.min_length = 3;
config.max_length = Some(256);
config.byte_order = ByteOrder::LE;
config.confidence_threshold = 0.6;
```

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent terminology: The documentation uses "character count" in some places (line 222) and "character length" in other places (line 287). The code itself uses "min_length" for the configuration field which counts UTF-16 code units/characters.

For clarity and consistency, standardize on one term throughout the documentation. "Character count" or "characters" is clearer than "character length" when referring to the number of characters.

Copilot uses AI. Check for mistakes.
Comment thread src/extraction/util.rs
/// assert!(result.is_ok());
/// assert_eq!(result.unwrap(), "Hello");
/// ```
pub fn decode_utf16le_bytes(bytes: &[u8], trim_nulls: bool) -> Result<String> {

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decode_utf16le_bytes function in util.rs is a utility function but doesn't match the signature used in pe_resources.rs. The pe_resources.rs calls decode_utf16le_bytes(bytes, true) with a trim_nulls parameter, but the function exported from utf16.rs (line 335) has signature pub fn decode_utf16le_bytes(bytes: &[u8]) -> Result<String, ()> without a trim_nulls parameter.

This inconsistency will cause a compilation error.

Copilot uses AI. Check for mistakes.
String::from_utf16(&u16_slice)
.map(|s| s.trim_end_matches('\0').to_string())
.map_err(|_| crate::types::StringyError::EncodingError { offset: 0 })
crate::extraction::util::decode_utf16le_bytes(bytes, true)

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to crate::extraction::util::decode_utf16le_bytes(bytes, true) passes a trim_nulls boolean parameter, but the function decode_utf16le_bytes exported from the utf16 module (line 335 in utf16.rs) has signature pub fn decode_utf16le_bytes(bytes: &[u8]) -> Result<String, ()> without the trim_nulls parameter.

This will cause a compilation error. Either:

  1. The util.rs function should be used here instead of the utf16 module's function, or
  2. The function signature in utf16.rs should match the utility function's signature with the trim_nulls parameter.
Suggested change
crate::extraction::util::decode_utf16le_bytes(bytes, true)
crate::extraction::util::decode_utf16le_bytes(bytes)

Copilot uses AI. Check for mistakes.
Comment thread src/extraction/utf16.rs
Comment on lines +1102 to +1118
// Calculate section data slice with bounds checking
let section_offset = section.offset as usize;
let section_size = section.size as usize;

// Check if section is out of bounds
if section_offset >= data.len() {
return Vec::new();
}

// Calculate end offset with checked arithmetic
let end_offset = section_offset
.checked_add(section_size)
.unwrap_or(data.len())
.min(data.len());

// Extract section data slice
let section_data = &data[section_offset..end_offset];

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation: The extract_from_section function checks if the section is out of bounds (line 1107) but doesn't validate that section.size is reasonable or that section.offset doesn't overflow when combined with section.size.

While there's a checked_add at line 1112, the function doesn't handle the case where section.size is extremely large (e.g., u64::MAX), which could lead to unexpected behavior. Consider adding validation for:

  1. Maximum reasonable section size
  2. Ensuring section.offset + section.size doesn't overflow u64 before the checked_add conversion to usize

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +48
// Helper to create UTF-16LE test data
fn create_utf16le_string(text: &str) -> Vec<u8> {
let mut bytes = Vec::new();
for ch in text.chars() {
let code_point = ch as u32;
if code_point <= 0xFFFF {
let u16_val = code_point as u16;
bytes.extend_from_slice(&u16_val.to_le_bytes());
} else {
// Surrogate pair
let code_point = code_point - 0x10000;
let high = 0xD800 + ((code_point >> 10) as u16);
let low = 0xDC00 + ((code_point & 0x3FF) as u16);
bytes.extend_from_slice(&high.to_le_bytes());
bytes.extend_from_slice(&low.to_le_bytes());
}
}
bytes
}

// Helper to create UTF-16BE test data
fn create_utf16be_string(text: &str) -> Vec<u8> {
let mut bytes = Vec::new();
for ch in text.chars() {
let code_point = ch as u32;
if code_point <= 0xFFFF {
let u16_val = code_point as u16;
bytes.extend_from_slice(&u16_val.to_be_bytes());
} else {
// Surrogate pair
let code_point = code_point - 0x10000;
let high = 0xD800 + ((code_point >> 10) as u16);
let low = 0xDC00 + ((code_point & 0x3FF) as u16);
bytes.extend_from_slice(&high.to_be_bytes());
bytes.extend_from_slice(&low.to_be_bytes());
}
}
bytes
}

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The helper functions create_utf16le_string (lines 10-27) and create_utf16be_string (lines 30-47) in the test file are duplicates of the helper functions with the same names in the module's tests section (lines 1176-1213 in utf16.rs).

Consider extracting these helpers to a shared test utility module or using the ones from the utf16.rs module to avoid duplication. This would improve test maintainability.

Suggested change
// Helper to create UTF-16LE test data
fn create_utf16le_string(text: &str) -> Vec<u8> {
let mut bytes = Vec::new();
for ch in text.chars() {
let code_point = ch as u32;
if code_point <= 0xFFFF {
let u16_val = code_point as u16;
bytes.extend_from_slice(&u16_val.to_le_bytes());
} else {
// Surrogate pair
let code_point = code_point - 0x10000;
let high = 0xD800 + ((code_point >> 10) as u16);
let low = 0xDC00 + ((code_point & 0x3FF) as u16);
bytes.extend_from_slice(&high.to_le_bytes());
bytes.extend_from_slice(&low.to_le_bytes());
}
}
bytes
}
// Helper to create UTF-16BE test data
fn create_utf16be_string(text: &str) -> Vec<u8> {
let mut bytes = Vec::new();
for ch in text.chars() {
let code_point = ch as u32;
if code_point <= 0xFFFF {
let u16_val = code_point as u16;
bytes.extend_from_slice(&u16_val.to_be_bytes());
} else {
// Surrogate pair
let code_point = code_point - 0x10000;
let high = 0xD800 + ((code_point >> 10) as u16);
let low = 0xDC00 + ((code_point & 0x3FF) as u16);
bytes.extend_from_slice(&high.to_be_bytes());
bytes.extend_from_slice(&low.to_be_bytes());
}
}
bytes
}
// Import shared UTF-16 test helpers
use crate::utf16::tests::{create_utf16le_string, create_utf16be_string};

Copilot uses AI. Check for mistakes.
Comment thread src/extraction/mod.rs
pub min_confidence_threshold: f32,
/// Minimum confidence threshold for UTF-16LE strings (default: 0.7)
///
/// UTF-16LE strings with confidence below this threshold will be filtered out.

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states "UTF-16LE strings with confidence below this threshold will be filtered out" (line 182), but based on the implementation in extract_from_section (line 1143 in utf16.rs), this threshold is actually applied to the combined confidence score (minimum of UTF-16 confidence and noise filter confidence), not just the UTF-16-specific confidence.

The documentation should clarify that this threshold applies to the final combined confidence score after noise filtering, not just the UTF-16 encoding confidence.

Suggested change
/// UTF-16LE strings with confidence below this threshold will be filtered out.
/// UTF-16LE strings with a combined confidence score (minimum of UTF-16 encoding confidence and noise filter confidence) below this threshold will be filtered out.

Copilot uses AI. Check for mistakes.
Comment thread src/extraction/mod.rs
Comment on lines +126 to +129
pub use utf16::{
ByteOrder, Utf16ExtractionConfig, extract_from_section as extract_utf16_from_section,
extract_utf16_strings,
};

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The module exports two functions with the same name extract_from_section from different modules (line 121 for ASCII, line 127 for UTF-16), with the UTF-16 version aliased as extract_utf16_from_section.

This creates an inconsistent API where:

  • ASCII uses: extract_from_section
  • UTF-16 uses: extract_utf16_from_section

For consistency, consider either:

  1. Also aliasing the ASCII version (e.g., extract_ascii_from_section)
  2. Not aliasing the UTF-16 version and letting users import it explicitly from the utf16 submodule
  3. Using a more consistent naming pattern across all encoding extractors

Copilot uses AI. Check for mistakes.
Comment thread src/extraction/utf16.rs
Comment on lines +1136 to +1146
// Compute confidence if filtering is enabled
if let Some(ref noise_filter) = filter {
// Combine UTF-16 confidence with noise filter confidence
let noise_confidence = noise_filter.calculate_confidence(&string.text, &filter_context);
// Use the minimum of UTF-16 confidence and noise confidence
string.confidence = string.confidence.min(noise_confidence);
// Apply threshold filtering
if noise_filtering_enabled && string.confidence < min_confidence_threshold {
continue;
}
} else {

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential logic issue in confidence combination: The code combines UTF-16 confidence with noise filter confidence using .min() (line 1141), which means the final confidence is always the lower of the two scores.

However, the configuration has two separate thresholds:

  • utf16_confidence_threshold (applied at line 796/941 inside extraction)
  • utf16_min_confidence or min_confidence_threshold (applied at line 1143 in extract_from_section)

This creates a situation where a string could pass the initial UTF-16 confidence threshold (0.5 default) but then fail the final combined threshold (0.7 default for utf16_min_confidence). While this may be intentional, it creates confusion about which threshold actually controls filtering. Consider documenting this two-stage filtering process more clearly.

Copilot uses AI. Check for mistakes.
unclesp1d3r added a commit that referenced this pull request Feb 25, 2026
…m length filtering (#109)

* Implement UTF-16LE String Extraction with Confidence Scoring

- Added UTF-16LE extraction capabilities to the string extraction framework, enabling the extraction of UTF-16LE encoded strings from Windows PE binaries.
- Introduced `extract_utf16le_strings` and `Utf16ExtractionConfig` for customizable extraction parameters, including minimum character length and confidence thresholds.
- Implemented confidence scoring to enhance the accuracy of string detection, incorporating factors such as printable character percentage and null termination.
- Updated documentation to include detailed usage examples and configuration options for UTF-16LE extraction.
- Added unit and integration tests to validate the functionality and performance of the new extraction features.

This enhancement significantly improves the library's ability to handle UTF-16LE encoded strings, facilitating better analysis of binary data.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>

* Enhance UTF-16 String Extraction with Comprehensive Support

- Updated the UTF-16 extraction module to support both UTF-16LE and UTF-16BE encodings, including automatic byte order detection.
- Introduced advanced confidence scoring mechanisms to improve the accuracy of string detection, incorporating multiple heuristics for filtering false positives.
- Enhanced the `Utf16ExtractionConfig` to allow customizable parameters such as byte order selection and confidence thresholds.
- Updated documentation to reflect the new features, including detailed usage examples for both extraction methods.
- Added unit and integration tests to validate the functionality and performance of the updated extraction capabilities.

This enhancement significantly improves the library's ability to handle UTF-16 encoded strings, facilitating better analysis of binary data.

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>

---------

Signed-off-by: UncleSp1d3r <unclesp1d3r@evilbitlabs.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:analyzer Binary analyzer functionality documentation Improvements or additions to documentation enhancement New feature or request lang:rust Rust implementation size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

2 participants