CISCN 2024 reverse 国赛复盘

asm_re

手撕汇编,配合 GPT 分析,加上一点点的猜测。
在汇编推出可以看到加密逻辑:

value = ord(f[1])
value *= 0x50;
value += 0x14;
value ^= 0x4D;
value += 0x1E;
print(value)

已经知道第一个为 f , f 经过加密得到 0x1FD7, l 可以得到 0x21B7。同理,根据数据段可以
还原出flag,代码如下【部分非16进制数手动调整】:


import re

text = """
__const:0000000100003F10 D7                            DCB 0xD7
__const:0000000100003F11 1F                            DCB 0x1F
__const:0000000100003F12 00                            DCB    0
__const:0000000100003F13 00                            DCB    0
__const:0000000100003F14 B7                            DCB 0xB7
__const:0000000100003F15 21                            DCB 0x21 ; !
__const:0000000100003F16 00                            DCB    0
__const:0000000100003F17 00                            DCB    0
__const:0000000100003F18 47                            DCB 0x47 ; G
__const:0000000100003F19 1E                            DCB 0x1E
__const:0000000100003F1A 00                            DCB    0
__const:0000000100003F1B 00                            DCB    0
__const:0000000100003F1C 27                            DCB 0x27 ; '
__const:0000000100003F1D 20                            DCB 0x20
__const:0000000100003F1E 00                            DCB    0
__const:0000000100003F1F 00                            DCB    0
__const:0000000100003F20 E7                            DCB 0xE7
__const:0000000100003F21 26                            DCB 0x26 ; &
__const:0000000100003F22 00                            DCB    0
__const:0000000100003F23 00                            DCB    0
__const:0000000100003F24 D7                            DCB 0xD7
__const:0000000100003F25 10                            DCB 0x10
__const:0000000100003F26 00                            DCB    0
__const:0000000100003F27 00                            DCB    0
__const:0000000100003F28 27                            DCB 0x27 ; '
__const:0000000100003F29 11                            DCB 0x11
__const:0000000100003F2A 00                            DCB    0
__const:0000000100003F2B 00                            DCB    0
__const:0000000100003F2C 07                            DCB 0x07
__const:0000000100003F2D 20                            DCB 0x20
__const:0000000100003F2E 00                            DCB    0
__const:0000000100003F2F 00                            DCB    0
__const:0000000100003F30 C7                            DCB 0xC7
__const:0000000100003F31 11                            DCB 0x11
__const:0000000100003F32 00                            DCB    0
__const:0000000100003F33 00                            DCB    0
__const:0000000100003F34 47                            DCB 0x47 ; G
__const:0000000100003F35 1E                            DCB 0x1E
__const:0000000100003F36 00                            DCB    0
__const:0000000100003F37 00                            DCB    0
__const:0000000100003F38 17                            DCB 0x17
__const:0000000100003F39 10                            DCB 0x10
__const:0000000100003F3A 00                            DCB    0
__const:0000000100003F3B 00                            DCB    0
__const:0000000100003F3C 17                            DCB 0x17
__const:0000000100003F3D 10                            DCB 0x10
__const:0000000100003F3E 00                            DCB    0
__const:0000000100003F3F 00                            DCB    0
__const:0000000100003F40 F7                            DCB 0xF7
__const:0000000100003F41 11                            DCB 0x11
__const:0000000100003F42 00                            DCB    0
__const:0000000100003F43 00                            DCB    0
__const:0000000100003F44 07                            DCB 0x07
__const:0000000100003F45 20                            DCB 0x20
__const:0000000100003F46 00                            DCB    0
__const:0000000100003F47 00                            DCB    0
__const:0000000100003F48 37                            DCB 0x37 ; 7
__const:0000000100003F49 10                            DCB 0x10
__const:0000000100003F4A 00                            DCB    0
__const:0000000100003F4B 00                            DCB    0
__const:0000000100003F4C 07                            DCB 0x07
__const:0000000100003F4D 11                            DCB 0x11
__const:0000000100003F4E 00                            DCB    0
__const:0000000100003F4F 00                            DCB    0
__const:0000000100003F50 17                            DCB 0x17
__const:0000000100003F51 1F                            DCB 0x1F
__const:0000000100003F52 00                            DCB    0
__const:0000000100003F53 00                            DCB    0
__const:0000000100003F54 D7                            DCB 0xD7
__const:0000000100003F55 10                            DCB 0x10
__const:0000000100003F56 00                            DCB    0
__const:0000000100003F57 00                            DCB    0
__const:0000000100003F58 17                            DCB 0x17
__const:0000000100003F59 10                            DCB 0x10
__const:0000000100003F5A 00                            DCB    0
__const:0000000100003F5B 00                            DCB    0
__const:0000000100003F5C 17                            DCB 0x17
__const:0000000100003F5D 10                            DCB 0x10
__const:0000000100003F5E 00                            DCB    0
__const:0000000100003F5F 00                            DCB    0
__const:0000000100003F60 67                            DCB 0x67 ; g
__const:0000000100003F61 1F                            DCB 0x1F
__const:0000000100003F62 00                            DCB    0
__const:0000000100003F63 00                            DCB    0
__const:0000000100003F64 17                            DCB 0x17
__const:0000000100003F65 10                            DCB 0x10
__const:0000000100003F66 00                            DCB    0
__const:0000000100003F67 00                            DCB    0
__const:0000000100003F68 C7                            DCB 0xC7
__const:0000000100003F69 11                            DCB 0x11
__const:0000000100003F6A 00                            DCB    0
__const:0000000100003F6B 00                            DCB    0
__const:0000000100003F6C C7                            DCB 0xC7
__const:0000000100003F6D 11                            DCB 0x11
__const:0000000100003F6E 00                            DCB    0
__const:0000000100003F6F 00                            DCB    0
__const:0000000100003F70 17                            DCB 0x17
__const:0000000100003F71 10                            DCB 0x10
__const:0000000100003F72 00                            DCB    0
__const:0000000100003F73 00                            DCB    0
__const:0000000100003F74 D7                            DCB 0xD7
__const:0000000100003F75 1F                            DCB 0x1F
__const:0000000100003F76 00                            DCB    0
__const:0000000100003F77 00                            DCB    0
__const:0000000100003F78 17                            DCB 0x17
__const:0000000100003F79 1F                            DCB 0x1F
__const:0000000100003F7A 00                            DCB    0
__const:0000000100003F7B 00                            DCB    0
__const:0000000100003F7C 07                            DCB 0x07
__const:0000000100003F7D 11                            DCB 0x11
__const:0000000100003F7E 00                            DCB    0
__const:0000000100003F7F 00                            DCB    0
__const:0000000100003F80 47                            DCB 0x47 ; G
__const:0000000100003F81 0F                            DCB 0x0F
__const:0000000100003F82 00                            DCB    0
__const:0000000100003F83 00                            DCB    0
__const:0000000100003F84 27                            DCB 0x27 ; '
__const:0000000100003F85 11                            DCB 0x11
__const:0000000100003F86 00                            DCB    0
__const:0000000100003F87 00                            DCB    0
__const:0000000100003F88 37                            DCB 0x37 ; 7
__const:0000000100003F89 10                            DCB 0x10
__const:0000000100003F8A 00                            DCB    0
__const:0000000100003F8B 00                            DCB    0
__const:0000000100003F8C 47                            DCB 0x47 ; G
__const:0000000100003F8D 1E                            DCB 0x1E
__const:0000000100003F8E 00                            DCB    0
__const:0000000100003F8F 00                            DCB    0
__const:0000000100003F90 37                            DCB 0x37 ; 7
__const:0000000100003F91 10                            DCB 0x10
__const:0000000100003F92 00                            DCB    0
__const:0000000100003F93 00                            DCB    0
__const:0000000100003F94 D7                            DCB 0xD7
__const:0000000100003F95 1F                            DCB 0x1F
__const:0000000100003F96 00                            DCB    0
__const:0000000100003F97 00                            DCB    0
__const:0000000100003F98 07                            DCB 0x07
__const:0000000100003F99 11                            DCB 0x11
__const:0000000100003F9A 00                            DCB    0
__const:0000000100003F9B 00                            DCB    0
__const:0000000100003F9C D7                            DCB 0xD7
__const:0000000100003F9D 1F                            DCB 0x1F
__const:0000000100003F9E 00                            DCB    0
__const:0000000100003F9F 00                            DCB    0
__const:0000000100003FA0 07                            DCB 0x07
__const:0000000100003FA1 11                            DCB 0x11
__const:0000000100003FA2 00                            DCB    0
__const:0000000100003FA3 00                            DCB    0
__const:0000000100003FA4 87                            DCB 0x87
__const:0000000100003FA5 27                            DCB 0x27 ; '
__const:0000000100003FA6 00                            DCB    0
__const:0000000100003FA7 00                            DCB    0

"""

