喵喵喵喵

前言

当我做到第一题我就觉得头痛,要玩2048玩到有一个2048的格子才算胜利,奈何本人实在苦手。 习惯性地按出Inspector,发现所有代码都在本地运行,于是决定今年走邪道解法,手动误入歧途。
简单说一下思路,就是使用抓包工具(以fiddler为例)拦截javascript的http请求,并替换为修改后的代码,以达到不用做题就通关的目的。

打开Fiddler之后,将浏览器的Proxy指向Fiddler的监听端口(默认为8866),使用AutoResponder即可将指定的请求替换为本地修改过的js文件。

Level1

2048,出现2048的方块才能通关。

将game_manager保存在本地,修改70行处的 GameManager.prototype.addRandomTile 函数。

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    // var value = Math.random() < 0.9 ? 2 : 4;
    //var tile = new Tile(this.grid.randomAvailableCell(), value);

    var tile = new Tile(this.grid.randomAvailableCell(), 1024);

    this.grid.insertTile(tile);
  }
};

注释掉产生随机数的一行, 并将下一行的value修改为1024。 修改完成之后在Fiddler中添加Auto Responder规则

刷新页面(如果reload之后不是修改之后的js文件,清除缓存即可),所有新出现的方块都是1024,合并一次即可过关。

Level2

俄罗斯方块,要获得1024分才能过关。

首先将level22.js保存在本地。

从第二题开始js代码就开始有混淆了。但是数值是没法隐藏的。

通关条件是1024, 十六进制为0x400,在js中搜索0x400,只有两个结果,而且都与分数有关。

case '10':
    if (!_0x5d1071['TNKOn'](validateBlock, nextBlock)) {
        status = 0x2;
        // if (_0x5d1071['qVkxf'](score, 0x400)) {
        if (_0x5d1071['qVkxf'](score, 0x1)) {
            document['getElementById'](_0x5d1071['smtEb'])['innerText'] = _0x5d1071['NqDyV'];
        } else {
            document['getElementById'](_0x5d1071['smtEb'])['innerText'] = _0x5d1071['cpSmM'];
        }
        document['getElementById'](_0x5d1071['xWOsg'])['disabled'] = ![];
        return;
    }
    continue;
