# 2016 Flare-On Challenge ( Part-1 )
>Author: Lays
> http://blog.l4ys.tw/
>
[Flare-On Challenge](http://flare-on.com) 是由 FireEye 舉辦的類似 CTF 的線上競賽,題型主要以 Reverse 為主,時間長達一個多月
去年的 Flare-On 因為太忙所以不小心就錯過了,身為一個逆向工程狂熱份子當然不能放過今年的機會
台灣似乎沒有多少人在玩,最後破台的只有我跟 `Lucas`,期待明年可以有更多人嘗試
![](https://www.fireeye.com/content/dam/fireeye-www/blog/images/Flare-On%203%20Solutions/image3.png)
雖然已經有官方版本的 [Solution](https://www.fireeye.com/blog/threat-research/2016/11/2016_flare-on_challe.html) ,但還是順手紀錄了一下解題過程
- [Part1](https://hackmd.io/s/HyOUlP9gg) ( 1 - 5 )
- [Part2](https://hackmd.io/s/B1Pr8gAlg) ( 6 - 10 )
---
## Challenge 1: challenge1.exe
程式會將輸入字串做 `base64` 之後與特定結果比對
需要注意的是 `base64` 的 table 為 `ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/`
```c
int main()
{
char Buffer[128]; // [sp+0h] [bp-94h]@1
_BYTE *v2; // [sp+80h] [bp-14h]@1
const char *v3; // [sp+84h] [bp-10h]@1
HANDLE stdin; // [sp+88h] [bp-Ch]@1
HANDLE stdout; // [sp+8Ch] [bp-8h]@1
DWORD NumberOfBytesWritten; // [sp+90h] [bp-4h]@1
stdout = GetStdHandle(STD_INPUT_HANDLE);
stdin = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(stdout, "Enter password:\r\n", 18u, &NumberOfBytesWritten, 0);
ReadFile(stdin, Buffer, 128u, &NumberOfBytesWritten, 0);
v2 = MyBase64(Buffer, NumberOfBytesWritten - 2);
if ( !strcmp(v2, "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q") )
WriteFile(stdout, "Correct!\r\n", 0xBu, &NumberOfBytesWritten, 0);
else
WriteFile(stdout, "Wrong password\r\n", 0x11u, &NumberOfBytesWritten, 0);
return 0;
}
```
Solution:
```python
#!/usr/bin/python
import string
import base64
STANDARD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
CUSTOM_ALPHABET = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
DECODE_TRANS = string.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)
def decode(input):
return base64.b64decode(input.translate(DECODE_TRANS))
print decode("x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q")
```
Flag: `sh00ting_phish_in_a_barrel@flare-on.com`
---
## Challenge 2: DudeLocker.exe
題目是個 ransomeware,使用 `Windows Crypto API` 來加密檔案
提供了一個加密過的附檔 `BusinessPapers.doc`
### 程式行為
1. 檢查 `%USER%\Desktop\Briefcase` 目錄存在
2. 透過 `GetVolumeInformationA` 檢查磁碟序號是否為 `0x7DAB1D35`
3. 使用磁碟序號解密資料,產生一組 AES-256 key,內容固定為 ` sha1("thosefilesreallytiedthefoldertogether")` 的結果
4. 透過 AES-256 加密 `Briefcase` 目錄下的所有檔案,IV 為檔案名稱的 md5 hash
5. 將桌布修改為資源檔中的 `ve_vant_ze_money.jpg`,告知使用者檔案已被 `RSA-4096` 加密
![](https://i.imgur.com/DL34UlW.jpg)
### 解密
分析後得知並非使用 `RSA-4096` 而是 `AES-256`,可直接用同一把 key 解密檔案
最簡單的做法是直接 patch binary,將 `CryptEncrypt` 的呼叫換成 `CryptDecrypt`,讓程式自己來替我們解密檔案
`CryptEncrypt` 比 `CryptDecrypt` 多了一個 `dwBufLen` 的參數,需要把第一個 `PUSH` 指令 `NOP` 掉
```
> 0040165A 90 NOP
0040165B 8D4D E4 LEA ECX, DWORD PTR SS:[EBP-0x1C]
0040165E 51 PUSH ECX
0040165F 8B55 F8 MOV EDX, DWORD PTR SS:[EBP-0x8]
00401662 52 PUSH EDX
00401663 6A 00 PUSH 0x0
00401665 0FB645 FF MOVZX EAX, BYTE PTR SS:[EBP-0x1]
00401669 50 PUSH EAX
0040166A 6A 00 PUSH 0x0
0040166C 8B4D 08 MOV ECX, DWORD PTR SS:[EBP+0x8]
0040166F 8B11 MOV EDX, DWORD PTR DS:[ECX]
00401671 52 PUSH EDX
> 00401672 E8 0D1CEA75 CALL ADVAPI32.CryptDecrypt
> 00401677 90 NOP
```
另外還需要 patch `GetVolumeInformationA` 的結果為 `0x7DAB1D35`,直接修改 `0x401040` 的 return value:
```asm
00401040 B8 351DAB7D MOV EAX, 0x7DAB1D35
00401045 C3 RETN
```
將 `BusinessPapers.doc` 放到 `Briefcase` 後執行即可解密出一張圖片,得到 Flag
![](https://i.imgur.com/jHX9L1X.jpg)
Flag: `clOse_t3h_f1le_On_th1s_One@flare-on.com`
> 其實這題我一開始的解法是直接把整隻程式自己寫了一遍,加上解密功能,結果 Debug 了半天...
>
> 解完後與 Lucas 討論後才被點醒,可以直接 patch binary
而 LingLing 則是直接用 hexrays decompile 之後, 配合 `defs.h` 再小修改就能編譯成執行檔,IDA Pro 實在是太強大了
>
> 覺得傻傻直接重寫一份出來的我真是笨蛋... Orz
---
## Challenge 3: unknown
### 程式行為:
1. 會先呼叫 `wcsrchr(argv[0], 'r');` 取得檔名中字元 `r` 位置後面的子字串
2. 將取得的子字串進行一連串 hash,透過修改過的 `md5` 及 `RC4` 解出一串長度為 104 bytes 的密文
3. 從 `argv[1]` 中每次依序取一個字元字元進行 hash,每次結果 為 4 bytes,最後與密文比對
### 分析
檔案名稱中並沒有字元 `r`,但可以在程式中找到 pdb path 為 `C:\extraspecial.pdb`,可推測出執行檔原始名稱為 `extraspecial.exe`
hash 函數行為如下:
```python
def hash(s):
v = 0
for i in s:
v = ord(i) + 37 * v
return v
```
而 hash `argv[1]` 的方式為:
```python
for i in range(27):
hash(chr(argv[1][i]) + chr(ord("`") + i) + "FLARE On!")
```
簡單的窮舉一下就能得到 Flag:
```python
#!/usr/bin/env python
l = [0xee613e2f, 0xde79eb45, 0xaf1b2f3d, 0x8747bbd7, 0x739ac49c, 0xc9a4f5ae, 0x4632c5c1, 0xa0029b24, 0xd6165059, 0xa6b79451, 0xe79d23ba, 0x8aae92ce, 0x85991a18, 0xfee05899, 0x430c7994, 0x1ab9f36f, 0x70c42481, 0x05bd27cf, 0xc4ff6e6f, 0x5a77847c, 0xdd9277b3, 0x25843cff, 0x5fdca944, 0x8ee42896, 0x2ae961c7, 0xa77731da]
def hash(s):
v = 0
for i in s:
v = ord(i) + 37 * v
return v
out = ""
for i, h in enumerate(l):
for c in range(ord(' '), ord('~')):
if hash(chr(c) + chr(ord("`") + i) + "FLARE On!") & 0xffffffff == h:
out += chr(c)
print out
```
Flag: `Ohs0pec1alpwd@flare-on.com`
---
## Challenge 4: flareon2016challenge.dll
只給了一個 dll,以 `ordinary` export 了 51 個函數:
前面 48 個函數的長相都差不多,都是在對 `byte_10007014` 這個 array 做操作:
```c
signed int flareon2016challenge_1()
{
byte_10007014[dword_10007010-- % 16u] += 79;
return 51;
}
signed int flareon2016challenge_2()
{
char v0; // cl@1
v0 = __ROL1__(byte_10007014[dword_10007010 % 0x10u], 7);
byte_10007014[dword_10007010-- % 0x10u] = v0;
return 8;
}
signed int flareon2016challenge_3()
{
char v0; // cl@1
v0 = __ROL1__(byte_10007014[dword_10007010 % 0x10u], 1);
byte_10007014[dword_10007010-- % 0x10u] = v0;
return 26;
}
...
```
第 51 個函數會拿 `byte_1007014` array 來當 key 解密一些資料:
```c
void flareon2016challenge_51()
{
char subkey[140]; // [sp+0h] [bp-90h]@1
set_cast128_subkey(subkey, byte_10007014, 16u);
cast128_decrypt_round16(subkey, byte_10007060, byte_10007060, 6672);
printf("play me a song, have me play along..\n");
}
```
仔細觀察這些 export 的函數會發現每個函數都會 return 一個不同的數字,猜測就是呼叫的順序
透過 `hexrays decompiler` 配合簡單的文字取代及排序得到函數跟回傳值的對應表:
```
1: 51
2: 8
3: 26
4: 17
5: 47
6: 35
7: 16
8: 13
9: 4
10: 25
11: 21
12: 9
13: 28
14: 11
15: 42
16: 29
17: 33
18: 12
19: 38
20: 36
21: 27
22: 2
23: 6
24: 41
25: 3
26: 32
27: 23
28: 45
29: 39
30: 15
31: 44
32: 22
33: 31
34: 19
35: 1
36: 7
37: 10
38: 40
39: 24
40: 48
41: 43
42: 18
43: 46
44: 34
45: 5
46: 14
47: 20
48: 37
```
排序一下可以知道第一個呼叫的函數應該要是 `flareon2016challenge_30`
寫個 python script 來得到呼叫順序,依照順序呼叫函數並 dump 出解密的資料:
> 寫這篇時才發現可以用 `c_char` 直接從 memory 讀取資料
```python
with open("list.txt") as f:
for l in f.readlines():
t = l.split(":")
no, ret = int(t[0]), int(t[1])
d[no] = ret
chain = [30,]
while True:
if chain[-1] not in d:
break
nxt = d[chain[-1]]
chain.append(nxt)
print chain # [30, 15, 42, 18, 12, 9, 4, 17, 33, 31, 44, 34, 19, 38, 40, 48, 37, 10, 25, 3, 26, 32, 22, 2, 8, 13, 28, 45, 5, 47, 20, 36, 7, 16, 29, 39, 24, 41, 43, 46, 14, 11, 21, 27, 23, 6, 35, 1, 51]
dll = cdll.LoadLibrary("./flareon2016challenge.dll")
for i in chain:
dll[i]()
# dump memory from dll base + 0x7060
data = (c_char * 6672).from_address(dll._handle + 0x7060)
open("./dump", "wb").write(data.raw)
```
Dump 出來的是一個 PE file,丟進 IDA Pro:
```c
int main()
{
Beep(0x1B8u, 0x1F4u);
Beep(0x1B8u, 0x1F4u);
Beep(0x1B8u, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x3E8u);
Beep(0x293u, 0x1F4u);
Beep(0x293u, 0x1F4u);
Beep(0x293u, 0x1F4u);
Beep(0x2BAu, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x19Fu, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x3E8u);
return 0;
}
```
這對應的是 dll 中的第 50 個函數:
```c
void __cdecl flareon2016challenge_50(DWORD dwFreq, DWORD dwDuration)
{
Beep(dwFreq, dwDuration);
byte_10007028[dword_10008DD4] -= dwFreq;
byte_10007028[dword_10008DD4] ^= dwDuration;
if ( ++dword_10008DD4 == 18 )
flareon2016challenge_49();
}
```
用同樣的參數呼叫 18 次 `flareon2016challenge_50` ,程式會播放一段 `Star Wars` 的音樂,接著印出 flag
Solution:
```python
from ctypes import *
chain = [30, 15, 42, 18, 12, 9, 4, 17, 33, 31, 44, 34, 19, 38, 40, 48, 37, 10, 25, 3, 26, 32, 22, 2, 8, 13, 28, 45, 5, 47, 20, 36, 7, 16, 29, 39, 24, 41, 43, 46, 14, 11, 21, 27, 23, 6, 35, 1, 51]
dll = cdll.LoadLibrary("./flareon2016challenge.dll")
for i in chain:
dll[i]()
# data = (c_char * 6672).from_address(dll._handle + 0x7060)
# open("./dump", "wb").write(data.raw)
# dll[49]()
Beep = dll[50]
Beep(0x1B8, 0x1F4);
Beep(0x1B8, 0x1F4);
Beep(0x1B8, 0x1F4);
Beep(0x15D, 0x15E);
Beep(0x20B, 0x96);
Beep(0x1B8, 0x1F4);
Beep(0x15D, 0x15E);
Beep(0x20B, 0x96);
Beep(0x1B8, 0x3E8);
Beep(0x293, 0x1F4);
Beep(0x293, 0x1F4);
Beep(0x293, 0x1F4);
Beep(0x2BA, 0x15E);
Beep(0x20B, 0x96);
Beep(0x19F, 0x1F4);
Beep(0x15D, 0x15E);
Beep(0x20B, 0x96);
Beep(0x1B8, 0x3E8);
```
Flag: `f0ll0w_t3h_3xp0rts@flare-on.com`
---
## Challenge 5: smokestack.exe
### 程式行為
會從 `argv[1]` 拿 10 個字元進行複雜的運算,將結果進行 100 次的 md5 hash 後當成 RC4 key 解出 flag
分析 `0x401610` 之後發現進行運算的其實是一個 stack based vm,執行前會把 `argv[1]` 依序 push 進 vm stack
### 分析
- 有 14 個指令集,每個指令為 2 bytes
- 四個 register: `R0` `R1` `SP` `IP`,大小為 2 bytes
- 14 個指令的處理函數位於 `0x40DEE0`,`op code` 分別為 0 ~ 13,大致分為
- push / pop
- add / sub
- lsl / lsr
- xor
- neg
- cmp
- push by flag
- jmp
- load reg
- write reg
- nop
- pcode 位於 `0x40A140`
寫一個簡單的 dissasembler 來幫助理解 `pcode` 流程:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
INS = [0x0000,0x0021,0x0002,0x0000,0x0091,0x0008,0x0000,0x0016,0x0000,0x000C,0x0009,0x000A,0x000B,0x0000,0x0000,0x000C,0x0002,0x000C,0x0000,0x0000,0x001D,0x000A,0x000B,0x0000,0x0000,0x0063,0x0002,0x000C,0x0000,0x0000,0x0018,0x0006,0x0000,0x0054,0x0008,0x0000,0x0033,0x0000,0x0029,0x0009,0x000A,0x000B,0x0000,0x0000,0x002C,0x0002,0x000C,0x0000,0x0000,0x003D,0x000A,0x0000,0x000E,0x0001,0x000B,0x0000,0x0000,0x0059,0x0002,0x000C,0x0000,0x000B,0x0000,0x0000,0x0000,0x000C,0x0001,0x0000,0x0009,0x000C,0x0000,0x000B,0x0001,0x0000,0x0002,0x0002,0x000C,0x0001,0x000B,0x0000,0x0000,0x0001,0x0003,0x000C,0x0000,0x000B,0x0000,0x0000,0x0000,0x0008,0x0000,0x0047,0x0000,0x0060,0x0009,0x000A,0x000C,0x0000,0x000B,0x0001,0x0003,0x0000,0x005D,0x0008,0x0000,0x007C,0x0000,0x006E,0x0009,0x000A,0x000B,0x0000,0x0000,0x0007,0x0003,0x000C,0x0000,0x0000,0x005B,0x000C,0x0001,0x0000,0x0087,0x000A,0x0000,0x0036,0x000C,0x0001,0x000B,0x0000,0x000B,0x0001,0x0002,0x000C,0x0001,0x000B,0x0001,0x0000,0x0058,0x0002,0x0006,0x0000,0x00F9,0x0008,0x0000,0x00A0,0x0000,0x0096,0x0009,0x000A,0x000B,0x0000,0x0000,0x004D,0x0006,0x000C,0x0000,0x0000,0x00AE,0x000A,0x0000,0x0323,0x0000,0x012B,0x0003,0x000C,0x0001,0x000B,0x0000,0x000B,0x0001,0x0002,0x000C,0x0001,0x000C,0x0001,0x000B,0x0001,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x0000,0x0003,0x0002,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x00B2,0x0000,0x00C7,0x0009,0x000A,0x0007,0x0000,0xFE77,0x0008,0x0000,0x00D8,0x0000,0x00D1,0x0009,0x000A,0x000B,0x0000,0x0000,0x0058,0x0002,0x000C,0x0000,0x0000,0x0003,0x0004,0x0000,0x008C,0x0002,0x0000,0x6094,0x0008,0x0000,0x00EE,0x0000,0x00E7,0x0009,0x000A,0x000B,0x0000,0x0000,0x00E7,0x0002,0x000C,0x0000,0x000B,0x0001,0x0002,0x0000,0x000C,0x0006,0x0000,0x0074,0x0008,0x0000,0x0107,0x0000,0x00FD,0x0009,0x000A,0x000B,0x0000,0x0000,0x0009,0x0003,0x000C,0x0000,0x0000,0x011D,0x000A,0x0000,0x000A,0x000C,0x0001,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x010B,0x0000,0x011D,0x0009,0x000A,0x0000,0x0006,0x0005,0x0000,0x1DC0,0x0008,0x0000,0x0133,0x0000,0x0129,0x0009,0x000A,0x000B,0x0000,0x0000,0x0071,0x0002,0x000C,0x0000,0x0000,0x013D,0x000A,0x000B,0x0000,0x0000,0x0077,0x0002,0x000C,0x0000,0x0000,0x013D,0x000A,0x0000,0x0016,0x0002,0x0000,0x000E,0x0003,0x0000,0x0061,0x0008,0x0000,0x0153,0x0000,0x014C,0x0009,0x000A,0x000B,0x0000,0x0000,0x002C,0x0003,0x000C,0x0000,0x000C,0x0001,0x000B,0x0001,0x0000,0x212C,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x0000,0x0007,0x0003,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x0159,0x0000,0x016E,0x0009,0x000A,0x0000,0x01CA,0x0006,0x0000,0x1FF5,0x0008,0x0000,0x0181,0x0000,0x017A,0x0009,0x000A,0x000B,0x0000,0x0000,0x0012,0x0002,0x000C,0x0000,0x000D]
ip = 0
sp = 9
r0 = 0
r1 = 0
while ip < len(INS):
op = INS[ip]
print "%04X:" % ip,
if op == 0:
ip += 1
arg = INS[ip]
print "PUSH 0x%X" % arg
ip += 1
elif op == 1:
print "POP"
ip += 1
elif op == 2:
print "ADD"
ip += 1
elif op == 3:
print "SUB"
ip += 1
elif op == 4:
print "LSL"
ip += 1
elif op == 5:
print "LSR"
ip += 1
elif op == 6:
print "XOR"
ip += 1
elif op == 7:
print "NEG"
ip += 1
elif op == 8:
print "CMP"
ip += 1
elif op == 9:
print "FPUSH"
ip += 1
elif op == 10:
print "JMP"
ip += 1
elif op == 11:
ip += 1
arg = INS[ip]
reg = ["R0", "R1", "SP", "IP"][arg]
print "LOAD %s" % reg
ip += 1
elif op == 12:
ip += 1
arg = INS[ip]
reg = ["R0", "R1", "SP", "IP"][arg]
print "WRITE %s" % reg
ip += 1
elif op == 13:
print "NOP"
ip += 1
else:
print "UNKNOWN: %04X" % op
ip += 1
```
```
$ python disasm.py
0000: PUSH 0x21
0002: ADD
0003: PUSH 0x91
0005: CMP
0006: PUSH 0x16
0008: PUSH 0xC
000A: FPUSH
000B: JMP
000C: LOAD R0
000E: PUSH 0xC
0010: ADD
0011: WRITE R0
0013: PUSH 0x1D
0015: JMP
0016: LOAD R0
0018: PUSH 0x63
001A: ADD
001B: WRITE R0
001D: PUSH 0x18
001F: XOR
0020: PUSH 0x54
0022: CMP
0023: PUSH 0x33
0025: PUSH 0x29
0027: FPUSH
0028: JMP
0029: LOAD R0
002B: PUSH 0x2C
002D: ADD
002E: WRITE R0
0030: PUSH 0x3D
0032: JMP
0033: PUSH 0xE
0035: POP
0036: LOAD R0
0038: PUSH 0x59
003A: ADD
003B: WRITE R0
003D: LOAD R0
003F: PUSH 0x0
0041: WRITE R1
0043: PUSH 0x9
0045: WRITE R0
0047: LOAD R1
0049: PUSH 0x2
004B: ADD
004C: WRITE R1
004E: LOAD R0
0050: PUSH 0x1
0052: SUB
0053: WRITE R0
0055: LOAD R0
0057: PUSH 0x0
0059: CMP
005A: PUSH 0x47
005C: PUSH 0x60
005E: FPUSH
005F: JMP
0060: WRITE R0
0062: LOAD R1
0064: SUB
0065: PUSH 0x5D
0067: CMP
0068: PUSH 0x7C
006A: PUSH 0x6E
006C: FPUSH
006D: JMP
006E: LOAD R0
0070: PUSH 0x7
0072: SUB
0073: WRITE R0
0075: PUSH 0x5B
0077: WRITE R1
0079: PUSH 0x87
007B: JMP
007C: PUSH 0x36
007E: WRITE R1
0080: LOAD R0
0082: LOAD R1
0084: ADD
0085: WRITE R1
0087: LOAD R1
0089: PUSH 0x58
008B: ADD
008C: XOR
008D: PUSH 0xF9
008F: CMP
0090: PUSH 0xA0
0092: PUSH 0x96
0094: FPUSH
0095: JMP
0096: LOAD R0
0098: PUSH 0x4D
009A: XOR
009B: WRITE R0
009D: PUSH 0xAE
009F: JMP
00A0: PUSH 0x323
00A2: PUSH 0x12B
00A4: SUB
00A5: WRITE R1
00A7: LOAD R0
00A9: LOAD R1
00AB: ADD
00AC: WRITE R1
00AE: WRITE R1
00B0: LOAD R1
00B2: LOAD R1
00B4: PUSH 0x1
00B6: SUB
00B7: WRITE R1
00B9: PUSH 0x3
00BB: ADD
00BC: LOAD R1
00BE: PUSH 0x0
00C0: CMP
00C1: PUSH 0xB2
00C3: PUSH 0xC7
00C5: FPUSH
00C6: JMP
00C7: NEG
00C8: PUSH 0xFE77
00CA: CMP
00CB: PUSH 0xD8
00CD: PUSH 0xD1
00CF: FPUSH
00D0: JMP
00D1: LOAD R0
00D3: PUSH 0x58
00D5: ADD
00D6: WRITE R0
00D8: PUSH 0x3
00DA: LSL
00DB: PUSH 0x8C
00DD: ADD
00DE: PUSH 0x6094
00E0: CMP
00E1: PUSH 0xEE
00E3: PUSH 0xE7
00E5: FPUSH
00E6: JMP
00E7: LOAD R0
00E9: PUSH 0xE7
00EB: ADD
00EC: WRITE R0
00EE: LOAD R1
00F0: ADD
00F1: PUSH 0xC
00F3: XOR
00F4: PUSH 0x74
00F6: CMP
00F7: PUSH 0x107
00F9: PUSH 0xFD
00FB: FPUSH
00FC: JMP
00FD: LOAD R0
00FF: PUSH 0x9
0101: SUB
0102: WRITE R0
0104: PUSH 0x11D
0106: JMP
0107: PUSH 0xA
0109: WRITE R1
010B: LOAD R1
010D: PUSH 0x1
010F: SUB
0110: WRITE R1
0112: LOAD R1
0114: PUSH 0x0
0116: CMP
0117: PUSH 0x10B
0119: PUSH 0x11D
011B: FPUSH
011C: JMP
011D: PUSH 0x6
011F: LSR
0120: PUSH 0x1DC0
0122: CMP
0123: PUSH 0x133
0125: PUSH 0x129
0127: FPUSH
0128: JMP
0129: LOAD R0
012B: PUSH 0x71
012D: ADD
012E: WRITE R0
0130: PUSH 0x13D
0132: JMP
0133: LOAD R0
0135: PUSH 0x77
0137: ADD
0138: WRITE R0
013A: PUSH 0x13D
013C: JMP
013D: PUSH 0x16
013F: ADD
0140: PUSH 0xE
0142: SUB
0143: PUSH 0x61
0145: CMP
0146: PUSH 0x153
0148: PUSH 0x14C
014A: FPUSH
014B: JMP
014C: LOAD R0
014E: PUSH 0x2C
0150: SUB
0151: WRITE R0
0153: WRITE R1
0155: LOAD R1
0157: PUSH 0x212C
0159: LOAD R1
015B: PUSH 0x1
015D: SUB
015E: WRITE R1
0160: PUSH 0x7
0162: SUB
0163: LOAD R1
0165: PUSH 0x0
0167: CMP
0168: PUSH 0x159
016A: PUSH 0x16E
016C: FPUSH
016D: JMP
016E: PUSH 0x1CA
0170: XOR
0171: PUSH 0x1FF5
0173: CMP
0174: PUSH 0x181
0176: PUSH 0x17A
0178: FPUSH
0179: JMP
017A: LOAD R0
017C: PUSH 0x12
017E: ADD
017F: WRITE R0
0181: NOP
```
由於靜態分析還是會有些運算過程比較難分析,因此再修改出一個類似模擬器的版本,並在 stack 中放入 0xAA 開頭的資料幫助我們快速找出比對處:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
INS = [0x0000,0x0021,0x0002,0x0000,0x0091,0x0008,0x0000,0x0016,0x0000,0x000C,0x0009,0x000A,0x000B,0x0000,0x0000,0x000C,0x0002,0x000C,0x0000,0x0000,0x001D,0x000A,0x000B,0x0000,0x0000,0x0063,0x0002,0x000C,0x0000,0x0000,0x0018,0x0006,0x0000,0x0054,0x0008,0x0000,0x0033,0x0000,0x0029,0x0009,0x000A,0x000B,0x0000,0x0000,0x002C,0x0002,0x000C,0x0000,0x0000,0x003D,0x000A,0x0000,0x000E,0x0001,0x000B,0x0000,0x0000,0x0059,0x0002,0x000C,0x0000,0x000B,0x0000,0x0000,0x0000,0x000C,0x0001,0x0000,0x0009,0x000C,0x0000,0x000B,0x0001,0x0000,0x0002,0x0002,0x000C,0x0001,0x000B,0x0000,0x0000,0x0001,0x0003,0x000C,0x0000,0x000B,0x0000,0x0000,0x0000,0x0008,0x0000,0x0047,0x0000,0x0060,0x0009,0x000A,0x000C,0x0000,0x000B,0x0001,0x0003,0x0000,0x005D,0x0008,0x0000,0x007C,0x0000,0x006E,0x0009,0x000A,0x000B,0x0000,0x0000,0x0007,0x0003,0x000C,0x0000,0x0000,0x005B,0x000C,0x0001,0x0000,0x0087,0x000A,0x0000,0x0036,0x000C,0x0001,0x000B,0x0000,0x000B,0x0001,0x0002,0x000C,0x0001,0x000B,0x0001,0x0000,0x0058,0x0002,0x0006,0x0000,0x00F9,0x0008,0x0000,0x00A0,0x0000,0x0096,0x0009,0x000A,0x000B,0x0000,0x0000,0x004D,0x0006,0x000C,0x0000,0x0000,0x00AE,0x000A,0x0000,0x0323,0x0000,0x012B,0x0003,0x000C,0x0001,0x000B,0x0000,0x000B,0x0001,0x0002,0x000C,0x0001,0x000C,0x0001,0x000B,0x0001,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x0000,0x0003,0x0002,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x00B2,0x0000,0x00C7,0x0009,0x000A,0x0007,0x0000,0xFE77,0x0008,0x0000,0x00D8,0x0000,0x00D1,0x0009,0x000A,0x000B,0x0000,0x0000,0x0058,0x0002,0x000C,0x0000,0x0000,0x0003,0x0004,0x0000,0x008C,0x0002,0x0000,0x6094,0x0008,0x0000,0x00EE,0x0000,0x00E7,0x0009,0x000A,0x000B,0x0000,0x0000,0x00E7,0x0002,0x000C,0x0000,0x000B,0x0001,0x0002,0x0000,0x000C,0x0006,0x0000,0x0074,0x0008,0x0000,0x0107,0x0000,0x00FD,0x0009,0x000A,0x000B,0x0000,0x0000,0x0009,0x0003,0x000C,0x0000,0x0000,0x011D,0x000A,0x0000,0x000A,0x000C,0x0001,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x010B,0x0000,0x011D,0x0009,0x000A,0x0000,0x0006,0x0005,0x0000,0x1DC0,0x0008,0x0000,0x0133,0x0000,0x0129,0x0009,0x000A,0x000B,0x0000,0x0000,0x0071,0x0002,0x000C,0x0000,0x0000,0x013D,0x000A,0x000B,0x0000,0x0000,0x0077,0x0002,0x000C,0x0000,0x0000,0x013D,0x000A,0x0000,0x0016,0x0002,0x0000,0x000E,0x0003,0x0000,0x0061,0x0008,0x0000,0x0153,0x0000,0x014C,0x0009,0x000A,0x000B,0x0000,0x0000,0x002C,0x0003,0x000C,0x0000,0x000C,0x0001,0x000B,0x0001,0x0000,0x212C,0x000B,0x0001,0x0000,0x0001,0x0003,0x000C,0x0001,0x0000,0x0007,0x0003,0x000B,0x0001,0x0000,0x0000,0x0008,0x0000,0x0159,0x0000,0x016E,0x0009,0x000A,0x0000,0x01CA,0x0006,0x0000,0x1FF5,0x0008,0x0000,0x0181,0x0000,0x017A,0x0009,0x000A,0x000B,0x0000,0x0000,0x0012,0x0002,0x000C,0x0000,0x000D]
stack = [0xAA01, 0xAA02, 0xAA03, 0xAA04, 0xAA05, 0xAA06, 0xAA07, 0xAA08, 0xAA09, 0xAA0A]
ip = 0
sp = 9
r0 = 0
r1 = 0
def pop():
global sp
sp -= 1
return stack.pop()
def push(v):
global sp
sp += 1
stack.append(v)
while ip < len(INS):
op = INS[ip]
print "%04X:" % ip,
if op == 0:
ip += 1
v = INS[ip]
ip += 1
push(v)
print "PUSH 0x%X" % v
elif op == 1:
print "POP"
ip += 1
pop()
elif op == 2:
print "ADD",
ip += 1
v1 = pop()
v2 = pop()
push(v1 + v2)
print "(0x%04X + 0x%04X) = 0x%04X" % (v1, v2, v1 + v2)
elif op == 3:
print "SUB",
ip += 1
v1 = pop()
v2 = pop()
push(v2 - v1)
print "(0x%04X - 0x%04X) = 0x%04X" % (v2, v1, v2 - v1)
elif op == 4:
print "LSL",
ip += 1
v1 = pop()
v2 = pop()
v = ((v2 << (16 - v1)) | (v2 >> v1)) & 0xffff
push(v)
print "(0x%04X << 0x%04X) = 0x%04X" % (v2, v1, v)
elif op == 5:
print "LSR",
ip += 1
v1 = pop()
v2 = pop()
v = ((v2 >> (16 - v1)) | (v2 << v1)) & 0xffff
push(v)
print "(0x%04X >> 0x%04X) = 0x%04X" % (v2, v1, v)
elif op == 6:
print "XOR",
ip += 1
v1 = pop()
v2 = pop()
push(v2 ^ v1)
print "(0x%04X ^ 0x%04X) = 0x%04X" % (v1, v2, v2 ^ v1)
elif op == 7:
print "NEG",
ip += 1
v = pop()
push(~v & 0xffff)
print " 0x%04X (PUSH 0x%04X)" % (v, ~v & 0xffff)
elif op == 8:
print "CMP",
ip += 1
v1 = pop()
v2 = pop()
print "(%04X == %04X) ?" % (v1, v2),
if v1 == v2:
push(1)
print "PUSH 0x1"
else:
push(0)
print "PUSH 0x0"
elif op == 9:
print "FPUSH",
ip += 1
v1 = pop()
v2 = pop()
if stack.pop() == 1:
push(v1)
print "(0x%04X)" % v1
else:
push(v2)
print "(0x%04X)" % v2
elif op == 10:
print "JMP",
v = pop()
print "%04X" % v
ip = v
elif op == 11:
ip += 1
arg = INS[ip]
reg = ["R0", "R1", "SP", "IP"][arg]
print "LOAD %s" % reg,
ip += 1
if reg == "R0":
v = r0
elif reg == "R1":
v = r1
elif reg == "SP":
v = sp
elif reg == "IP":
v = ip - 1
push(v)
print "(PUSH %04X)" % v
elif op == 12:
ip += 1
arg = INS[ip]
reg = ["R0", "R1", "SP", "IP"][arg]
print "WRITE %s" % reg,
ip += 1
v = pop()
if reg == "R0":
r0 = v
elif reg == "R1":
r1 = v
elif reg == "SP":
sp = v
elif reg == "IP":
ip = v
print "(%s = %04X)" % (reg, v)
elif op == 13:
print "NOP"
ip += 1
else:
print "UNKNOWN: %04X" % op
ip += 1
```
```
$ python emu.py
0000: PUSH 0x21
0002: ADD (0x0021 + 0xAA0A) = 0xAA2B
0003: PUSH 0x91
0005: CMP (0091 == AA2B) ? PUSH 0x0
0006: PUSH 0x16
0008: PUSH 0xC
000A: FPUSH (0x0016)
000B: JMP 0016
0016: LOAD R0 (PUSH 0000)
0018: PUSH 0x63
...
```
以上例來說就可以發現 `argv[1][9]` + `0x21` 必須等於 `0x91`,
因此 `argv[1][9]` 為 `0x91-0x21` = `0x70 (p)`
結合靜態與動態分析方式後得出 `argv[1]` 必須為 `kYwxCbJoLp`,執行後得到 Flag
Flag: `A_p0p_pu$H_&_a_Jmp@flare-on.com`
---