pattern = re.compile(r'DCB\s+(0x[\da-fA-F]{2})')
matches = pattern.findall(text)
result = []

# 打印所有匹配到的十六进制数字
for match in matches:
    result.append(match)

for i in range (0, len(result),2):
    temp = result[i+1]+result[i][2:]
    value = int(temp, 16)
    value -= 0x1E
    value ^= 0x4D
    value -= 0x14;
    print(chr(value // 0x50),end="")

结果:flag{67e9a228e45b622c2992fb5174a4f5f5}


androidso_re

动态调试一下拿到DES的Key和IV【下面是十进制,化为 16 进制后解密即可】
image.png

Key: 4138556457616571 
IV: 576633444c757073

CBC模式解密即可
JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==
image.png


gdb_debug

逻辑并不难,加密的过程:
设置随机数种子 -> 异或随机数 -> 置换[随机数生成的] -> 异或随机数 -> 得到v31
最后一遍异或的在内存中可以得到:

s2 = "congratulationstoyoucongratulationstoy"
print(len(s2))
v32 = [191, 215, 46, 218, 238, 168, 26, 16, 131, 115, 172,241, 6, 190, 173, 136, 4, 215, 18, 254, 181, 226, 97,183, 61, 7, 74, 232, 150, 162, 157, 77, 188, 129, 140,233, 136, 120]
print(len(v32))
v31 = []
for i in range(0, len(s2)):
    v31.append(v32[i]^ord(s2[i]))
print(v31)

接着前面还有三个加密过程 设置随机数种子 -> 异或随机数 -> 置换[随机数生成的] -> 异或随机数,本来我想随机数种子范围有限,想要暴破,不过后来发现,不需要暴破直接按照原来的逻辑就可以,代码如下【注意,由于 linux 和 windows 的随机数生成不同,所以代码要在 linux 上运行】:

#include <iostream>
#include <string>


int main(int argc, const char** argv, const char** envp) {
    // 加密过程 异或随机数 -> 置换[随机数生成的] -> 异或随机数 -> 得到v31
    unsigned char v18; // [rsp+55h] [rbp-DBh]
    char v19; // [rsp+56h] [rbp-DAh]
    char ptr[38] = { 0 };
    int v31[] = { 220, 184, 64, 189, 156, 201, 110, 101, 239, 18, 216, 152, 105, 208, 222, 252, 107, 174, 125, 139, 214, 141, 15, 208, 79, 102, 62, 157, 250, 195, 233, 36, 211, 239, 255, 157, 231, 1 };
    int v28[38] = { 0 };
    int len = 38;
    int v3 = time(0LL);
    srand(v3 & 0xF0000000);

    // 存储随机数,之后使用
    int arr1[38] = { 0 };
    for (int i = 0; i < 38; i++) {
        arr1[i] = rand();
    }

    // ptr 生成
    for (int j = 0LL; ; ++j) {
        if (j >= 38)
            break;
        *((char*)ptr + j) = j;
    }

    // 随机置换ptr数组的元素
    for (int k = len - 1; k > 0; --k) {
        v18 = rand() % (k + 1);
        v19 = *((char*)ptr + k);
        *((char*)ptr + k) = *((char*)ptr + v18);
        *((char*)ptr + v18) = v19;
    }

    // 异或
    for (int i = 0; i < 38; i++) {
        v31[i] ^= rand();
    }
    // 置换恢复
    for (int m = 0; m < 38; m++) {
        v28[ptr[m]] = v31[m];
    }
    // 异或
    for (int i = 0; i < 38; i++) {
        v28[i] ^= arr1[i];
    }

    for (int i = 0; i < 38; i++) {
        printf("%c", v28[i]);
    }

    printf("\n");
}

image.png
结果:flag{78bace5989660ee38f1fd980a4b4fbcd}


whereThel1b

所实话,依旧是不理解。半蒙半猜吧。
先看 python 文件:

import whereThel1b

flag = input("where is my flag:")
flag = flag.encode()
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
whereThel1b.whereistheflag(flag) # 加密逻辑
ret = whereThel1b.trytry(flag) # 加密逻辑

if ret == encry:
    print("rrrrrrrrrrrright")
else:
    print("wwwwwwwwwwwwwwwrong")

可以看到调用了 whereThel1b 中的 whereistheflag 函数和 trytry 函数,接着 IDA 分析 .os 文件,硬读 Cpython。
CreateStringTabAndInitStrings 看看初始化以及一些关键函数的调用:
可以看到有 randomrandintseed
image.png
还可以看到 base64 的加密函数:
image.png
在_pyx_pymod_exec_whereThel1b,可以看到属性的设置。
base64randint 在 whereistheflag1 中:
【_pyx_pw_11whereThel1b_5whereistheflag1】
image.png
image.png
seed 的调用在 trytry 中。
image.png
整理下逻辑应该是先对加密后的字符串进行b64encode操作,然后通过seed设置随机数种子后调用random函数获取随机数字符串对明文进行加密然后。

import random
import base64

encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

random.seed(0)
for i in range(len(encry)):
    encry[i] = encry[i] ^ random.randint(0, len(encry))

flag = base64.b64decode(bytes(encry))
print(flag)

rust_baby

方法一:z3

这一题挺有意思的,没有想到还有 z3 的神奇解法。
加密逻辑:
image.png
image.png
其中加密函数:
image.png

import base64
import claripy #一个类似z3的求解库
enc = "igdydo19TVE13ogW1AT5DgjPzHwPDQle1X7kS8TzHK8S5KCu9mnJ0uCnAQ4aV3CSYUl6QycpibWSLmqm2y/GqW6PNJBZ/C2RZuu+DfQFCxvLGHT5goG8BNl1ji2XB3x9GMg9T8Clatc="
enc = list(base64.b64decode(enc))

# 最后面的一次异或
# 通过动态调试找到 xor_box,就是原来的v96
xor_box = [220, 95, 32, 34, 194, 121, 25, 86, 53, 218, 139, 71, 211, 25, 252, 85, 20, 205, 210, 123, 88, 89, 9, 66, 222, 44, 180, 72, 217, 242, 27, 169, 64, 225, 166, 251, 255, 56, 193, 213, 226, 232, 119, 120, 111, 34, 4, 230, 22, 62, 12,
           53, 82, 92, 253, 193, 229, 89, 28, 208, 174, 90, 178, 221, 25, 248, 66, 230, 44, 137, 89, 229, 17, 156, 200, 123, 129, 112, 127, 111, 188, 111, 2, 143, 247, 244, 200, 112, 174, 2, 248, 91, 226, 114, 8, 9, 111, 191, 75, 57, 181, 208, 30, 163]
for i in range(len(enc)):
    enc[i] ^= xor_box[i]

# 在内存中直接得到
key1 = [0x0E71675B493928150, 0x37C65D4C7BA24118, 0x2F6E0584C3B26920,
        0x0C74625ECF3321978, 0x9FFE15F4FB724940, 0x27C69D1453F2E1B0]
# 动态调试得到【好臭的数字】
key2 = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 1, 0] 

# 模拟程序执行逻辑,用claripy求解
# 程序执行逻辑:
""""
    v8 = *((_BYTE *)&v13 + 2 * v7 + 1);
    v9 = (*((_BYTE *)&v13 + 2 * v7) ^ *((_BYTE *)&retaddr + 2 * v7) | v8 ^ *((_BYTE *)&retaddr + 2 * v7 + 1)) & 1;
    *((_BYTE *)input + 2 * v7) = v7 + *((_BYTE *)input + (*((_BYTE *)&v13 + 2 * v7) & 7)) - v9 + 4;
    *((_BYTE *)input + 2 * v7 + 1) = v7 + *((_BYTE *)input + (v8 & 7)) - v9 + 4;
"""

# res 是加密后的数据
# a2 , a3 是 key 不变【注意类型】
def rev(res, a2, a3):
    # 初始化一个长度为8的符号数组,每个元素为8位的符号变量,用于代表未知输入a1的每一位
    # 简单点,就是参数
    a1_arr = [claripy.BVS(f"inp_{i}", 8) for i in range(8)]
    # 将输入a2/a3转换为十六进制字符串,反转字节序后,再转换为整数列表表示
    # 原因:输入的是内存中的值,原始程序中是_int64,需要反转字节序【即内存序转_int64】
    a2_arr = [int(i) for i in list(bytes.fromhex(hex(a2)[2:].zfill(16)))[::-1]]
    a3_arr = [int(i) for i in list(bytes.fromhex(hex(a3)[2:].zfill(16)))[::-1]]

    # 初始化返回值数组
    ret_arr = []
    # 遍历4个块,每个块处理a2和a3的两个字节
    for i in range(4):
        # 计算v9,表示两个对应字节异或结果的最低位
        v9 = ((a2_arr[i*2] ^ a3_arr[i*2]) |
              (a2_arr[i*2+1] ^ a3_arr[i*2+1])) & 1
        # 根据a2的字节值索引a1数组,并应用变换计算ret_arr的元素
        ret_arr.append(i-v9+a1_arr[a2_arr[i*2] & 7])
        ret_arr.append(i-v9+a1_arr[a2_arr[i*2+1] & 7])
    # 对ret_arr中的每个元素执行逆向异或操作0x33,模拟原始编码的逆过程
    for i in range(len(ret_arr)):
        ret_arr[i] ^= 0x33

    # 创建一个求解器实例,用于解决约束满足问题
    s = claripy.Solver()
    # 添加约束条件,确保计算后的ret_arr与给定的res数组中的元素一一对应
    for i in range(len(res)):
        s.add(ret_arr[i] == res[i])
    # 执行求解,寻找满足条件的a1_arr的具体值,这里只求解一组解
    result = s.batch_eval(a1_arr, 1)[0]
    print("".join(chr(c) for c in result), end="")

# 循环调用rev函数,依次解密8个块
for i in range(8):
    rev(enc[i*8:(i+1)*8], key1[i%6], key2[i])
# flag{6e2480b3-4f02-4cf1-9bc0-123b75f9a922}

方法二:插桩暴破

在比赛的时候,我就想暴破了【已经发现计数器的位置了】,但是手动的效率太低了,而且我暂时不太会 frida,在赛后看到一个大佬用 firda 插桩暴破,记录一下。

var number = 0
// frida -l .\hook.js -n rust_baby.exe 
function main()
{
  // 寻找“rust_baby.exe”的基础地址
  var base =  Module.findBaseAddress("rust_baby.exe")

  if(base){
    // 在找到的基础地址上添加拦截器,拦截指定函数
    Interceptor.attach(base.add(0x3EA8), {  //序号加1的位置
      onEnter: function(args) {
        // console.log("成功!");
        // 当函数被调用时执行的逻辑,这里增加了全局变量number的值并发送
        number += 1;
        // 发送数据,这里给 python 脚本发送,如果是命令行,会有发送的回显
        send(number); 
        // 实现延迟,延迟时间单位为毫秒,延迟 4 毫秒
        var delay = 0x4;
        var start = new Date().getTime();
        while (new Date().getTime() < start + delay);
        // console.log("Delay complete.");
      }
    });
  }
}
setImmediate(main);
import subprocess
import frida

# 定义目标文件名和初始命令行参数
filename = "rust_baby.exe"
cmd = ['./rust_baby.exe']

# 从文件读取JavaScript代码
jscode = open("Hook.js", "rb").read().decode()

# 自定义异常类,用于表示成功找到flag
class SUCCESSFLAG(Exception):
    pass

def brute(F):
    """
    通过Frida对目标程序进行动态分析,使用爆破方式获取正确的flag。

    参数:
    F: 用于传递给程序的输入,类型为bytes。

    返回值:
    result: 运行程序后的结果。

    抛出:
    SUCCESSFLAG: 当找到正确的flag时抛出。
    """
    def on_message(message, data):
        global result
        if message['type'] == 'send':
            result = message['payload']
        else:
            print(message)
    # 创建子进程运行目标程序,并使用Frida附加到进程上
    process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               universal_newlines=True)
    session = frida.attach(filename)
    script = session.create_script(jscode)
    script.on('message', on_message)
    script.load()

    # 向进程输入F,并等待进程结束
    process.stdin.write(F.decode())
    output, error = process.communicate()

    # 若输出中包含"correct flag",则打印并抛出SUCCESSFLAG异常
    if "correct flag" in output:
        print("正确的flag是:", F.decode())
        raise SUCCESSFLAG("抛出flag成功的异常!!!")
    process.terminate()
    return result

