巅峰极客 2020 WriteUp

发布于 2020-09-26  197 次阅读


好难好难好难。。。

Misc

签到

其实应该是个Web题来着,打开网页容器,如下图。
签到
是个游戏题,那么既然是签到,自然不会太难,所以要么是传分数返回flag,要么是js里就有flag。咱们看一下源码。


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <title>拼</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="photo">
        <img src="games/img/geek0/geek.png" class="selected" />
    </div>
    <div class="btn-group">
        <input type="button" value="开始游戏" />
    </div>
    <div class="g-box" id="J_layoutBox">
        <ul id="box"></ul>
    </div>
    <script>
        var config = {
            imgPath:'games/'
        }
    </script>
    <script src="game.js"></script>
</body>
</html>

有一个js,那么看一下js。

/*
    拼图小游戏
*/
var zIndex = 1;

window.onload = function() {
    var oPhoto = document.getElementById("photo");
    var aThumb = oPhoto.getElementsByTagName("img");
    var oBox = document.getElementById("box");
    var oLayoutBox = document.getElementById("J_layoutBox");
    var aLi = oBox.getElementsByTagName("li");
    var oInput = document.getElementsByTagName("input")[0];
    var i = 0;
    var imgPath = 0;
    var oDateStart = null;
    var aPos = [];
    var aData = [];

    for (i = 0; i < 15; i++) aData.push(i + 1);

    //缩略图
    for (i = 0; i < aThumb.length; i++) {
        aThumb[i].index = i;
        aThumb[i].onmouseover = function() {
            this.className += " hover"
        };
        aThumb[i].onmouseout = function() {
            this.className = this.className.replace(/\shover/, "")
        };
        aThumb[i].onclick = function() {
            for (i = 0; i < aThumb.length; i++) aThumb[i].className = "";
            this.className = "selected";
            imgPath = this.index;
            oBox.innerHTML = "";
            oInput.value = "开始游戏";
            createMask();
            aData.sort(function(a, b) {
                return a - b
            });
            GAME(false)
        }
    }

    //创建遮罩层
    function createMask() {
        if (document.getElementById('mask')) {
            return false;
        }
        var oMask = document.createElement("div");
        oMask.id = "mask";
        oMask.style.zIndex = zIndex;
        oLayoutBox.appendChild(oMask)
    }
    createMask();

    //游戏处理函数
    function GAME(ran) {
        //随机排列数组
        ran && aData.sort(function(a, b) {
            return Math.random() > 0.5 ? -1 : 1
        });
        //插入结构
        var oFragment = document.createDocumentFragment();
        var oLi, row, col;
        for (i = 0; i < aData.length; i++) {
            oLi = document.createElement("li");

            //使用background方式以减少http请求和减去图片切割步骤
            // var oImg = document.createElement("img");
            // oImg.src = config.imgPath + "img/geek" + imgPath + "/" + aData[i] + ".png";
            // oLi.appendChild(oImg);
            row = parseInt((aData[i] - 1) / 5);
            col = parseInt((aData[i] - 1) % 5);

            oLi.style.backgroundImage = 'url(' + config.imgPath + 'img/geek' + imgPath + '/geek.png)';
            oLi.style.backgroundPosition = '-' + col * 82 + 'px -' + row * 190 + 'px';
            oLi.setAttribute('data-index', aData[i]);
            oFragment.appendChild(oLi)
        }
        oBox.appendChild(oFragment);
        oBox.style.background = "url(" + config.imgPath + "img/geek" + imgPath + "/back.png)  no-repeat";

        //布局转换
        for (i = 0; i < aLi.length; i++) {
            aLi[i].index = i;
            aLi[i].style.top = aLi[i].offsetTop + "px";
            aLi[i].style.left = aLi[i].offsetLeft + "px";
            aPos.push({
                "left": aLi[i].offsetLeft,
                "top": aLi[i].offsetTop
            })
        }
        for (i = 0; i < aLi.length; i++) {
            aLi[i].style.position = "absolute";
            aLi[i].style.margin = "0";
            drag(aLi[i])
        }

        //拖拽函数
        function drag(obj, handle) {
            var handle = handle || obj;
            handle.style.cursor = "move";
            handle.onmousedown = function(event) {
                var event = event || window.event;
                var disX = event.clientX - this.offsetLeft;
                var disY = event.clientY - this.offsetTop;
                var oNear = null;
                obj.style.zIndex = zIndex++;
                document.onmousemove = function(event) {
                    var event = event || window.event;
                    var iL = event.clientX - disX;
                    var iT = event.clientY - disY;
                    var maxL = obj.parentNode.clientWidth - obj.offsetWidth;
                    var maxT = obj.parentNode.clientHeight - obj.offsetHeight;

                    iL < 0 && (iL = 0);
                    iT < 0 && (iT = 0);
                    iL > maxL && (iL = maxL);
                    iT > maxT && (iT = maxT);
                    obj.style.left = iL + "px";
                    obj.style.top = iT + "px";

                    for (i = 0; i < aLi.length; i++) aLi[i].className = "";

                    oNear = findNearest(obj);

                    oNear && (oNear.className = "hig");

                    return false
                };
                document.onmouseup = function() {
                    document.onmousemove = null;
                    document.onmouseup = null;
                    if (oNear) {
                        var tIndex = obj.index;
                        obj.index = oNear.index;
                        oNear.index = tIndex;
                        startMove(obj, aPos[obj.index]);
                        startMove(oNear, aPos[oNear.index], function() {
                            if (finish()) {
                                var iHour = iMin = iSec = 0;
                                var oDateNow = new Date();
                                var iRemain = parseInt((oDateNow.getTime() - oDateStart.getTime()) / 1000);

                                iHour = parseInt(iRemain / 3600);
                                iRemain %= 3600;
                                iMin = parseInt(iRemain / 60);
                                iRemain %= 60;
                                iSec = iRemain;

                                alert("flag{ed6033f8-8e9a-47c2-87aa-3c2c18abdc6b}\n\n\u7528\u65f6\uff1a" + iHour + "\u5c0f\u65f6" + iMin + "\u5206" + iSec + "\u79d2");
                                createMask();
                            }
                        });
                        oNear.className = "";
                    } else {
                        startMove(obj, aPos[obj.index])
                    }
                    handle.releaseCapture && handle.releaseCapture()
                };
                this.setCapture && this.setCapture();
                return false
            }
        }

        //找出相遇点中最近的元素
        function findNearest(obj) {
            var filterLi = [];
            var aDistance = [];

            for (i = 0; i < aLi.length; i++) aLi[i] != obj && (isButt(obj, aLi[i]) && (aDistance.push(getDistance(obj, aLi[i])), filterLi.push(aLi[i])));

            var minNum = Number.MAX_VALUE;
            var minLi = null;

            for (i = 0; i < aDistance.length; i++) aDistance[i] < minNum && (minNum = aDistance[i], minLi = filterLi[i]);

            return minLi
        }
    }
    GAME();

    //开始游戏
    oInput.onclick = function() {

        document.getElementById('mask') && oLayoutBox.removeChild(document.getElementById('mask'));
        oDateStart = new Date();
        oBox.innerHTML = "";
        this.value = "\u91cd\u65b0\u5f00\u59cb";
        GAME(true)
    };

    //判断是否完成
    function finish() {
        var aTemp = [];
        var success = true;
        aTemp.length = 0;
        for (i = 0; i < aLi.length; i++) {
            for (var j = 0; j < aLi.length; j++) {
                if (i == aLi[j]["index"]) {
                    // aTemp.push(aLi[j].getElementsByTagName("img")[0].src.match(/(\d+)\./)[1]);
                    aTemp.push(parseInt(aLi[j].getAttribute('data-index')));
                }
            }
        }
        for (i = 1; i <= aTemp.length; i++) {
            if (i != aTemp[i - 1]) {
                success = false;
                break
            }
        }
        return success
    }
};

