owned this note
owned this note
Published
Linked with GitHub
---
title: HitconQuals-2019 EmojiVM
description: CTF
tags: CTF
lang: zh_tw
---
# HitconQuals-2019 EmojiVM
[TOC]
解題隊伍/全隊伍: 77/1116
## 題目敘述
A simple VM that takes emojis as input! Try figure out the secret!
emojivm-d967bd1b53b927820de27960f8eec7d7833150ca.zip
Author: bruce30262
## 解題過程
看起來題目實作了一個 VM emojivm
並給了一個這個 VM 的執行檔 chal.evm 要跑
首先看了一下 chal.evm 中, symbol 有這些
```
🈳⏬😅😍❌😀➕🆕😜😂📥😁🤣😄😆😉😊📝📄📤💯🈶🚀🈚❓💀➖❎👫🛑
```
對應的 hex
| Emoji | hex | case | 備註 |
| -------- | -------- | --- | -- |
| 😀 | 0xf0 0x9f 0x98 0x80 | 0x0 | 0
| 😁 | 0xf0 0x9f 0x98 0x81 | 0x0 | 1
| 😂 | 0xf0 0x9f 0x98 0x82 | 0x0 | 2
| 🤣 | 0xf0 0x9f 0xa4 0xa3 | 0x0 | 3
| 😜 | 0xf0 0x9f 0x98 0x9c | 0x0 | 4
| 😄 | 0xf0 0x9f 0x98 0x84 | 0x0 | 5
| 😅 | 0xf0 0x9f 0x98 0x85 | 0x0 | 6
| 😆 | 0xf0 0x9f 0x98 0x86 | 0x0 | 7
| 😉 | 0xf0 0x9f 0x98 0x89 | 0x0 | 8
| 😊 | 0xf0 0x9f 0x98 0x8a | 0x0 | 9
| 😍 | 0xf0 0x9f 0x98 0x8d | 0x0 | a
| 🈳 | 0xf0 0x9f 0x88 0xb3 | 0x1 |
| ➕ | 0xe2 0x9e 0x95 | 0x2 |
| ➖ | 0xe2 0x9e 0x96 | 0x3 |
| ❌ | 0xe2 0x9d 0x8c | 0x4 |
| ❓ | 0xe2 0x9d 0x93 | 0x5 |
| ❎ | 0xe2 0x9d 0x8e | 0x6 |
| 👫 | 0xf0 0x9f 0x91 0xab | 0x7 |
| 💀 | 0xf0 0x9f 0x92 0x80 | 0x8 |
| 💯 | 0xf0 0x9f 0x92 0xaf | 0x9 |
| 🚀 | 0xf0 0x9f 0x9a 0x80 | 0xa |
| 🈶 | 0xf0 0x9f 0x88 0xb6 | 0xb |
| 🈚 | 0xf0 0x9f 0x88 0x9a | 0xc |
| ⏬ | 0xe2 0x8f 0xac | 0xd |
| 🔝 | | 0xe | 賽到ㄉ |
| 📤 | 0xf0 0x9f 0x93 0xa4 | 0xf |
| 📥 | 0xf0 0x9f 0x93 0xa5 | 0x10 |
| 🆕 | 0xf0 0x9f 0x86 0x95 | 0x11 |
| 🆓 | | 0x12 | 賽到ㄉ
| 📄 | 0xf0 0x9f 0x93 0x84 | 0x13 |
| 📝 | 0xf0 0x9f 0x93 0x9d | 0x14 |
| 🔡 | | 0x15 | 詳見 "意外發現" |
| 🔢 | | 0x16 | 詳見 "意外發現" |
| 🛑 | 0xf0 0x9f 0x9b 0x91 | 0x17 |
根據指令 case 排序一下
落在 0x0 ~ 0x17 中
只要是表情符號的都是 0x0 (該不會是 registers 吧ㄏ)
啊我怎知道 case 的?
創一個 test.evm, 裡頭一次放其中一個 emoji
在 main 的 switch 開跳之前 break
蛤, 我怎知道 main 有 switch ?
IDA pro 的 F5 很好用
![](https://i.imgur.com/bLvflVb.png)
蛤, 我怎麼下斷點的 ?
先
```
gdb ./emojivm
```
後, 下 gdb 指令
```
starti
```
慢慢追到 call \_\_libc\_start\_main\_ptr 的時候
![](https://i.imgur.com/4OxW1TV.png)
如此你就知道 IDA 靜態分析的位址跟實際動態的位址 offset 為多少
就能下斷點啦
---
main 大概長這樣
![](https://i.imgur.com/Oy0oddj.png)
阿其實一堆 function 裡面在幹嘛我不知道, 還沒追
settingInit 很簡單, setvbuf 之類的在這
他接著的 sub_4221 跟 readCmd 詳細在幹嘛不知道, 開了兩個坑
再來的 exe 裡頭有 switch case, 看起來很 VM
就先看看 exe 裡頭, 搞懂每個指令在做啥
在這之前, 可以很簡單發現, 指令 0xE 0x12 0x15 0x16 沒有用到
好想先找出對應這些指令的 emoji @@
~~ㄏㄏ 被我亂賽找到一個~~
不不不還是先分析好了
---
### 0x0
Invalid, 爽ㄛ
### 🈳 0x1
![](https://i.imgur.com/CxcSlUr.png)
水喔, 下面一位
### ➕ 0x2
![](https://i.imgur.com/GvgN0Xk.png)
### ➖ 0x3
![](https://i.imgur.com/zfdJzpE.png)
### ❌ 0x4
![](https://i.imgur.com/BxvD6Cz.png)
### ❓ 0x5
![](https://i.imgur.com/8BjxBRy.png)
### ❎ 0x6
![](https://i.imgur.com/ioChUAH.png)
### 👫 0x7
![](https://i.imgur.com/cNDZBiF.png)
### 💀 0x8
![](https://i.imgur.com/AfMO09i.png)
### 💯 0x9
![](https://i.imgur.com/eMyegr5.png)
### 🚀 0xa
![](https://i.imgur.com/DmA7bkV.png)
jmp 的感覺
### 🈶 0xb
![](https://i.imgur.com/quuhvEe.png)
if 的感覺
### 🈚 0xc
![](https://i.imgur.com/vkFm557.png)
if ! 的感覺
### ⏬ 0xd
![](https://i.imgur.com/JvfF6MR.png)
看起來多一點了
```
如果 stack pointer == 1024
{
輸出 unk_9870 的東西
exit
}
將 cmdString[index + 1] 餵進 function_d
存回 stack 上
```
也就是說這個指令後面要跟 1 emoji 的參數
看一下 function_d
![](https://i.imgur.com/MBqLKnX.png)
首先執行 sub_5F04 不能為 0
看一下 sub_5F04
![](https://i.imgur.com/dcVT8v7.png)
要他不回傳 0, 那 sub_691A 要是 0
看一下 sub_691A
![](https://i.imgur.com/7gt8fIq.png)
rename 一下, 叫做 isValueSame 好了
更新一下 sub_5F04
![](https://i.imgur.com/kjDdzeh.png)
所以 v3 v4 的 value 不能一樣
順下來看 sub_68CE
![](https://i.imgur.com/QgM9PpV.png)
參數是 a1 是 0x20e1c0
所以接著會 call sub_7AD0(&v2, 0x20e1c8)
![](https://i.imgur.com/3MI3q9e.png)
rename 一下, 叫做 setValue 好了
所以 sub_68CE 也只是回傳 a1 + 8
rename 為 plus_8_ 好了
再來看 sub_67FC
![](https://i.imgur.com/8GZVMIM.png)
...
哎呀好多
反正最後就是會存一個 data 就對ㄌ @@
然後有找到一個意外發現, 寫在下面ㄛ
### 🔝 0xe
![](https://i.imgur.com/rxiedbT.png)
```
if stack poiner 不是底
{
輸出 unk_98C0 的東西
exit
}
pop
```
總之就是 pop
~~從出題者的角度找適合 pop 的 emoji 就賽到ㄌㄏㄏ mind reversing~~
### 📤 0xf
![](https://i.imgur.com/57suert.png)
![](https://i.imgur.com/qjTpqK4.png)
將 stack 前兩個 pop 出來餵進 function_f 存回 stack
function_f 去拿 memoryStruct[index] 存的第 location 個字
### 📥 0x10
![](https://i.imgur.com/Yam2iO5.png)
三元指令
看一下 function_10
![](https://i.imgur.com/cB7oMMb.png)
a1 沒有在 0 ~ 10 之間或是 20E200[a1] 沒值, GG
所以 20E200 應該是個 array[10] 只是裡頭放什麼還不知道
取 array[a1] 出來後值也要 0 <= a2 < array[a1]
array[a1] 表示這一項的 size
a2 不能超過
之後就能將 a3 存進 [a2 + [array[a1] + 4]]
最後更新如下
![](https://i.imgur.com/mNhW3PI.png)
參數:
- index
- location
- data
將 data 寫入 memoryStruct[index] 存著的文字的第 location 個字
### 🆕 0x11
![](https://i.imgur.com/kTKFzmh.png)
從 emoji 看來, 感覺就是 new memory 出來的操作 XD
![](https://i.imgur.com/zFbeL3W.png)
還真的
所以這 op 後面要帶 size 多大
這邊有回頭更新 0x10 ㄛ
### 🆓 0x12
![](https://i.imgur.com/ipZ3lBC.png)
![](https://i.imgur.com/y6iAvW4.png)
Free 掉
### 📄 0x13
![](https://i.imgur.com/Fua8qVh.png)
![](https://i.imgur.com/VUBBved.png)
寫入 memoryStruct[index] 的文字
### 📝 0x14
![](https://i.imgur.com/0tA2tDv.png)
![](https://i.imgur.com/x4YIQAd.png)
print 出來 memoryStruct[index] 的文字
### 🔡 0x15
![](https://i.imgur.com/djYE9O8.png)
![](https://i.imgur.com/d0SqQMK.png)
把 stack pop & print 出來直到遇到 NULL Byte
### 🔢 0x16
![](https://i.imgur.com/wRwqE7N.png)
![](https://i.imgur.com/VEI02Lm.png)
把 stack pop & print 一個出來
### 🛑 0x17
![](https://i.imgur.com/LOdlLMt.png)
88888
### 意外發現
發現 20E1C0 是個 emojiTable
好像是在前面的 function setting 的
這 20E1C0 存著三個 pointer
位址分別在 [20E1C0 + 0x10] [20E1C0 + 0x18] [20E1C0 + 0x20]
三個 pointer 指過去後 + 0x20 會指向第一個 emoji 的 code print
再來每 0x30 就是下一個 code print
![](https://i.imgur.com/I56sPpn.png)
統整下來 三個表裡頭的 emoji 如下
```
😄 😅 😆 😉 😊 😍
0x0001f604
0x0001f605
0x0001f606
0x0001f609
0x0001f60a
0x0001f60d
😀 😁 😂 🤣 😜 😄 😅 😆 😉 😊 😍
0x0001f600
0x0001f601
0x0001f602
0x0001f923
0x0001f61c
0x0001f604
0x0001f605
0x0001f606
0x0001f609
0x0001f60a
0x0001f60d
🤣 😜 😄 😅 😆 😉 😊 😍
0x0001f923
0x0001f61c
0x0001f604
0x0001f605
0x0001f606
0x0001f609
0x0001f60a
0x0001f60d
```
~~我到底在幹嘛~~
然後回頭看一開始 switch cmd 的部分
也有類似的結構
0x20E180 跟上述的 20E1C0 emojitable 長一樣
也有三個 pointer, offset 也一樣
```
➖❌❓❎👫💀💯🚀🈶🈚⏬🔝📤📥🆕🆓📄📝🔡🔢🛑😀😁😂🤣😜😄😅😆😉😊😍
0x00002796
0x0000274c
0x00002753
0x0000274e
0x0001f46b
0x0001f480
0x0001f4af
0x0001f680
0x0001f236
0x0001f21a
0x000023ec
0x0001f51d
0x0001f4e4
0x0001f4e5
0x0001f195
0x0001f193
0x0001f4c4
0x0001f4dd
0x0001f521
0x0001f522
0x0001f6d1
0x0001f600
0x0001f601
0x0001f602
0x0001f923
0x0001f61c
0x0001f604
0x0001f605
0x0001f606
0x0001f609
0x0001f60a
0x0001f60d
```
通通 cmd number <-> emoji 都知道ㄌ
### 題目
以上差不多對全部指令有了解了
來看看題目ㄅ 先看看前面能不能看懂
再想要不要自動化
初始時
- stack pointer
sp = -1
- memoryStruct
ms[10] 全空
- cmd index
index = 0
---
好的現在超晚
經過長時間奮戰
因為我實在對寫這東西的工具感到很麻煩
手工逆了出來, 腳本是 [solve.py](https://github.com/LJP-TW/CTF/blob/master/HitconCTF-2019/reverse/EmojiVM/Temp/emojivm_reverse/solve.py)
~~chal_bp.evm 是簡化版的, 方便手工逆向時看的 chal.evm 可以不要看~~
~~chal_bp2.evm 是拿來算 index 的, 不簡化只做換行 可以不要看~~
~~solve_bp.py 是初版 [solve.py](https://github.com/LJP-TW/CTF/blob/master/HitconCTF-2019/reverse/EmojiVM/Temp/emojivm_reverse/solve.py) 可以不要看~~
test.evm 是拿來測試emojivm的小測資