def bp(startflag, old_number1, tag1):
    """
    基于brute函数的辅助爆破函数,用于逐字符爆破并处理特殊情况。

    参数:
    startflag: 当前已知的flag部分,列表形式。
    old_number1: 上一次爆破得到的数字结果。
    tag1: 标志位,用于控制爆破逻辑。

    返回值:
    startflag: 更新后的flag部分。
    old_number1: 更新后的数字结果。
    tag1: 更新后的标志位。
    """
    global temptag
    global maxnumber
    idx = 0
    temp = old_number1
    targetidx = -1
    table = "-0123456789abcdefghijklmnopqrstuvwxyz}"
    
    # 循环遍历可能的字符,尝试爆破
    for ch in table:
        startflag.append(ord(ch))
        startflag.append(10)
        my_bytearray = bytearray(startflag)
        new_number = brute(my_bytearray)

        if(new_number > old_number1):
            if tag1 == 0:
                old_number1 = new_number
                targetidx = idx
            if(tag1 > 0):
                tag1 -= 1

        idx += 1
        startflag = startflag[:-2]

    # 若未找到目标字符,进行回溯处理
    if(targetidx == -1):
        print("错误的:")
        my_bytearray = bytearray(startflag)
        print(my_bytearray.decode())
        print("去除错误字符!")
        startflag = startflag[:-1]
        print("magic:", temp-1)
        temptag = temptag + 1
        print("magictag:", temptag+1)
        return startflag, temp-1, temptag

    # 找到目标字符,进行相应处理
    bpret = table[targetidx:targetidx+1]
    print("目标数据:", bpret, ord(bpret))
    startflag.append(ord(bpret))
    my_bytearray = bytearray(startflag)
    print(my_bytearray.decode())
    return startflag, old_number1, 0