//求两点之间的距离
function getDistance(obj1, obj2) {
    var a = (obj1.offsetLeft + obj1.offsetWidth / 2) - (obj2.offsetLeft + obj2.offsetWidth / 2);
    var b = (obj1.offsetTop + obj1.offsetHeight / 2) - (obj2.offsetTop + obj2.offsetHeight / 2);
    return Math.sqrt(a * a + b * b)
}

//碰撞检测
function isButt(obj1, obj2) {
    var l1 = obj1.offsetLeft;
    var t1 = obj1.offsetTop;
    var r1 = obj1.offsetLeft + obj1.offsetWidth;
    var b1 = obj1.offsetTop + obj1.offsetHeight;

    var l2 = obj2.offsetLeft;
    var t2 = obj2.offsetTop;
    var r2 = obj2.offsetLeft + obj2.offsetWidth;
    var b2 = obj2.offsetTop + obj2.offsetHeight;

    return !(r1 < l2 || b1 < t2 || r2 < l1 || b2 < t1)
}

//获取最终样式
function getStyle(obj, attr) {
    return parseFloat(obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj, null)[attr])
}

//运动框架
function startMove(obj, pos, onEnd) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        doMove(obj, pos, onEnd)
    }, 30)
}

function doMove(obj, pos, onEnd) {
    var iCurL = getStyle(obj, "left");
    var iCurT = getStyle(obj, "top");
    var iSpeedL = (pos.left - iCurL) / 5;
    var iSpeedT = (pos.top - iCurT) / 5;
    iSpeedL = iSpeedL > 0 ? Math.ceil(iSpeedL) : Math.floor(iSpeedL);
    iSpeedT = iSpeedT > 0 ? Math.ceil(iSpeedT) : Math.floor(iSpeedT);
    if (pos.left == iCurL && pos.top == iCurT) {
        clearInterval(obj.timer);
        onEnd && onEnd()
    } else {
        obj.style.left = iCurL + iSpeedL + "px";
        obj.style.top = iCurT + iSpeedT + "px";
    }
}

