# 2025-12-18_"twilight": 251218
**2025,12,18 0:13 以前のバージョンに対する解答です。**
<https://alpacahack.com/daily/challenges/twilight>
Level: Medium
Type: Reverse Engineering
solved day: 2025,12,18
## solution
```sh
python3 solve.py
Flag: Alpaca{AlpacaHack_in_Wonderland}
```
## How I thought.
```sh
ls -la
.rwxrwxr-x@ 16k s12810162 18 Dec 00:12 chall
.rw-rw-r--@ 193 s12810162 18 Dec 00:12 out.txt
```
### 1. ファイルの確認
```sh
file chall
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=cad54f8480fb7120a6bf06f1647f1801e946a02c, for GNU/Linux 3.2.0, not stripped
```
* 64-bit ELF バイナリ、not stripped(シンボル情報あり)
* `out.txt` には暗号化された値が16進数で格納されている
```
0x41, 0x6D, 0x72, 0x62, 0x67, 0x64, 0x81, 0x46, 0x74, 0x79, 0x6B, 0x68, 0x6D, 0x45, 0x6F, 0x6C, 0x7B, 0x4E, 0x7B, 0x7D, 0x73, 0x42, 0x85, 0x79, 0x7C, 0x7C, 0x8C, 0x77, 0x7D, 0x73, 0x82, 0x62,
```
### 2. バイナリの解析
```sh
strings chall
Enter the flag:
%33s
0x%X,
```
* フラグを入力として受け取り、何らかの処理をして16進数で出力する
* 最大33文字の入力を受け付ける
### 3. `objdump` でディスアセンブル
#### main関数の主要部分
```asm
main:
; "Enter the flag: " を出力
lea rax, [rip + 0xdea]
mov rdi, rax
call printf
; 入力を受け取る (scanf("%33s", buffer))
lea rax, [rbp - 0x40]
mov rsi, rax
lea rax, [rip + 0xde0] # "%33s"
mov rdi, rax
call scanf
; 関数ポインタの配列を準備
lea rax, [rip - 0x80] # function 'a'
mov qword ptr [rbp - 0x50], rax
lea rax, [rip - 0x73] # function 'b'
mov qword ptr [rbp - 0x48], rax
; ループ処理
mov dword ptr [rbp - 0x54], 0x0 # i = 0
loop_start:
mov eax, dword ptr [rbp - 0x54] # eax = i
cdq
shr edx, 0x1f
add eax, edx
and eax, 0x1 # eax = i % 2
sub eax, edx
cdqe
mov rcx, qword ptr [rbp + 8*rax - 0x50] # rcx = (i%2==0) ? a : b
mov edx, dword ptr [rbp - 0x54] # edx = i (引数2)
mov eax, dword ptr [rbp - 0x54] # eax = i
cdqe
movzx eax, byte ptr [rbp + rax - 0x40] # eax = input[i] (引数1)
mov esi, edx
mov edi, eax
call rcx # result = func(input[i], i)
; 結果を出力
mov esi, eax
lea rax, [rip + 0xd85] # "0x%X, "
mov rdi, rax
call printf
add dword ptr [rbp - 0x54], 0x1 # i++
; strlen(input) と比較してループ継続
```
#### 関数 a (0x11c9)
```asm
a:
endbr64
push rbp
mov rbp, rsp
mov dword ptr [rbp - 0x4], edi # arg1 (文字)
mov dword ptr [rbp - 0x8], esi # arg2 (インデックス)
mov edx, dword ptr [rbp - 0x4]
mov eax, dword ptr [rbp - 0x8]
add eax, edx # return arg1 + arg2
pop rbp
ret
```
#### 関数 b (0x11e1)
```asm
b:
endbr64
push rbp
mov rbp, rsp
mov dword ptr [rbp - 0x4], edi # arg1 (文字)
mov dword ptr [rbp - 0x8], esi # arg2 (インデックス)
mov eax, dword ptr [rbp - 0x4]
xor eax, dword ptr [rbp - 0x8] # return arg1 ^ arg2
pop rbp
ret
```
### 4. アルゴリズムの理解
バイナリは以下の処理を行う:
* 入力文字列の各文字に対して、インデックス `i` ごとに異なる関数を適用
* **偶数インデックス** (0, 2, 4, ...): 関数 `a` → `char + index`
* **奇数インデックス** (1, 3, 5, ...): 関数 `b` → `char ^ index`
* 結果を16進数で出力
### 5. 復号化ロジック
逆算するには:
* **偶数インデックス**: `encrypted_value = char + index` なので `char = encrypted_value - index`
* **奇数インデックス**: `encrypted_value = char ^ index` なので `char = encrypted_value ^ index` (XORは自己逆演算)
### 6. 解答スクリプト (`solve.py`)
```python
#!/usr/bin/env python3
# Encrypted values from out.txt
encrypted = [0x41, 0x6D, 0x72, 0x62, 0x67, 0x64, 0x81, 0x46, 0x74, 0x79, 0x6B, 0x68, 0x6D, 0x45, 0x6F, 0x6C, 0x7B, 0x4E, 0x7B, 0x7D, 0x73, 0x42, 0x85, 0x79, 0x7C, 0x7C, 0x8C, 0x77, 0x7D, 0x73, 0x82, 0x62]
flag = []
for i, enc_val in enumerate(encrypted):
if i % 2 == 0:
# Even index: used function a (char + index)
# So: enc_val = char + index
# Therefore: char = enc_val - index
original_char = enc_val - i
else:
# Odd index: used function b (char ^ index)
# So: enc_val = char ^ index
# Therefore: char = enc_val ^ index (XOR is its own inverse)
original_char = enc_val ^ i
flag.append(chr(original_char))
flag_string = ''.join(flag)
print(f"Flag: {flag_string}")
```
### 7. 実行結果
```sh
python3 solve.py
Flag: Alpaca{AlpacaHack_in_Wonderland}
```
## まとめ
* バイナリは入力文字列に対して、偶数インデックスでは「文字+インデックス」、奇数インデックスでは「文字^インデックス」の演算を適用
* `objdump` でディスアセンブルし、2つの関数(加算とXOR)が交互に呼ばれることを特定
* XORは自己逆演算、加算の逆は減算という性質を利用して復号化スクリプトを作成
* `out.txt` の暗号化データを逆算することでフラグを取得