Skip to content

Instantly share code, notes, and snippets.

@ourai
Last active July 24, 2018 07:32
Show Gist options
  • Save ourai/6239347 to your computer and use it in GitHub Desktop.
Save ourai/6239347 to your computer and use it in GitHub Desktop.
阿里巴巴集团 2014 校园招聘攻略

智勇大闯关第三季

8月20日,阿里校园招聘前端岗位的在线笔试将统一开始。在这之前,我们先玩一下热身赛吧! http://ued.taobao.com/quiz3/ 截至8月18日11:00之前成功通关并且表现优异的同学,将有机会收到我们的惊喜邮件!

以上是阿里巴巴集团校园招聘的某一条微博的内容。

虽然我早已不是学生,本着好奇心也要玩一玩此游戏!经过几个小时的奋斗,我看到了美女,但不知道那是不是 True Ending。也许很多人把游戏通关之后就不玩不去探索了,可我不一样!我玩游戏向来都是要尽量把所有隐藏要素都挖掘出来才算结束。

正是因为知道结果,才有可能去优化过程,一个工程师的职责难道不正是这个么?“如何自动化、智能地去过每一关”的想法让我的血液稍微沸腾了起来。又经过几个小时的编码及测试,自我感觉已经把“隐藏要素”挖得差不多了。现将各个关卡的过关要点及自动获取过关条件的 JavaScript 脚本放出来。

进入每个关卡后,按 F12 打开开发者工具,在控制台中粘贴脚本代码并执行就可以。

突破,带锁的门

解密码

打开控制台,在 DOM 树中就已经给出提示了——

调用 powder.blow() 显示指纹痕迹!

从代码的命名不难理解:撒粉。现实中提取指纹也是通过一种化学药剂。

这个方法需要执行多次才能够看清数字上的指纹,这时就需要把那几个数字排列组合一下挨个试了。不过这是一般人的做法,其实想进屋不止输入密码这一条路:

location.href = atob( document.getElementById("page").getAttribute("data-t") );

激光,前进的方向

"翻镜子"

通关的方法就是翻转镜子,让折射光线照到黑点。当光线照射到第一个黑点后,第二个黑点就显示出来了。

(function() {

    var pharaoh = getNode("pharaoh");
    var sacrifice = getNode("sacrifice");
    var sacrificeCoord = getDotCoord( sacrifice );
    
    ejaculate();
    
    // 射出
    function ejaculate() {
        var mirrors = document.getElementsByClassName( "mirror" );
    
        [].forEach.call( mirrors, magicMirror );
    }
    
    // 镜子魔法
    function magicMirror( mirror ) {
        setCssStyle( mirror, mirrorStyles( mirror ) );
    }
    
    // 镜子样式
    function mirrorStyles( mirror ) {
        var id = mirror.id;
        var position = mirrorPosition( id );
    
        return {
            "webkitTransformOrigin": "50% 0 0",
            "webkitTransform": "rotate(" + (id === "ma" ? -67.5 : 180) + "deg)",
            "left": position.left,
            "top": position.top
        }
    }
    
    // 镜子位置
    function mirrorPosition( id ) {
        var position;
    
        if ( id === "ma" ) {
            position = getMaCoord();
        }
        else {
            position = getMbCoord();
        }
    
        return position;
    }
    
    // 通过 id 获取节点
    function getNode( id ) {
        return document.getElementById( id );
    }
    
    // 获取样式
    function getCssStyle( node, ruleKey ) {
        return getComputedStyle(node, null)[ruleKey];
    }
    
    // 设置样式
    function setCssStyle( node, rules ) {
        for ( var key in rules ) {
            node.style[ key ] = rules[key];
        }
    }
    
    // 获取黑点坐标
    function getDotCoord( node ) {
        return {
            x: parseFloat( getCssStyle(node, "left") ) + parseFloat( getCssStyle(node, "width") )/2,
            y: parseFloat( getCssStyle(node, "top") ) + parseFloat( getCssStyle(node, "height") )/2
        };
    }
    
    // 计算 ma 镜子的图形中心坐标
    function getMaCoord() {
        var mirror = getNode("ma");
        var light = getNode("source");
        var coord_y = parseFloat( getCssStyle(light, "top") ) + parseFloat( getCssStyle(light, "height") )/2;
        var dot2ray = coord_y - sacrificeCoord.y;
    
        return {
            left: sacrificeCoord.x + dot2ray - parseFloat( getCssStyle(mirror, "width") )/2 + "px",
            top: coord_y + "px"
        };
    }
    
    // 计算 mb 镜子的图形中心坐标
    function getMbCoord() {
        var pharaohCoord = getDotCoord( pharaoh );
        var mirror = getNode( "mb" );
        var dot2dot = (sacrificeCoord.x - pharaohCoord.x)/2;
        var coor_x = pharaohCoord.x + dot2dot - parseFloat(getCssStyle(mirror, "width"))/2;
    
        return {
            left: coor_x + "px",
            top: pharaohCoord.y - dot2dot + "px"
        };
    }

})();