直接全文搜索一下flag,就能找到flag。

flag:flag{ed6033f8-8e9a-47c2-87aa-3c2c18abdc6b}

Reverse

virus

下载得到一个exe, 在ida里打开。
virus
可以发现输入的数据就是最后的flag。
第一次for循环是在分割输入的数据。

for ( i = 0; i < v12; ++i )
{
    if ( flag[i] == '-' )
    {
        v3 = v14++;
        *(&v6 + v3) = i;
    }
    if ( !v14 )
    {
        *(&v5 + i) = flag[i] - '0';
        if ( *(&v5 + i) > 9 || *(&v5 + i) < 0 )
            return 0;
    }
}

其中v6数组记录了-的位置,v14记录了-的个数,v5数组记录了还没出现-时的每一个数字。
然后循环结束后,有如下语句:

if ( v14 != 4 )
    return 0;

那么说明有4个-
然后第二次for循环如下:

for ( i = 1; i <= v14; ++i )
{
    v11 = *(&v6 + i) - *(&v6 + i - 1) - 1;
    if ( step[i] != v11 )
        return 0;
    strncpy(&road[200 * i], &flag[*(&v6 + i - 1) + 1], v11);
}

循环4次,每次算出v6数组数据前后的差值,然后和step数组进行匹配,最后将-直接的字符串存到road数组里。其中int step[6] = {0, 19, 25, 26, 28, 0}
也就是说flag格式为:flag{一串数字-19个字符-25个字符-26个字符-28个字符}
然后road[200]/road[400]/road[600]/road[800]分别为19个字符/25个字符/26个字符/28个字符
然后接着看下个for循环。

for ( i = 0; i <= 3; ++i )
{
    if ( check_flag((int)&global_map + 200 * *(&v5 + i), *(&v5 + i), &road[200 * (i + 1)]) )
    {
        puts("How about try again?");
        return 0;
    }
    if ( i == 3 )
        printf("Great! We will defeat it!!! your flag is flag{%s}", flag);
}