# 初始化参数并开始爆破流程
# startflag1 = [102, 108, 97, 103, 123]  # flag的格式是固定的,因此初始值已知
startflag = [102, 108, 97, 103, 123, 54, 101, 50, 52, 56, 48, 98, 51, 45, 52, 102, 48, 50, 45, 52, 99, 102]
startflag.append(10)
my_bytearray = bytearray(startflag)

old_number = brute(my_bytearray)
startflag = startflag[:-1]  # 去除回车
temptag = 0
tag = 0
idx = 0
temp = len(startflag)  # 初始长度

cleartag = 5
while True:
    startflag, old_number, tag = bp(startflag, old_number, tag)  # 执行爆破逻辑

    if(temp < len(startflag)):
        temp = len(startflag)
    print("----->", temp-5)
    if((temp-cleartag)%3 == 0):
        print(">>>>清理temptag")
        temptag = 0
        cleartag += 2

GoReverse

ciscn24-GoReverse 速览_哔哩哔哩_bilibili
加密套娃。分析后的加密流程如下:
a8576dcddd4a696db70cfd8bf27f856.png
通过远程连接得到结果,这里复现,就本地创建 flag 文件,假设加密的结果为:
VPAFNU3PTHPAUQTCYUBTVJY6TGBWY3NGGKZ6OFKZ74JJBPF7LSQ3ZYO4IZLOXPVEIE7KZS46VJKNDJGRBJPUTVKTNFLZRQBLLZUD7VI=
base32 解密,AES 的 CBC 模式解密:
image.png
得到:69124cadc128ffe15752488b318e78c68f359d69fb2b8e4bc35ae98ee05493ca31ceb32174201bb091c9406e56430d53c046d1d128a0de5862ee5e6d04040404
下面的 sm4 好麻烦:
sm4并不是对上一步的xxtea密文加密
而是先随机生成16字节(叫做randbytes1)
然后把randbytes1最后1字节加1得到randbytes2,再加1得到randbytes3
sm4加密的密文即为randbytes1+ randbytes2+ randbytes3
得到的密文与上一步的xxtea密文异或
实际上就是SM4 CTR模式。
(CBC 得到的结果的前面就是前 16 位就是 randbytes1)【调试一下】
image.png
image.png
模拟加密流程,可以得到:
66e63d01bbe3a9fdeaf58ff981f941d2a18ca83e76ba547317c13bd0552cd43717415b81d2aebc08f20f6380cb19f21526bde0afa274aa6e0cf8ffdc016763f311bae241451ce9f6ea515263f5415b29
将sm4密文(去掉padding部分)与上面enc除前16字节的部分异或即可得到xxtea密文

