Files
exe-reverse/tests/cli/test_cli_dart_snapshot.py
T
2026-10-24 01:50:24 +08:00

265 lines
12 KiB
Python

"""Subprocess contract tests for skills/exe-reverse/scripts/dart_snapshot_strings.py.
The input is the generated ELF64 from `conftest.build_synthetic_elf`, never a sample: a valid
container with one one-byte literal, one two-byte literal and a `.text` that carries neither of the
idioms the pool observer counts.
Two conventions are pinned because they are not what the docstring's exit-code table suggests:
* `--json` returns 0 whatever the filter did, while the text mode returns 1 for "nothing matched";
* `--at` widens a match by `offset + len(text)`, and for a two-byte literal that is half the bytes
the literal occupies, so an offset inside the literal's second half is reported as a miss.
"""
import json
import pytest
pytestmark = pytest.mark.cli
def plain_lines(text):
return [line for line in text.splitlines() if line.strip()]
def reported_count(text):
"""The `N literal(s) reported.` line the text view ends with."""
for line in reversed(plain_lines(text)):
if "literal(s) reported." in line:
return int(line.split()[0])
raise AssertionError("no 'literal(s) reported.' line in:\n%s" % text)
# --- refusals ----------------------------------------------------------------
def test_a_file_that_is_not_an_elf_is_refused_with_the_documented_code(run_script, tmp_path):
path = tmp_path / "notes.txt"
path.write_text("this is not a snapshot\n", encoding="utf-8")
result = run_script("dart_snapshot_strings.py", path)
assert result.returncode == 3
assert "not an ELF image" in result.stderr
assert "Traceback" not in result.stderr
assert result.stdout == ""
def test_a_truncated_image_is_refused_without_a_traceback(run_script, synthetic_elf, tmp_path):
path = tmp_path / "truncated.so"
path.write_bytes(synthetic_elf.data[:0x30])
result = run_script("dart_snapshot_strings.py", path)
assert result.returncode == 3
assert "not an ELF image" in result.stderr
assert "Traceback" not in result.stderr
def test_a_missing_file_exits_two_with_a_reason(run_script, tmp_path):
result = run_script("dart_snapshot_strings.py", tmp_path / "absent.so")
assert result.returncode == 2
assert "error:" in result.stderr
assert "Traceback" not in result.stderr
def test_an_elf_with_no_code_section_is_refused(run_script, synthetic_elf_without_text):
"""A shared object without `.text` is not an AOT code image, and the reader says so."""
result = run_script("dart_snapshot_strings.py", synthetic_elf_without_text.path)
assert result.returncode == 3
assert "no .text section" in result.stderr
assert "Traceback" not in result.stderr
# --- recovery ----------------------------------------------------------------
def test_the_default_run_reports_every_literal_it_recovered(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path)
assert result.returncode == 0, result.stderr
assert "x86-64, shared-object" in result.stdout
for entry in synthetic_elf.literals:
assert hex(entry["vaddr"]) in result.stdout
assert entry["text"] in result.stdout
assert "literals recovered : 3" in result.stdout
assert reported_count(result.stdout) == 3
def test_min_length_filters_the_short_literals_out(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--min-length", "12")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "premium_license" in result.stdout
assert "trial_mode" not in result.stdout
assert ">= 12 chars" in result.stdout
def test_limit_caps_how_many_are_reported(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--limit", "1")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
def test_section_selection_is_honoured(run_script, synthetic_elf):
only_rodata = run_script("dart_snapshot_strings.py", synthetic_elf.path,
"--sections", ".rodata")
assert only_rodata.returncode == 0, only_rodata.stderr
assert reported_count(only_rodata.stdout) == 3
only_text = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--sections", ".text")
assert only_text.returncode == 1
assert reported_count(only_text.stdout) == 0
absent = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--sections", ".absent")
assert absent.returncode == 1
assert reported_count(absent.stdout) == 0
# --- keyword filter ----------------------------------------------------------
def test_find_matches_case_insensitively(run_script, synthetic_elf):
lower = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "premium")
upper = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "PREMIUM")
assert lower.returncode == 0, lower.stderr
assert upper.returncode == 0, upper.stderr
assert reported_count(lower.stdout) == reported_count(upper.stdout) == 1
assert "premium_license" in lower.stdout
assert "trial_mode" not in lower.stdout
def test_find_accepts_a_comma_separated_list(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "trial, VIP")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 2
assert "trial_mode" in result.stdout
assert "vip_pass" in result.stdout
assert "premium_license" not in result.stdout
def test_find_can_reach_a_two_byte_literal(run_script, synthetic_elf):
"""A keyword search that only read ASCII would miss this one, which is why both are recovered."""
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "vip")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "[two-byte]" in result.stdout
def test_a_keyword_that_matches_nothing_changes_the_exit_code(run_script, synthetic_elf):
"""A miss is a negative finding, not an empty success."""
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_keyword")
assert result.returncode == 1
assert reported_count(result.stdout) == 0
assert "Traceback" not in result.stderr
# --- --at --------------------------------------------------------------------
def test_at_resolves_a_literal_by_file_offset(run_script, synthetic_elf):
entry = synthetic_elf.literal("premium_license")
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(entry["offset"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "premium_license" in result.stdout
def test_at_resolves_a_two_byte_literal_by_file_offset(run_script, synthetic_elf):
entry = synthetic_elf.literal("vip_pass")
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(entry["offset"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "vip_pass" in result.stdout
def test_at_resolves_a_literal_by_virtual_address(run_script, synthetic_elf):
"""The vaddr is far above the file size, which is how the reader tells the two apart."""
entry = synthetic_elf.literal("vip_pass")
assert entry["vaddr"] > synthetic_elf.size
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", hex(entry["vaddr"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "vip_pass" in result.stdout
assert hex(entry["vaddr"]) in result.stdout
def test_at_inside_the_code_section_matches_nothing(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at",
str(synthetic_elf.text["offset"] + 1))
assert result.returncode == 1
assert reported_count(result.stdout) == 0
def test_at_finds_every_byte_of_a_two_byte_literal(load_script, run_script, synthetic_elf):
"""`--at` measures its window in bytes, so **every** byte offset inside the literal matches.
The window used to be `offset + len(text)`. For a two-byte literal the offsets are bytes and the
text is characters, so the window covered only the first half of it and a real byte offset in
the second half came back as a miss. Note which byte is which: in UTF-16LE an ASCII character is
a printable byte followed by a zero byte, so the last meaningful offset in the literal is the
high byte of its final character, which is `0x00` and is a perfectly valid offset to probe.
"""
entry = synthetic_elf.literal("vip_pass")
assert entry["encoding"] == "two-byte"
span = 2 * len(entry["text"])
lo = entry["offset"]
last_low_byte = lo + span - 2
assert synthetic_elf.data[last_low_byte] in range(0x20, 0x7F)
assert synthetic_elf.data[lo + span - 1] == 0, "the final byte is the high byte of the character"
for probe in (lo, lo + 2, last_low_byte, lo + span - 1):
found = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(probe))
assert found.returncode == 0, (probe, found.stdout, found.stderr)
assert reported_count(found.stdout) == 1, (probe, found.stdout)
# The module computes the same window the CLI does.
recovered = load_script("dart_snapshot_strings.py").recover_strings(
synthetic_elf.data, {"offset": synthetic_elf.rodata["offset"],
"vaddr": synthetic_elf.rodata["vaddr"],
"size": synthetic_elf.rodata["size"]})
matched = [row[2] for row in recovered
if row[0] <= lo + span - 1 < row[0] + 2 * len(row[2])]
assert matched == ["vip_pass"]
def test_at_can_be_combined_with_find(run_script, synthetic_elf):
entry = synthetic_elf.literal("trial_mode")
hit = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "trial",
"--at", str(entry["offset"]))
assert hit.returncode == 0, hit.stderr
assert reported_count(hit.stdout) == 1
miss = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "premium",
"--at", str(entry["offset"]))
assert miss.returncode == 1
assert reported_count(miss.stdout) == 0
# --- json --------------------------------------------------------------------
def test_json_returns_one_when_the_filter_matched_nothing(run_script, synthetic_elf):
"""The exit code means the same thing in both output modes.
It used to be 1 from the text path and 0 from the JSON path for the identical filter, so a
caller that asked for JSON could not tell a match from a miss by reading the exit code.
"""
text_mode = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_word")
json_mode = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_word",
"--json")
assert text_mode.returncode == 1
assert json_mode.returncode == 1
payload = json.loads(json_mode.stdout)
assert payload["matches"] == []
assert payload["literals_recovered"] == 0
# An unfiltered run is a listing, not a search, and a listing that finds literals still exits 0.
unfiltered = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--json")
assert unfiltered.returncode == 0
assert json.loads(unfiltered.stdout)["matches"]
def test_an_unknown_flag_is_a_usage_error(run_script):
result = run_script("dart_snapshot_strings.py", "--definitely-not-a-flag")
assert result.returncode == 2
assert "usage:" in result.stderr.lower()
assert "Traceback" not in result.stderr
def test_help_exits_zero(run_script):
result = run_script("dart_snapshot_strings.py", "--help")
assert result.returncode == 0
assert "usage:" in result.stdout.lower()
assert "app.so" in result.stdout