可见需要验证四个road数组数据,全部通过后即为flag,check_flag传入的参数分别为global_map[200*v5[0/1/2/3]]v5[0/1/2/3]road[200/400/600/800],其中v5数组即为之前存储的一串数字,查看一下global_map数组,可以发现存在global_map[0:199/200:399/400:599/600:799/800:999],然后global_map[0:199]又为空数据,所以猜测一串数字应当以1234构成,且有四位。
所以check_flag传入的参数分别为global_map[200/400/600/800]1/2/3/4road[200/400/600/800]
然后观察check_flag函数。
check_flag
可以发现处理a3传入的是个switch分支,所以合法传入应该是[wsad],所以猜测可能是个走路游戏,然后ws是上下走,改动v6,ad是左右走,改动v5,然后配合下面的20*v6+v5,猜测v6是行,v5是列,然后地图大小应该是20w10h,所以上面初始化v6、v5用的start数组和dword_403444数组,就是起始位置,v6[]={3,1,1,8}v5[]={2,2,11,3}
然后又因为下面是与a1核对,所以a1传入的global_map数组就是地图数据,去看一下global_map数组,以20w
10h分割,得到如下地图。

global_map[200]
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 73 2E 2E 2E 2E 2E 2E 2E 2E 2E 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 7C 00 00 00 00 00
7C 7C 64 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 7C 00 00 00 00 00
7C 7C 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
global_map[400]
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00
7C 7C 73 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 64 7C 7C 00
7C 7C 2E 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 2E 2E 7C 7C 00
7C 7C 7C 2E 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 2E 2E 7C 7C 7C 00
7C 7C 7C 7C 2E 2E 7C 7C 7C 7C 7C 7C 7C 2E 2E 7C 7C 7C 7C 00
7C 7C 7C 7C 7C 2E 2E 7C 7C 7C 7C 7C 2E 2E 7C 7C 7C 7C 7C 00
7C 7C 7C 7C 7C 7C 2E 2E 7C 7C 7C 2E 2E 7C 7C 7C 7C 7C 7C 00
7C 7C 7C 7C 7C 7C 7C 2E 2E 7C 2E 2E 7C 7C 7C 7C 7C 7C 7C 00
7C 7C 7C 7C 7C 7C 7C 7C 2E 2E 2E 7C 7C 7C 7C 7C 7C 7C 7C 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00
global_map[600]
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 2E 2E 2E 2E 2E 2E 2E 2E 73 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 2E 2E 2E 2E 2E 2E 2E 2E 2E 64 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
global_map[800]
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00
7C 7C 7C 2E 2E 2E 2E 2E 2E 2E 2E 2E 2E 7C 7C 00 00 00 00 00
7C 7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 00 00 00 00 00
7C 7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 00 00 00 00 00
7C 7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 00 00 00 00 00
7C 7C 7C 2E 7C 7C 7C 7C 7C 7C 7C 7C 2E 7C 7C 00 00 00 00 00
7C 7C 7C 73 7C 7C 7C 7C 7C 7C 7C 7C 64 7C 7C 00 00 00 00 00
7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 7C 00 00 00 00 00

根据上面的分析,0x73就是起始位置,0x64就是终点位置,然后结合一下以下语句:

if ( *(_BYTE *)(a1 + 20 * v6 + v5) != 0x2E )
    return 1;

可以知道0x2E就是路径,所以四张地图的路径分别是dddddddddsssssaaaaaaaaawwwsdsdsdsdsdsdsddwdwdwdwdwdwdwaaaaaaaaasssssssdddddddddwwwwwdddddddddsssss
又因为flag中的四个路径都有长度要求,所以按照19, 25, 26, 28的长度来排,就是4312,那么由于a2传入的就是之前的v5,也就是flag的首段数字,所以为了和地图一一对应,首段即为4312
最终拼接即可得到flag。

flag:flag{4312-wwwwwdddddddddsssss-aaaaaaaaasssssssddddddddd-dddddddddsssssaaaaaaaaawww-sdsdsdsdsdsdsddwdwdwdwdwdwdw}