import struct

# SM4密文的16进制字符串转换为字节列表
enc = list(bytes.fromhex(
    "69124cadc128ffe15752488b318e78c68f359d69fb2b8e4bc35ae98ee05493ca31ceb32174201bb091c9406e56430d53c046d1d128a0de5862ee5e6d04040404"))

# 分割密文,前16字节(SM4块大小)作为SM4解密的明文部分
sm4_plain = enc[:len(enc)//4]
# 打印SM4明文部分的16进制表示
print(bytes(sm4_plain).hex())

# SM4解密后的明文用于异或的密文,从给定的16进制字符串转换
sm4_plain_enc = list(bytes.fromhex("66e63d01bbe3a9fdeaf58ff981f941d2a18ca83e76ba547317c13bd0552cd43717415b81d2aebc08f20f6380cb19f215"))

# 对剩余的密文部分与SM4解密明文异或操作
inp_enc = enc[len(enc)//4:]
for i in range(len(inp_enc)):
    inp_enc[i] ^= sm4_plain_enc[i]

# 将异或后的结果转换为字节串
inp_enc = bytes(inp_enc)

# 按每4字节分组,转换为小端整数并打印
for i in range(0, len(inp_enc), 4):
    print(hex(struct.unpack("<I", inp_enc[i:i+4])[0]), end=",")

# 0x68a0d3e9,0xb627c840,0x7766af29,0x18d2ad61,0xbce30834,0x8c6889,0xf57d3732,0x9f6a71b2,0xcf62c39b,0xd5579840,0xb2429b2b,0xad81ba1a

xxtea 解密 【有被魔改】:
image.png
image.png

#include <stdio.h>
#include <stdint.h>

#define DELTA 0x7FAB4CAD
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y)) + ((key[(p & 3) ^ e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    //加密
    if (n > 1)
    {
        rounds = 6 + 52 / n;
        sum = 0;
        z = v[n - 1];
        do
            {
                sum += DELTA;
                e = (sum >> 2) & 3;
                for (p = 0; p < n - 1; p++)
                    {
                        y = v[p + 1];
                        z = v[p] += MX;
                    }
                y = v[0];
                z = v[n - 1] += MX;
            } while (--rounds);
    }
        //解密
    else if (n < -1)
    {
        n = -n;
        rounds = 6 + 52 / n;
        sum = rounds * DELTA;
        y = v[0];
        do
            {
                e = (sum >> 2) & 3;
                for (p = n - 1; p > 0; p--)
                    {
                        z = v[p - 1];
                        y = v[p] -= MX;
                    }
                z = v[n - 1];
                y = v[0] -= MX;
                sum -= DELTA;
            } while (--rounds);
    }
    printf("sum==0x%x\n", sum);
}

//打印数据 hex_or_chr: 1-hex 0-chr
void dump_data(uint32_t *v, int n, bool hex_or_chr)
{
    if (hex_or_chr)
    {
        for (int i = 0; i < n; i++)
            {
                printf("0x%x,", v[i]);
            }
    }
    else
    {
        for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < sizeof(uint32_t) / sizeof(uint8_t); j++)
                    {
                        printf("0x%x,", (v[i] >> (j * 8)) & 0xFF);
                    }
            }
    }
    printf("\n");
    return;
}