// if (_0x43e5f0['wLwev'](score, 0x400)) {
if (_0x43e5f0['wLwev'](score, 0x1)) {
    var _0x597490 = _0x43e5f0['QIEww']['split']('|')
        , _0x1f49d0 = 0x0;

将0x400修改为0x1,添加Auto Responder规则

只要获得1分以上即可过关

Level3

这他妈smdx

果断打开Inspector,发现有一个Textarea,并且会随时时检查输入,应该是输flag的地方

level33.js里找到一个看起来像是flag的东西

if (_0x473dc8['lGuXU'](ined['value'], 'I\x20Luv\x20South\x20China\x20Normal\x20University')) {

在前面找到的Textarea输入I Luv South China Normal University即可过关

Level4

四个按钮,每按一次就将button显示的值加到指示器上,并且当前button会产生新的数字。

打开Inspector,找到被隐藏显示的区域。

删掉style属性,即可看见隐藏内容。

将任意btn修改为1024,点击一次即可过关。

Level5

粗略地看下代码,发现是一个按键发声的钢琴程序。

level55.js中发现看起来像是答案的内容。

let arr_ans = [0x1, 0x1, 0x5, 0x5, 0x6, 0x6, 0x5, 0x4, 0x4, 0x3, 0x3, 0x2, 0x2, 0x1];

依次按下对应的数字键,即可过关。

顺便一提,敲出来的旋律是小星星,也和网页里的图片吻合。

Level6_0

先来说正常解法。

在网页偏右下的地方有一个Textarea,左边显示的内容原封不动地敲进去即可过关。

第二部分也是类似,不赘述了。

Level6_1

现在来说邪道解法。

第一次玩的时候没注意到那个Textarea,加上被邪道蛊惑心灵,于是直接走了邪道玩法。

首先在level66.js中找到过关时会执行的函数。

text22InChange = function() {
    var structture = {
        'kKSsu': function(_0x147d4c, _0x2fd784) {
            return _0x147d4c === _0x2fd784;
        },
        'qokWA': '7|5|8|6|2|1|3|0|4',
        'UcTHH': '61f76181c5d100b3',
        'eKSyF': 'post_form',
        'sRhcg': '04ad5938eaf0efb6',
        'cPaAT': 'textarea'
    };
    splatStack['push'](0x1);
    
    if (structture['kKSsu'](my_text22['value'], my_text['value'])) {
        var _0x31ae2e = structture['qokWA']['split']('|')
          , _0x84efb3 = 0x0;
        while (!![]) {
            switch (_0x31ae2e[_0x84efb3++]) {
            case '0':
                _0x41a986['submit']();
                continue;
            case '1':
                _0x41a986['appendChild'](_0x2e9287);
                continue;
            case '2':
                _0x2e9287['value'] = structture['UcTHH'];
                continue;
            case '3':
                document['body']['appendChild'](_0x41a986);
                continue;
            case '4':
                return;
            case '5':
                var _0x41a986 = document['getElementById'](structture['eKSyF']);
                continue;
            case '6':
                _0x2e9287['name'] = structture['sRhcg'];
                continue;
            case '7':
                splatStack['push'](0x32);
                continue;
            case '8':
                var _0x2e9287 = document['createElement'](structture['cPaAT']);
                continue;
            }
            break;
        }
    }
}
;

很显然代码被混淆过。先来推断一下逻辑,输入时检查输入内容是否符合答案,如果符合就提交。

上面函数里的if很明显就对应着判断输入的逻辑。

再看接下来的4行。(下面的structure是被我重命名之后的变量名,忘记原来的变量名了orz)

        var _0x31ae2e = structture['qokWA']['split']('|')
          , _0x84efb3 = 0x0;
        while (!![]) {
            switch (_0x31ae2e[_0x84efb3++]) {

_0x31ae2e是一个数组({7,5,8,6,2,1,3,0,4}),_0x84efb3是索引。

_0x31ae2e[_0x84efb3++]就是对数组进行遍历。

再结合case中的内容来看,就是以{7,5,8,6,2,1,3,0,4}的顺序来执行各个case来完成提交的过程。

要直接过关,只需要删去判断条件,再按顺序执行各个case中的代码即可。

以下为修改后的函数

text22InChange = function() {
    var structture = {
        'kKSsu': function(_0x147d4c, _0x2fd784) {
            return _0x147d4c === _0x2fd784;
        },
        'qokWA': '7|5|8|6|2|1|3|0|4',
        'UcTHH': '61f76181c5d100b3',
        'eKSyF': 'post_form',
        'sRhcg': '04ad5938eaf0efb6',
        'cPaAT': 'textarea'
    };
    splatStack['push'](0x1);
    
    var _0x31ae2e = structture['qokWA']['split']('|')
    , _0x84efb3 = 0x0;

    splatStack['push'](0x32);
    var _0x41a986 = document['getElementById'](structture['eKSyF']);
    var _0x2e9287 = document['createElement'](structture['cPaAT']);
    _0x2e9287['name'] = structture['sRhcg'];
    _0x2e9287['value'] = structture['UcTHH'];
    _0x41a986['appendChild'](_0x2e9287);
    document['body']['appendChild'](_0x41a986);
    _0x41a986['submit']();

}
;

拦截并修改请求后,在Console中直接执行text22InChange()即可过关。

Level7_0

输入表达式,会生成蓝色的函数图,与目标拟合程度足够高时即可进入下一关。

像这样:

题目数量不少,想靠我的数学知识全部写出正确答案基本是在做梦,因为后面有一些不做人的题目:

所幸所有答案的表达式都以明文的形式写在level77.js里面。

levels = ['x', 'x^2', 'x<0', '0.1/x', 'abs(x)', 'abs(x^3)', 'sin(10x)<10y', 'sin(PI*x)', 'exp(E*x)', 'x%0.2>y', 'sqrt(0.5-x^2)', 'x^2+x', 'x^2+y^2<0.5', 'ceil(x*10)/10', 'x^3+0.3*x^2-0.5*x-0.3', 'x^10+y^10<0.1', 'tan(x^2*3.8)', 'ceil(x*10)/10+floor(x*10)/10', 'min(x^2-x,x)', 'sin(x)^2+sin(y)^2<0.5', 'tan(x)^2+tan(y)^2<0.5', 'sin(10x+0.3sin(1000x))', 'log(E*x)', 'gamma(10x)/10', 'x\x20mod\x200.4', 'x^x', 'pow(x^3,x)', 'abs(sin(x))', '((2x)^2+(2y)^2-1)^3<(2x)^2(2y)^3', 'x*y<0', 'x*y<0.1', 'x<-0.2\x20or\x20x>0.2', '-0.8<x<0.2', '-0.2<x<y<0.2', '(abs(x)-0.5)^2+y^2<0.1', 'abs(sin(x^2*5))', 'max(x%0.2,sin(10*PI*x))', 'gamma(abs(x)*10)/10', 'max(sin(x*10)/10,cos(x*10)/10)', 'log(abs(x*5)-0.1)/5', 'x%(0.2)+sin(x*10)/10', 'max(0,x)', 'max(0.1x,x)', 'sign(sin(x*10))/10', 'atan(x*1.5)', 'tanh(x*1.5)', '1/(1+e^(-x*10))', 'sign(sin(x*1000))/2', '0.1sin(10x)+0.2sin(20x)+0.3sin(30x)+0.4sin(40x)', 'abs(x)+abs(y)<0.5', 'sin(10x)%1-0.5', '0.2*isPrime(ceil(x*20))', 'norm(cos(i*10x))/10', 'gcd(6,ceil(10*x))/10', 'sin(PI*(x+sin(x*1000)))'];

一一输入即可过关,最后提交flag即可。(这就没意思了)

Level7_1

正常解法2

直接在level77.js中找到flag

    'tNthG': '分(满分20000)。flag:\x201db8d352466b5e5b',

Level7_2

邪道解法

当时又是被邪道蛊惑心灵,想都没想直奔js代码orz

    function judge() {
    var _0x1e2712 = {
        'xOjFj': function(_0x41ee66) {
            return _0x41ee66();
        },
        'BaqMh': function(_0x3eee3a, _0xe0eaef) {
            return _0x3eee3a < _0xe0eaef;
        },
        'heHun': '1px\x20solid\x20green',
        'EiJaU': '1px\x20solid\x20red'
    };
    _0x1e2712['xOjFj'](reMes);
    // if (_0x1e2712['BaqMh'](R, dd)) {
        bu['disabled'] = ![];
        bu['style']['border'] = _0x1e2712['heHun'];
    // } else {
    //     bu['disabled'] = !![];
    //     bu['style']['border'] = _0x1e2712['EiJaU'];
    // }
}

找到judge()函数,直接将判断对错的部分注释掉,只留下结果正确的代码。

添加相应的Auto Responder规则,之后无论输入什么内容都可以进入下一关,像这样:

credit: Timothy Meinberg