DASCTF 2025上半年赛 reverse wp

鱼音乐

如果直接通过打开.xianyu文件播放的话出现这个

没有flag

反编译main.pyc

# Source Generated with Decompyle++
# File: main.pyc (Python 3.8)

import sys
import os
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QLabel, QVBoxLayout, QFileDialog, QMessageBox
from PyQt5.QtGui import QPixmap
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl
from xianyu_decrypt import load_and_decrypt_xianyu

class MainWindow(QMainWindow):

    def __init__(self = None):
        super().__init__()
        self.setWindowTitle('Fish Player - 楸奸煶涔愷煇?

导入了xianyu_decrypt

但是只能找到xianyu_decrypt.cp38-win_amd64.pydpyd文件

import xianyu_decrypt
print(dir(xianyu_decrypt))
print(help(xianyu_decrypt))

输出为

['AES', 'MASTER_KEY', 'XIANYU_HEADER', '__builtins__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__test__', 'decrypt_aes_ecb', 'json', 'load_and_decrypt_xianyu', 'os', 'read_part1', 'read_part2', 'read_part3', 'read_part4', 'read_part5', 'struct', 'tempfile', 'unpad']
Help on module xianyu_decrypt:

NAME
    xianyu_decrypt

FUNCTIONS
    decrypt_aes_ecb(enc_data: 'bytes', key: 'bytes') -> 'bytes'
        decrypt_aes_ecb(bytes enc_data: bytes, bytes key: bytes) -> bytes

    load_and_decrypt_xianyu(xianyu_path: 'str') -> 'dict'
        load_and_decrypt_xianyu(unicode xianyu_path: str) -> dict

    read_part1(data, offset)
        read_part1(data, offset)

    read_part2(data, offset)
        read_part2(data, offset)

    read_part3(data, offset)
        read_part3(data, offset)

    read_part4(data, offset, file_key, meta_length)
        read_part4(data, offset, file_key, meta_length)

    read_part5(data, offset, file_key)
        read_part5(data, offset, file_key)

    unpad(data: 'bytes') -> 'bytes'
        unpad(bytes data: bytes) -> bytes

DATA
    MASTER_KEY = b'XianYuAESKey0000'
    XIANYU_HEADER = b'XIANYUFS'
    __test__ = {}

FILE
    e:\comp\das\das6-_35d00818974deefff91f84abdb0815ea\tempdir\reverse附件\xianyu_decrypt.cp38-win_amd64.pyd


None

decrypt_aes_ecb函数参数为(enc_data,key)

key为XianYuAESKey0000

但是没有找到enc_data

load_and_decrypt_xianyu函数的参数只有.xianyu文件的path

所以可以使用load_and_decrypt_xianyu函数解密

from xianyu_decrypt import load_and_decrypt_xianyu

result = load_and_decrypt_xianyu("E:\\comp\\das\\DAS6-_35d00818974deefff91f84abdb0815ea\\tempdir\\REVERSE附件\\miao.xianyu")
print(result)  

输出为

Traceback (most recent call last):
  File "E:\comp\das\DAS6-_35d00818974deefff91f84abdb0815ea\tempdir\REVERSE附件\temp.py", line 2, in <module>
    load_and_decrypt_xianyu("E:\\comp\\das\\DAS6-_35d00818974deefff91f84abdb0815ea\\tempdir\\REVERSE附件\\miao.xianyu")
NameError: name 'load_and_decrypt_xianyu' is not defined

(py38) E:\CTF\tool\pypypy\pyinstxtractor>python E:\comp\das\DAS6-_35d00818974deefff91f84abdb0815ea\tempdir\REVERSE附件\temp.py
{'meta': {'name': '耄耋A梦', 'artist': '圆头🐱', 'fl4g': '(=ↀωↀ=)哈~!!!我听说就在音乐的“信息文件”里!!', 'flag': 'DASCTF{fl5h_mus1c_miao_m1a0_mlaO}'}, 

flag:DASCTF{fl5h_mus1c_miao_m1a0_mlaO}

xuans(复现)

参考wp

https://su-team.cn/2025/06/21/2025-DASCTF上半年/#xuans

https://blog.rkk.moe/2025/06/23/DASCTF-2025-First-Writeup/#xuans

https://matriy330.github.io/f1dd543a/#xuans

image

根据这里可以判定是sm4加密

image

在main函数里面,sm4加密前有shellcode,这是在对key进行加密

image

shellcode放进ida看出是异或

异或得到真正的key

#include<stdio.h>
int main(){
	int a[]={0x39,0x5A,0x3B,0x5D,0x3C,0xA,0x3E,0x8,0x3E,0x5F,0x6F,0x5D,0x65,0x7,0x61,0x3};
	int len=sizeof(a)/sizeof(a[0]);
	int b[len];
	for(int i=len-1;i>0;i--){
		a[i]=a[i]^a[i-1];
	}
	for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
		printf("0x%x,",a[i]);
	}
}
//0x39,0x63,0x61,0x66,0x61,0x36,0x34,0x36,0x36,0x61,0x30,0x32,0x38,0x62,0x66,0x62

但是解出fakeflag

image

真正的密文大概两种方法找到,一是手翻函数,二是动调方式追踪到替换函数(没有复现成功)

找到sub_4019A5函数

image

z3求解

from z3 import *

s = Solver()  # 创建了一个 Solver 对象 s
a1 = [BitVec('a%d' % i,16) for i in range(32)]  # 变量

s.add(118 * a1[2] + 173 * a1[0] + 48 * a1[1] + 193 * a1[3] == 66131)
s.add(196 * a1[1] + 68 * a1[0] + 104 * a1[2] + 10 * a1[3] == 52620)
s.add(88 * a1[2] + 22 * a1[0] + 37 * a1[1] + 71 * a1[3] == 36011)
s.add(59 * a1[2] + 141 * a1[1] + 89 * a1[0] + 194 * a1[3] == 61842)
s.add(175 * a1[6] + 88 * a1[5] + 40 * a1[4] + 89 * a1[7] == 65258)
s.add(26 * a1[6] + 166 * a1[5] + 82 * a1[4] + 78 * a1[7] == 58176)
s.add(149 * a1[6] + 73 * a1[4] + 10 * a1[5] + 116 * a1[7] == 62478)
s.add(176 * a1[6] + 198 * a1[4] + 80 * a1[5] + 193 * a1[7] == 114069)
s.add(178 * a1[10] + 100 * a1[9] + 83 * a1[8] + 30 * a1[11] == 56170)
s.add(143 * a1[10] + 148 * (a1[8] + a1[9]) + 168 * a1[11] == 70647)
s.add(33 * a1[8] + 194 * a1[9] + 10 * a1[10] + 186 * a1[11] == 53174)
s.add(32 * a1[10] + (a1[8] << 7) + 33 * a1[9] + 152 * a1[11] == 26118)
s.add(184 * a1[14] + 115 * a1[13] + 164 * a1[12] + 29 * a1[15] == 81254)
s.add(129 * a1[13] + 35 * a1[12] + 129 * a1[14] + 165 * a1[15] == 78646)
s.add(134 * a1[13] + 54 * a1[12] + 39 * a1[14] + 18 * a1[15] == 29827)
s.add(106 * a1[14] + 133 * a1[13] + 80 * a1[12] + 43 * a1[15] == 53660)
s.add(121 * a1[18] + 187 * a1[16] + 32 * a1[17] + 2 * a1[19] == 24667)
s.add(170 * a1[17] + 66 * a1[16] + 58 * a1[18] + 36 * a1[19] == 44188)
s.add(103 * a1[16] + 120 * a1[17] + 12 * a1[18] + 175 * a1[19] == 52310)
s.add(83 * a1[16] + 92 * a1[17] + 129 * a1[18] + 143 * a1[19] == 46020)
s.add(141 * a1[22] + 54 * a1[21] + 100 * a1[20] + 122 * a1[23] == 66732)
s.add(85 * a1[21] + 171 * a1[20] + 69 * a1[22] + 7 * a1[23] == 46817)
s.add((a1[22] << 7) + 197 * a1[20] + 48 * a1[21] + 132 * a1[23] == 83536)
s.add(181 * a1[21] + 101 * a1[20] + 79 * a1[22] + 144 * a1[23] == 80587)
s.add(149 * a1[24] + 187 * a1[25] + 24 * a1[26] + 142 * a1[27] == 92687)
s.add(49 * a1[26] + 86 * a1[25] + 118 * a1[24] + 50 * a1[27] == 49285)
s.add(164 * a1[26] + 170 * a1[25] + 70 * a1[24] + 193 * a1[27] == 92711)
s.add(95 * a1[26] + 198 * a1[25] + 96 * a1[24] + a1[27] == 61904)
s.add(114 * a1[28] + 179 * a1[29] + 37 * a1[30] + 163 * a1[31] == 53864)
s.add(132 * a1[30] + 94 * a1[29] + 49 * a1[28] + 99 * a1[31] == 36980)
s.add(150 * a1[30] + 113 * a1[29] + 43 * a1[28] + (a1[31] << 7) == 40829)
s.add(115 * a1[30] + 139 * a1[29] + a1[28] + 44 * a1[31] == 22448)


if s.check() == sat: #检查当前添加的所有约束条件是否存在满足的解,sat为满足
    m = s.model()
    # 按顺序打印所有变量的值
    result = [m[a1[i]].as_long() % 0xff for i in range(32)]
    print(','.join([hex(x) for x in result]))
#0x1d,0x7f,0xea,0x8e,0x9b,0x89,0x91,0xf3,0x50,0xe6,0x91,0x18,0xd7,0x32,0xb3,0xfc,0x49,0xc1,0x26,0x79,0xa8,0x71,0x62,0xf6,0xa1,0xd4,0x2d,0xc5,0xe4,0x3b,0x59,0x56

因为有移位操作,所以用BitVec,并且是16位二进制

image

posted @ 2025-06-27 13:22  zzz222666  阅读(109)  评论(0)    收藏  举报