int main()
{
    // v为要加解密的数据
    uint32_t v[] = {0x68a0d3e9, 0xb627c840, 0x7766af29, 0x18d2ad61, 0x5c673dae, 0x1b929a85, 0x5d5bb8f6, 0x5e795a38, 0x67de5300, 0x3414e420, 0x54c5910b};
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {0x385e7342, 0x345a772a, 0x6f38756c, 0x6b402652};

    int n = sizeof(v) / sizeof(uint32_t);
    /*
    printf("加密前明文数据:");
    dump_data(v, n, 1);

    btea(v, n, k);
    */
    printf("加密后密文数据:");
    dump_data(v, n, 1);

    btea(v, -n, k);

    printf("解密后明文数据:");
    dump_data(v, n, 1);

    printf("解密后明文字符:");
    dump_data(v, n, 0);

    return 0;
}
// 0x22,0x5b,0x23,0x2d,0x37,0x40,0x2e,0x5f,0xc,0x77,0x53,0x45,0x57,0x49,0x34,0x71,0x5b,0x56,0x1e,0x7d,0x77,0x59,0x7a,0x5c,0x6c,0x63,0x55,0x45,0x73,0x35,0x9,0xe,0x27,0x53,0x0,0x5e,0x59,0x1e,0x0,0x72,0x4,0xc,0x0,0x0

