【crackmes.one】———— Zatryoshka

Language: c/c++ | Arch: x86-64 | Platform: Windows |Tech: crackme,overlay,xor,base64

这个程序基本信息如下
EXE info

challenge.exe
Enter 26 integers separated by spaces: 1 2 3 4 5 6 7 8 9 10 11 12 13 45 15 16 17 18 19 20 21 22 23 24 25 26

--- Mathematical Results ---
1. a + b - c = 0
2. (d * e) / f = 3
3. g % h = 7
4. i^2 + j^2 = 181
5. (k + l) * (m - n) = -736
6. o + (p * q) - (r / s) = 287
7. Average(t, u, v, w) = 21
8. (x * y * z) + 100 = 15700
9. (a - z) * (b - y) = 575
10. (c + d) % (e + 1) = 1

--- Logical Results (1 = True, 0 = False) ---
11. f == g: 0
12. (h > i) && (j < k): 0
13. (l != m) || (n == o): 1
14. !(p > q): 1
15. r >= s >= t: 0
16. Is u even?: 0
17. v positive && w negative: 0
18. x == y + z: 0
19. Complex condition: 0
20. Ternary (g > h ? i : j): 10

直接运行该程序,并没有找到跟flag有关的线索,IDA里main函数也只是一个计算器,再看一下strings列表
strings
发现第一个字符串有点可疑,查找交叉引用也只是一个运行时函数
runtime function
所以表面上没有任何与flag有关内容,那接下来开始排查PE文件。
据AI所述,相关flag代码可能被隐藏在如下区域,其中overlay最典型。

区域 加载器是否映射 能否藏数据
overlay(最后节之后) 不映射 可以,最典型
节内 SizeOfRawData > VirtualSize 的尾部 不映射(多余 raw 被丢弃) 可以,极易漏检
节内未被使用的字节(真实数据之后的填充区) 映射但不读取 可以
节与节之间的空隙(PointerToRawData 不连续) 不映射 可以
第一个节之前的区域(DOS stub、Rich header、节表之后到首节) DOS stub 部分不映射 可以
.rsrc 资源(RT_RCDATA / 图标 / 版本信息) 映射 可以(最常见于“内嵌文件”题)
调试目录(RSDS/PDB 路径、GUID) 映射 可以
导入表里的 DLL/函数名 映射 可以
节名字段、未使用的头字段 不映射 可以
证书表(Authenticode) 不映射 本质在 overlay 内,但属于“合法签名区”
那么先排查一下overlay区域
import base64, pefile

exe = pefile.PE(r"challenge.exe", fast_load=True)

overlay_offset = exe.get_overlay_data_start_offset()
overlay = exe.get_overlay() or ""

print(f"overlay {overlay_offset:x}\nlen {len(overlay)}")

# 输出结果
# overlay 105600
# len 2484

可以看到存在overlay区域
用010Editor看一下overlay存放的内容
overlay
可以看到都是可打印字符且以==结尾,是base64编码的特征,
用脚本解码得到如下数据

