VolgaCTF TrustVM Writeup
下載題目,總共三個檔案: reverse
, encrypt
, data.enc
題目地址 另外附上 idb檔案 地址方便除錯
根據題目名和檔案猜測, 應該是一個虛擬機器, encrypt
檔案中儲存opcode, data.enc
是通過 reverse
根據 encypt
加密得來的密文。
ida 分析 reverse
程式碼
執行 reverse
需要輸入 progname
( encrypt
) 和 filetoprocess
( 被加密檔案
) 做引數。
構造 test
測試檔案
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
第一次執行
分析 reverse
檔案的 main
函式程式碼
得到讀取檔案的函式 記 read_
(sub_55CBF7147210)
程式開始時通過傳入引數讀入 encrypttest
檔案
接著解碼opcode 並跳轉
開始除錯,跟蹤執行流程
發現出題人對程式碼做了處理,每個opcode對應的操作被寫在一塊彙編程式碼中,無法反編譯。但是有一個特徵,會經常跳回 0055D1D8DB1AD0
段程式碼, 在此處下斷點,猜測是對 encrypt
檔案進行解碼跳轉到下一個指令。
根據ida的流程圖,在附近轉轉就會發現寫入資料到加密檔案的塊
完整分析這個塊就知道,寫入資料由暫存器 rbp
確定, 在 Hex View
設定 Synchronize with RBP
再次除錯,發現 rbp
就是 test
檔案資料存放的位置,而且整個過程 rbp
值沒有變動
每次F9, 記錄F9次數, 直到資料改變,由此可以得到,修改 rbp
指向資料的程式碼塊(地址: 0561D0A7D5F20
)
多次除錯發現,該塊程式碼是將固定地址( 0561D0A9D7240
)的資料複製到 rbp
指向的資料,每次複製16*4位元組資料。猜測加密處理的基本單位是 64 位元組。
新開一個 Hex View
看 0561D0A9D7240
處資料,除錯得到對該處資料進行修改的程式碼段。發現這段資料是由兩處資料( 0561D0A9D7380
[^1] 0561D0A9D7280
[^2])異或 ( 0000561D0A7D5CB0
)後通過 encode_1
( 0561D0A7D5C10
)操作的結果。第二段資料就是待加密資料(有很明顯特徵,可以更改資料進行驗證)。
分析 encode_1
,發現其接收兩個引數,一個被操作資料,另一short型別的資料。除錯發現引數只存在兩種情況( 0561D0A9D7380
, 0x6f
) 和 ( 0561D0A9D7280
, 0x4d
)
跟蹤地址 000055CBF7348380(key[i]) 000055CBF7348280(buf[i]) 000055CBF7348240(enc[i]) 1->>> E1 A9 E1 2E 0B 15 44 9C08 DC DC F3 1A 91 9C 6E 34 5C E4 5E F9 E2 5F F1F0 86 05 A8 70 6E 04 53 9D 31 EC 10 AB EA F6 7444 79 0F 28 53 40 37 2C 17 9A C3 67 95 2F 4B 27D9 3F F9 1D 2A 70 77 5D---> magic/key[0] xor(buf[0], key[0]) --> D0 9B D2 1A 3E 23 73 A431 EC BD 91 79 F5 F9 08 05 6E D7 6A CC D4 68 C9C9 B6 64 CA 13 0A 61 35 AC 03 DF 24 9E DC C1 4C7D 49 6E 4A 30 24 52 4A 26 A8 F0 53 A0 19 7C 1FE0 0F 98 7F 49 14 12 3B encode_1(| , 0x4d) --> EF 03 FC 01 F3 2F 89 4262 07 7A 53 5A C3 67 64 8E 34 86 BD 37 32 AF 3E1F A1 C0 ED 5A 8D 99 1A 2D 39 D9 96 4C 79 42 21AC 86 75 E0 9B C4 93 3B 98 A9 2F C9 4D 09 86 444A C9 04 15 7E 0A 34 83---> enc[0] 2->>> E1 A9 E1 2E 0B 15 44 9C08 DC DC F3 1A 91 9C 6E 34 5C E4 5E F9 E2 5F F1F0 86 05 A8 70 6E 04 53 9D 31 EC 10 AB EA F6 7444 79 0F 28 53 40 37 2C 17 9A C3 67 95 2F 4B 27D9 3F F9 1D 2A 70 77 5D---> key[0] encode_1(key[0], 0x6f) --> E1 B3 CA 97 A5 93 EC 9FFC 0E 15 B8 BB AE F0 D4 70 97 85 0A 22 4E 04 6EEE 79 8D 48 4E 37 1A 2E 72 AF 7C F1 AF 78 78 C302 54 38 37 82 A9 CE 18 76 88 55 75 7B 3A A2 BC07 94 29 A0 1B 96 0B CD xor(|, buf[0]) --> D0 81 F9 A3 90 A5 DB A7C5 3E 74 DA D8 CA 95 B2 41 A5 B6 3E 17 78 33 56D7 49 EC 2A 2D 53 7F 48 43 9D 4F C5 9A 4E 4F FB3B 64 59 55 E1 CD AB 7E 47 BA 66 41 4E 0C 95 843E A4 48 C2 78 F2 6E AB---> key[1] xor(buf[1], key[1]) --> E1 B3 CA 97 A5 93 EC 9FFC 0E 15 B8 BB AE F0 D4 70 97 85 0A 22 4E 04 6EEE 79 8D 48 4E 37 1A 2E 72 AF 7C F1 AF 78 78 C302 54 38 37 82 A9 CE 18 76 88 55 75 7B 3A A2 BC07 94 29 A0 1B 96 0B CD encode_1(| , 0x4d) --> 94 F7 80 32 05 74 C3 72A1 39 7C 56 F9 B2 74 92 FD 93 DF A1 02 77 D7 159E 1A EE B2 50 41 C4 89 C0 CD 3D AF 11 C9 E9 46C3 45 EE 95 2F FE 15 0F 6F 58 80 0A E7 46 30 D519 C3 0E B1 AA 6E 4F 47--> enc[1] 3->>> D0 81 F9 A3 90 A5 DB A7C5 3E 74 DA D8 CA 95 B2 41 A5 B6 3E 17 78 33 56D7 49 EC 2A 2D 53 7F 48 43 9D 4F C5 9A 4E 4F FB3B 64 59 55 E1 CD AB 7E 47 BA 66 41 4E 0C 95 843E A4 48 C2 78 F2 6E AB ...
得到加密演算法
# encoding=utf-8 magic = [225, 169, 225, 46, 11, 21, 68, 156, 8, 220, 220, 243, 26, 145, 156, 110, 52, 92, 228, 94, 249, 226, 95, 241, 240, 134, 5, 168, 112, 110, 4, 83, 157, 49, 236, 16, 171, 234, 246, 116, 68, 121, 15, 40, 83, 64, 55, 44, 23, 154, 195, 103, 149, 47, 75, 39, 217, 63, 249, 29, 42, 112, 119, 93] def xor(a, b): res = [] for i in range(len(a)): res.append(a[i] ^ b[i]) return res def encode_1(arr, num): result = [0] * 0x40 cl = num & 7 idx = num >> 3 for i in range(0x40): result[(idx+i)%0x40] = ((arr[(0x3f+i+1)%0x40] << cl) | (arr[(0x3f+i)%0x40]>>(8-cl)))&0xff return result def encrypt(buf): key = [] * len(buf) enc = [] * len(buf) key[0] = magic enc[0] = encode_1(xor(magic, buf[0]), 0x4d) for i in range(len(buf) - 1): key[i+1] = xor(encode_1(key[i], 0x6f), buf[i]) enc[i+1] = encode_1(xor(key[i+1], buf[i+1]), 0x4d) return enc # 解密 # de_decode_1 來自 yype def de_encode_1(arr,num): result = [0] * 0x40 cl = num & 7 idx = num >> 3 for i in range(0x40): result[(0x3f+i+1)%0x40] += arr[(idx+i)%0x40] >> cl result[(0x3f+i)%0x40] += (arr[(idx+i)%0x40] << (8-cl))&0xff return result def decrypt(): # get enc data = open("./data.enc", "rb").read() data += b'x00' * (len(data)%0x40)# padding len_0 = len(data)//0x40 enc = [] buf = [] key = [] for i in range(len_0): enc.append([x for x in data[i*0x40: (i+1)*0x40]]) buf.append([]) key.append([]) key[0] = magic buf[0] = xor(de_encode_1(enc[0], 0x4d), magic) for i in range(1, len_0): key[i] = xor(encode_1(key[i-1], 0x6f), buf[i-1]) buf[i] = xor(de_encode_1(enc[i], 0x4d), key[i]) data = b'' # show buf for x in buf: data += bytearray(x) open("data.png", "wb").write(data) if __name__ == "__main__": decrypt()
得到flag
總結一下,這個題目關鍵是除錯,由於演算法部分只能看彙編,直接全部看明白需要花費較長時間。除錯和猜測能大大減少時間。