今年又来出题凑热闹了。依旧是五道题,由于前面挂了一层 Cloudflare,并为这个域名开了浏览器检验,因此题目就直接用纯静态文件了。这次的界面比上次稍微好了一点,框的图片用的是我很久以前做的游戏中的窗口。具体的实现方法就请大家自行扒代码吧。下面说说题目。
Level 1
传送门:https://red-packet.rexskz.info/2018/?s=the_first_level
const encrypted = 'gard dna kcilc';
const decrypted = reverse(encrypted);
const { id } = new ComicCollection('xkcd').findComicByName(decrypted);
const delta = 0x3E7986;
const t = '0' + (delta + id * id);
if (/^\d{8}$/.test(t)) {
// 答案就是 ans 的值
const ans = t + '_reversed';
} else {
console.log('error!');
}
入门题,考察英语水平……代码中的思路是先将 gard dna kcilc
字符串反转得到 click and drag
,再从 XKCD 的漫画集里面搜索名称为这个的漫画,获取它的 ID(1110),最后对这个 ID 做一些基础运算。
XKCD 是我非常喜欢的漫画系列,有兴趣的可以看看。第 1110 幅漫画超级大,里面有非常多的梗,例如 MineCraft 中的 Creeper。
Level 2
传送门:https://red-packet.rexskz.info/2018/?s=05326442_reversed
const encrypted = `
54 68 65 20 61 6E 73 77 65 72 20 74 6F 20 6C 69
66 65 2C 20 74 68 65 20 75 6E 69 76 65 72 73 65
20 61 6E 64 20 65 76 65 72 79 74 68 69 6E 67 0A
`;
const decrypted = decrypt(encrypted);
if (/^\d+$/.test(decrypted)) {
// 答案就是 ans 的值
const ans = '' + (155902 + Math.exp(decrypted) % 1e6) + decrypted + '_bins';
} else {
console.log('error!');
}
看到十六进制,可以尝试转为字符串:
encrypted.split(/\s/).map(t => String.fromCharCode(parseInt(t, 16))).join('')
发现是“The answer to life, the universe and everything”,这是《银河系漫游指南》中很著名的一个梗,答案是 42,如果不知道,直接谷歌也能得出答案。然后对这个答案做一些基础运算就好了。
Level 3
传送门:https://red-packet.rexskz.info/2018/?s=65689442_bins
const t1 = '5L2b5puw77ya572w6IiN5YOn5oGQ5ZOG6ICo6Kuz5rOi5omA6YGT572w6Kum576v5qWe6YO96Yav5L+x5Yip5ZOG6LGG5ryr5Yud6Kuz5b6X5LuW572w5a+r5L6E44CC6Ky555yf5a6k5L2b5b6X5aWi5rOi5ZKS5oCb6ICF6Jed5LuW57y95b2M5L6d5a+m5bqm55qk5YiH5Yal5LiJ5ZOG6JiH5aSa5LiW5ZCJ57y96Jap5amG5amG572w6YO96Ium5rOi5oCv5Yip';
const decrypted1 = decode1(t1);
const t2 = '70352f41061eda4ff3c322094af068ba70c3b38b';
const decrypted2 = decode2(t2);
const t3 = '圆周率小数点后第一次出现 12345678 的位置';
const decrypted3 = decode3(t3);
const t4 = ' ゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');';
const decrypted4 = decode4(t4);
// 答案就是 ans 的值
const ans = (+decrypted1) + (+decrypted2) + (+decrypted3) * Math.floor(1 / decrypted4) + '_wtf';
一堆比较神奇的东西。t1
Base64 解密后是一个“与佛论禅”加密,解出来是 佛祖保佑,永无 BUG!36464142
;t2
经过 SHA1 反查之后是 00000000
;t3
可以通过一些网站查到,但如果你能发现 t4
可以直接执行并且结果是 100
的话,就会发现其实只有 t1
是有用的。因此最终答案是 36464142_wtf
。
Level 4
传送门:https://red-packet.rexskz.info/2018/?s=36464142_wtf
题目中第一句话“宁肯听任自己失望,也绝不乱存奢望”可以搜到作者是弗洛伊德,再看到下面给的一大堆数据,对角线是 0,还是个对称矩阵,不难想到图论中的弗洛伊德算法。最下面的提示是将最短路中的几条路经长度拼起来,即为最终的答案。
// 生成数据的代码如下
#include <iostream>
#include <cstdlib>
#include <cstring>
#define n 50
using namespace std;
int main() {
int g[n][n];
srand(time(0));
memset(g, 0, sizeof(g));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
g[i][j] = rand() % 1000;
g[j][i] = g[i][j];
}
}
for (int i = 0; i < n; ++i) {
cout << g[i][0];
for (int j = 1; j < n; ++j) {
cout << ' ' << g[i][j];
}
cout << endl;
}
return 0;
}
// 参考的解题代码如下
#include <iostream>
#define n 50
using namespace std;
int main() {
int g[n][n];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> g[i][j];
}
}
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (g[i][k] + g[k][j] < g[i][j]) {
g[i][j] = g[i][k] + g[k][j];
}
}
}
}
cout << g[30][21] << g[5][25] << g[44][26] << g[36][2];
return 0;
}
至于数据错误的同学们,别忘了弗洛伊德曾经说过:要先枚举中间点……
Level 5
传送门:https://red-packet.rexskz.info/2018/?s=33151942
只有 这一个文件,下下来发现是一个带有密码的 7zip 压缩包,里面有一个叫“解压密码.txt”的文件,只有 5 个字节大小,CRC32 是 03639BCA
,于是可以用 Hashcat 暴力跑出密码是 Billy
,不过后来发现好像谷歌也可以直接查到……
解压之后发现里面的图片是一张王的演讲:
检查图片可以发现最后面被嵌入了一段代码:
const clearText = '????????';
export default function encrypt(clearText) {
if (!/^\d+$/.test(clearText)) {
return false;
}
const cipher = ((+clearText) * 99990001);
return `${cipher}`.substr(-12);
}
console.log(encrypt(clearText)); // => 207079295042
// 答案就是 ans 的值
const ans = clearText.substr(-8);
发现 encrypt
函数的作用是将传入的数字乘上 99990001
再取最后 12 位。巧合的是,(1e12 + 1) / 99990001 === 10001
,因此根据数论中同余的性质 (a * b) % c === (a % c * b % c) % c
,将 207079295042
乘上 10001
再取最后 12 位即可。当然,写程序跑出结果也是可以的。