Investigative Reversing 3: 9-Byte Stride & Interleaved Dummy Carving

“We have recovered yet another binary and image. See what the third variation does.”
picoCTF{...}💡 THE INTUITIVE ANALOGY (The 9th Dummy Spacer)
Imagine writing secret letters into a book where every 8 words contain 1 secret letter, but between every sentence you intentionally insert 1 random, meaningless filler word to throw off automated decoders. If an automated script reads continuously without accounting for that 1 filler word, all subsequent letters get desynchronized into complete gibberish!
1. Inspecting the `mystery` Binary
// Ghidra Decompilation: mystery (main)
int main(void) {
FILE *orig_bmp = fopen("original.bmp", "r");
FILE *enc_bmp = fopen("encoded.bmp", "a");
// 1. Copy first 723 bytes directly (Offset 0x2D3)
for (int i = 0; i < 723; i++) {
char b = fgetc(orig_bmp);
fputc(b, enc_bmp);
}
char flag[50];
fread(flag, 50, 1, flag_file);
// 2. 100 iterations: Interleaving data with dummy filler bytes
for (int j = 0; j < 100; j++) {
if (j % 2 == 0) {
// EVEN iterations (j = 0, 2, 4, ...): Encode 1 character into 8 LSB bytes
char val = flag[j / 2];
for (int bit = 0; bit < 8; bit++) {
char orig_b = fgetc(orig_bmp);
char enc_b = codedChar(bit, val, orig_b);
fputc(enc_b, enc_bmp);
}
} else {
// ODD iterations (j = 1, 3, 5, ...): Write 1 unmodified dummy byte
char orig_b = fgetc(orig_bmp);
fputc(orig_b, enc_bmp);
}
}
}2. Manual Stride Decoding Table
Starting from offset 723 (Hex: 0x2D3), read 8 bytes for Character 0, skip the 9th byte (C5), and continue to Character 1 at offset 732 (0x2DC):
Character 0 ('p') at Offset 0x2D3:
| Position | Hex Byte | Even or Odd? | LSB Bit | Weight | Value |
|---|---|---|---|---|---|
| Bit 0 | BC | Even | 0 | 2⁰ = 1 | 0 |
| Bit 1 | C1 | Odd | 1 | 2¹ = 2 | 2 |
| Bit 2 | BC | Even | 0 | 2² = 4 | 0 |
| Bit 3 | BD | Odd | 1 | 2³ = 8 | 8 |
| Bit 4 | BC | Even | 0 | 2⁴ = 16 | 0 |
| Bit 5 | BD | Odd | 1 | 2⁵ = 32 | 32 |
| Bit 6 | C1 | Odd | 1 | 2⁶ = 64 | 64 |
| Bit 7 | BC | Even | 0 | 2⁷ = 128 | 0 |
| Byte 8 (Dummy) | C5 | — | — | DUMMY | [SKIP] |
Calculation: 0 + 2 + 0 + 8 + 0 + 32 + 64 + 0 = 112 = 'p'.
3. Automated Extraction Script (`solve.py`)
# Read the encoded BMP image
with open('encoded.bmp', 'rb') as f:
data = f.read()
start_offset = 723 # Starting offset from binary decompilation
flag = []
# 50 characters, 9-byte stride (8 data bytes + 1 dummy byte)
for i in range(50):
char_offset = start_offset + i * 9
val = 0
for bit in range(8):
byte = data[char_offset + bit]
lsb = byte & 1
val |= (lsb << bit)
flag.append(chr(val))
print("🎉 Decoded Flag:", ''.join(flag))python -c "d=open('encoded.bmp','rb').read(); print(''.join(chr(sum(((d[723+i*9+b]&1)<<b) for b in range(8))) for i in range(50)))"4. Decoded Flag
Extracted Secret Flag:
5. The Complete Investigation Path & Mental Roadmap
Here is the step-by-step mental roadmap from binary decompilation to interleaved stride extraction:
Opened mystery in Ghidra. Traced the 723-byte header copy loop and spotted the j % 2 == 0 modulo condition inserting 1 dummy byte every 8 data bytes.
Formulated the exact character starting offset: Offset(i) = 723 + (i * 9).
Extracted 8 LSB bits per character without applying mathematical shifts and skipped the 9th filler byte.
Submitted verified flag string: picoCTF{4n0th3r_L5b_pr0bl3m_0000000000000743a2607}.