异或:

enc = [0x22, 0x5b, 0x23, 0x2d, 0x37, 0x40, 0x2e, 0x5f, 0xc, 0x77, 0x53, 0x45, 0x57, 0x49, 0x34, 0x71, 0x5b, 0x56, 0x1e, 0x7d, 0x77,
       0x59, 0x7a, 0x5c, 0x6c, 0x63, 0x55, 0x45, 0x73, 0x35, 0x9, 0xe, 0x27, 0x53, 0x0, 0x5e, 0x59, 0x1e, 0x0, 0x72, 0x4, 0xc, 0x0, 0x0]
box = [0x44, 0x37, 0x42, 0x4A, 0x4C, 0x73, 0x4F, 0x6B, 0x39, 0x40,
       0x66, 0x26, 0x31, 0x64, 0x57, 0x49, 0x6E, 0x35, 0x33, 0x49,
       0x44, 0x6C, 0x4A, 0x71, 0x55, 0x53, 0x36, 0x24, 0x5E, 0x57,
       0x68, 0x6B, 0x41, 0x6B, 0x32, 0x6B, 0x6B, 0x2A, 0x32, 0x47,
       0x61, 0x71, 0x6D, 0x4C, 0x77, 0x69, 0x4C, 0x58, 0x5E, 0x62,
       0x47, 0x47, 0x45, 0x24, 0x26, 0x64, 0x6D, 0x71, 0x52, 0x5E,
       0x67, 0x35, 0x62, 0x4C, 0x33, 0x6C, 0x43, 0x41, 0x35, 0x5E,
       0x48, 0x47, 0x4B, 0x24, 0x39, 0x71, 0x6F, 0x35, 0x54, 0x40,
       0x42, 0x77, 0x6F, 0x6D, 0x39, 0x76, 0x45, 0x58, 0x79, 0x61,
       0x30, 0x48, 0x41, 0x56, 0x33, 0x4C, 0x72, 0x57, 0x57]
for i in range(len(enc)):
    enc[i] ^= box[i]
print(bytes(enc))
# flag{3a4575cf-c85c-4350-90ca-baef8252425e}
posted @ 2024-05-28 10:25  harveyX  阅读(626)  评论(0)    收藏  举报