83 lines
3.0 KiB
Python
83 lines
3.0 KiB
Python
"""Tests for fixed-domain DGA discovery / shellcode placement."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import struct
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
TOOLS = Path(__file__).resolve().parents[1]
|
|
if str(TOOLS) not in sys.path:
|
|
sys.path.insert(0, str(TOOLS))
|
|
|
|
from _common import CORE_DYLIB, GROUP_DYLIBS # noqa: E402
|
|
from _domain_patch import ( # noqa: E402
|
|
_AUTIBSP,
|
|
_PACIBSP,
|
|
_discover_dga,
|
|
iter_slices,
|
|
patch_fixed_domains_in_dylib,
|
|
)
|
|
|
|
|
|
class DiscoverDgaTests(unittest.TestCase):
|
|
def test_group_b_entry_is_pacibsp_not_prev_tail_branch(self) -> None:
|
|
blob = GROUP_DYLIBS["B"].read_bytes()
|
|
entry, body, end = _discover_dga(blob)
|
|
self.assertEqual(body, 0x24E5C)
|
|
# Real entry is pacibsp; the word at body-8 is the *previous* function's `b`.
|
|
self.assertEqual(entry, body - 4)
|
|
self.assertEqual(struct.unpack_from("<I", blob, entry)[0], _PACIBSP)
|
|
prev_b = struct.unpack_from("<I", blob, body - 8)[0]
|
|
self.assertEqual(prev_b & 0xFC000000, 0x14000000)
|
|
# Patch window must cover the arm64e autibsp return sequence.
|
|
self.assertEqual(struct.unpack_from("<I", blob, body + 0x360)[0], _AUTIBSP)
|
|
self.assertGreater(end, body + 0x360)
|
|
|
|
def test_group_a_arm64_entry_is_body(self) -> None:
|
|
blob = GROUP_DYLIBS["A"].read_bytes()
|
|
entry, body, end = _discover_dga(blob)
|
|
self.assertEqual(entry, body)
|
|
self.assertLess(entry, end)
|
|
|
|
def test_core_arm64e_slice_entry_is_pacibsp(self) -> None:
|
|
data = CORE_DYLIB.read_bytes()
|
|
pac_hits = 0
|
|
for sl in iter_slices(data):
|
|
blob = data[sl.file_offset : sl.file_offset + sl.size]
|
|
entry, body, _end = _discover_dga(blob)
|
|
if body >= 4 and struct.unpack_from("<I", blob, body - 4)[0] == _PACIBSP:
|
|
pac_hits += 1
|
|
self.assertEqual(entry, body - 4)
|
|
self.assertGreaterEqual(pac_hits, 1)
|
|
|
|
|
|
class FixedDomainPatchTests(unittest.TestCase):
|
|
def test_group_b_shellcode_starts_with_pacibsp(self) -> None:
|
|
src = GROUP_DYLIBS["B"].read_bytes()
|
|
# Use seeds already present in the pristine type0x01.
|
|
from _common import ORIGINAL_DEPLOYMENT_SEED, ORIGINAL_REPORTING_SEED
|
|
|
|
out = patch_fixed_domains_in_dylib(
|
|
src,
|
|
["kklsdfw.cc"],
|
|
["ttrrood.cc"],
|
|
deployment_seed=ORIGINAL_DEPLOYMENT_SEED,
|
|
reporting_seed=ORIGINAL_REPORTING_SEED,
|
|
label="test-B",
|
|
)
|
|
entry, body, end = _discover_dga(src)
|
|
self.assertEqual(struct.unpack_from("<I", out, entry)[0], _PACIBSP)
|
|
# Must not overwrite the previous function's trailing branch.
|
|
self.assertEqual(
|
|
struct.unpack_from("<I", out, body - 8)[0],
|
|
struct.unpack_from("<I", src, body - 8)[0],
|
|
)
|
|
self.assertIn(b"kklsdfw.cc\x00", out[entry:end])
|
|
self.assertIn(b"ttrrood.cc\x00", out[entry:end])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|