坐标,隐藏的线索

"二维码"

本关要点就是运用 HTML 源码注释中的那一大段数字字符串通过 canvas 完成二维码。

(function() {
    var qrdata = getQrData().split(" ");
    var ctx = document.getElementById("qr-canvas").getContext("2d");

    qrdata.forEach(function( code ) {
        ctx.fillRect.apply( ctx, code.split( "," ) );
    });

    function getQrData() {
        var data;

        [].forEach.call(document.body.childNodes, function( node ) {
            if ( node.nodeType === document.COMMENT_NODE ) {
                data = node.data.replace(/\r|\n/g, "");
            }
        });

        return data;
    }
})();

图案,疯狂的猜测

"猜图片"

将随机的几个图片是什么全部猜对即过关。

(function() {

    var map = {
        "T1eaRYFftbXXcuU8sK-225-225.jpg": "ubuntu",
        "T1ifFNFklcXXbMbfEI-194-279.png": "sprites",
        "T1hspWFgxbXXbufJvw-466-303.jpg": "wordpress",
        "T15vVUFgNcXXX.oZHL-401-270.png": "grunt",
        "T1FPXRFn8fXXcHpdDI-474-246.png": "less",
        "T1e9FHFkhgXXbwWSnH-578-406.jpg": "php",
        "T1J0JVFi4aXXXoqkr9-518-202.png": "npm",
        "T1UB4UFg8dXXb1KC.o-586-89.jpg": "stackoverflow",
        "T1JGxUFkJeXXbxRAc_-557-264.jpg": "w3",
        "T16whWFdBXXXcpN87C-71-212.png": "v",
        "T11_JTFa8gXXX5c4Pp-356-192.png": "github",
        "T13ghQFdtgXXaaz0ft-569-116.png": "underscore",
        "T1VcpUFaFeXXb0Z3E6-448-391.png": "sublime",
        "T1ZRhTFfdeXXcRZNZO-441-244.png": "jade"
    };

    var img = node("J_pic");
    var btn = node("J_btn");

    img.onload = function() {
        findAnswer();
    };

    btn.addEventListener( "mouseover", handler, false );

    function handler( e ) {
        var answer = map[ img.src.split("\/").pop() ];

        if ( answer === undefined ) {
            console.log( "data map 中没有该图片对应的答案,请自行查找答案。" );
        }
        else {
            node("J_text").value = answer;
        }
    }

    function node( id ) {
        return document.getElementById( id );
    }

    function findAnswer() {
        handler.call(img);
        btn.click();
    }

    findAnswer();

})();

寻找,无尽的房间

 "闯房间"

把 URI 中的房间号 room 替换成页面上“NEXT ROOM”中显示的。每页都会给出一点点文字提示信息,总共四十多页,如果完全靠人工获取的话,估计你要成鼠标手啦!为了预防疾病,请用下面的代码:

(function( $ ) {

    var stack = [ $("#message").text() ];
    var pathname = location.pathname;

    (function trace( room ) {
        if ( !!room ) {
            var search = location.search;
            var count = stack.length;

            if ( count === 1 ) {
                enterlLog( search.match(/room\=(\d+)/)[1], count, room );
            }

            $.ajax({
                url: pathname + search.replace(/room\=\d+/, function() {
                        return "room=" + room;
                    }),
                dataType: "html",
                success: function( res ) {
                    var nextRoom = $("#next-room", $(res)).text();

                    stack.push( $("#message", $(res)).text() );
                    enterlLog( room, stack.length, nextRoom );

                    delay(function() {
                        trace( nextRoom );
                    }, 500);
                }
            });
        }
        else {
            console.log( "经过重重困难险阻终于把所有线索收集齐了,将它们放到一起之后,原本上面空无一物的白纸突然发光,好像有什么要显现出来一样..." );

            delay(function() {
                var q = quiz( stack.join("") );

                console.log( "“" + q.msg + "”——如来" );

                delay(function() {
                    console.log( "“......”" );

                    delay(function() {
                        console.log( "“尼玛,你耍我?!”取经人赞美道。" );

                        delay(function() {
                            console.log( "“5 秒后会将你传送到 " + q.url + " 接受最后一次试炼,通过之后便得真经...”在白纸上方的空中显示道。" )

                            delay(function() {
                                location = q.url;
                            }, 5000);
                        }, 3000);
                    }, 3000);
                }, 5000);
            }, 5000);
        }
    })( $("#next-room").text() );

    function quiz( str ) {
        return {
            msg: str.substring( 0, str.indexOf("好吧") ),
            url: str.substring( str.indexOf( pathname ) )
        };
    }

    function delay() {
        setTimeout.apply( window, [].slice.call( arguments, 0 ) );
    }

    function enterlLog( room, count, nextRoom ) {
        var msg;

        if ( count === 1 ) {
            msg = "你在房间 " + room + " 中找到了第 1 条获得真经的线索,上面写着“" + stack[0] + "”。";
        }
        else {
            msg = "你从房间 " + room + " 破门而入,找到了第 " + count + " 条获得真经的线索,居然是块白纸!!!";
        }

        if ( !!nextRoom ) {
            msg += "并且发现旁边有个标记提示到房间 " + nextRoom + " 去...";
        }
        else {
            msg += "并且这回连房间提示都没有了?!?!?!";
        }

        console.log( msg );
    }

})(jQuery); 

消除!最后的任务

"消指纹"

控制台中的提示——

注意 cover

看到这个之后,一般都会立刻到 DOM 树中查找 id 或者 classcover 的节点,并且理解到也许是利用 cover 让指纹的图片无法被看到,正常人都是这么想的。

在页面上除了指纹图片,还有更为明显的控件——文本框和提交按钮!这时大家都会明白是需要通过提交什么东西来达到目的。这时就要用到“伪·跨站攻击(XSS)”的技术手段了。

<style>
    .cover {
        position: absolute;
        left: 588px !important;
        top: 340px !important;
        z-index: 9999999999999999;
        background: wheat;
        opacity: 1 !important;
    }
</style>

其实还有个不按正常套路过关的方法,这个方法跟最开始开密码锁时的非主流方法是一样的:

location.href = atob( document.getElementById("page").getAttribute("data-p") );

X,新的任务...

"任务完成"

经过长期奋战,千辛万苦之后只换得一个美女的背影,是不是很想上去拍她屁股一下啊?没关系,尽情地去实践你的想法吧!

@shenwa12
Copy link

var c = document.getElementById('qr-canvas').getContext('2d');
var s= "请自行复制源码中一大段注释中的数字字符串";
var a=s.split(' ');
alert(a.length);
for(i=0;i<a.length;i++){
var b=a[i].split(',');
c.fillRect(b[0],b[1],b[2],b[3]);
}

@ourai
Copy link
Author

ourai commented Aug 15, 2013

@shenwa12 不优雅。

@liyao
Copy link

liyao commented Aug 15, 2013

nice job!

@xydiva
Copy link

xydiva commented Aug 16, 2013

学习来了~

@fakefish
Copy link

虽然昨天中午就过关了,但好像过的不优雅=。=

@yuanyuanlife
Copy link

原来提交框还可以 xss 。。。我说呢 干嘛用的

@599316527
Copy link

最后一关我提交的是script,结果被Chrome自动拦截了

@mariodu
Copy link

mariodu commented Aug 16, 2013

用base64的界面,都可以用Base64.decode进行解码

@mariodu
Copy link

mariodu commented Aug 16, 2013

另外,要是自动化的话,无尽房间那个的解法,第一次请求的room和t的传参可以通过url分析出来,实现完全自动化。

@mariodu
Copy link

mariodu commented Aug 16, 2013

哦,看错了,以为你那段文字是说那个下面代码的。。。上条可以无视。

@ourai
Copy link
Author

ourai commented Aug 16, 2013

@bokeyy 是的,每次提交后会检测指纹图片是否有被遮挡。

我昨天在写脚本的时候,想用 <script> 注入了,但是抛异常了。


@599316527 没错,真正的跨站攻击被拦截掉了。


@mariodu Base64 是淘宝提供的, atob 是原生的。

@lauigi
Copy link

lauigi commented Sep 13, 2013

@ourai Base64是原生的,大概写反了?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment