R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。

Rex 的 2018 新年红包例行题解

注意:本文发布于 2254 天前,文章中的一些内容可能已经过时。

今年又来出题凑热闹了。依旧是五道题,由于前面挂了一层 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!36464142t2 经过 SHA1 反查之后是 00000000t3 可以通过一些网站查到,但如果你能发现 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,不过后来发现好像谷歌也可以直接查到……

解压之后发现里面的图片是一张王的演讲:

0

检查图片可以发现最后面被嵌入了一段代码:

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 位即可。当然,写程序跑出结果也是可以的。

Disqus 加载中……如未能加载,请将 disqus.com 和 disquscdn.com 加入白名单。

这是我们共同度过的

第 3072 天