# reverse 2022 edu ctf
## [LAB] Sacred Arts

先open /tmp/flag

沒辦法open的話, print wrong 程式結束
可以open的話,讀裡面的資料,然後 jmp short loc_4010c3

loc_4010c3的行為是將 /tmp/flag 裡的 data 做2的補數,交換eax的高八位和低八位,最後跟 byte_40108B 每8個byte做比較,正確的話jmp loc_4010fd 輸出correct
```python
with open('./byte_40108B') as f:
t = f.readlines()
t = [i.strip() for i in t]
#xchg al, ah
t = [bin(int(i,16))[2:] for i in t]
t = [i[:-16] + i[-8:] + i[-16:-8] for i in t]
#neg rax
t = [int(i, 2)-1 for i in t]
mask = int('1'*64, 2)
t = [i^mask for i in t]
t = [bin(i)[2:] for i in t]
s = ""
for i in t:
i = i.rjust(64, '0')
for j in range(0,64,8):
s += chr(int(i[j:j+8],2))
print(s[::-1])
```
用python 模擬即可得flag

## [LAB] Kekkou

把檔案丟進ida 觀察main function,程式的行為是輸入flag,call sub_11f1, sub_125f再用sub131c的result做判斷,正確就輸出correct

sub_11f1 的行為是傳一個structure *a1和char** flag當參數

用 gdb 觀察 sub_11f1 行為,發現做完後會將剛剛的 flag[i] 用linked list的形式 存在 a1

sub_125f 的行為是將 linked list 的 data xor byte_4040[idx+2]

sub131c 確認 linked list 的 data 是否和 arr410 相同

把 function 名子改成看得懂的,然後在將剛剛的 sub_125f (對input做xor的function) 設斷點,然後用gdb看 linked-list裡面存的值被xor後變成了多少。

- 將 input做完xor後 存在 linked list 裡的值用 gdb print出來
( input xor $X_i$ ) xor input = $X_i$, 因此能知道 input 裡每一個 byte 會 xor 甚麼值,然後再把 arr410[i] xor $X_i$ 就能回推出正確的 input 了
```python
with open('xor_output', 'r') as f:
t = f.read()
after_xor = [t.split('\n')[i] for i in range(1, len(t.split('\n')), 2)]
after_xor = [i.split(': ')[1] for i in after_xor]
mod = []
for i in range(65):
input_i = 49+(i+1)
x = input_i ^ int(after_xor[i], 16)
mod.append(x)
arr410 = "41 92 41 47 EF BC 65 8B F2 6F 75 5F 6D 75 DF 9A 5F B3 8F 61 89 31 61 F5 3F 5D 61 69 8F 21 9D 96 A7 61 5C EC 03 5F 70 3C C0 DC 79 56 6E 25 6F 5F BD DD 72 FF 73 34 69 B5 6D 58 5F 0C 49 40 72 C8 5D"
arr410 = [i for i in arr410.split(' ') if i != '']
correct_input = []
for i in range(65):
correct_input.append(int(arr410[i],16) ^ mod[i])
t = b""
for i in correct_input:
t += (i).to_bytes(1, byteorder='big')
print(t)
```
執行python script後得

## [LAB] Why

用ida看 main的結構,why的行為是檢查輸入值 -10 後是否等於 enc_flag ,是的話 pass = 1


從fini_array可以看到有sub_11f8 若 pass 則 correct

gdb

ida
不知道為甚麼用ida 看是檢查輸入值 -10 後是否等於 enc_flag,gdb是+10
```python
#gdb
with open('enc_flag') as f:
t = f.read()
t = [int(i, 16) - 10 for i in t.split()]
t = [chr(i) for i in t]
print(''.join(t))
#ida
with open('enc_flag') as f:
t = f.read()
t = [int(i, 16) + 10 for i in t.split()]
t = [chr(i) for i in t]
print(''.join(t))
```
-10 跟 +10 都試試看,ida好像寫錯了

## [HW] trace
ㄧ

執行一遍,程式行為是輸入flag錯了就 print wrong,用ida 看main,稍微分析後重新命名function name,第十行call了一個奇怪的東西先不管他往下看

- fork_child() : fork 一個process 成功的話執行cs_2022_fall_ouo

- cs_2022_fall_ouo() : 執行/tmp/cs_2022_fall_ouo, argv[1]是 cs_2022_fall_ouo,由此可知剛剛main裡第十行應改是寫了一個elf 到 /tmp/cs_2022_fall_ouo
fork child process 後進入一個無限迴圈執行兩個function: single_step_getReg(),readAddr()

- single_step_getReg() : child 往前走一步然後 load child 的registers 進 regs

- readAddr() : 若regs_80 (regs+0x80) == 0xe8cbccdeadbeefe8 就把regs_80 換成0x90909090909090,因為90 是 nop 的 machine code,推測regs_80 是program counter的address,若program counter指到的地方存的值是 0xe8cbccdeadbeefe8 就換成 換成0x90909090909090

先執行看看剛剛的 child process在幹啥


用ida 看 /tmp/cs_2022_fall_ouo在做啥,call了一個奇怪的ptr 還有 int 3,看起來不太合理

用hex editor把0xe8cbccdeadbeefe8 取代成0x90909090909090

再用ida分析可以發現行為變得稍微正常了,plz() 和 give_me_flag() print了一些無關緊要的東西

- xor_s2_0_to_0x15() : 把s2 前 15個bytes xor 0x71

- compare_and_print_():輸入 flag 若 == s2 就輸出 well done!
```python
with open('s2', 'r') as f:
t = f.read()
t = t.split()
t = [int(i,16) for i in t]
for i in range(0x17):
t[i] ^= 0x71
t = [chr(i) for i in t]
print(t)
print("".join(t))
```
執行後可得

## [HW] pwn_myself
執行之後看起來毫無反應就直接exit了,用ida分析找到一個叫做main的function,main function會確認user的權限,權限太低就離開。改用root執行並且在main附近設個中斷點,用gdb 看 callstack 找到 readKeyboard 這個 function

readKeyboard的行為是讀取鍵盤的輸入經過一連串判斷後進入set_string

set_string 給arrr[0:44] 賦值,當index == 44 進入 printflag()

print_flag 第十行先設置 sending_buf 若 sending_buf 前48個char 都等於 flag0 用 udp 傳出text,否則傳出 sending_buf,看來這邊的關鍵是第十行的set_buf()

進去set_buf裡面看,第16行推測是根據剛剛 set_string 裡 arr的內容來加密後賦值給 sending_buf。


因為 set_buf() 前面幾行的function點進去看也不知道在幹嘛,且題目提示有使用 openssl,所以用 ida 提供的flair70製作openssl的signature 檔

將剛剛製作的openssl.sig load進ida,可以看到第十五行變成aes128cbc加密,推測這邊的行為是將 text2_near_flag, text1_near_flag 當做iv或key 然後對arr加密後傳給sending_buf,因此將 flag0 用 aes128 解密就能知道要將arr是多少


---
## [LAB] AMessageBox

觀察程式行為發現隨便輸入個flag會跳出一個msgBox

user32.dll->MsgBox
從符號的視窗中將所有跟msgbox有關的function設置breakpoint

- call stack

從上圖的call stack可以找到疑似處理flag的function

第一層的判斷是比較amessagebox_904ac0c699abc2f6.B63018和 flag 兩個string的長度,若相同則進入下一關

第二層的判斷是將flag的每個字元做 rol 2 和 xor 87 後若與b63018的string相同,則進入下一層

```python
t = "B5 E5 8D BD 5C 46 36 4E 4E 1E 0E 26 A4 1E 0E 4E 46 06 16 AC B4 3E 4E 16 94 3E 94 8C 94 8C 9C 4E A4 8C 2E 46 8C 6C"
for c in t.split():
n = "{:08b}".format(int(c,16) ^ 0x87)
n = n[-3:] + n[:-3]
print(chr(int(n, 2)), end='')
```
做完運算結果為 FLAG{8699314d319802ef792b7babac9da58a}
## [LAB] IAT Hook -------------------------
## [LAB] GetProcAddress-------------------------
## [HW]ooxx

遊戲結束時會跳出msgbox

從符號裡面把msgbox都設斷點

從呼叫堆疊裡面找是誰call msgbox


assembly 看不懂用ida看,可以看到這邊有三個條件print出各種不同的結果推測其行為是確認:贏或輸或平手

進去check()裡面看,很明顯這邊是判斷如果2連成線對手就贏了,val存的是每個格子裡是X或是O

找到val的位置在(.data +d388)


用cheat engine打開oxox,找到val的位置 7FF6B1B4D338 (ooxx.exe.text+d388)


cheat engine有個功能可以看哪些instruction 在修改 指定address的值,隨便走一步後,可以看到這行 count為1的 **mov [rcx+rax*4],00002** 修改了val


把 **mov [rcx+rax*4],00002** 改成 nop 後對手的行為就開始變得異常了

## [HW] trojan

用 wireshark 開 log.pcapng

用 ida 找到 winmain,從 pcapng 推測 while loop 前面幾行是在localhost:19832 bind 一個 tcp server

用 x64dbg 分析,在 sreeenshot() 可以看到一個 png檔的路徑,png檔打開來看是螢幕截圖

winmain 第十行的 function把 callBack 綁到 (v7+0x64),第十一行的 function 行為是設定socket,最後createThread()

callback()的行為是收一個512 bytes的 bufffer,比較其是否等於 "cDqr0hUUz",若等於讀取 **/temp/_____.png** 的 bytes,tcp 回傳 png 檔案 bytes數,然後第二十行的encrypt 對每一個bytes xor,最後把 加密後的 bytes 由 tcp 回傳

把剛剛 log.pcappng tcp 回傳的第二個封包裡 的 bytes 覆蓋 /temp/_______.png,tcp server 再次 callback() 時剛剛的 encrypt() 會把 bytes xor 成未加密的模樣並回傳給client
```python
import socket
host = "127.0.0.1"
port = 19832 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(b'cDqr0hUUz1')
data = s.recv(1024) # drop
data = s.recv(999999)
with open('./data.png', 'wb') as f:
f.write(data)
s.close()
```
python script執行完,可得一png檔

---
## [LAB] Exception [12]
## [LAB] TLS Callback [11]
## [LAB] Meow [11]
## [HW] dropper

用 die看發現 dropper 是用 upx 加殼

用 upx 脫殼

ida 看 main,可以觀察到 CS_2002 字樣和一些疑似加密解密的動作

用 x64dbg 分析 callstack 程式最後是跑到這行就停住了,這邊的行為是 sleep(9a7ec800),手動把 ecx 改成0,往下走幾步就能看到flag了

___
## note
IAT - import address table
gdb
x *(void**)0x600fe8
As @zwol mentioned in the comment, gdb needs type information about the operand to decide its size. This type casting tells gdb that 0x600fe8 is a pointer to a pointer.
python [gdb.execute('ni') for i in range(50)]

---
## x86 instruction
- lea (load effective address)
- lea eax, [ebp+8]
- eax = (ebp + 8)
- mov eax, [ebp+8]
- eax = *(ebp + 8)
---
## 看瞴

https://youtu.be/1EAV4XeQ8C4?t=5451
### PEB(process enviroment block)


### handle
### hmodule