为期 4 个星期的 HGAME2021 结束了,今年的 hgame 依然有着较高质量的题目和出题人的手把手 hint,可以说对👴这样的萌新很友好。所有的题目里我只做了 Re 和 Misc 的,没办法太菜了其他的不敢染指,题目(re/pwn/misc)及官方 wp 我都整理到了 网盘(提取码:3oyi),8说了,自闭去了

Week1

Re

apacha

分值:150pts

描述:一杯阿帕茶

main 函数:

hgame2021

输入 35 个字符,程序将每个字符零扩展成四字节,保存在元素个数为 35 的 DWORD 数组中作为明文,用已知的 key 进行 xxtea 加密,结果与已知的数组比较。脚本(xxtea 函数是网上找的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <stdbool.h>

#define MX ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))
bool xxtea(unsigned int* v, int n, unsigned int* k) {
unsigned int z = v[n - 1], y = v[0], sum = 0, e, DELTA = 0x9e3779b9;
unsigned int p, q;
if (n > 1) { /* Coding Part */
q = 6 + 52 / n;
while (q-- > 0) {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
y = v[p + 1], z = v[p] += MX;
y = v[0];
z = v[n - 1] += MX;
}
return 0;
}
else if (n < -1) { /* Decoding Part */
n = -n;
q = 6 + 52 / n;
sum = q * DELTA;
while (sum != 0) {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
z = v[p - 1], y = v[p] -= MX;
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
}
return 0;
}
return 1;
}

int main(int argc, char const* argv[]) {
unsigned int v[35] =
{
3880694563u, 3081185334u, 1506439138u, 2524759489u,
3883935348u, 1026381030u, 2325545814u, 2581382044u,
1881594093u, 1781792173u, 4103492874u, 1553756062u,
468045900u, 1730391575u, 1383114178u, 2890011402u,
2227070898u, 1885128569u, 1548828056u, 4214676013u,
571971141u, 1558401693u, 3515474427u, 3898332297u,
1942540575u, 1421197718u, 3061626000u, 555214026u,
2648963476u, 794468778u, 2816999933u, 3272437419u,
464379036u, 877899850u, 2460223225u
};
unsigned int key[4] = { 1, 2, 3, 4 };
xxtea(v, -35, key);
for (int i = 0; i < 35; i++)
printf("%c", v[i]);
}
// hgame{l00ks_1ike_y0u_f0Und_th3_t34}

helloRe

分值:150pts

描述:Welcome to reverse world !

shift + F12 查找字符串 “checking flag” 的交叉引用,来到函数 sub_1400014C0,逻辑很简单:

hgame2021

脚本:

1
2
3
4
5
6
7
8
9
10
11
# python2
target = "97 99 9C 91 9E 81 91 9D 9B 9A 9A AB 81 97 AE 80 83 8F 94 89 99 97".split(' ')
target = [int(i, 16) for i in target]

flag = ''
b = 0xFF
for t in target:
flag += chr(t ^ b)
b -= 1
print flag
# hgame{hello_re_player}

pypy

分值:150pts

描述:pypy。flag的格式为hgame{your_flag_here}

题目是一个 python 程序的字节码,阅读一遍后还原 python 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
raw_flag = input("give me your flag:\n")
cipher = list(raw_flag[6:-1])
length = len(cipher)

for i in range(length // 2):
cipher[2*i], cipher[2*i+1] = cipher[2*i+1], cipher[2*i]

res = []
for i in range(length):
res.append(ord(cipher[i]) ^ i)
res = bytes(res).hex()
print("your flag: " + res)
# your flag: 30466633346f59213b4139794520572b45514d61583151576638643a

解题脚本:

1
2
3
4
5
6
7
8
9
10
cipher = []
res = "30466633346f59213b4139794520572b45514d61583151576638643a".decode("hex")

for i in range(len(res)):
cipher.append(ord(res[i]) ^ i)
for i in range(len(res) // 2):
cipher[2*i], cipher[2*i+1] = cipher[2*i+1], cipher[2*i]
cipher = map(chr, cipher)
print ''.join(cipher)
# hgame{G00dj0&_H3r3-I$Y@Ur_$L@G!~!~}

Misc

Base全家福

分值:50pts

描述:新年即将来临之际,Base家族也团聚了,他们用他们特有的打招呼方式向你问了个好,你知道他们在说什么吗?

base64 -> base32 -> base16

1
2
3
4
5
from base64 import b64decode, b32decode, b16decode

data = "R1k0RE1OWldHRTNFSU5SVkc1QkRLTlpXR1VaVENOUlRHTVlETVJCV0dVMlVNTlpVR01ZREtSUlVIQTJET01aVUdSQ0RHTVpWSVlaVEVNWlFHTVpER01KWElRPT09PT09"
print b16decode(b32decode(b64decode(data)))
# hgame{We1c0me_t0_HG4M3_2021}

不起眼压缩包的养成的方法

分值:100pts

描述:0x4qE给了张图给我,说这图暗藏玄机,你能帮我找出来吗?

图片备注里有提示 Secret hidden IN picture.,binwalk 分离得到一个 zip 压缩包。打开发现备注里提示 Password is picture ID,那肯定是 P 站的 ID 了,百度识图找到 ID 为 70415155

解压得到一个文本文档和一个压缩包,而这个 txt 恰好在 plain.zip 里也有,明文攻击得到口令 C8uvP$DP

解压得到 flag.zip,该压缩包是伪加密,将文件偏移 0x11E 处的 01 改成 00 后解压,得到:

1
&#x68;&#x67;&#x61;&#x6D;&#x65;&#x7B;&#x32;&#x49;&#x50;&#x5F;&#x69;&#x73;&#x5F;&#x55;&#x73;&#x65;&#x66;&#x75;&#x31;&#x5F;&#x61;&#x6E;&#x64;&#x5F;&#x4D;&#x65;&#x39;&#x75;&#x6D;&#x69;&#x5F;&#x69;&#x35;&#x5F;&#x57;&#x30;&#x72;&#x31;&#x64;&#x7D;

这是 html 转义字符,可以创建一个 flag.html,将上面的内容复制进去,再用浏览器打开,得到 flag hgame{2IP_is_Usefu1_and_Me9umi_i5_W0r1d}


Galaxy

分值:100pts

描述:Akira的信物:用于提升Akira的潜能。一张藏着秘密的星空壁纸,不幸的是似乎在某次行动中遗失了。

跟踪 tcp 流 26,将图片导出,把 png 高度改大点,得到 flag hgame{Wh4t_A_W0nderfu1_Wa11paper}


Word RE:MASTER

分值:150pts

描述:timmix不知所踪,只留下了两个word文档,作为word专家的你能帮忙找出他的去向吗?

first.docx 后缀改为 zip 解压,在 word 目录下有一个 password.xml:

1
2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<password>+++++ +++[- >++++ ++++< ]>+++ +.<++ +[->+ ++<]> ++.<+ ++[-> +++<] >+.<+ ++[-> ---<] >-.++ ++++. <+++[ ->--- <]>-. +++.+ .++++ ++++. <+++[ ->--- <]>-- ----. +.--- --..+ .++++ +++++ .<+++ [->-- -<]>- ----- .<</password>

妥妥的 brainfuck,解码得到 DOYOUKNOWHIDDEN?,是 maimai.docx 的密码。打开 maimai.docx,图片下面有隐藏文字,选中它们,右键字体,取消勾选隐藏文字后,发现其由空格和 tab 组成,联系图片想到 snow 隐写。将其复制到 snow.txt 中:

hgame2021

ubuntu 下使用指令 stegsnow -C snow.txt,得到 flag hgame{Cha11en9e_Whit3_P4ND0R4_P4R4D0XXX}


Week2

Re

ezApk

分值:200pts

描述:input your flag to check!

jadx 打开 apk,在 com.ryen.ezapk.MainActivity 类中找到 onClick 方法,和 flag 有关的只有一句判断:

hgame2021

由于存在混淆,许多符号被替换成了单个字母,不过不影响通过函数行为判断其作用。c.d.b.a.a 方法的代码:

hgame2021

不难看出是 isEqual,由此可知 MainActivity.s(mainActivity3, editText2.getText().toString()) 的返回值就是 flag,其中 editText2.getText() 就是输入。MainActivity.s 对输入进行了加密:

hgame2021

可以发现,key 和 iv 都被 mainActivity.t 函数处理了:

hgame2021

该函数的作用是对 str2 进行 str 指定的 hash 算法,于是 key 为 sha256(R.string.key),iv 为 md5(R.string.key),R.string.flag 和 R.string.key 都可以从资源文件中取到(路径在 resources.arsc/res/values/strings.xml):

hgame2021

解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
from base64 import b64decode
from Crypto.Cipher import AES
from hashlib import sha256, md5

ori = "EEB23sI1Wd9Gvhvk1sgWyQZhjilnYwCi5au1guzOaIg5dMAj9qPA7lnIyVoPSdRY"
cipher = b64decode(ori)

key = sha256("A_HIDDEN_KEY").digest()
iv = md5("A_HIDDEN_KEY").digest()
aes = AES.new(key, AES.MODE_CBC, iv)
print aes.decrypt(cipher).rstrip()
# hgame{jUst_A_3z4pp_write_in_k07l1n}

helloRe2

分值:250pts

描述:Input 2 passwords and get your flag !

main 函数中验证第一个密码的部分:

hgame2021

将输入(16个字符)视作 128 位的数值与 xmmword_4030F0 比较,所以 pwd1 = 2b0c5e6a3a20b189。pwd1 正确后,程序:

  • 以暂停状态创建新进程
  • 通过 CreateFileMappingA 创建一个共享文件映射对象
  • 将密钥变换后映射到文件中去(可以理解成往共享内存里写密钥)
  • 启动新进程

新进程由于已经设置了共享对象,所以走 main 函数开头的 if 判断里,进行 pwd2 的检测:
hgame2021

由 BCryptOpenAlgorithmProvider 参数 pszAlgId 可知加密方式为 AES,由 BCryptSetProperty 参数 pbInput 可知加密模式为 CBC。BCryptGenerateSymmetricKey 的参数 pbSecret 为被映射的密钥,可以用调试器附加新进程取得:

hgame2021

由 BCryptEncrypt 的第五个参数可知 iv 为 unk_40312C,加密结果与 xmmword_4030E0 比较。解题脚本:

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import AES

pwd1 = "2b0c5e6a3a20b189"

cipher = "B7FEFED9077679653F4E5F62D502F67E".decode("hex")
key = "2c2`1`0f;h8;n<66"
iv = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
aes = AES.new(key, AES.MODE_CBC, iv)
pwd2 = aes.decrypt(cipher)
print "hgame{%s_%s}" % (pwd1, pwd2)
# hgame{2b0c5e6a3a20b189_7a4ad6c5671fb313}

fake_debugger beta

分值:150pts

描述:你写了一个什么奇怪的debugger?这东西能用?看起来规则也不太一样?

首先测试得到最短 flag 长度为 11,输入 hgame{aaaaa,根据单步调试结果:

hgame2021

分析可知程序会从头开始逐一字符检查输入,每个字符要单步两次才知道对不对,ecx 记录经检查已正确的字符数。对于 flag 的前 6 个字符(”hgame{“),每个字符的第一次单步中,eax ^ ebx 的结果就是这个字符应为的 ascii 值。对于 flag 的后面部分,每个字符第一次单步的 ebx 与 第二次单步的 ebx 异或的结果就是这个字符应为的 ascii 值,如上图

逐一测试后得到 flag hgame{You_Kn0w_debuGg3r}


Misc

Tools

分值:100pts

描述:工欲善其事,必先利其器。

第一层:F5 隐写,密码在 属性 -> 详细信息 ->备注 里,java Extract ~/Matryoshka.jpg -p \!LyJJ9bi\&M7E72\*JyD(ubuntu 下执行,注意加反斜杠转义),得到 F5.7z 压缩包密码 e@317S*p1A4bIYIs1M

第二层:Steghide 隐写,密码也在备注里,steghide extract -sf 01.jpg -p A7SL9nHRJXLh\@\$EbE8,得到 Steghide.7z 压缩包密码 u0!FO4JUhl5!L55%$&

第三层:Outguess 隐写,密码在备注里,outguess -k "z0GFieYAee%gdf0%lF" -r 02.jpg out.txt ,得到 Outguess.7z 压缩包密码 @UjXL93044V5zl2ZKI

第四层:使用软件 jphs,密码在备注里,得到 JPHS.7z 压缩包密码 xSRejK1^Z1Cp9M!z@H

将 4 块二维码拼接起来扫描得到 flag hgame{Taowa_is_N0T_g00d_but_T001s_is_Useful}


Telegraph:1601 6639 3459 3134 0892

分值:150pts

描述:他曾经最喜欢的曲师写的曲子,让人犹如漫步在星空之下,可如今他听见只觉得反胃。请将flag以hgame{your_flag_here}形式提交,flag为全大写。

粗听一遍,在 1:10 左右听到一连串的摩斯电码,Audacity 打开,查看频谱图:
hgame2021

故摩斯电码在 850Hz 左右的频段,注意到摩斯电码中间部分与音乐本身重叠,波形图有些难以辨认,而且速度很快。考虑使用 pr 将 850Hz 的音频提取出来。音频效果里加图形均衡器(30段),把 800 和 1khz 拉高,其他全拉到最低,并延长播放时间(保持音调),导出后再用 Audacity 打开,波形图就很清晰了(修正后的音频见 exp 目录下的 pr_modified.mp3)。得到摩斯电码:

1
2
-.-- --- ..- .-. ..-. .-.. .- --. .. ... ---... ....- --. ----- ----- -.. ... ----- -. --. -... ..- - -. ----- - ....- --. ----- ----- -.. -- .- -. ----- ...-- ----. ...-- .---- ----- -.- ..
YOURFLAGIS4G00DS0NGBUTN0T4G00DMAN039310KI

故 flag 为 hgame{4G00DS0NGBUTN0T4G00DMAN039310KI}


Hallucigenia

分值:200pts

描述:“我们不仅弄错了他的上下,还颠倒了它的左右。”

stegsolve 打开,在 Red plane 0 通道发现被反相的二维码,截图出来反相、扫描,得到:

1
gmBCrkRORUkAAAAA+jrgsWajaq0BeC3IQhCEIQhCKZw1MxTzSlNKnmJpivW9IHVPrTjvkkuI3sP7bWAEdIHWCbDsGsRkZ9IUJC9AhfZFbpqrmZBtI+ZvptWC/KCPrL0gFeRPOcI2WyqjndfUWlNj+dgWpe1qSTEcdurXzMRAc5EihsEflmIN8RzuguWq61JWRQpSI51/KHHT/6/ztPZJ33SSKbieTa1C5koONbLcf9aYmsVh7RW6p3SpASnUSb3JuSvpUBKxscbyBjiOpOTq8jcdRsx5/IndXw3VgJV6iO1+6jl4gjVpWouViO6ih9ZmybSPkhaqyNUxVXpV5cYU+Xx5sQTfKystDLipmqaMhxIcgvplLqF/LWZzIS5PvwbqOvrSlNHVEYchCEIQISICSZJijwu50rRQHDyUpaF0y///p6FEDCCDFsuW7YFoVEFEST0BAACLgLOrAAAAAggUAAAAtAAAAFJESEkNAAAAChoKDUdOUIk=

base64 解一下发现是顺序反过来的 png,脚本生成 png:

1
2
3
4
5
6
7
# python2
import base64

raw = "gmBCrkRORUkAAAAA+jrgsWajaq0BeC3IQhCEIQhCKZw1MxTzSlNKnmJpivW9IHVPrTjvkkuI3sP7bWAEdIHWCbDsGsRkZ9IUJC9AhfZFbpqrmZBtI+ZvptWC/KCPrL0gFeRPOcI2WyqjndfUWlNj+dgWpe1qSTEcdurXzMRAc5EihsEflmIN8RzuguWq61JWRQpSI51/KHHT/6/ztPZJ33SSKbieTa1C5koONbLcf9aYmsVh7RW6p3SpASnUSb3JuSvpUBKxscbyBjiOpOTq8jcdRsx5/IndXw3VgJV6iO1+6jl4gjVpWouViO6ih9ZmybSPkhaqyNUxVXpV5cYU+Xx5sQTfKystDLipmqaMhxIcgvplLqF/LWZzIS5PvwbqOvrSlNHVEYchCEIQISICSZJijwu50rRQHDyUpaF0y///p6FEDCCDFsuW7YFoVEFEST0BAACLgLOrAAAAAggUAAAAtAAAAFJESEkNAAAAChoKDUdOUIk="
data = base64.b64decode(raw)[::-1]
with open("flag.png", 'wb') as f:
f.write(data)

得到一个上下颠倒的 flag:

hgame2021

ps 里垂直翻转一下得到 flag hgame{tenchi_souzou_dezain_bu}


DNS

分值:100pts

描述:A significant invention.

Wireshark 打开,跟踪编号为 71 的 http 流:

hgame2021

访问的域名是 flag.hgame2021.cf,根据提示的 SPF 和题目名字 DNS,联想到通过 dig 指令查看该域名的 TXT 记录,在 ubuntu 命令行键入 dig flag.hgame2021.cf TXT,得到 flag:

hgame2021


Week3

Re

gun

分值:350pts

描述:I shoot flag out, Can you catch it?

jadx 打开,发现包 com.SecShell.SecShell,梆梆加固。反射大师可以脱,但是题目附件有问题,装在手机上运行就闪退,所以就不做了= =


FAKE

分值:300pts

描述:你能算出来吗?

init_array 数组中存在 sub_406A11,在 main 函数执行前的初始化阶段被调用,该函数检测进程是否被调试,未被调试的话就调用 sub_40699B 进行 SMC。用 ida python 脚本还原:

1
2
3
4
5
6
7
8
9
import idc

table = "00 00 00 00 00 00 00 00 00 08 91 88 C6 45 FC 48 89 BD 28 B6 74 BA A0 C5 16 90 D3 74 FF D3 D0 35 48 8B FC FA 48 83 C0 74 02 D7 98 88 E9 C6 47 88 75 BA 17 BE 55 C0 0C 4C 85 AF 3E 34 72 AB EC 48 8B 82 6D 80 7D 3F E7 7C FA 89 D0 06 65 CE FF 2F FE 5F DE D0 01 06 CD 5B BB 17 B7 4F 35 80 8B D7 0C 04 3F 1F F9 87 09 8D 14 C6 CD 53 BB 17 B7 37 3A 00 00 C7 0E DC 95 3F BC C2 0C 48 8B 82 6D A8 7D 3F BB C7 D1 6B C0 11 84 26 B6 74 BA ED 4E 00 6B 07 98 E9 3C B7 74 CB 2B 48 83 07 C9 67 FE 94 3F DC AD C2 48 4C C0 18 B6 7C 3F 39 24 00 6B 07 3A 79 F2 FD B7 11 F0 E8 48 44 45 DC 75 EF 76 18 72 E0 03 C6 55 71 EA FE B7 B0 E8 E8 48 44 45 70 74 FF 94 8A 65 8D 0C C5 CD 8F BA 17 B7 D3 6D 68 8B D7 0C D8 3E 1D FC E7 06 C1 E0 C4 84 CD B7 74 BA 54 B9 83 C0 E7 0E 00 76 2F 3E F0 ED 29 D0 06 65 10 72 EB FE 53 68 45 E8 8F 06 D8 CF 74 FF E9 21 EC 01 05 CD 97 BA 17 B7 37 16 7C 8B C7 EE E0 E5 FE 3D 99 35 45 E8 8F 06 E4 A7 74 FF 39 07 BA 8D CB 87 60 74 BA 17 95 42 C0 1C 4C 95 A5 2F 3E 1F 5F BD D0 01 06 CD BB BA 17 B7 A3 7B 34 8B D7 0C E4 FE 3F FE 57 07 E0 04 4A 91 39 B7 74 BA EC FE 05 84 C7 85 3C 74 FF 94 95 03 01 C2 8F 0E 05 17 B7 7C E5 B9 8B 00 AC 45 9F FE 3D B7 B4 F1 E8 48 44 45 3C 74 FF 94 D6 AA 01 C2 8F 0E 09 17 B7 FA 10 B6 00 00 4C 85 5B 3E 43 72 64 02 48 8B 82 6D 7C 7D 3F EB EC 10 89 D0 06 67 3B D7 2F FE A1 8D 14 01 8F 0E 79 16 B7 7C AD 28 8B 00 AC 45 DD FF 3D B7 EE 45 E8 48 44 45 7C 75 FF 94 BB 5A 01 C2 8F 0E 0D 16 B7 7C 80 5C 8B 00 AC 45 A5 FF 3D B7 D4 45 E8 48 44 45 00 75 FF 94 86 DF 01 C2 8F 0E 11 16 B7 7C 81 6C 8B 00 C6 47 10 75 BA 17 03 83 C0 78 4C 85 D5 3F 3E 1F 46 29 C1 89 0F 84 A2 B6 74 BA B7 48 83 C0 A3 0E 64 95 3F A7 67 C2 48 8B 82 6D 20 7D 3F EF E7 00 6B C0 2A 84 AE B6 74 BA 89 48 83 C0 87 0E 70 95 3F 30 66 C2 48 8B 82 6D 3C 7D 3F 9F AA 00 6B C0 9E 84 BA B6 74 BA C5 48 83 C0 CF 0E 7C 95 3F 22 65 C2 48 8B 82 6D C8 7D 3F B7 E4 00 6B C0 7A 84 54 C3 69 03 A0 FF 0F 85 C3 D0 88 FE B7 74 1C E8 48 83 07 F1 07 EE 76 2F F1 E0 02 01 17 72 48 B6 74 AA 9D 48 83 C2 9F 0E 86 95 2D C3 5E C2 48 8B 82 6D D0 7D 3F CF E0 00 6B C0 34 08 90 FC B7 74 2A E8 48 83 07 F5 2B EE 76 2F AF E0 02 01 17 08 B0 FF B7 74 32 E8 48 83 07 C1 23 FE 94 3F FB 01 C2 48 4C C0 44 B6 7C 3F 77 8B 00 6B 07 DF B1 3C B7 74 08 E8 48 83 07 AD 3F FE 94 3F 7D 01 C2 48 4C C0 50 B6 7C 3F 63 8B 00 6B 07 BC BD 3C B7 74 38 E8 48 83 07 29 77 01 6B C0 A5 C6 87 B0 8B 45 E8 8B E9 D2 C0 D8 01 05 0D 7F 45 E8 48 83 29 E3 8B 00 6B 07 7D F1 C2 48 8B 45 01 CD 83 E8 80 00 55 71 48 11 E1 C6 28 11 88 08 04 CE 89 0D 7F 44 38 00 1B 4B 94 0E 40 76 2F 3E 69 56 D1 59 11 E1 C3 00 11 49 4B CC 2A C3 40 7C 01 D0 48 13 58 04 C4 44 E0 02 01 D0 49 4B 84 E9 B6 74 BA A0 49 53 4B 08 02 D1 02 85 F1 49 D1 D1 01 E1 D2 00 10 88 03 C3 CE B1 E9 98 CB 58 E3 0F 95 B9 2E 3E 1F 0D AE 18 06 41 F9 C1 5B 44 28 49 53 C1 F4 02 C2 02 84 35 E1 D6 A4 D5 87 80 99 4B DC 0E 15 17 B7 7C 43 55 7B 01 E8 BD C9 04 CD C6 FA BA 17 B7 00 85 A0 8A 83 16 34 5F 0E 4C 1F 74 BA 17 CB C6 38 3D 08 7D 93 C5 C8 8F FF B7 74 BA 2F 0D 6F C0 48 8B 00 2A B8 C1 44 2A 48 8B 45 E8 A3 C6 4B 41 67 89 BB C1 DE 00 12 49 4B CC 2A C3 C6 28 19 5B 48 F3 4B 63 84 92 B7 74 BA 63 1D 6F 49 9C 8A C0 6A 10 B0 C1 4B 8A 00 00 00 49 D5 CC 98 8B 84 0E C0 95 3F D0 38 03 3C 8C 82 AD B4 83 C0 04 8B 83 2E 28 ED 82 BF A0 8E 3B 5D CB 40 64 01 83 7D 67 05 15 62 C8 44 3E 81 48 00 04 70 40 00 00 00 00 00".split(' ')
table = [int(i, 16) for i in table]

addr = 0x401216
for i in range(0x43F):
b = get_bytes(addr + i, 1)
idc.PatchByte(addr + i, ord(b) ^ table[i])

之后 main 函数调用 sub_401216 检查输入,该函数意在解多元一次方程组,使用 z3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from z3 import *

flag = [BitVec('flag_%d' % i, 8) for i in range(36)]
solver = Solver()
solver.add(104 * flag[0] + 64 * flag[1] + 95 * flag[2] + 45 * flag[3] + 117 * flag[4] + 95 * flag[5] == 55030)
solver.add(103 * flag[0] + 95 * flag[1] + 102 * flag[2] + 100 * flag[3] + 95 * flag[4] + 83 * flag[5] == 61095)
solver.add(97 * flag[0] + 70 * flag[1] + 108 * flag[2] + 111 * flag[3] + 107 * flag[4] + 77 * flag[5] == 60151)
solver.add(109 * flag[0] + 65 * flag[1] + 97 * flag[2] + 95 * flag[3] + 111 * flag[4] + 67 * flag[5] == 57247)
solver.add(101 * flag[0] + 75 * flag[1] + 103 * flag[2] + 89 * flag[3] + 110 * flag[4] + 63 * flag[5] == 56780)
solver.add(123 * flag[0] + 69 * flag[1] + 33 * flag[2] + 48 * flag[3] + 119 * flag[4] + 125 * flag[5] == 55726)
solver.add(104 * flag[6] + 64 * flag[7] + 95 * flag[8] + 45 * flag[9] + 117 * flag[10] + 95 * flag[11] == 46642)
solver.add(103 * flag[6] + 95 * flag[7] + 102 * flag[8] + 100 * flag[9] + 95 * flag[10] + 83 * flag[11] == 52931)
solver.add(97 * flag[6] + 70 * flag[7] + 108 * flag[8] + 111 * flag[9] + 107 * flag[10] + 77 * flag[11] == 53580)
solver.add(109 * flag[6] + 65 * flag[7] + 97 * flag[8] + 95 * flag[9] + 111 * flag[10] + 67 * flag[11] == 50437)
solver.add(101 * flag[6] + 75 * flag[7] + 103 * flag[8] + 89 * flag[9] + 110 * flag[10] + 63 * flag[11] == 50062)
solver.add(123 * flag[6] + 69 * flag[7] + 33 * flag[8] + 48 * flag[9] + 119 * flag[10] + 125 * flag[11] == 44186)
solver.add(104 * flag[12] + 64 * flag[13] + 95 * flag[14] + 45 * flag[15] + 117 * flag[16] + 95 * flag[17] == 44909)
solver.add(103 * flag[12] + 95 * flag[13] + 102 * flag[14] + 100 * flag[15] + 95 * flag[16] + 83 * flag[17] == 46490)
solver.add(97 * flag[12] + 70 * flag[13] + 108 * flag[14] + 111 * flag[15] + 107 * flag[16] + 77 * flag[17] == 46024)
solver.add(109 * flag[12] + 65 * flag[13] + 97 * flag[14] + 95 * flag[15] + 111 * flag[16] + 67 * flag[17] == 44347)
solver.add(101 * flag[12] + 75 * flag[13] + 103 * flag[14] + 89 * flag[15] + 110 * flag[16] + 63 * flag[17] == 43850)
solver.add(123 * flag[12] + 69 * flag[13] + 33 * flag[14] + 48 * flag[15] + 119 * flag[16] + 125 * flag[17] == 44368)
solver.add(104 * flag[18] + 64 * flag[19] + 95 * flag[20] + 45 * flag[21] + 117 * flag[22] + 95 * flag[23] == 54990)
solver.add(103 * flag[18] + 95 * flag[19] + 102 * flag[20] + 100 * flag[21] + 95 * flag[22] + 83 * flag[23] == 61884)
solver.add(97 * flag[18] + 70 * flag[19] + 108 * flag[20] + 111 * flag[21] + 107 * flag[22] + 77 * flag[23] == 61202)
solver.add(109 * flag[18] + 65 * flag[19] + 97 * flag[20] + 95 * flag[21] + 111 * flag[22] + 67 * flag[23] == 58139)
solver.add(101 * flag[18] + 75 * flag[19] + 103 * flag[20] + 89 * flag[21] + 110 * flag[22] + 63 * flag[23] == 57730)
solver.add(123 * flag[18] + 69 * flag[19] + 33 * flag[20] + 48 * flag[21] + 119 * flag[22] + 125 * flag[23] == 54964)
solver.add(104 * flag[24] + 64 * flag[25] + 95 * flag[26] + 45 * flag[27] + 117 * flag[28] + 95 * flag[29] == 48849)
solver.add(103 * flag[24] + 95 * flag[25] + 102 * flag[26] + 100 * flag[27] + 95 * flag[28] + 83 * flag[29] == 51026)
solver.add(97 * flag[24] + 70 * flag[25] + 108 * flag[26] + 111 * flag[27] + 107 * flag[28] + 77 * flag[29] == 49629)
solver.add(109 * flag[24] + 65 * flag[25] + 97 * flag[26] + 95 * flag[27] + 111 * flag[28] + 67 * flag[29] == 48219)
solver.add(101 * flag[24] + 75 * flag[25] + 103 * flag[26] + 89 * flag[27] + 110 * flag[28] + 63 * flag[29] == 47904)
solver.add(123 * flag[24] + 69 * flag[25] + 33 * flag[26] + 48 * flag[27] + 119 * flag[28] + 125 * flag[29] == 50823)
solver.add(104 * flag[30] + 64 * flag[31] + 95 * flag[32] + 45 * flag[33] + 117 * flag[34] + 95 * flag[35] == 46596)
solver.add(103 * flag[30] + 95 * flag[31] + 102 * flag[32] + 100 * flag[33] + 95 * flag[34] + 83 * flag[35] == 50517)
solver.add(97 * flag[30] + 70 * flag[31] + 108 * flag[32] + 111 * flag[33] + 107 * flag[34] + 77 * flag[35] == 48421)
solver.add(109 * flag[30] + 65 * flag[31] + 97 * flag[32] + 95 * flag[33] + 111 * flag[34] + 67 * flag[35] == 46143)
solver.add(101 * flag[30] + 75 * flag[31] + 103 * flag[32] + 89 * flag[33] + 110 * flag[34] + 63 * flag[35] == 46102)
solver.add(123 * flag[30] + 69 * flag[31] + 33 * flag[32] + 48 * flag[33] + 119 * flag[34] + 125 * flag[35] == 46744)

if solver.check() == sat:
result = solver.model()
for i in range(36):
print(chr(result[flag[i]].as_long()), end = '')
# hgame{E@sy_Se1f-Modifying_C0oodee33}

helloRe3

分值:350pts

描述:开发者留下了调试信息,会不会有用呢? 试试DbgView。

Alt +T 搜索 input length,来到 sub_1400C8BD0,0x1400C8CBF 处调用加密函数 sub_14008BC07 对输入序列进行加密,该函数是典型的 RC4,解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
res = "63 80 04 F0 D3 D6 62 D0 D8 3C 76 C5 93 14 CF BC 3A 05 F4 6C".split(' ')
res = [int(i, 16) for i in res]
table = []
for i in range(0x14):
table.append(res[i] ^ 0x15)

order = range(21, 33) + range(37, 50) + range(54, 65) + range(66, 76)
char = r"1234567890-+qwertyuiop{}|asdfghjkl;'zxcvbnm,./".upper()
dic = {}
for i in range(len(order)):
dic[order[i]] = char[i]

flag = ''
target = "4D AF 27 AD E1 EC 6D DA F0 31 5E 9A 9E 29 FA BE 6B 08 C8 49".split(' ')
target = [int(i, 16) for i in target]
for i in range(0x14):
o = target[i] ^ table[i]
flag += dic[o]
print flag

Misc

A R K

分值:250pts

描述:星藏点雪 月隐晦明 以夕为墨 泼雪作屏

hint:明日方舟是一款塔防游戏,可以将可部署单位放置在场地中。并且具有自律功能,可以记录部署的操作。翻译:没用 没用 出题人用可部署单位画了个东西 背景是白色的

首先通过后面部分的 FTP 流量获得 CS 交互的 ssl.log,导入 wireshark 解密 TLS 流量:

hgame2021

导出 http 对象,在 getBattleReplay json 文件中,battleReplay 的值为一长串 base64。解码后得到一个 zip,但是开头是 50 4B 05 06,改为 50 4B 03 04,解压得到文件 default_entry,其中记录了很多单位的 xy 坐标,使用脚本提取:

1
2
3
4
5
6
7
8
9
10
import json

with open("default_entry") as f:
data = json.load(f)
logs = data["journal"]["logs"]

with open("points.txt", 'w') as g:
for log in logs:
pos = log["pos"]
g.write("%s %s \n" % (pos["row"], pos["col"]))

再用 gnuplot 绘制,得到一个二维码,扫码得到 flag hgame{Did_y0u_ge7_Dusk?}


A R C

分值:350pts

描述:

​ Akira虽然不玩🐍,但有些东西成功引起了他的注意。

hint:

​ hint1: 8558应该理解成85和58,BV号是___所以图片里的是___
​ hint2: 用tables而不是table,是因为字体是用来对照图片里字符串的内容的算一个table,而这个字体表示的东西也是一个table。注意本字体的i和j,I(0x49)和l(0x6C)有点相似
​ hint3: 用了某种ROT的范围,但是位移不一样。词频分析是个好东西,别忘了视频里的问题。
​ hint4: 有的东西可以参考Crypto WEEK-1 第一题。
​ hint5: / 不是可输入的意思,是网站路径

8558.png 中的字符串摘取下来,base85 解码得到 BV 号编码的 table(BV 号基于 base58),从 知乎 上找来 mcfx 大佬的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# python3
import base64

string = "BK0ICG]Qr*88_$gC,'-j2+KH86?Q\"%928;LG@O*!Am0+`;E7iV2agSE<c'U;6Yg^#H?!YBAQ]"
table = base64.a85decode(string).decode()

tr={}
for i in range(58):
tr[table[i]]=i
s=[11,10,3,8,4,6]
xor=177451812
add=8728348608

def dec(x):
r=0
for i in range(6):
r+=tr[x[s[i]]]*58**i
return (r-add)^xor

def enc(x):
x=(x^xor)+add
r=list('BV1 4 1 7 ')
for i in range(6):
r[s[i]]=table[x//58**i%58]
return ''.join(r)
print(enc(10001540))
# BV17f411J77h

得到 7z 密码,解压得到一个视频和 Fragments.txt,视频中的问题百度一下得到答案 42,所以视频里的两行字符串应该与 rot42 有关。多次测试后,发现第一行分为前后两段,前半段 #)+F7IIMEH:?I 是 rot42,后半段 njiikffi 是 rot47,解密后拼接起来得到 MSUpasswordis:6557225

使用 VirtualDub + MSU 插件(去年的题目也用到了这俩),从 mkv 中提取出一个 txt,得到一个网址和用户名密码:

1
2
3
arc.hgame2021.cf
Hikari
Tairitsu

访问网址,输入用户名密码,页面中只有一个斜杠代表网站根路径:

hgame2021

脚本处理 Fragments.txt 中的内容(也是 rot42 和 rot47):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s = ''
s1 = """zB7= ?I DEJ >;H;` 8KJ } MH?J; ?J 8;97KI; OEK C7O D;;: CEH; MEH:I JE 7D7BOI?I M>7J ;D9E:?D= J>; B?D;e ?Ib
zEH B?D;f` \"?A? >7I JEB: OEK M>7J ?J ?I` 7D: uA?H7 ?I D;9;II7HO JE :E ?Jb
*ME OEKD= =?HBI ;NFBEH; 7 I>7JJ;H;: MEHB:` <?BB;: M?J> IEKD:n 7 F7IJ JE 8; KD9EL;H;:bbb
y79> 7M7A;DI ?D J>?I 8B7DA` HK?Da:EJJ;: MEHB: JE :?I9EL;H J>7J I>; ?I ;GK7BBO 8B7DA` H;C;C8;H?D= DEJ>?D= E< M>7J 97C; 8;<EH;b
uD: J>;D J>;O C7A; 7 I;9ED: :?I9EL;HOn J>; uH97;7` CKBJ?JK:;I E< <BE7J?D= =B7IIaB?A; I>7H:I 9EDJ7?D?D= L?L?: C;CEH?;I E< J>; F7IJb"""
for i in range(len(s1)):
if s1[i] not in ' \n':
if s1[i] >= '`':
tmp = ord(s1[i]) - 52
else:
tmp = ord(s1[i]) + 42
s += chr(tmp)
else:
if s1[i] == ' ':
s += ' '
else:
s += '\n'
print(s)

得到:

1
2
3
4
5
Flag is not here, but I write it because you may need more words to analysis what encoding the line1 is.
For line2, Liki has told you what it is, and Akira is necessary to do it.
Two young girls explore a shattered world, filled with sound: a past to be uncovered...
Each awakens in this blank, ruin-dotted world to discover that she is equally blank, remembering nothing of what came before.
And then they make a second discovery: the Arcaea, multitudes of floating glass-like shards containing vivid memories of the past.

对于视频中第二行字符串,根据 hint,需要用到 Crypto WEEK-1 第一题用过的维吉尼亚密码,密文是 pwbvmpoakiscqdobil 密钥是 Akira,解密得到 pmtempestissimobyd,访问 arc.hgame2021.cf/pmtempestissimobyd 得到 flag:

hgame2021


accuracy

分值:350pts

描述:石碑上的文字,究竟隐藏着怎样的秘密……

考的人工智能,需要训练一个模型来识别所有的图片然后提交字符串,官方说准确率到 95 就给 flag。我用 imageai 库训练了 10 个小时,得到的模型却并不理想,于是就放弃了。看官方 wp 别人手撸了神经网络,告辞!


Week4

Re

vm

分值:450pts

描述:ovm++ hates debugger

类构造函数 sub_140001640 初始化虚拟机的指令和数据。构造函数 sub_140001610 调用 sub_14000128F 将进程加入一个 Job 中(可以理解为沙箱),防止调试器附加(不过如果是调试启动的话就没有影响了)

查字符串 Welcome to ovm++! 的交叉引用来到 main 函数,流程比较简单:

hgame2021

run_vm 函数就在运行虚拟机改变输入了,但是我不想去理解这个虚拟机的操作了。在动调的时候发现 case22 每次只修改输入的一个字符,而且运算结果与其他位置的字符无关,所以就写了个 ida python 脚本来爆破 flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import idc
import idaapi
import string

charset = string.printable
# Initialize flag as "hgame{'a'*27}"
flag = [104, 103, 97, 109, 101, 123] + [0x61] * 27 + [125]
# Here's the target array picked from IDA
target = "CF BF 80 3B F6 AF 7E 02 24 ED 70 3A F4 EB 7A 4A E7 F7 A2 67 17 F0 C6 76 36 E8 AD 82 2E DB B7 4F E6 09".split(' ')
target = [int(i, 16) for i in target]

image_base = 0x7FF62DD90000
for i in range(32, 5, -1):
isFound = 0
for j in range(len(charset)):
# run_vm(): ret
RunTo(image_base + 0x3FC4)
GetDebuggerEvent(WFNE_SUSP, -1)
input_addr = Qword(image_base + 0xE3A0)
bs = get_bytes(input_addr, 34)
# Check last trial
if ord(bs[i]) == target[i]:
if j == 0:
print("Found [%d]: %s" % (i, charset[j]))
else:
print("Found [%d]: %s" % (i, charset[j - 1]))
isFound = 1
if not isFound:
# Assign and patch input for next trial
flag[i] = ord(charset[j])
for k in range(34):
PatchByte(input_addr + k, flag[k])
# Reset regs
PatchByte(image_base + 0xE3A8, 0)
PatchByte(image_base + 0xE3AD, 0)
PatchDword(image_base + 0xE3A9, 0)
# Set function param
SetRegValue(image_base + 0xE378, "rcx")
SetRegValue(input_addr, "rdx")
# Redo
SetRegValue(image_base + 0x3CC0, "rip")
# If current char is found, still need to do things above
if isFound:
# So we can only 'break' at here
break

print ''.join(map(chr, flag))

在 ida7.0 中设置好 Local Windows debugger,运行脚本就能爆出 flag 了:

hgame2021


A 5 Second Challenge

分值:400pts

描述:

​ oops! 我不小心把一些不该泄露的文件也打包进去了,不过还好,有个 dll 被我提前动过了手脚,别想通过 ILSpy 一把梭了~!

hint:

​ hint 1: 这个扫雷甚至没做胜利判定,雷的位置就是 flag,多踩几局雷就能发现是二维码了 囧

​ hint 2: il2cpp 中间文件(源码)已经直接给了,就不太需要用 il2cppdumper 这类工具去死怼 GameAssembly.dll 了 囧
​ hint 3: 动的手脚只是把某个函数 nop 掉了,那个 dll 还是有用的
(除非你想从内存里把 matrix 动调出来

il2cpp 打包的 unity 游戏,用 IL2cppDumper 去怼 GameAssembly.dll。ida 加载 IL2cppDumper 的脚本,找到一个有意思的方法 Brick::OnMouseUp,这个方法就是在方块上抬起鼠标左键会调用的回调函数。函数会检查是否超时:

hgame2021

那就把它 nop 掉:

hgame2021

然后检查这个方块有没有雷,有雷就把游戏结束标志置为 1:

hgame2021

所以把 isOver = 1 patch 成 0,让我们就算踩了雷也不会结束:
hgame2021

为了正确运行,还需要 patch BombChecker::CheckBombAt

hgame2021

然后应用一下 patch,打开游戏把雷块都点开:

hgame2021

像不像一个二维码……然后👴是龙鸣,👴 手点 了一个 45*45 的二维码:

hgame2021

扫码得到 flag hgame{YOU~hEn-duO_yOU-X|~DOU-sHi~un1Ty~k4i-fA_de_O}


nllvm

分值:350pts

描述:cosmos掉入了异世界中,帮助cosmos回到现实……

main 函数是 sub_1400105F0,其他没啥好说的,动调跟一下就能知道程序逻辑了,加密函数也不难(其实就是 AES,这里闲的没事我选择手撸),用 python 翻译加密函数就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# python2
const_key = "43 72 79 70 74 6F 46 41 49 4C 55 52 45 66 6F 72 52 53 41 32 30 34 38 4B 65 79 21 21 21 21 21 21 BF 8F 84 8D CB E0 C2 CC 82 AC 97 9E C7 CA F8 EC 94 27 00 FC A4 13 38 B7 C1 6A 19 96 E0 4B 38 B7 0E 88 2D 6C C5 68 EF A0 47 C4 78 3E 80 0E 80 D2 59 8C CD 49 FD 9F F5 FE 3C F5 EC 68 DC BE D4 DF A4 C0 B3 EA 61 A8 5C 4A 26 6C 24 74 A6 62 A4 A6 7D 26 84 6D 80 B9 71 93 BC 4C 9D FB 60 F2 49 24 25 FB 85 3A 44 53 D9 70 62 3F FD 04 C4 5D 59 A2 61 6A 4F 57 E1 D3 3E C4 5D 9F A3 3F 3D 6D EA 1B 09 7C 2A 1D 4D 2F F3 6D 2F 10 0E 69 EB 4D 57 CB 88 89 14 48 69 5A 2A 8C 34 C5 89 B3 09 A8 63 A8 EB 87 E8 1C A6 A8 1B 71 89 B8 15 18 62 F5 42 D3 22 6F 38 2E 4B 35 12 A2 7F F0 9B 11 76 58 F8 B9 C1 C6 BE 24 67 6E A5 55 EE D6 B0 4D 8C 23 F2 9E".split(' ')
const_key = [int(i, 16) for i in const_key]
table = "63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16".split(' ')
table = [int(i, 16) for i in table]

def xor(n, a, b):
for i in range(4):
for j in range(4):
a[4*i+j] ^= b[16*n+4*i+j]

def find_table(a):
for i in range(4):
for j in range(4):
idx = a[4*j+i]
a[4*j+i] = table[idx]

def swap(a):
a[1], a[5], a[9], a[13] = a[5], a[9], a[13], a[1]
a[2], a[10] = a[10], a[2]
a[6], a[14] = a[14], a[6]
a[3], a[15], a[11], a[7] = a[15], a[11], a[7], a[3]

func = lambda a : ((27 * ((a >> 7) & 1)) ^ (2 * a)) & 0xFF

def deal(a):
for i in range(4):
t1 = a[4*i]
t2 = a[4*i+3] ^ a[4*i+2] ^ a[4*i+1] ^ t1
a[4*i] = t2 ^ func(a[4*i+1] ^ t1) ^ t1
a[4*i+1] ^= t2 ^ func(a[4*i+2] ^ a[4*i+1])
a[4*i+2] ^= t2 ^ func(a[4*i+3] ^ a[4*i+2])
a[4*i+3] ^= t2 ^ func(t1 ^ a[4*i+3])

def encrypt(plain):
plain = map(ord, plain)
v4 = range(16)
cipher = []
for i in range(0, 64, 16):
inp = plain[i:]
xor(0, inp, v4)
xor(0, inp, const_key)
j = 1
while 1:
find_table(inp)
swap(inp)
if j == 14:
break
deal(inp)
xor(j, inp, const_key)
j += 1

xor(0xE, inp, const_key)
v4 = inp[:16]
cipher += v4
return map(hex, cipher)

print encrypt('a' * 64)

所以对应写个解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
target = "91 B3 C1 EB 14 5D D5 CE 3A 1D 30 E4 70 6C 6B D7 69 78 79 02 A3 A5 DF 1B FD 1C 02 89 14 20 7A FD 24 52 F8 A9 F9 F1 6B 1C 0F 5D 50 5B EC 42 D1 8C B8 12 CF 2C A9 69 31 46 FD 9B EA DE C8 BF 94 69".split(' ')
target = [int(i, 16) for i in target]
const_key = "43 72 79 70 74 6F 46 41 49 4C 55 52 45 66 6F 72 52 53 41 32 30 34 38 4B 65 79 21 21 21 21 21 21 BF 8F 84 8D CB E0 C2 CC 82 AC 97 9E C7 CA F8 EC 94 27 00 FC A4 13 38 B7 C1 6A 19 96 E0 4B 38 B7 0E 88 2D 6C C5 68 EF A0 47 C4 78 3E 80 0E 80 D2 59 8C CD 49 FD 9F F5 FE 3C F5 EC 68 DC BE D4 DF A4 C0 B3 EA 61 A8 5C 4A 26 6C 24 74 A6 62 A4 A6 7D 26 84 6D 80 B9 71 93 BC 4C 9D FB 60 F2 49 24 25 FB 85 3A 44 53 D9 70 62 3F FD 04 C4 5D 59 A2 61 6A 4F 57 E1 D3 3E C4 5D 9F A3 3F 3D 6D EA 1B 09 7C 2A 1D 4D 2F F3 6D 2F 10 0E 69 EB 4D 57 CB 88 89 14 48 69 5A 2A 8C 34 C5 89 B3 09 A8 63 A8 EB 87 E8 1C A6 A8 1B 71 89 B8 15 18 62 F5 42 D3 22 6F 38 2E 4B 35 12 A2 7F F0 9B 11 76 58 F8 B9 C1 C6 BE 24 67 6E A5 55 EE D6 B0 4D 8C 23 F2 9E".split(' ')
const_key = [int(i, 16) for i in const_key]
table = "63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16".split(' ')
table = [int(i, 16) for i in table]

def xor(n, a, b):
for i in range(4):
for j in range(4):
a[4*i+j] ^= b[16*n+4*i+j]

def d_swap(a):
a[5], a[9], a[13], a[1] = a[1], a[5], a[9], a[13]
a[2], a[10] = a[10], a[2]
a[6], a[14] = a[14], a[6]
a[15], a[11], a[7], a[3] = a[3], a[15], a[11], a[7]

def d_find_table(a):
for i in range(3, -1, -1):
for j in range(3, -1, -1):
idx = table.index(a[4*j+i])
a[4*j+i] = idx

func = lambda a : ((27 * ((a >> 7) & 1)) ^ (2 * a)) & 0xFF

def solve(x):
for i in range(256):
if i ^ func(i) == x:
return i

def solve_f(x):
for i in range(256):
if func(i) == x:
return i

def re(x):
t01 = x[0] ^ x[1]
for i in range(256):
eq = t01 ^ func(i)
old01 = solve(eq)
old02 = old01 ^ i
t02 = x[0] ^ x[2]
old23 = solve_f(t02 ^ old02 ^ func(old01))
old03 = old02 ^ old23
a2 = x[3] ^ func(old03) ^ old01
a3 = old23 ^ a2
a0 = old02 ^ a2
a1 = old01 ^ a0
if(x[0] == a1 ^ a2 ^ a3 ^ func(a0 ^ a1)
and x[1] == a0 ^ a2 ^ a3 ^ func(a2 ^ a1)
and x[2] == a1 ^ a0 ^ a3 ^ func(a2 ^ a3)
and x[3] == a1 ^ a2 ^ a0 ^ func(a3 ^ a0)):
return [a0, a1, a2, a3]

def d_deal(a):
for i in range(4):
ori = re(a[4*i:4*(i+1)])
a[4*i] = ori[0]
a[4*i+1] = ori[1]
a[4*i+2] = ori[2]
a[4*i+3] = ori[3]

def decrypt(cipher):
plain = []
for i in range(48, -1, -16):
inp = cipher[i:i+16]
if i == 0:
v4 = range(16)
else:
v4 = cipher[i-16:i]
xor(0xE, inp, const_key)

d_swap(inp)
d_find_table(inp)
for j in range(13, 0, -1):
xor(j, inp, const_key)
d_deal(inp)
d_swap(inp)
d_find_table(inp)
xor(0, inp, const_key)
xor(0, inp, v4)
plain = inp + plain
return plain

flag = decrypt(target)
print(''.join(map(chr, flag)))
# hgame{cOsm0s_is_still_fight1ng_and_NEVER_GIVE_UP_O0o0o0oO00o00o}

直接用 AES 库的话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# python3
from Crypto.Cipher import AES
from binascii import a2b_hex

def decrypt(text):
key = b"CryptoFAILUREforRSA2048Key!!!!!!"
iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"

mode = AES.MODE_CBC
cryptor = AES.new(key, mode, iv)
plain_text = cryptor.decrypt(a2b_hex(text))
return plain_text.decode()

raw = "91 B3 C1 EB 14 5D D5 CE 3A 1D 30 E4 70 6C 6B D7 69 78 79 02 A3 A5 DF 1B FD 1C 02 89 14 20 7A FD 24 52 F8 A9 F9 F1 6B 1C 0F 5D 50 5B EC 42 D1 8C B8 12 CF 2C A9 69 31 46 FD 9B EA DE C8 BF 94 69"
target = raw.replace(' ', '')
print(decrypt(target))
# hgame{cOsm0s_is_still_fight1ng_and_NEVER_GIVE_UP_O0o0o0oO00o00o}

Misc

Akira之瞳-1

分值:350pts

描述:有人想问 Akira 为什么总喜欢用眼睛当头像,Akira 说:“我给你讲个故事吧,从前有一天一位原画师在上班,不幸的是突然起了火灾,情急之下 IT 部门把她没保存的工作 dump 了下来并传到了网上 …… ”

volatility -f important_work.raw imageinfo,选择 Win7SP1x64 作为 profile 参数

volatility -f important_work.raw --profile=Win7SP1x64 pslist,得知 important_work 进程的 pid 为 1092

volatility -f important_work.raw --profile=Win7SP1x64 memdump -p 1092 -D .,得到 1092.dmp

foremost 1092.dmp,得到压缩包 00002256.zip,压缩包备注里提示 Password is sha256(login_password)

获取压缩包密码:

1
2
3
4
5
root@kali:~# volatility -f important_work.raw --profile=Win7SP1x64 hashdump
Volatility Foundation Volatility Framework 2.6
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Genga03:1001:aad3b435b51404eeaad3b435b51404ee:84b0d9c9f830238933e7131d60ac6436:::

84b0d9c9f830238933e7131d60ac6436 去 cmd5 查,得到密码 asdqwe123,sha256 一下得到压缩包密码

压缩包里两张一样的图,很明显的盲水印,用 github 的脚本解一下得到含有 flag 的图片(需要放大仔细看),hgame{7he_f1ame_brin9s_me_end1ess_9rief}


Akira之瞳-2

分值:400pts

描述:

​ ……
​ “最后呢?”
​ “最后她还是没能幸免,人们在保险箱旁发现了她烧焦的尸体,打开保险箱人们发现了一个U盘,是她将回家画好的原稿带来时用的 ……”

👴懒得做了,和第二名差的分一道题补不回来,寄!