b'!T@B)5*7\x0e!\xeb\xbb\xb9\xeb\xbb\xb9\xeb\xbb\xb9\xeb\xbb\xb9\x0e5$4$kfn\x0e88$+KtajEgpmkj$88$+NW$,XjJnIcJi]cJiQcJ~IcJ~UcInEcJPccI6UcJSMcI~McI~McI~McIiIcI~AcI~gcI~IcJSUcI6McJnIcJi]cJiQcJ~IcJ~UcInEcJnQcI6UcJn]cJ~QcJiQcJnIcJ~UcJnocJi]cJiQcInccJnQcInocJ6McJnIcJi]cJiQcJ~IcJ~UcInEcJi]cI6UcInccJiQcJnQcJ~gcInEcJPUcJnQcJ~ccJ~UcJ@QcJiQcJnIcJi]cJnUcJnQcJ~McInocIiQcJnQcJiQcJnIcJi]cJnUcJnQcInccJnQcInocIiQcJiUcJnAcJ~EcInccInccJnQcIiIcJi]cInocI6UcI6QcJnQcJSQcJPccJSMcJi]cInQcJPccIiQcJiIcJnQcJiQcJngcJ~UcJnccJSUcInocI6McJiIcJnQcJ~UcInEcJiQcI6UcInMcInMcI6McJn]cJi]cJ~McInccJiIcJnQcJ~UcInEcJPccI6UcI~EcI6McJPccI6IcJi]cIiQcJiIcJnQcJiQcJngcJ~UcJnccI6McJPccIiMcIiMcInocJiQcIiMcI6UcJPIcJ~UcJ~McJnocJiQcJngcIiQcJn]cJ~McJi]cJiUcJ@IcJnccJnAcJ~McJ@IcJi]cJnUcJnQcInccJi]cJSMcJPccJSUcInocI6McJ~McJnQcJ~UcJ~QcJ~McJiQcInEcJnMcJ~UcJi]cJnAcInccJiQcInocJ6UcInccInMcJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccJPccInMcInocI6McJnIcJi]cJiQcJ~IcJi]cJiIcJnQcIiQcJiIcJi]cJngcInccInMcJ@QcJiQcJnIcJi]cJnUcJnQcJnUcInEcI6AcInMcIiIcJnQcInocI6McJnIcJi]cJiQcJ~IcJ~UcInEcJi]cI6UcJnUcJnQcJnIcJi]cJnUcJnQcJPMcJnQcJnIcJnocJ~EcJnQcInccInMcJnccJ@ocJnAcJiIcI~]cJCIcJPEcJPEcI~gcJnIcJiUcI~]cJ~ocJCIcJ~McJ~ocJiUcJCAcJ~ocJ~ocJ~ocJC]cJ~]cJPAcInMcInocI6McJnIcJi]cJiQcJ~IcJi]cJiIcJnQcIiQcJiIcJi]cJngcInccInMcJ@UcJnQcJnIcJi]cJnUcJnQcJnUcInEcI6AcInMcIiIcJi]cInocI6M99Xj-$+W$+NereWgvmtp$+P}ta$+Egpmkj$::$+Tecaw$6$4$V$+P}ta$+Gepehkc$::\x0eaj`kfn\x0e6$4$kfn\x0e88$+Gkqjp$5$+Om`w$_$7$4$V$Y$+P}ta$+Tecaw$::\x0eaj`kfn\x0e7$4$kfn\x0e88$+Gkjpajpw$0$4$V$+Ia`meFk|$_$4$4$256$3=6$Y$+Tevajp$6$4$V$+Vawkqvgaw$88$::$+P}ta$+Teca$::\x0eaj`kfn\x0e0$4$kfn\x0e88$+Hajcpl$4$+Bmhpav$+Bhepa@agk`a$::\x0ewpvaei\x0e\x0eaj`wpvaei\x0eaj`kfn\x0e|vab\x0e4$1\x0e4444444444$21171$b$\x0e4444444451$44444$j$\x0e444444573=$44444$j$\x0e444444507<$44444$j$\x0e4444445100$44444$j$\x0epvemhav$88$+Vkkp$5$4$V$+Wm~a$1$+M@$_87020303f=0a3377<f0gea10771<=gg35:87020303f=0a3377<f0gea10771<=gg35:Y$::\x0ewpevp|vab\x0e5250\x0e!!AKB\x0e'

该数据被加密了,具体用的什么加密算法可以从前面作者说的那句话猜测
!!!!!!!!!!!!!!!!IMPORTANT \n i will use the same eMudhra first key as xor key just make sure to change it and remove this function !
eMudhra first key其实指的是印度 eMudhra 中间证书私钥,所以就是用0x04作为xor秘钥解密

