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 View0561D0A9D7240处数据,调试得到对该处数据进行修改的代码段。发现这段数据是由两处数据(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 9C  08 DC DC F3 1A 91 9C 6E
34 5C E4 5E F9 E2 5F F1  F0 86 05 A8 70 6E 04 53
9D 31 EC 10 AB EA F6 74  44 79 0F 28 53 40 37 2C
17 9A C3 67 95 2F 4B 27  D9 3F F9 1D 2A 70 77 5D     ---> magic/key[0]

xor(buf[0], key[0]) -->
D0 9B D2 1A 3E 23 73 A4  31 EC BD 91 79 F5 F9 08
05 6E D7 6A CC D4 68 C9  C9 B6 64 CA 13 0A 61 35
AC 03 DF 24 9E DC C1 4C  7D 49 6E 4A 30 24 52 4A
26 A8 F0 53 A0 19 7C 1F  E0 0F 98 7F 49 14 12 3B

encode_1(| , 0x4d) -->
EF 03 FC 01 F3 2F 89 42  62 07 7A 53 5A C3 67 64
8E 34 86 BD 37 32 AF 3E  1F A1 C0 ED 5A 8D 99 1A
2D 39 D9 96 4C 79 42 21  AC 86 75 E0 9B C4 93 3B
98 A9 2F C9 4D 09 86 44  4A C9 04 15 7E 0A 34 83     ---> enc[0]


2->>>
E1 A9 E1 2E 0B 15 44 9C  08 DC DC F3 1A 91 9C 6E
34 5C E4 5E F9 E2 5F F1  F0 86 05 A8 70 6E 04 53
9D 31 EC 10 AB EA F6 74  44 79 0F 28 53 40 37 2C
17 9A C3 67 95 2F 4B 27  D9 3F F9 1D 2A 70 77 5D     ---> key[0]

encode_1(key[0], 0x6f) -->
E1 B3 CA 97 A5 93 EC 9F  FC 0E 15 B8 BB AE F0 D4
70 97 85 0A 22 4E 04 6E  EE 79 8D 48 4E 37 1A 2E
72 AF 7C F1 AF 78 78 C3  02 54 38 37 82 A9 CE 18
76 88 55 75 7B 3A A2 BC  07 94 29 A0 1B 96 0B CD  

xor(|, buf[0]) -->
D0 81 F9 A3 90 A5 DB A7  C5 3E 74 DA D8 CA 95 B2
41 A5 B6 3E 17 78 33 56  D7 49 EC 2A 2D 53 7F 48
43 9D 4F C5 9A 4E 4F FB  3B 64 59 55 E1 CD AB 7E
47 BA 66 41 4E 0C 95 84  3E A4 48 C2 78 F2 6E AB     ---> key[1]

xor(buf[1], key[1]) -->
E1 B3 CA 97 A5 93 EC 9F  FC 0E 15 B8 BB AE F0 D4
70 97 85 0A 22 4E 04 6E  EE 79 8D 48 4E 37 1A 2E
72 AF 7C F1 AF 78 78 C3  02 54 38 37 82 A9 CE 18
76 88 55 75 7B 3A A2 BC  07 94 29 A0 1B 96 0B CD 

encode_1(| , 0x4d) -->
94 F7 80 32 05 74 C3 72  A1 39 7C 56 F9 B2 74 92
FD 93 DF A1 02 77 D7 15  9E 1A EE B2 50 41 C4 89
C0 CD 3D AF 11 C9 E9 46  C3 45 EE 95 2F FE 15 0F
6F 58 80 0A E7 46 30 D5  19 C3 0E B1 AA 6E 4F 47    --> enc[1]

3->>>
D0 81 F9 A3 90 A5 DB A7  C5 3E 74 DA D8 CA 95 B2
41 A5 B6 3E 17 78 33 56  D7 49 EC 2A 2D 53 7F 48
43 9D 4F C5 9A 4E 4F FB  3B 64 59 55 E1 CD AB 7E
47 BA 66 41 4E 0C 95 84  3E 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

总结一下,这个题目关键是调试,由于算法部分只能看汇编,直接全部看明白需要花费较长时间。调试和猜测能大大减少时间。

(完)