# Flag Aperisal

### **Challenge Overview**

We are given a binary that reads a flag, transforms it using `sub_1199`, and then compares the mutated buffer against a constant target (`s2`) using:

```c
if ( !strncmp(s, &s2, 0x25uLL) )
```

So the goal is clear:\
We must reverse the transformation performed in `sub_1199` and recover the original input that produces the expected transformed bytes.

The core transformation loop is:

```c
v3 = (257 * input[i]) ^ (509 * input[i+1]) ^ (33 * v3);
*(_WORD *)(i + input) = v3;
```

Critical observations:

1. **`v3` is `__int16` → 16-bit**&#x20;
   * Every iteration implicitly works modulo `2^16`.
   * Without applying `& 0xFFFF` in Python, the brute-force breaks after the first block.
   * This was the most important technical detail.
2. **Little-endian write**

   <pre class="language-c"><code class="lang-c"><strong>*(_WORD *)(i + input) = v3; //WORD = 2 BYTES
   </strong></code></pre>

   means:

   ```
   input[i]     = low byte
   input[i + 1] = high byte
   ```

   So `9c 85` corresponds to `0x859c`.
3. **Block-based processing**\
   The loop advances by `i += 2`, so the algorithm operates on **2-byte blocks**.\
   Each block depends only on:

   * its two input bytes
   * the previous state (`v3`)

   This makes complexity **linear**, not exponential over the whole flag.

***

### **Exploitation**

From the loop:

```
v3_new = 257*a ^ 509*b ^ 33*v3_prev
```

Rewriting:

```
257a ^ 509b = v3_new ^ (33*v3_prev)
```

So for each 2-byte output block (`v3_new`), we brute-force `a` and `b`.

Since:

```
a ∈ [0,255]
b ∈ [0,255]
```

That’s only:

```
256 × 256 = 65536 possibilities
```

Completely feasible.

The important detail in Python:

```python
x = ((257 * a) ^ (509 * b) ^ (33 * prev_x)) & 0xFFFF
```

#### Why `& 0xFFFF` matters

C automatically truncates `__int16` values to 16 bits.

Python integers do **not** overflow.

Without:

```python
& 0xFFFF
```

the state keeps growing beyond 16 bits and the reconstruction fails after the first block.

This truncation is absolutely essential.

***

### **Python Bruteforce Script**

```python
blocks = "9c 85 b5 8d 12 a0 9b 10 e8 1f 2b b3 db 4a 87 1e 39 bd 03 32 c6 d0 82 db cd 46 82 a1 6d 09 80 e5 6c 7f 6c 82 91"

data = bytes.fromhex(blocks)
targets = []

for i in range(0, len(data), 2):
    value = int.from_bytes(data[i:i+2], byteorder="little")
    targets.append(value)

prev_x = 0

for candidate in targets:
    found = False

    for a in range(256):
        for b in range(256):
            x = ((257 * a) ^ (509 * b) ^ (33 * prev_x)) & 0xFFFF

            if x == candidate:
                print(chr(a) + chr(b), end="")
                prev_x = x
                found = True
                break

        if found:
            break
```

***

### **Key Takeaways**

* The write is **little-endian**.
* The search space is only **65536 per block**, which is trivial.
* Correctly updating `prev_x` is mandatory or the chain breaks.

Everything else (odd-length handling, formatting, printing) is minor compared to these core insights.

*made by k0d*
