JQCTF2025 Customize Virtual Machine Official Writeup

大意是用输入的 50 位 flag 解密 vm 的 handler 然后需要正确输出对应字符串,函数地址和长度表在二进制文件里直接给了,可以直接提取。

注意到前 15 个是全部解密,后 35 个少解密一个字节,不难想到常规函数的最后一个字节都是 retn 指令 0xC3,利用这个特性可以解出前 15 byte 的 flag 并且获得 15 个函数样本,不难发现所有被加密的 Handler 的模式都是大致相同的,可以用这些样本预测后续的函数。

后续 35 字节需要爆破,根据 flag 格式字符范围在 36 个左右,先判断解密结果有没有包含非法指令(用 pwntools 反汇编就是不出现 bad 或 byte),如果没有的话直接把解密结果和之前的 15 个样本喂给 AI 判断相似程度即可。

测试用的 gpt-4o-mini,构造的 prompt 如下:

You are now a professional assembly code analyst. There are 20 example code snippets here(separated by =====). You need to determine whether the given code is similar to these example codes. Only needs to be similar to any one of the example code snippets. We consider two code segments to be similar if both code segments perform similar functions. Example code: {sample}, given code: {TestCode}. If similar respond True otherwise False, do not include any additional information.

exp 如下,脸黑网慢的话大概要跑个 15 分钟左右。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pwn import *
from openai import OpenAI

#Function address table 0x47C0
addr = [4592, 4704, 4816, 4928, 5008, 5136, 5248, 5344, 5376, 5504, 5520, 5632, 5776, 5856, 5936, 6032, 6160, 6192, 6240, 6352, 6464, 6576, 6672, 6800, 6912, 6928, 7056, 7152, 7184, 7296, 7328, 7424, 7536, 7568, 7648, 7664, 7776, 7872, 7968, 8080, 8176, 8256, 8336, 8464, 8560, 8688, 8800, 8832, 8960, 9072]
# Function length table 0x46E0
lens = [109, 109, 108, 78, 119, 98, 84, 22, 117, 13, 97, 138, 76, 80, 94, 113, 18, 42, 110, 107, 99, 93, 120, 109, 16, 128, 83, 24, 112, 29, 87, 112, 24, 75, 13, 105, 86, 84, 101, 91, 77, 79, 113, 88, 121, 102, 19, 122, 110, 98]

sample = ""
flag = ""

# Use Byte retn to get first 15 byte of flag and sample functions.
for j in range(15):
    with open("customVM", 'rb') as f:
        data = bytearray(f.read())[addr[j]:addr[j]+lens[j]]
    print(data)
    key = data[-1]^0xc3
    flag += chr(key)
    c = b""
    for _ in data:
        c += (_^key).to_bytes(1, byteorder="little")
    assembly_code = disasm(c, arch='amd64', os='linux')
    sample += assembly_code+"\n"
    sample += "============\n"

print("First 15 byte: ", flag)

# Use AI to Predict the rest
for j in range(15, 50):
    table = "0123456789abcdefghijklmnopqrstuvwxyz_"

    for ii in table:
        got = False
        i = ord(ii)
        c = b""
        with open("customVM", 'rb') as f:
            machine_code = bytearray(f.read())[addr[j]:addr[j]+lens[j]-1]
        
        for _ in machine_code:
            c += (_^i).to_bytes(1, byteorder="little")
      
        TestCode = disasm(c, arch='amd64', os='linux')
        runnable = True
        for lines in TestCode.split('\n'):
            if "byte" in lines or "(bad)" in lines: # Not Runnable Code
                runnable = False
                break
        if runnable:
            print("TESTING: ", ii)
            print(TestCode)
            print("============")
            prompt = f"You are now a professional assembly code analyst. There are 20 example code snippets here(separated by =====). You need to determine whether the given code is similar to these example codes. Only needs to be similar to any one of the example code snippets. We consider two code segments to be similar if both code segments perform similar functions. Example code: {sample}, given code: {TestCode}. If similar respond True otherwise False, do not include any additional information."
            
            client = OpenAI(
                base_url='xxxx',
                api_key='xxxx',
            )
            chat_completion = client.chat.completions.create(
                messages=[
                    {
                        "role": "user",
                        "content": prompt,
                    }
                ],
                model="gpt-4o-mini",
            )
            print(chat_completion.choices[0].message.content)
            if "True" in chat_completion.choices[0].message.content or "true" in chat_completion.choices[0].message.content:
                got = True
                flag += ii
                print(f"Got flag[{ii}]: {flag}")
                break
    
    if not got:
        print("WARN: TRY AGAIN ON ", j)

print(flag)
0%