The original code stored only one IAT address per import (addr=0 fallback
on master, addr=first with break in prior fix). When an import has multiple
data references, instruction-level lookups could miss the one actually
referenced, breaking API feature extraction and causing spurious
cross-section-flow characteristics.
Collect all data-ref addresses into a list and register the import under
each, matching how map_fake_import_addrs already stores all refs. Also
preserves ex_loc registration when no data refs exist.
Both extractors were yielding AbsoluteVirtualAddress for file-scope strings,
inconsistent with all other backends (common, pefile, elffile, viv, ida)
which yield FileOffsetAddress. Convert VAs to file offsets using each
backend's respective API.
carve_pe returns offsets into a raw byte buffer read from the segment.
Convert to file offset using the segment's data_offset rather than
emitting a virtual address.
ELF r_offset in executables/shared objects is a virtual address (GOT slot),
not a file offset. Only relocatable .o files use file offsets, which capa
does not analyze.
Removes capa.engine, capa.helpers, capa.features, and capa.features.insn
imports that were never referenced in each script. Adds missing capa.loader
import to show-capabilities-by-function.py which was already being used.
json.JSONDecodeError is a subclass of ValueError, so the broader except ValueError
was shadowing the more specific handler, making it unreachable. Keep only the
specific except json.JSONDecodeError handler.
The entire main() body was indented inside `if argv is None:`, causing
main() to silently return None when called with an explicit argv list.
Closes SURF-90.
When all runs for a backend fail, durations_by_backend[backend] is empty,
causing StatisticsError from statistics.quantiles (needs >= 2 points) and
statistics.mean (needs >= 1 point). Print placeholder messages instead.
Without parentheses, Python's operator precedence caused `kid.name != "Some"`
to only guard the `Not` branch; `And` and `Or` kids named `"Some"` would
bypass the Some-handling block and enter recursive convert_rule unguarded.
The `or "currentcontrolset" in pat` branch triggered the lint for any
regex containing "currentcontrolset", even unrelated paths like
HKLM\Software\CurrentControlSet that don't need the system\\ fix.
Fix by requiring "system\\\\" in both branches of the condition.
When rule.meta lacks a "scopes" key, rule.meta.get("scopes") returns None
and "static"/"dynamic" not in None raises TypeError, crashing lint_rule.
Add isinstance(scopes, dict) guard so both checks return False (no violation)
when scopes is absent, letting MissingScopes report the real problem.
The check was reading rule.meta.get("scope") which no longer exists in the
current schema (replaced by scopes.static/scopes.dynamic), causing the lint
to never fire for function/basic-block rules missing example offsets.
The condition was skipping FUNCTION-scope rules instead of keeping them,
causing the script to never annotate any functions. Invert to match the
correct logic in import-to-bn.py.
The triple-quoted string at lines 230-239 was never assigned and contained
an incomplete sentence ("The scripts"). Deleted entirely as the surrounding
code is self-explanatory.
Remove capa.helpers, capa.features.basicblock (never referenced), and
redundant bare capa.features.extractors.base_extractor (covered by the
from-import on the next line).
Closes SURF-78
Removes the bare `assert isinstance(meta.analysis, rd.StaticAnalysis)` that
blocked dynamic ResultDocument from being validated, removes the incorrect
direct list comparison in assert_dynamic_analyis, and adds dynamic_a0000a6_rd
to test_doc_to_pb2 so the dynamic proto serialization path is exercised.
RangeStatement.max was compared against itself, so a protobuf round-trip
that corrupted the max field would pass undetected. Fix to compare against
the protobuf counterpart sb.max.