b'%PDF-1.3\n%\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\n1 0 obj\n<< /OpenAction << /JS (\\nNjMgNmYgNmUgNzMgNzQgMjAgNTggM2QgNWIgMzIgMzIgMzIgMmMgMzEgMzcgMzMgNWQgM2IgNjMgNmYgNmUgNzMgNzQgMjAgNjUgM2QgNjYgNzUgNmUgNjMgNzQgNjkgNmYgNmUgMjggNjUgMjkgN2IgNjMgNmYgNmUgNzMgNzQgMjAgNmYgM2QgMjggNmUgNjUgNzcgMjAgNTQgNjUgNzggNzQgNDUgNmUgNjMgNmYgNjQgNjUgNzIgMjkgMmUgNjUgNmUgNjMgNmYgNjQgNjUgMjggNjUgMjkgMmUgNmQgNjEgNzAgMjggMjggNjUgMmMgNmYgMjkgM2QgM2UgNjUgNWUgNTggNWIgNmYgMjUgNTggMmUgNmMgNjUgNmUgNjcgNzQgNjggNWQgMjkgM2IgNmMgNjUgNzQgMjAgNmUgM2QgMjIgMjIgM2IgNjYgNmYgNzIgMjggNmMgNjUgNzQgMjAgNTggM2QgMzAgM2IgNTggM2MgNmYgMmUgNmMgNjUgNmUgNjcgNzQgNjggM2IgNTggMmIgMmIgMjkgNmUgMmIgM2QgNTMgNzQgNzIgNjkgNmUgNjcgMmUgNjYgNzIgNmYgNmQgNDMgNjggNjEgNzIgNDMgNmYgNjQgNjUgMjggNmYgNWIgNTggNWQgMjkgM2IgNzIgNjUgNzQgNzUgNzIgNmUgMjAgNjIgNzQgNmYgNjEgMjggNmUgMjkgN2QgMjggMjIgNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggNTggMjIgMjkgM2IgNjMgNmYgNmUgNzMgNmYgNmMgNjUgMmUgNmMgNmYgNjcgMjggMjIgNDUgNmUgNjMgNmYgNjQgNjUgNjQgMjAgM2EgMjIgMmMgNjUgMjkgM2IgNjMgNmYgNmUgNzMgNzQgMjAgNmYgM2QgNjQgNjUgNjMgNmYgNjQgNjUgNTIgNjUgNjMgNjkgNzAgNjUgMjggMjIgNjggNDkgNjEgNmMgMzYgNGMgNTAgNTAgMzcgNjMgNmQgMzYgNzkgNGMgNzIgNzkgNmQgNGEgNzkgNzkgNzkgNGYgNzYgNTEgMjIgMjkgM2IgNjMgNmYgNmUgNzMgNmYgNmMgNjUgMmUgNmMgNmYgNjcgMjggMjIgNDQgNjUgNjMgNmYgNjQgNjUgNjQgMjAgM2EgMjIgMmMgNmYgMjkgM2I==\\n) /S /JavaScript /Type /Action >> /Pages 2 0 R /Type /Catalog >>\nendobj\n2 0 obj\n<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>\nendobj\n3 0 obj\n<< /Contents 4 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << >> /Type /Page >>\nendobj\n4 0 obj\n<< /Length 0 /Filter /FlateDecode >>\nstream\n\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f \n0000000015 00000 n \n0000001379 00000 n \n0000001438 00000 n \n0000001544 00000 n \ntrailer << /Root 1 0 R /Size 5 /ID [<3464747b94e77338b4cae5433589cc71><3464747b94e77338b4cae5433589cc71>] >>\nstartxref\n1614\n%%EOF\n'

不难看出,这是一个pdf文件,且有内嵌js脚本,base64解码后得到

const X = [222, 173];
const e = (function (e) {
	const o = new TextEncoder().encode(e).map((e, o) => e ^ X[o % X.length]);
	let n = "";
	for (let X = 0; X < o.length; X++) n += String.fromCharCode(o[X]);
	return btoa(n);
})("XXXXXXXXXXXXXXXXXXXXXXXX");
console.log("Encoded :", e);
const o = decodeRecipe("hIal6LPP7cm6yLrymJyyyOvQ");
console.log("Decoded :", o);

这段代码是把明文字符串的奇数位与173异或,偶数位与222异或,得到密文hIal6LPP7cm6yLrymJyyyOvQ。
写出解密算法如下

raw = base64.b64decode(s)
plain = "".join(chr(raw[i] ^ [222, 173][i % 2]) for i in range(len(raw)))
print(f"plain: {plain}")

最后拼接得到flag: CMO{Emb3dded_F1le5}\

所用到的全部脚本如下

import base64, pefile

# 检测overlay
exe = pefile.PE(r"challenge.exe", fast_load=True)

overlay_offset = exe.get_overlay_data_start_offset()
overlay = exe.get_overlay() or ""

print(f"overlay: {overlay_offset:x}\nlen: {len(overlay)}\n")

# 0x04作为xor秘钥解密overlay
pdf = bytes(b ^ 0x04 for b in base64.b64decode(overlay))
assert pdf.startswith(b"%PDF"), "不是 PDF,检查 XOR key / 解码顺序"
print(f"pdf:\n{pdf}\n")

# 提取js脚本
i = pdf.index(b"/JS (") + 5
inner = pdf[i : pdf.index(b")", i)]
inner = inner.replace(b"\\n", b"")
js = bytes.fromhex(base64.b64decode(inner).decode())
print(f"js:\n{js.decode()}\n")

# 提取密文
start = js.index(b'decodeRecipe("') + len(b'decodeRecipe("')
end = js.index(b'"', start)
s = js[start:end].decode()
print(f"enc: {s}\n")  # hIal6LPP7cm6yLrymJyyyOvQ

# 解密
raw = base64.b64decode(s)
plain = "".join(chr(raw[i] ^ [222, 173][i % 2]) for i in range(len(raw)))
print(f"plain: {plain}\n")

# flag拼接
flag = "CMO{" + plain[plain.index("{") + 1 : plain.rindex("}")] + "}"
print(f"flag: {flag}\n")
posted on 2026-11-07 21:05  %HuTao%  阅读(3)  评论(0)    收藏  举报