传送门:2020新年解谜

祝大家2020元旦快乐。以下是游戏的题解。

第一关

传送门:第一关

答案:

打开后只有闪烁的背景颜色。查看网页源码后发现,颜色闪烁是由 websocket 服务器控制的。

window.addEventListener("load", function () {
    websocket = new WebSocket("wss://harrynull.tech/2020/ws/lvl1");
    websocket.onmessage = function (evt) { $("body").animate({
        backgroundColor: evt.data == 'L' ? '#FFC500' : '#FFA500'
        }, 500); };
}, false);

实际上,这里的颜色闪烁代表摩尔斯电码。写一个程序分析亮灭之间时间间隔(注意在较差的网络环境下得到时间可能会不大稳定)。

websocket = new WebSocket("wss://harrynull.tech/2020/ws/lvl1");
function now(){return new Date().getTime(); }
var lastL=new Date().getTime();
var lastO=new Date().getTime();
websocket.onmessage = function (evt) { 
    if(evt.data=='L') {console.log("Empty " + Math.round((now() - lastO)/1000) ); lastL=now();}
    if(evt.data=='O') {console.log("On " + Math.round((now() - lastL)/1000) ); lastO=now();}
};

可发现间隔时间有Empty 1代表一个短间隔,Empty 3代表一个长间隔,Empty 6代表序列重复间的间隔。On 1代表短,On 2代表长。按照此规则可写出解码程序。

var res = "";
websocket.onmessage = function (evt) { 
    if(evt.data=='L') {res+=(Math.round((now() - lastO)/1000)==1?"":" "); lastL=now();}
    if(evt.data=='O') {res+=(Math.round((now() - lastL)/1000)==1?".":"-"); lastO=now();}
};

得到摩尔斯电码. ..... .- ..-. -... ----.,解码得E5AFB9,即的 utf-8 编码。

附本关 python 源码

async def lvl1(websocket):
    pattern = '. ..... .- ..-. -... ----.' # E5AFB9 对
    while True:
        for c in pattern:
            if c==' ':
                await asyncio.sleep(2)
                continue
            await websocket.send('L')
            await asyncio.sleep(1 if c=='.' else 2)
            await websocket.send('O')
            await asyncio.sleep(1)
        await asyncio.sleep(5)

第二关

传送门:第二关

答案:

观察 URL 可得本关地址。这一关既可以手动/js半手动解,也可以写脚本解。基本方法是二分+递归。

附本关 python 源码

async def lvl2(websocket):
    import random
    import time
    FLAG = '年'
    CHECK_INTERVAL = 10

    i=random.randint(0, 999)
    j=i
    while j==i: j=random.randint(0, 999)

    while True:    
        message = await websocket.recv()
        message = message[0] + decompress([ord(i) for i in message[1:]])
        await asyncio.sleep(CHECK_INTERVAL)
        if message[0]=='A':
            await websocket.send(FLAG if message.count('1')==2 and message[i]=='1' and message[j]=='1' else 'Incorrect')
        elif message[0]=='T':
            await websocket.send('Conflict' if message[i]=='1' and message[j]=='1' else 'OK')
    

第三关

传送门:第三关

答案:

设计解法:

反编译 wasm 得到判断密码正确的算法。其原始 C 代码如下:

int f(int g){
    return g==0?0:g+f(g/2);
}
int checkPassword(int pwd){
    return f(pwd)==64859?pwd:0; // 纳 32435
}

然后这里可以用遍历等方式获得 pwd 值为 32435,即“纳”的 utf-16 编码。

暴力解法:

直接用 javascript 遍历可能的 unicode 值。

for(var i=0;i<100000;i++) if(checkpwd(i)) console.log(i);

得到32435, String.fromCharCode(32435) 得“纳”。

第四关

传送门:第四关

答案:

提示是 esolang,即 “esoteric programming languages”。这里网页里实际上包括了一段 whitespace 代码,如下。

   	 	 	 		  		 		
   	
	   

转换成伪代码则是

push 21915
push 1
add
end

21916的 UTF-16 编码。

红包

根据主页的说明,红包口令即为对年纳喜20