前言
这比赛初赛是我在实习上班摸鱼时打的,有解题基本上都打出来了,剩下两道应该是无解或者极少解,看了下也没有做的欲望,有道 Cython 或许后面可以找机会结合去年 CTFCON 上的 talk 看看。
比赛时还做了 AI 的对抗样本,由于一些低级错误没有打出来,有点可惜。
决赛我更是请了两天假,提前离职飞到广州,比赛场地远得离谱,打车花了 ¥200 ,还差点被黑车中介骗 ¥280 ,比完马上马不停蹄地飞回合肥准备第二天的期末缓考,打 CTF 以来最不值的一次线下赛,赛题完全是渗透,我们打了一道 robots.txt 之后就美美爆零了,不得不在小红书狠狠输出了。
sedRust_happyVm
赛时由 Ichild✌ 解出,脑子也手太快了。
第一阶段去皮后每三位处理输入,类似 base64 但是不换表。
第二阶段每两位进行复杂加密,即题目名称中提到的 vm。
Src 变量是内存空间,该函数(40aba0)利用虚拟机机制做加密,首先通过定义如下的 VM Struct 优化逆向体验:
00000000 VM struc ; (sizeof=0x420, mappedto_65)
00000000 ; XREF: sub_40B2E0/r
00000000 box db 1024 dup(?) ; XREF: sub_40B2E0+C0/r
00000000 ; sub_40B2E0+C8/r ...
00000400 wregs dw 8 dup(?) ; XREF: sub_40B2E0+B8A/w
00000400 ; sub_40B2E0+B93/w
00000410 cregs db 8 dup(?)
00000418 fregs db 8 dup(?) ; XREF: sub_40B2E0+1B76/r
00000420 VM ends
正过来看有些复杂,干脆倒过来看检查点,sub_40A800 中第一个 switch 的 case 7 是影响检查点的唯一处,sub_40A800 是一个递归调用的函数:
void __fastcall sub_40A800(VM *this, unsigned __int8 op)
{
char op_2_5; // al
unsigned __int8 op_0_2; // di
bool v5; // cl
char v6; // al
unsigned __int8 v7; // r8
size_t v8; // rcx
size_t v9; // rcx
__int64 _op_0_2; // rax
// 这个函数把 8 位的 op 分成三段:第一段为高三位,第二段为中三位,第三段为低两位,并根据这三段进行运算
op_2_5 = (op >> 2) & 7;
op_0_2 = op & 3;
if ( (op & 0x80u) != 0 ) // 高三位 >= 2
{
switch ( op_2_5 )
{
case 0:
this->fregs[0] += this->fregs[1];
return;
case 1:
this->fregs[0] ^= this->fregs[1];
return;
case 2:
sub_40A800(this, op_0_2 + 0xAC);
sub_40A800(this, op_0_2 + 0xB8);
sub_40A800(this, op_0_2 + 0x8C);
sub_40A800(this, op_0_2 + 0x90);
sub_40A800(this, op_0_2 + 0xB8);
op = 0xB0;
goto LABEL_15;
case 3:
v9 = this->wregs[(unsigned __int8)(((op & 0x20) != 0) | (2 * op_0_2))];
if ( v9 >= 0x100 )
index_out_of_bounds(v9, 0x100ui64, &off_442EC0);
_op_0_2 = op_0_2;
goto LABEL_20;
case 4:
LABEL_15:
v8 = this->wregs[(unsigned __int8)(((op & 0x20) != 0) | (2 * op_0_2))];
if ( v8 >= 0x100 )
index_out_of_bounds(v8, 0x100ui64, &off_442ED8);
this->box[(unsigned __int64)op_0_2][v8] = this->fregs[0];
return;
case 5:
_op_0_2 = op_0_2;
v9 = this->fregs[0];
LABEL_20:
this->fregs[1] = this->box[_op_0_2][v9];
break;
case 6:
*(_WORD *)this->fregs = this->fregs[1];
break;
case 7:
sub_40A800(this, op_0_2 | 0xA4);
if ( this->fregs[0] )
++this->fregs[4];
break;
}
}
else
{
v5 = (op & 0x20) != 0;
if ( (op & 0x40) != 0 ) // 高三位为 1
{
switch ( op_2_5 )
{
case 0:
this->wregs[(unsigned __int8)(v5 | (2 * op_0_2))] = this->fregs[0];
break;
case 1:
this->fregs[0] = this->wregs[(unsigned __int8)(v5 | (2 * op_0_2))];
break;
case 2:
this->wregs[(unsigned __int8)(v5 | (2 * op_0_2))] = this->fregs[1];
break;
case 3:
this->fregs[1] = this->wregs[(unsigned __int8)(v5 | (2 * op_0_2))];
break;
default:
return;
}
}
else // 高三位为 0
{
v6 = 4 * (op_2_5 == 1);
if ( (op & 0x20) != 0 )
{
this->fregs[2] |= op_0_2 << v6;
}
else
{
v7 = this->fregs[2];
this->fregs[1] += (v7 & (unsigned __int8)(0xF << v6)) << (6 - v6);
this->fregs[2] = v7 & (0xF0u >> v6);
}
}
}
}
通过这一段分析可以基本确定此 VM 将一字节分为多段进行解释的范式,然而这个过程依然非常复杂,逆向耗时较长,考虑到第二阶段的处理是每两位进行,而每一位是 64*64,搜索空间可以接受,直接考虑 python 复现加密过程然后爆破解决:
#!/usr/bin/env python3
from copy import deepcopy
saved_cregs = [0] * 8
saved_wregs = [0] * 8
saved_fregs = [0] * 8
saved_box = [
list(bytes.fromhex('2e5a7e1e4c493037aa0538ea00f6c5eb48ec990f3c6b8bb02571baac834a702bd50612fb21845669442224d68776983ef731365f4f6865288aafa0de26b51d9f55b38ea75e2cd7c24d1f5bd2dbab17fe7c0ab10189a1be7afa888cb2b657bda8c1e7d195b7a48d3bcd53f146c873544316349acb3a6a9cd0ef6007cc2abc80320bdcda7fdd276de4e18fe0d99475e37b14a2cecab447c0039d02e24bff5204596fd80c1a918510f9b950417df8c9a96745e5b8df2386f00ec778a54e97815ca640519039fc72290dd39377ae3fad614218c39e2f2dc4626679749b5d33cf6ef415fdee589220e8098213c66ca396bb630835e6e9191cf51b3dedbff2f36411d4')),
list(bytes.fromhex('7ccf2ec82409c9b4dfd37680aee64c3e83045694bdc71db1c39fb27e39603c6a18ec3d710c41a7bfe890d2375c821e310850d6f96d02ca42af2a788c49a67093eb15a4bafd2c65d711266e0d81d474b9797d5f0734c0da120aab5d2b21c43aede766b814448a67358b1f579ec53f849903cec66368db88eea37a51334097de4b4faafc616f32cdb791851a4ee06c9c5a8d0bb620170f4da187efd913a92f55ccbb9a5ee454ffe57bd8b55b23488f98f77305625943ea7fcb47ac75c1009df0b3530616f5dd9246d5fbf8a2271b4af16b86f6d12595e1e9be2d38f396c228fa581c3b8e9b30f4a8f2690e721945e35277fea02910bcaddc36d08922a5e201b064')),
list(bytes.fromhex('9a2696de92c251254940233781c312489b038915053595462190e8577aaae5755ae258e9fbc128cead2c702e5e9e19413c5cac590b66936a8329137353ba6faea8a45f444dc554d3fee30199568ca534d924f46d07c4644e0088b0eaf9329c506b84b4a0ec16a6dd8aa32f47602d43ff0c1c8565cbd17b7fc73fc8d8cd4c0d3671d0080467a11ed7779739b2fcf74f0ef34b42d276b578bf1dc9ab453a7e8f09796caf3e7238a2b3f5985d8beb55e7ef2b6202f6b7da1f30e6c6fadb5bb891ccf0878d9ddf7c20b1101886ee8ebb6982d41b0a52f26e9fbee4ed3d14e063f1b96822e1311adca7803bc01727cfa9bd7461117d06fd2a0f4a3394bcf8b6cad5d6')),
list(bytes.fromhex('c81e1f27707a832e998b2cd82ddecfbc372854b4e73c90c69eccbb0e3f65a72314b1f7785c0095321581ae6e9ff60502eb8d9b5fac0760d21bd9faa0b8a930790f503ba3b601934a1d6661ee56f2af82dd58916835334c4bd740d0c37de30b6794dfe5760d46c00aa5b5eca29ac4b0b2d113591a5e6f88da16c5d42226714fffc936499262c1b32fc7be0c447319ed3e55ad8547db21cee46a3aa61238fc6b45774e1c347557ca696c1018430851259dfbd653848efdd5bd8a80f4ef2a7e6d29d39786e2eaa88ff3a4e9f87b03bff54d7409dc043198e1fe5224115ae85dabc27c4117a19c8c207f5b3963e696bab9f164f006422bf9873dcb48cde08972b7aa'))
]
def load():
global cregs, wregs, fregs, box
cregs = deepcopy(saved_cregs)
wregs = deepcopy(saved_wregs)
fregs = deepcopy(saved_fregs)
box = deepcopy(saved_box)
def save():
global saved_cregs, saved_wregs, saved_fregs, saved_box
saved_cregs = cregs
saved_wregs = wregs
saved_fregs = fregs
saved_box = box
def a(op):
op_0_2 = op & 3
op_2_5 = (op >> 2) & 7
op_5_6 = (op >> 5) & 1
op_6_8 = (op >> 6) & 3
assert op_6_8 <= 2
if op_6_8 == 0:
assert op_2_5 <= 1
offset = 4 * op_2_5
if op_5_6:
# print(f'fregs[2] |= {hex(op_0_2 << offset)}')
fregs[2] |= op_0_2 << offset
else:
# print(f'fregs[1] += (fregs[2] & {hex(0xf << offset)}) << {(6 - offset)}')
fregs[1] += (fregs[2] & (0xf << offset)) << (6 - offset)
# print(f'fregs[2] &= {hex(0xf0 >> offset)}')
fregs[2] &= 0xf0 >> offset
elif op_6_8 == 1:
assert op_2_5 <= 3
# src = f'fregs[{op_2_5 >> 1}]'
# dst = f'wregs[{op_0_2 * 2 + op_5_6}]'
if op_2_5 & 1:
# src, dst = dst, src
fregs[op_2_5 >> 1] = wregs[op_0_2 * 2 + op_5_6]
else:
# pass
wregs[op_0_2 * 2 + op_5_6] = fregs[op_2_5 >> 1]
# print(f'{dst} = {src}')
elif op_6_8 == 2:
if op_2_5 == 0:
# print(f'fregs[0] += fregs[1]')
fregs[0] += fregs[1]
fregs[0] &= 0xff
elif op_2_5 == 1:
# print(f'fregs[0] ^= fregs[1]')
fregs[0] ^= fregs[1]
elif op_2_5 == 2:
a(0xac | op_0_2)
a(0xb8 | op_0_2)
a(0x8c | op_0_2)
a(0x90 | op_0_2)
a(0xb8 | op_0_2)
a(0xb0 | op_0_2)
elif op_2_5 == 3:
# print(f'fregs[1] = box[{op_0_2}][wregs[{op_0_2 * 2 + op_5_6}]]')
fregs[1] = box[op_0_2][wregs[op_0_2 * 2 + op_5_6]]
elif op_2_5 == 4:
# print(f'box[{op_0_2}][wregs[{op_0_2 * 2 + op_5_6}]] = fregs[0]')
box[op_0_2][wregs[op_0_2 * 2 + op_5_6]] = fregs[0]
elif op_2_5 == 5:
# print(f'fregs[1] = box[{op_0_2}][fregs[0]]')
fregs[1] = box[op_0_2][fregs[0]]
elif op_2_5 == 6:
# print(f'fregs[0] = fregs[1]');
fregs[0] = fregs[1]
# print(f'fregs[1] = 0');
fregs[1] = 0
elif op_2_5 == 7:
a(0xa4 | op_0_2)
# print(f'fregs[4] += fregs[0] != 0')
fregs[4] += fregs[0] != 0
else:
assert False
def b(op):
op_1_3 = (op >> 1) & 3
op_3_5 = (op >> 3) & 3
op_5_6 = (op >> 5) & 1
op_6_8 = (op >> 6) & 3
if op_6_8 == 0:
# print(f'cregs[{2 * op_1_3}] = cregs[{2 * op_1_3 + 1}]')
cregs[2 * op_1_3] = cregs[2 * op_1_3 + 1]
# print(f'cregs[{2 * op_1_3 + 1}] = 0')
cregs[2 * op_1_3 + 1] = 0
# print(f'cregs[{2 * op_3_5}] = cregs[{2 * op_3_5 + 1}]')
cregs[2 * op_3_5] = cregs[2 * op_3_5 + 1]
# print(f'cregs[{2 * op_3_5 + 1}] = 0')
cregs[2 * op_3_5 + 1] = 0
elif op_6_8 == 1:
# print(f'cregs[{op_3_5 | (4 * op_5_6)}] = cregs[{op_1_3}]')
cregs[op_3_5 | (4 * op_5_6)] = cregs[op_1_3]
elif op_6_8 == 2:
if op_5_6:
# print(f'fregs[{op_3_5}] = cregs[{2 * op_1_3}]')
fregs[op_3_5] = cregs[2 * op_1_3]
else:
# print(f'cregs[{2 * op_1_3 + 1}] = fregs[{op_3_5}]')
cregs[2 * op_1_3 + 1] = fregs[op_3_5]
elif op_6_8 == 3:
# print(f'fregs[3] = {hex(op & 0x3f)}')
fregs[3] = op & 0x3f
else:
assert False
def c(op1, op2):
op1 &= 3
assert op2 <= 0xf
a(0x4c | op1)
b(0xa0 | (op2 << 1))
a(0x80)
a(0x40 | op1)
a(0x8c | op1)
a(0x64 | op1)
a(0x80)
a(0x60 | op1)
a(0x88 | op1)
def f(op1, op2):
op1_bytes = op1.to_bytes(4, 'little')
op2_0 = op2 & 0xff
op1_bytes_op2_0 = op1_bytes[op2_0]
b(0xc0 | (op1_bytes_op2_0 & 0x3f))
b(0x98 | ((((op1_bytes_op2_0 >> 6) + op2_0) & 3) << 1))
op2_1 = (op2 >> 8) & 0xff
op1_bytes_op2_1 = op1_bytes[op2_1]
b(0xc0 | (op1_bytes_op2_1 & 0x3f))
b(0x98 | ((((op1_bytes_op2_1 >> 6) + op2_1) & 3) << 1))
op2_2 = (op2 >> 16) & 0xff
op1_bytes_op2_2 = op1_bytes[op2_2]
b(0xc0 | (op2_2 & 0x3f))
b(0x98 | ((((op2_2 >> 6) + op2_2) & 3) << 1))
op2_3 = (op2 >> 24) & 0xff
op1_bytes_op2_3 = op1_bytes[op2_3]
b(0xc0 | (op2_3 & 0x3f))
b(0x98 | ((((op2_3 >> 6) + op2_3) & 3) << 1))
b(((op2_2 << 3) | (op2_3 << 1)) & 0xff)
c(op2_0, op2_2)
c(op2_1, op2_3)
b(0xc0 | (op1_bytes_op2_2 & 0x3f))
b(0x98 | ((op2_2 & 3) << 1))
b(0xc0 | (op1_bytes_op2_3 & 0x3f))
b(0x98 | ((op2_3 & 3) << 1))
a(0x20 | (op1_bytes_op2_2 >> 6))
a(0x24 | (op1_bytes_op2_3 >> 6))
b(((op2_2 << 3) | (op2_3 << 1)) & 0xff)
b(((op2_0 << 3) | (op2_1 << 1)) & 0xff)
a(0x8c | op2_0)
a(0xb8 | op2_0)
a(0xac | op2_0)
a(0x80)
a(0xb4 | op2_0)
a(0x98 | op2_0)
b(0xa8 | (op2_0 << 1))
a(0x84)
b(0xa8 | (op2_2 << 1))
a(0x00)
a(0x9c)
a(0x8c | op2_1)
a(0xb8 | op2_1)
a(0xac | op2_1)
a(0x80)
a(0xb4 | op2_1)
a(0x98 | op2_1)
b(0xa8 | (op2_1 << 1))
a(0x84)
b(0xa8 | ((op2_3 & 3) << 1))
a(0x04)
a(0x9c)
def bf(x, y, x_0, x_1):
for i in range(0x40):
for j in range(0x40):
load()
f(x | (i << (8 * x_0)) | (j << (8 * x_1)), y)
if fregs[4] == 0:
save()
return i, j
assert False
values = [
[0xB1000018, 0x3000201, 1, 2],
[0xA4090000, 0x3020100, 0, 1],
[0x2AA600, 0x2010003, 3, 0],
[0x1B009E, 0x2000103, 3, 1],
[0x570096, 0x2000103, 3, 1],
[0xAD005D, 0x2000103, 3, 1],
[0xAE750000, 0x2030100, 0, 1],
[0x65AC00, 0x1020300, 0, 3],
[0x8C09, 0x1000203, 3, 2],
[0x76A0, 0x1000203, 3, 2],
[0x472C0000, 0x2030100, 0, 1],
[0x10000001, 0x30201, 1, 2],
[0x7C000F, 0x20301, 1, 3],
[0xBA0047, 0x20301, 1, 3],
[0x953000, 0x1020003, 3, 0],
[0x74009B00, 0x3010200, 0, 2],
[0x2D00003F, 0x3000102, 2, 1],
[0x9A2D, 0x1000203, 3, 2],
[0x3187, 0x1000302, 2, 3],
[0xBA43, 0x10302, 2, 3],
[0x2C70, 0x1000302, 2, 3],
[0x56004C00, 0x3010200, 0, 2]
]
flag_s = []
for value in values:
flag_s.extend(bf(*value))
print(flag_s)
flag = []
for i in range(0, len(flag_s), 4):
flag.append(((flag_s[i + 0] << 2) | (flag_s[i + 1] >> 4)) & 0xff)
flag.append(((flag_s[i + 1] << 4) | (flag_s[i + 2] >> 2)) & 0xff)
flag.append(((flag_s[i + 2] << 6) | (flag_s[i + 3] >> 0)) & 0xff)
print(bytes(flag))
# c669733af3ce4459b88016420b81cb15
pic
比较简单,爆破 rc4 key 即可:
def rc4_encrypt(key, plaintext):
S = list(range(256))
j = 0
out = []
# Key-scheduling algorithm (KSA)
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# Pseudo-random generation algorithm (PRGA)
i = j = 0
for char in plaintext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
out.append(char ^ k ^ 0x11)
return bytes(out)
with open('flag.png.bak', 'rb') as f: data = f.read()
data_ = [i ^ 0x31 for i in data]
from tqdm import tqdm
from string import printable
# key = bytes.fromhex('3031373364')
# plaintext = rc4_encrypt(key, data_)
# print(key)
# if plaintext.startswith(b'\x89PNG\r\n\x1a\n'):
# print(f"Key: {key.hex()}")
# with open('real_flag.png', 'wb') as f: f.write(plaintext)
for i1 in printable:
for i2 in tqdm(printable):
for i3 in printable:
for i4 in printable:
for i5 in printable:
key = bytes([ord(i1), ord(i2), ord(i3), ord(i4), ord(i5)])
plaintext = rc4_encrypt(key, data_)
print(key)
if plaintext.startswith(b'\x89PNG\r\n\x1a\n'):
print(f"Key: {key.hex()}")
exit()
# b'0173d'
# Key: 3031373364
docCrack
解压 docm 拿到 VBA ,用以下脚本反编译:
import oletools.olevba
def extract_vba_code(file_path):
vba_parser = oletools.olevba.VBA_Parser(file_path)
if vba_parser.detect_vba_macros():
for (filename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros():
print("\n\nFound VBA code in file:", vba_filename)
print("Code:\n", vba_code)
vba_parser.close()
file_path = 'vbaProject.bin'
extract_vba_code(file_path)
去除掉所用的 Debug.Print "Never Gonna Give U Up~~~"
得到:
Sub AutoOpen()
Set fso = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")
isContinue = 7
temp = MsgBox("I?am???Vcke?!!!_I???m??Hac?er??!!???_am_Hac?er!!!_I_a?_????ok?r!!!", vbCritical, "Hacked_by_??????")
Do
inflag = InputBox("Give me your flag", "Hacked_by_??????")
If inflag = "" Then
inflag = "noflag"
End If
Result = ""
For i = 1 To Len(inflag)
res = Chr(Asc(Mid(inflag, i, 1)) Xor 7)
Result = Result & res
Next i
tempPath = ThisDocument.Path & "\temp1"
Set tempfile = fso.CreateTextFile(tempPath, True)
fso.GetFile(tempPath).Attributes = 2
tempfile.WriteLine xpkdb
tempfile.Close
...
batPath = ThisDocument.Path & "\temp.bat"
Set batFile = fso.CreateTextFile(batPath, True)
fso.GetFile(batPath).Attributes = 2
batFile.WriteLine "@echo off"
batFile.WriteLine "cd /d " & ThisDocument.Path
batFile.WriteLine "certutil -decode temp1 temp|certutil -decode temp temp.exe"
batFile.WriteLine "del temp"
batFile.WriteLine "temp.exe " & """" & Result & """"
batFile.WriteLine "del temp.exe"
batFile.Close
Set objExec = objShell.Exec(batPath)
Set objStdOut = objExec.StdOut
Do While Not objStdOut.AtEndOfStream
output = Trim(objStdOut.ReadLine)
Loop
output = Left(output, Len(output))
StartTime = Timer
Do While Timer < StartTime + 1
DoEvents
Loop
fso.DeleteFile batPath
fso.DeleteFile tempPath
If output = "good" Then
temp = MsgBox("good!!!", , "congratulations!!!")
Exit Do
Else
temp = MsgBox("Sorry, U are wrong!!!", , "Hacked_by_??????")
isContinue = MsgBox("Continue?", vbYesNo + vbQuestion, "Warning")
End If
Loop While isContinue = 6
End Sub
中间有一大段字符串,两次解码之后得到一个 PE 文件,逻辑非常简单:
v9 = [0] * 54
v9[0] = 0x10C0
v9[1] = 4480
v9[2] = 5376
v9[3] = 4352
v9[4] = 5312
v9[5] = 4160
v9[6] = 7936
v9[7] = 5184
v9[8] = 6464
v9[9] = 6528
v9[10] = 5632
v9[11] = 3456
v9[12] = 7424
v9[13] = 5632
v9[14] = 6336
v9[15] = 6528
v9[16] = 6720
v9[17] = 6144
v9[18] = 6272
v9[19] = 7488
v9[20] = 6656
v9[21] = 7296
v9[22] = 7424
v9[23] = 2432
v9[24] = 2432
v9[25] = 2432
v9[26] = 5632
v9[27] = 4416
v9[28] = 3456
v9[29] = 7168
v9[30] = 6528
v9[31] = 7488
v9[32] = 6272
v9[33] = 5632
v9[34] = 3520
v9[35] = 6208
v9[36] = 5632
v9[37] = 4736
v9[38] = 6528
v9[39] = 6400
v9[40] = 7488
v9[41] = 3520
v9[42] = 5632
v9[43] = 5184
v9[44] = 3456
v9[45] = 0x1D40
v9[46] = 0x1C80
v9[47] = 0xC80
v9[48] = 0x1880
v9[49] = 0x1D00
v9[50] = 2432
v9[51] = 2432
v9[52] = 2432
v9[53] = 0x1E80
for i in range(0, 54):
v9[i] = v9[i] >> 6
v9[i] = v9[i] ^ 7
print(bytes(v9).decode('utf-8'))
# DASCTF{Vba_1s_dangerous!!!_B1ware_0f_Macr0_V1ru5es!!!}
你这主函数保真吗?
看样子只是简单的计算,离散余弦变换+ROT13
cip = [
513.355,
-37.7986,
8.7316,
-10.7832,
-1.3097,
-20.5779,
6.98641,
-29.2989,
15.9422,
21.4138,
29.4754,
-2.77161,
-6.58794,
-4.22332,
-7.20771,
8.83506,
-4.38138,
-19.3898,
18.3453,
6.88259,
-14.7652,
14.6102,
24.7414,
-11.6222,
-9.754759999999999,
12.2424,
13.4343,
-34.9307,
-35.735,
-20.0848,
39.689,
21.879,
26.8296
]
import numpy as np
def dct1d(data):
n = len(data)
dct_result = np.zeros_like(data, dtype=float)
for k in range(n):
sum_val = 0
for i in range(n):
sum_val += data[i] * np.cos((np.pi / n) * (i + 0.5) * k)
if k == 0:
dct_result[k] = np.sqrt(1/n) * sum_val
else:
dct_result[k] = np.sqrt(2/n) * sum_val
return dct_result
def idct1d(dct_data):
n = len(dct_data)
idct_result = np.zeros_like(dct_data, dtype=float)
for i in range(n):
sum_val = 0
for k in range(n):
if k == 0:
coeff = np.sqrt(1/n)
else:
coeff = np.sqrt(2/n)
sum_val += coeff * dct_data[k] * np.cos((np.pi / n) * (i + 0.5) * k)
idct_result[i] = sum_val
return idct_result
# 假设这是离散傅里叶变换后的结果
X = np.array(cip)
x = idct1d(X)
for i in range(len(x)):
print(chr(int(round(x[i]))), end='')
Wh0_1s_Ma1n_@nd_FunnY_Dct