asset_tracker
Overview
A Windows asset tracking binary with two gates: an anti-debug check that
exits immediately if a debugger is detected, and a serial comparison that
triggers flag assembly if the correct serial is supplied. The flag is stored
as three separate XOR-encoded byte arrays, strings finds nothing useful.
The solve is to identify and bypass the anti-debug check, then read the
XOR constants from the decompiler.
Step 1: Triage with strings
strings asset_tracker.exe | grep -i deadrop
# DEADROP Asset Tracking System v2.0
One hit, just the title, no flag fragments, no readable ciphertext. The flag is encoded with keys above 0x7F so all bytes are non-printable.
Step 2: Load in Ghidra, find main
Navigate to main. The structure is:
- Print banner
- Check argument count
- Call
is_debugged(), exit if true strcmp(argv[1], "UNIT7-ALPHA-1337")- If serial matches: call
decode_fragthree times, print flag
Step 3: Identify the anti-debug checks
is_debugged() contains two checks:
Check 1: IsDebuggerPresent
if (IsDebuggerPresent()) return 1;
The Win32 API call that returns nonzero if a debugger is attached.
Check 2: PEB NtGlobalFlag
uint8_t *peb = (uint8_t *)__readgsqword(0x60);
uint32_t nt_global = *(uint32_t *)(peb + 0xBC);
if (nt_global & 0x70) return 1;
Reads the NtGlobalFlag field from the Process Environment Block. A debugger
sets bits 0x70 here, so even if you patch IsDebuggerPresent, this catches
you.
Bypass options
Option A: Patch the binary
In Ghidra or a hex editor, find the JNZ (or JE) that jumps to the
INTEGRITY ERROR exit path and flip it to JMP (unconditional), or NOP
out the call is_debugged entirely. Run the patched binary normally.
Option B: ScyllaHide / anti-anti-debug plugin
x64dbg with ScyllaHide hides the debugger from both IsDebuggerPresent and
the PEB NtGlobalFlag check. Attach and run normally.
Option C: Static only (no execution needed)
Skip the anti-debug entirely, just read the XOR constants from the decompiler and decode offline.
Step 4: Read the XOR constants from Ghidra
With the anti-debug gated code visible, Ghidra shows:
_Memory = decode_frag(&frag_a, 0x19, 0xc3);
_Memory_00 = decode_frag(&frag_b, 0x0e, 0xd5);
_Memory_01 = decode_frag(&frag_c, 0x07, 0xe7);
Each call: encoded bytes, length, XOR key. Double-click each frag_X symbol
to see the byte arrays in .rodata.
Decode all three:
frag_a = bytes([
0x87,0x86,0x82,0x87,0x91,0x8c,0x93,0xb8,
0xa2,0xad,0xb7,0xaa,0x9c,0xa7,0xa6,0xa1,
0xb6,0xa4,0x9c,0xa0,0xab,0xa6,0xa0,0xa8,0x9c
])
frag_b = bytes([
0xa6,0xa5,0xb0,0xb0,0xb1,0xb7,0xa0,0xb8,
0xa5,0x8a,0xbb,0xba,0xa1,0x8a
])
frag_c = bytes([
0x86,0xb8,0x90,0x86,0x8b,0x8b,0x9a
])
a = bytes(b ^ 0xc3 for b in frag_a).decode()
b = bytes(b ^ 0xd5 for b in frag_b).decode()
c = bytes(b ^ 0xe7 for b in frag_c).decode()
print(a + b + c)
Flag: DEADROP{anti_debug_check_speedbump_not_a_wall}
Key Takeaway
Anti-debug checks are speedbumps not walls, the flag says so explicitly.
IsDebuggerPresent and PEB NtGlobalFlag are the two most common Windows
anti-debug techniques and both have well-known bypasses. More importantly,
they don't protect against static analysis at all: the XOR keys are visible
constants in the decompiler output regardless of whether you ever run the
binary.