Skip to content

Commit 031113d

Browse files
committed
add first blog
0 parents  commit 031113d

25 files changed

+1171
-0
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "themes/ananke"]
2+
path = themes/ananke
3+
url = https://github.com/theNewDynamic/gohugo-theme-ananke.git

.hugo_build.lock

Whitespace-only changes.

archetypes/default.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
+++
2+
date = '{{ .Date }}'
3+
draft = true
4+
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
5+
+++

content/_index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: "Starslayerx's Blog"
3+
---
4+
5+
欢迎来到我的博客!
6+
7+
8+
👉 [通过注释执行任意Python代码](/posts/python-execute-comment/)
9+

content/posts/hello-world.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
+++
2+
date = '2025-08-04T09:50:34+08:00'
3+
draft = true
4+
title = 'Hello World'
5+
+++
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
---
2+
title: "Executing arbitrary Python code from a comment"
3+
date: 2025-08-04T10:30:00+08:00
4+
draft: false
5+
---
6+
通过注释执行任意Python代码
7+
8+
### 问题描述
9+
Q: 只能控制一行的.py代码中注释的内容(\n\r均会被替换为空字符), 如何执行任意代码?
10+
A: 在注释#中, 构造一个.zip 文件, python 会将该内容当城一个zip包执行, 触发任意代码执行
11+
12+
### 解决方案
13+
- 从 Python 3.5 起, 可以直接执行一个 .zip 文件
14+
```python3
15+
python myapp.zip
16+
```
17+
前提是ZIP 包中包含一个顶层的`__main__.py`文件, Python 会把它当作 zipapp, 自动解压并运行`__main__.py`
18+
19+
> Python 会从末尾找到 ZIP 的目录结构, 而不是依赖文件头, 所以前面的“垃圾”字节会被忽略
20+
21+
Python 源码中的任何行, 只要以 # 开头, 解释器都会忽略后面内容, 因此可以:
22+
-ZIP 文件的数据藏在 Python 源码中的注释中(开头加 #
23+
-ZIP 数据直接拼接在 Python 文件的后面, 只保证文件头部分是合法 Python
24+
- ZIP 不关心前缀, Python 只要最前面是有效源码, 也不会管后面
25+
26+
### 难点
27+
ZIP 文件头包含**二进制字段**,比如
28+
- 偏移量(文件数据相对于 ZIP 开头的位置)
29+
- 长度(文件名长度、注释长度等)
30+
- 这些值写死在 header 里, 是十六进制整数
31+
- 如果这些字节中出现了像 \x00、\xFF 等非 ASCII 内容, Python 就不能把它当注释
32+
33+
解决方法: 暴力穷举合法组合
34+
35+
想办法 **调整偏移值和结构位置**,使得最终写出来的 ZIP 文件
36+
- 所有的字段值都转化为 可打印字符(ASCII 范围内)
37+
- 所有 binary 字段看起来都像合法的注释字符串
38+
于是用 `itertools.product(range(256), repeat=2)` 暴力尝试偏移组合,只要碰巧生成的 ZIP 包所有关键字节都在可打印范围内(ASCII 32~126),就认为成功。
39+
40+
41+
42+
下面是`generate_polygloy_zip.py`代码, 会生成一个符合要求的`polygloy.py`代码, 最后运行该代码, 可以执行Body里面的内容`BODY = b"print('FROM MAIN.py FILE!!!')#"
43+
44+
```python3
45+
# struct: 按字节结构打包数据,方便构造 ZIP 文件二进制头
46+
# itertools: 用来暴力枚举 CRC 校验和后缀(确保安全ASCII)
47+
# zlib: 计算 CRC32 校验和
48+
import struct, itertools, zlib
49+
50+
# 文件开头代码
51+
# encode(): Unicode 字符串 -> bytes 字节串
52+
JUNK_HEAD = """print("Hello World!")
53+
# This is a comment. Here's another:
54+
# """.encode()
55+
56+
# 文件结尾代码
57+
JUNK_TAIL = """
58+
print("Thanks for playing!")"""
59+
60+
61+
# zip 文件核心代码
62+
# b: 字节串
63+
FILENAME = b"__main__.py"
64+
BODY = b"print('FROM MAIN.py FILE!!!')#"
65+
66+
67+
# 校验 CRC 是否为 ASCII-safe
68+
def ascii_safe(x: int) -> bool:
69+
return all(((x >> (8*i)) & 0x80) == 0 for i in range(4))
70+
71+
72+
# 检查 32 位整数的四个字节,每个字节最高位(0x80)是否为 0,即是否为 ASCII 范围内的字节
73+
def find_suffix(core: bytes, length: int = 4) -> tuple[bytes, int]:
74+
"""
75+
- ZIP 文件 CRC32 计算结果必须 ASCII-safe(低于 0x80)
76+
- 这里用暴力方法,给 payload 后面加4字节后缀,找到合适的后缀让 CRC32 满足 ASCII-safe 条件
77+
"""
78+
printable = range(0x20, 0x7f)
79+
for tail in itertools.product(printable, repeat=length):
80+
payload = core + bytes(tail)
81+
crc = zlib.crc32(payload) & 0xFFFFFFFF
82+
if ascii_safe(crc):
83+
return bytes(tail), crc
84+
85+
raise RuntimeError("No ASCII-safe CRC found.")
86+
87+
# 计算最终 payload
88+
SUFFIX, CRC = find_suffix(BODY)
89+
PAYLOAD = BODY + SUFFIX
90+
SIZE = len(PAYLOAD)
91+
92+
def le32(x): return struct.pack("<I", x) # 4字节小端无符号整数
93+
def le16(x): return struct.pack("<H", x) # 2字节小端无符号整数
94+
95+
# ZIP 结构中各签名常量
96+
SIG_LFH = 0x04034B50 # 本地文件头 Local File Header
97+
SIG_CDH = 0x02014B50 # 中央目录头 Central Directory Header
98+
SIG_EOCD = 0x06054B50 # 结束目录头 End of Central Directory
99+
100+
# zip 文件偏移量设置
101+
delta = len(JUNK_HEAD)
102+
103+
# 构建 Local File Header
104+
"""
105+
Local File Header 是 ZIP 格式中的一部分,告诉解压程序该文件的元信息
106+
- version needed to extract,flags,compression method 等字段置 0 表示无压缩,简单存储
107+
- CRC32、压缩大小、解压大小都是我们计算的
108+
- 文件名长度和文件名
109+
"""
110+
lfh = (
111+
le32(SIG_LFH) +
112+
le16(0) + le16(0) + le16(0) + le16(0) + le16(0) +
113+
le32(CRC) + le32(SIZE) + le32(SIZE) +
114+
le16(len(FILENAME)) + le16(0) +
115+
FILENAME
116+
)
117+
118+
# 构建 Central Directory Header
119+
"""
120+
- Central Directory 是 ZIP 文件目录结构,记录每个文件信息和偏移,
121+
- 其中重要的是 relative offset of LFH,也就是 Local File Header 在整个 ZIP 文件里的偏移,必须加上 delta
122+
"""
123+
cdh = (
124+
le32(SIG_CDH) +
125+
le16(0) + le16(0) + le16(0) + le16(0) + le16(0) + le16(0) +
126+
le32(CRC) + le32(SIZE) + le32(SIZE) +
127+
le16(len(FILENAME)) + le16(0) + le16(0) +
128+
le16(0) + le16(0) + le32(0) + le32(delta) +
129+
FILENAME
130+
)
131+
132+
# 确保偏移量 ASCII-safe
133+
"""
134+
- ZIP 目录偏移需要是 ASCII 字节,否则写入 .py 文件时会出错
135+
- 这里通过填充若干 \x00 字节,保证偏移合法
136+
"""
137+
cd_offset = delta + len(lfh) + len(PAYLOAD)
138+
pad = 0
139+
while not ascii_safe(cd_offset + pad):
140+
pad += 1
141+
padding = b'\x00' * pad
142+
cd_offset += pad
143+
144+
# 构建 End of Central Directory Header
145+
"""
146+
EOCD 记录 ZIP 中央目录大小、偏移及注释长度等信息
147+
"""
148+
eocd = (
149+
le32(SIG_EOCD) +
150+
le16(0) + le16(0) +
151+
le16(1) + le16(1) +
152+
le32(len(cdh)) +
153+
le32(cd_offset) +
154+
le16(len(JUNK_TAIL))
155+
)
156+
157+
# 拼接完整 ZIP 内容
158+
zip_bytes = lfh + PAYLOAD + padding + cdh + eocd
159+
zip_bytes = bytearray(zip_bytes)
160+
assert all(b < 0x80 for b in zip_bytes), "非 ASCII 字节存在"
161+
162+
# 写入 polyglot.py 文件
163+
with open("polyglot.py", "wb") as f:
164+
f.write(JUNK_HEAD + zip_bytes + JUNK_TAIL.encode())
165+
166+
# 运行提示
167+
print("✅ polyglot.py 生成完毕。运行它即可执行嵌入的 __main__.py 内容:")
168+
print(" $ python3 polyglot.py")
169+
170+
```

hugo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
baseURL = 'https://example.org/'
2+
languageCode = 'en-us'
3+
title = 'My New Hugo Site'
4+
theme = 'ananke'

public/404.html

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<!DOCTYPE html>
2+
<html lang="en-us">
3+
<head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
6+
7+
<title>My New Hugo Site</title>
8+
<meta name="viewport" content="width=device-width,minimum-scale=1">
9+
<meta name="description" content="">
10+
<meta name="generator" content="Hugo 0.148.2">
11+
12+
13+
14+
<meta name="robots" content="noindex, nofollow">
15+
16+
17+
18+
19+
<link rel="stylesheet" href="/ananke/css/main.min.css" >
20+
21+
22+
23+
24+
25+
26+
27+
28+
29+
30+
31+
32+
33+
34+
35+
<link rel="canonical" href="http://localhost:1313/404.html">
36+
37+
38+
<meta property="og:url" content="http://localhost:1313/404.html">
39+
<meta property="og:site_name" content="My New Hugo Site">
40+
<meta property="og:title" content="404 Page not found">
41+
<meta property="og:locale" content="en_us">
42+
<meta property="og:type" content="website">
43+
44+
<meta itemprop="name" content="404 Page not found">
45+
<meta name="twitter:card" content="summary">
46+
<meta name="twitter:title" content="404 Page not found">
47+
48+
49+
</head><body class="ma0 avenir bg-near-white development">
50+
51+
52+
53+
<header>
54+
<div class="bg-black">
55+
<nav class="pv3 ph3 ph4-ns" role="navigation">
56+
<div class="flex-l center items-center justify-between">
57+
<a href="/" class="f3 fw2 hover-white white-90 dib no-underline">
58+
59+
My New Hugo Site
60+
61+
</a>
62+
<div class="flex-l items-center">
63+
64+
65+
66+
<div class="ananke-socials"></div>
67+
68+
</div>
69+
</div>
70+
</nav>
71+
72+
</div>
73+
</header>
74+
75+
76+
<main class="pb7" role="main">
77+
78+
<article class="center cf pv5 measure-wide-l">
79+
<h1>
80+
This is not the page you were looking for
81+
</h1>
82+
</article>
83+
84+
</main>
85+
<footer class="bg-black bottom-0 w-100 pa3" role="contentinfo">
86+
<div class="flex justify-between">
87+
<a class="f4 fw4 hover-white white-70 dn dib-ns pv2 ph3 no-underline" href="http://localhost:1313/" >
88+
&copy; My New Hugo Site 2025
89+
</a>
90+
<div><div class="ananke-socials"></div>
91+
</div>
92+
</div>
93+
</footer>
94+
95+
</body>
96+
</html>

public/ananke/css/main.css.map

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/ananke/css/main.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)