Init. commit
This commit is contained in:
68
reverse-ancient-processor/WRITEUP.md
Normal file
68
reverse-ancient-processor/WRITEUP.md
Normal file
@@ -0,0 +1,68 @@
|
||||
<h1 align="center">Ancient Processor</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/category-Reverse-blueviolet" alt="Reverse"/>
|
||||
<img src="https://img.shields.io/badge/points-912-orange" alt="912 pts"/>
|
||||
</p>
|
||||
|
||||
Мы получаем stripped ELF-бинарник `checker`. По названию таска и описанию можно уловить намек, что внутри сидит эмулятор какого-то «древнего процессора» — значит, вероятно, что сработает reverse эмулятора и восстановление его байткода.
|
||||
|
||||
## Решение
|
||||
|
||||
Открываем `checker` в IDA или Ghidra и сразу ищем главный цикл интерпретатора — он выдаёт себя большим `switch`'ем по опкодам. По коду VM восстанавливается набор инструкций:
|
||||
|
||||
| Категория | Опкоды |
|
||||
|---|---|
|
||||
| Стек | `PUSH`, `POP`, `DUP`, `SWAP`, `ROT` |
|
||||
| Арифметика | `ADD`, `SUB`, `XOR`, `MUL`, `AND`, `OR`, `NOT`, `SHL`, `SHR`, `MOD` |
|
||||
| Память / переходы | `LOAD`, `STORE`, `JMP`, `JZ`, `CALL`, `RET` |
|
||||
| I/O | `READ`, `PRINT`, `HALT` |
|
||||
| Антианализ | `SYSCALL`, `CHECKTIME`, `SELFMOD2` |
|
||||
|
||||
Самое интересное лежит в `vm.c`: перед выполнением программы в массив `code` сначала копируется `decoy_program`, а затем поверх него пишется уже расшифрованный `real_program_encrypted`. То есть первая программа — декорация, её можно даже не разбирать. А реальная цель — именно зашифрованная. Рагадка тут, на самом деле, элементарная: каждый байт XOR'ится с 8-байтным ключом. Можно либо выгрузить уже готовый массив из памяти в рантайме, либо вытащить ключ и шифротекст из бинарника и раскрутить всё статикой.
|
||||
|
||||
После расшифровки байткод раскладывается в повторяющийся шаблон проверки очередного символа:
|
||||
|
||||
```text
|
||||
READ
|
||||
PUSH xor_key
|
||||
XOR
|
||||
PUSH add_key
|
||||
ADD
|
||||
PUSH sub_key
|
||||
SUB
|
||||
[иногда MUL]
|
||||
PUSH expected
|
||||
CMP
|
||||
JZ fail
|
||||
```
|
||||
|
||||
Каждый символ флага проверяется независимо, и в обычном случае формула получается такой:
|
||||
|
||||
$$\bigl((ch \oplus k_{\mathrm{xor}}) + k_{\mathrm{add}} - k_{\mathrm{sub}}\bigr) \bmod 256 = \mathrm{expected}$$
|
||||
|
||||
Отсюда символ восстанавливается в одну строчку:
|
||||
|
||||
$$ch = \bigl((\mathrm{expected} + k_{\mathrm{sub}} - k_{\mathrm{add}}) \bmod 256\bigr) \oplus k_{\mathrm{xor}}$$
|
||||
|
||||
Но, вохможно и к сожалению, это еще не конец. Если на этом месте пройтись по всем символам и подставить константы — часть результатов не сойдётся. На этом этапе можно логично предположить, что причиной этому скорее всего служит самоизменяющийся код. Инструкция `SELFMOD2` меняет байты прямо внутри выполняющейся программы, и несколько уже прочитанных символов флага используются для модификации XOR-ключей в будущих проверках:
|
||||
|
||||
| Символ-источник | Модифицирует ключ символа |
|
||||
|:---:|:---:|
|
||||
| 5 | 12 |
|
||||
| 10 | 18 |
|
||||
| 15 | 25 |
|
||||
|
||||
Для этих позиций статический `xor_key` из байткода брать нельзя — надо считать его динамически с учётом уже восстановленной части флага. Здесь заложена еще одна мелкая подлянка: каждый четвёртый символ перед сравнением умножается на константу (`MUL`), и чтобы его обратить, нужен [modular inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) по модулю 256.
|
||||
|
||||
> Обратное $a^{-1} \bmod 256$ существует только для нечётных $a$ (чтобы $\gcd(a, 256) = 1$) — множители `MUL` в байткоде специально подобраны нечётными, иначе символ флага был бы однозначно невосстановим.
|
||||
|
||||
Остальные «защиты» можно игнорировать — это шум:
|
||||
|
||||
| Инструкция | Что делает | Эффект |
|
||||
|---|---|---|
|
||||
| `SYSCALL` | Читает байт из `/dev/urandom`, кладёт `rnd ^ rnd` в стек | Стабильный `0` |
|
||||
| `CHECKTIME` | XOR'ит значение с `0xFF` при медленном выполнении | Не триггерится в памяти |
|
||||
|
||||
## Флаг
|
||||
`caplag{v1rtu4l_m4ch1n3_m4st3r}`
|
||||
Reference in New Issue
Block a user