Hackergame 2021 Writeup

开篇废话

又是新的一年,也已经大二了(虽然不知道在学校学到了什么),不过不影响我参加这次比赛。

今年的成绩还算不错 有时间卷

score
弥补了前年画的饼

题目

签到

为了能让大家顺利签到,命题组把每一秒的 flag 都记录下来制成了日记本的一页。你只需要打开日记,翻到 Hackergame 2021 比赛进行期间的任何一页就能得到 flag!

没什么好说的,时间戳嘛,不懂的话自行查看 ISO_8601

sign-1

进制十六——参上

也没什么好说的,找个 ocr 软件(比如 Google lens)提取左边的 16 进制文本,人肉输入好麻烦。

sign-google lens

然后粘贴到 0xED (macOS) 之类的16进制编辑器软件
sign-compare
即可

flag{Y0U_SH0U1D_kn0w_H0W_t0_C0nv3rt_HEX_to_TexT}

去吧!追寻自由的电波

原始音频无法使用耳朵手动识别,这里我使用了 fl studio 瞎调,调到了个 MUL 参数,往右滑动就正常了,还有很多软件应该都可以调,我 Mac 里面刚好有 fl studio 才用的 我的音乐梦啊,嘿,嘿嘿,嘿嘿嘿🤤

radio-fl studio
(后面发现 MUL 并不需要拉到最右,拉到最右声音有点难听)
修正后的音频参考

可以听见这边的电波在报菜名(一堆单词) 听力考试现在开始:

foxtrot
lima
alpha
golf
leftbracket {
papa
hotel
oscar
november
echo
tango
india
charlie
alpha
bravo
rightbracket }

不过把所有菜名输入进去也是不能拿到 flag 的,后面搜索了下可以知道菜名的内容都来自这本书
然后这个书的封面是竖着 A-Z !

radio-compare

于是 flag 就是

flag{phoneticab}

最后补充:官方 writeup 解释了,这些单词是 北约音标字母(国际无线电通话拼写字母)

猫咪问答

这个问题也是每年例行的 都出到 Pro Max 了

q1

2017 年,中科大信息安全俱乐部(SEC@USTC)并入中科大 Linux 用户协会(USTCLUG)。目前,信息安全俱乐部的域名(sec.ustc.edu.cn)已经无法访问,但你能找到信息安全俱乐部的社团章程在哪一天的会员代表大会上通过的吗?

那闭着眼睛打开 wayback machine 就可以了。

https://web.archive.org/web/20170615000000*/sec.ustc.edu.cn

https://web.archive.org/web/20181004003308/http://sec.ustc.edu.cn/doku.php/codes

本章程在 2015 年 5 月 4 日,经会员代表大会审议通过。

20150504

q2

中国科学技术大学 Linux 用户协会在近五年多少次被评为校五星级社团?

https://lug.ustc.edu.cn/wiki/intro/

为了表彰其出色表现,协会于 2011 年 5 月被评为中国科学技术大学优秀学生社团,于 2012 年 5 月、2013 年 5 月及 2014 年 5 月分别被评为中国科学技术大学四星级学生社团,并于 2015 年 5 月、2017 年 7 月、2018 年 9 月、2019 年 8 月及 2020 年 9 月被评为中国科学技术大学五星级学生社团。

5 ¿

官方 write up 说明了:

所以答案是 4 年?这可不一定!请看页面下方更新时间:

更新时间: 2021 年 5 月 20 日

基于往年的评选时间,可以推断页面没有及时更新。那么怎么证明 2021 年 USTCLUG 是五星社团呢?搜索「2021 中国科大 五星级社团」,可以找到这个页面:我校举行2021年学生社团游园会。其中的图片证明了 2021 年 USTCLUG 也是五星级社团,所以答案为 5。

q3

中国科学技术大学 Linux 用户协会位于西区图书馆的活动室门口的牌子上“LUG @ USTC”下方的小字是?

以关键字 活动室 进行搜索

ustclug-board

https://lug.ustc.edu.cn/news/2016/06/new-activity-room-in-west-library/

Development Team of Library

q4

在 SIGBOVIK 2021 的一篇关于二进制 Newcomb-Benford 定律的论文中,作者一共展示了多少个数据集对其理论结果进行验证?

搜索,可以得到这个pdf,然后在 212-215 页之间。

然后目测,论文大概有 14 张图,那么从 14 开始向下穷举即可。其实是看不懂而已

13

q5

不严格遵循协议规范的操作着实令人生厌,好在 IETF 于 2021 年成立了 Protocol Police 以监督并惩戒所有违背 RFC 文档的行为个体。假如你发现了某位同学可能违反了协议规范,根据 Protocol Police 相关文档中规定的举报方法,你应该将你的举报信发往何处?

惯例的愚人节笑话,我们找到对应 rfc 为 rfc8962

Reporting Offenses
Send all your reports of possible violations and all tips about
wrongdoing to /dev/null. The Protocol Police are listening and will
take care of it.

/dev/null

卖瓜

和前年的达拉崩吧差不多,溢出
不过这次大数溢出需要构建刚好 = 20 才能 ok
这是我输入的例子:

9 1E19

9 1.024819115E18

9 206086200

9 1

# 第二轮

9 1E19

9 1.024819115E18

9 206,086,200

6 3
9 1

恭喜你逃过一劫!The flag is: flag{HUAQIANG!HUAQIANG!_3d70c4c6f9}

透明的文件

可能是在 cmd.exe 等劣质终端中被长期使用的原因,这个文件失去了一些重要成分,变成了一堆乱码,也不会再显示出 flag 了。

然后看了下,很明显就是要我们补全文件里面的格式(ANSI)

我们先打开文件看看,看这个 [39m 很不爽,先加个换行。

ansi-1

我们照着 ANSI 规范来补补。
这里我没有看 ANSI 的 rfc 之类的,直接看了本地有的。

neofetch > ansi-o

ansi-2
可以看到 格式中是用 ``「ESC」而不是 「空格」。

继续替换 ansi-3
[ 之间在加 ``(不是空格 是「esc」)

ansi-4
然后七彩的 flag 就显示了

flag{abxmiohkalmciwsayfiglet}

旅行照片

一张图里面叫我们回答五个问题
不多说了,一张图解决
travel-1

然后五个问题也很容易知道了:

Q1: 该照片拍摄者的面朝方向为?
A1: 东南

Q2: 该照片的拍摄时间大致为?
A2: 傍晚(17:00 左右)

Q3: 该照片的拍摄者所在楼层为?
A3: 14

Q4: 该照片左上角 KFC 分店的电话号码是?
A4: 0335-7168800 (大众点评/百度地图 请)

Q5: 该照片左上角 KFC 分店左侧建筑有三个水平排列的汉字,它们是?
A5: 在 bili 找圣地巡礼视频(图里有写 BV 号,百度全景也看得到,不过当时 KFC 还没建好)

然后那个 KFC 原来是蓝色,这玩意不应该是青色(Cyan)么,当时搜索什么结果都没有差点放弃。
travel-cyan

flag{D0n7-5hare-ph0t05-ca5ua11y}

FLAG 助力大红包

这道题也还算简单,主要是 rate limit 比较麻烦

  1. 每个用户只能够助力一次。为了建设世界一流大砍刀平台,活动要求位于同一 /8 网段的用户将会被视为同一个用户。(比如 IP 地址为 202.38.64.1 和 202.39.64.1 将被视为同一用户。)达到助力次数上线后,将无法再帮助好友助力。我们使用前后端方式检查用户的 IP 。

是兄弟帮我砍一下

点击帮忙助力,抓到 POST 包

curl 'http://202.38.93.111:10888/invite/89ce2c8a-ec4c-4c79-b2f2-aada0826fbb2' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Cookie: 你的' \
--data-raw 'ip=随机' \
--compressed

然而返回这个:

失败!检测到前后端检测 IPv4 地址不匹配,前端地址: 1.1.1.1 ,后端地址: 118.x.x.x 。疑似地址伪造!

一看就知道还要加个 X-Forwarded-For 头,最后成品脚本:

const { default: axios } = require("axios");
let uuid = '93ea1023-a57d-41ed-809e-8b1b4bc4c515'
pdd(0)
async function pdd(i) {
    if (i > 255) {
        console.log('bye')
        return
    }
    let ip = `${i}.1.1.1`
    let d = await axios({
        url: `http://202.38.93.111:10888/invite/${uuid}`,
        method: 'post',
        headers: {
            'X-Forwarded-For': ip
        },
        data: 'ip=' + ip
    })
    let data = d.data.split('<div class="col-sm-8 mx-auto mt-2 justify-content-center container">')[1].split('<div class=row>')[0]
    // rate limit
    if (data.includes('太快了')) {
        setTimeout(() => {
            pdd(i, true)
        }, 2000)
    } else {
        setTimeout(() => {
            pdd(i + 1)
        }, 100)
    }
    console.log((data.trim().split('\n')[1]).trim())
}

Amnesia

轻度失忆

经过几次尝试,const / printf(“xxx”) 会被直接洗掉,变量不会存在 .data / .rodata 里面

int puts(const char *s);
int main()
{
    char a[13] = "Hello, world!";
    puts(a);
    return 0;
}

「记忆清除」的 .text 都没了,扭曲,不会做(((

图之上的信息

Graphql 题目 我还是不懂 Graphql 能对我的几个垃圾项目带来什么

一开始我照猜 猜到了 user 表,但是邮箱列(privateEmail)猜不到,后面参考了 这篇文章 获得表结构然后解决。

拿表名:
grapql-get_table
拿结构:
grapql-get user schema
获得 flag:
grapql-get flag

{
    __schema {
        types {
            name
        }
    }
}

{
    __type(name: "GUser") {
        name
        fields {
            name
            type {
            name
            kind
            }
        }
    }
}

{
    user(id: 1) {
        privateEmail
    }
}

加密的 U 盘

前情概要:现在的市面上主流加密方案(BitLocker / FileVault / LUKS)都是有个 masterkey 的,用 masterkey 可以直接解密(比如 Windows / macOS 在加密的时候都会吐给你恢复密钥,如果为了安全,千万不能交给 Microsoft / Apple 托管密钥!)

第二天只是改了密码,并没有重新加密这块盘(或者换掉 masterkey),用旧盘(day1)的 masterkey 即可解密第二天的。

另外参考了这篇文章

工具准备:

  • Linux 任意发行版(大概)
  • cryptsetup
  • kpartx (arch: multipath-tools)
  • unzip

挂载 day1

解压 lukspwd.zip 里面有两个文件,分别是 day1.img 还有 day2.img 其中 day1.img 是已知密码的,先 fdisk 看看信息:

fdisk -l ./day1.img
Disk ./day1.img: 20 MiB, 20971520 bytes, 40960 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: E1D1730D-1029-44A4-898B-FEBC77E7884F

Device      Start   End Sectors Size Type
./day1.img1  2048 40926   38879  19M Linux filesystem

好像没啥,直接挂载吧

先用 kaprtx 拆分区出来(losetup 也 ok):

kaprtx -a ./day1.img

默认分区是在 /dev/loop0

再 fdisk 看看

fdisk -l /dev/loop0

/dev/loop0p1 2048 40926 38879 19M Linux filesystem

挂载 day1 分区测试

echo suijiguocheng123123 | cryptsetup open /dev/mapper/loop0p1 day1
mount -o ro /dev/mapper/day1 /mnt
ls /mnt

lost+found 随机过程.txt

警告,如果是自己的系统想要加密,绝对不能 echo xxx | 形式,会在 xxsh_history 看到明文密码!!!!!!

挂载成功,文件正常显示,可以进行下一步。

卸载磁盘:

umount /mnt
cryptsetup close day1
kpartx -d ./day1.img

提取 masterkey

现在可以尝试 dump day1 的 masterkey 了

echo "suijiguocheng123123" | cryptsetup luksDump --dump-master-key /dev/mapper/loop0p1

LUKS header information for /dev/mapper/loop0p1
Cipher name: aes
Cipher mode: xts-plain64
Payload offset: 32768
UUID: e9a660d5-4a91-4dca-bda5-3f6a49eea998
MK bits: 512
MK dump: be 97 db 91 5c 30 47 ce 1c 59 c5 c0 8c 75 3c 40
72 35 85 9d fe 49 c0 52 c4 f5 26 60 af 3e d4 2c
ec a3 60 53 aa 96 70 4d f3 f2 ff 56 8f 49 a1 82
60 18 7c 58 d7 6a ec e8 00 c1 90 c1 88 43 f8 9a

MK dump 就是主密钥,我们使用 16 进制工具保存成文件 并不会在 Linux 直接保存

echo "suijiguocheng123123" | cryptsetup luksDump --dump-master-key /dev/mapper/loop0p1 | grep -A 3 'MK dump' | sed -e 's/MK dump://g' -e 's/\s//g' | xxd -r -p > masterkey.bin

现在 masterkey 就已经成功提取了

挂载 day2

kpartx -a ./day2.img
# 如果之前卸载了磁盘,那么这边应该是 loop0p1
cryptsetup open /dev/mapper/loop1p1 --master-key-file ./masterkey.bin day2
mount -o ro /dev/mapper/day2 /mnt
ls /mnt
# flag.txt  lost+found
cat /mnt/flag.txt
# flag{changing_Pa55w0rD_d0esNot_ChangE_Luk5_ma5ter_key}

于是这题就解出来了,所以大家不要认为换密码就是安全了(((

flag{changing_Pa55w0rD_d0esNot_ChangE_Luk5_ma5ter_key}

赛博厨房

感觉就是 海龟 (pc logo) 就是给指令让机器人去做就是了

Level0

向右 2 步
拿起 10 个物品
向下 1 步
向左 2 步
放下 10 个物品
向上 1 步
向右 1 步
拿起 10 个物品
向下 1 步
向左 1 步
放下 10 个物品
向上 1 步

Level1

向右 1 步
拿起 73 个物品
向左 1 步
向下 1 步
放下 1 个物品
向上 1 步
如果手上的物品大于等于 1 向上跳转 3 行

Level 2+ 似乎会随机出题,不做了(还是太🥬了)

阵列恢复大师

Raid 日常爆炸,重建需要知道磁盘的顺序,组成阵列的方式,祈祷不是盘炸了

这道题我是使用工具手动猜顺序的,有空话也许还需要学习 mdadm 的办法,或者找些其它自动化工具。

准备 Diskgenius

RAID5

在 DiskGenius 加入 所有的 img 文件,然后打开,然后点击组建虚拟 RAID:
r5-1
然后就是猜 RAID 类型和数据了,这里最后读的到文件的排列方式和类型如图所示:
r5-2
觉得重组成功后,我们点击工具里面的 扇区复制(保存为 。img文件)
然后在 Linux 里面 mount 或者其实文件直接复制出来也 ok
r5-getflag

flag{a18325a1ec0f58292908455c2df8ffcd}

minecRaft

看上去是 web 题,那么肯定可以不通过通过游玩 mc 获得 flag 的。
打开有反调试器的,循环 debugger 中,不过不影响,然后我的 mac 风扇直接开始起飞了 我的配置很旧,可能会响,你忍一忍
首先看了下 index.html

// ...省略
// 反调试器
setInterval(() => {
    (function (a) {
        return (function (a) {
            return Function('Function(arguments[0]+"' + a + '")()');
        })(a);
    })("bugger")("de", 0, 0, (0, 0));
}, 1000);
// ...省略
function printcinput() {
    let content = document.getElementById('spann');
    // 第一个字是 M
    // 实际效果,踩了 M 后第二个红石灯就会亮了
    if (cinput[0] === 'M') {
        if (pressplateList[64].status === false) {
            pressplateList[64].TurnOn_redstone_lamp();
            pressplateList[64].status = true;
        }
    }
    // 踩32次压力板后触发判断
    // 如果是判断是真的,那么触发并且点亮第三个红石灯
    // 然后庆祝
    if (cinput.length >= 32) {
        let tbool = gyflagh(cinput.join(''));
        if (tbool) {
            pressplateList[65].TurnOn_redstone_lamp();
            content.innerText = 'Congratulations!!!';
            return;
        }
        cinput.length = 0;
    }
    content.innerText = cinput.join('');
}
// ...省略

很明显 函数 gyflagh 的结果就是 flag

而这个函数是在 /jsm/miscs/flag.js 似乎可以脱离出来直接运行,可以看到代码混淆了。
我们人肉去掉点混淆(干点变量,太长的换成几个字符什么的,替换字符串为真实值),搜索下关键词在 GitHub 看看有没有现成的代码可以抄,然后找到了((

repo: eyeos/extern/js/security.js

跟原版对比了下,看上去我们要补全 decrypt 函数。

并且由对比可以知道,LongToStr4 被换成了 LongToBase16
mc-and-eyeos-compare
(截图中的为稍微去掉了点混淆并且补全了 decrypt )

经过不断尝试(console.log加满),最后补全成功了:

String['prototype'].encrypt = function (password) {
        var v = new Array(2), k = new Array(4), s = "", i;
        plaintext = escape(this);
        for (var i = 0; i < 4; i++) k[i] = Str4ToLong(password.slice(i * 4, (i + 1) * 4));
        // 加密,四位一组,每次加密两组,所以是 8
        for (i = 0; i < plaintext.length; i += 8) {
            // str 到 Long
            v[0] = Str4ToLong(plaintext.slice(i, i + 4));
            v[1] = Str4ToLong(plaintext.slice(i + 4, i + 8));
            // 执行加密函数
            code(v, k);
            // 最后 Long 转成 Base16,可以发现位数翻倍了。
            s += LongToBase16(v[0]) + LongToBase16(v[1]);
        }
        return s
}, 
String['prototype'].decrypt = function (password) {
        var v = new Array(2), k = new Array(4), s = "", i;

        ciphertext = escape(this);

        for (var i = 0; i < 4; i++) k[i] = Str4ToLong(password.slice(i * 4, (i + 1) * 4));
        // 由于 encrypt 函数返回的是base16后的(8 位一组),所以我们这边要变成16(2组)
        for (i = 0; i < ciphertext.length; i += 16) {
            // 把 Base16 转回 long
            v[0] = (Base16ToLong(ciphertext.slice(i, i + 8)));
            v[1] = (Base16ToLong(ciphertext.slice(i + 8, i + 16)));
            // 解密,要从 eyeos/extern/js/security.js 抄过来
            decode(v, k);
            // 最后 Long 转回 str
            s += LongToStr4(v[0]) + LongToStr4(v[1]);
        }
        return s
    }

另外 flag.js 也可以完整地在 Node.js 里面跑,不需要修改什么

我的修改后的代码可以在 这里 找到

这道题有这几个难点:

  • 混淆(看着代码无从下手)
  • 反调试器(可能一些人看到循环 debugger 就被吓跑了)
  • 魔改了代码(即使找到了原始代码也只能解出乱码)
  • LongToBase16 过程中 位数翻倍了,如果尝试照着原版 decrypt 脚本改的话,还是解密不了(我也被坑了)

密码生成器

我们先观察下展板的网页,看看出题者给我们留下了哪些信息,然后把这些信息收集起来:
generator-web

  • 管理员用户名为 admin
  • 密码组合是 大小写字母 + 数字 + 特殊符号
  • 网页多次提到了时间
  • 管理员注册时间为 2021-09-22 23:11

于是很容易联想到密码是和时间有关系的。

这道题是有个典故的,今年卡巴斯基一个密码生成功能被曝光是以时间为 salt 的
新闻链接
确定了大概怎么做后,我们可以手动收集某个时间段的密码了(手动点击):

这样经过 120+次尝试后,终于拿到了 flag

generator-code
拿到 flag 时间点为 2021-09-22 23:10:59,密码为 $Z=CBDL7TjHu~mEX
写的简单固定时间脚本参考(需要管理员权限运行):

const { writeFileSync, readFileSync } = require('fs')
const exec = require('util').promisify((require('child_process')).exec)
let i = readFileSync('i').toString()
writeFileSync('pin.cmd',`time 23:10:${i}
exit`)
writeFileSync('i',(parseInt(i) + 1).toString())
console.log('current',i,'next',parseInt(i) + 1)
setInterval(() => {
    exec('start pin.cmd')
}, 500)

setTimeout(() => {
    process.exit()
}, 1650)

其它秒数参考(从23点10分开始)

EE"kK6zGXVfiZhPT
kdB)FRFbXFFMRtP0
$XN1@b,]NjKBuHOH
M81hPdRA,QcAALd5
MAfF2lzDQPWEFQK*
AH75WGl=A7FBDlFi
UUWHSFBO0MeaC;Ck
PA86I#WHdunAIMwD
NKaSim%rWKMR0$SJ
I.JFFBXdzUm&5$Kb
8LHCBvWQH-aITNAg
jTTCDgS]NouwJYl2
IhGqCAYZ*4EeJULM
AjFBHF4#KbBac~G;
L,MnJRLC1W_fDyHI
Bx8$XtEH,n]oBusj
AdLf3PG'pHhFGVPC
4HiPPp~Qj586EwJa
BF'Dj^G0BJfGFOVA
c3#(PNC%HBJiFzOS
C^CFcdgFGLMDATC4
KD&LnEsgJNReJ7Ye
(FLLJGRoiWUd^fM1
~5bPYuB31Sn6JJDO
BaSGXDLpF=S2vKIK
PPAJDHDdu]Ga1CK3
UZ7qcoO9QHFYfC#X
ZDAuFCcA76i@`9Qq
GNBH^QBdDdlL(rI3
ORILbBNvCFyZHq"1
4HQfJ/PBoIMHAmt1
^{VR*ZPfZBEMYp2h
~AFb&D!gEwWD1DHC
T8ExNAP}PbEjUKDC
A(EA!wnXSMUlGC2E
:dGdMYJKR3KKArBG
CUGFAhD3EDBBM^EC
FA@5A~AyEseALJNC
UeOAQyLE2sF{K6jF
U@Th%aoPJbPeEVM1
BDnQYUMPeDsc3g"B
4FDn4ATQiAH#KY9B
RDyeI59oSUG1xK^G
FA@5A~AyEseALJNC
YIRFISB~4FCDpErQ
ZTA9KCtCNAM'^Coa
2xGrMX~V@O*D4ZCg
#dHIshAbM5mPVk^I
OMDH`YZW8XVtBGlM
UeOAQyLE2sF{K6jF
uGXAIaRN[xPrP0PA
XyD=yfl)dL5pvhq#
$Z=CBDL7TjHu~mEX
~MBXa9VLudcLB>hD
KUjIBpEBGCIhN#2f
BuAdI%J1XJRLozMd
AaCFFT{Ak1oWxF&M
Q9wUh-GLfBCaBJ3N
g7ELNM%IgOs9ESWx
《正确 flag》
/sD8qMrmII7Ex$Hk
YtbZQA{_BF2aJ}NE
HH2_JHH$CLVGacDC
cCC^Q9LtUSBqgGUA
iowOKEe@Db0Lb_mV
{BYRBYX%A4IkANtn
Bl+P8XqyLmeOR~H+
EFXH"hLFohDA>6!B
JrICDVG0ACS@Ptbv
P7DD1GBBKn*zsQKC
IdDjCOTAVa0/2GAI
@(h65JEXpG0N{Gpg
$#OKnP(L5RcUtI+Y
CLbCOFLM1keT_KKz
U~WO4jEpGBlMS{TC
N^RaeLCmMPAGR9E>
HI2GhFSJCSX7ALv~
1SIpEeFFE`MC)BJL
giP2I*bfNV_pFQ)O
HHAFET!LLLE4sAAq
~F-SOQP3BZIamb5V
JMEvcAL1Ap(vSHLJ
QrzR2UEOIiQZA&NZ
^QBHI1ADoJOISGCj
7eB)CFngcGRBGFdS
g8JEyCPpP3hV!tJH
J}LPD6rFD!HqABKa
eC-FNPUNYFAEA1mc
1Er1XnkL!NPBjwcM
RRJO~HU7HBCMABPe
0RMYKCDTK!MCIIxK
kGMbG5PrASGCW_yE
t5nUCX@A%fDDgM!A
BCJER9VAGON[PLo$
\ElE40sMISBDUlEJ
x6iA{7AK4DJQKABB
TKUn;AE3HE1S`N0p
M(2KHSe^NYHExC9T
N-HSaNAYy-GABn2B
lPcQT!D0gLAThxXZ
Q*{PQMSjIRz5N3HP
xKNK+HML9rfS3QYL
PPiGILcTEOs0DT`C
FJZCK$DqsAAErTw2
FJZCK$DqsAAErTw2
RPIFuAZNPCK(_9C]
LF6D^aDAVXc"DEHj
L$QPQI@lR7RCAXJp
THQvZVLQMK0FN#Ll
3fMA6XOjO-KPscYa
RT)UEBlBKNnCva1D
C!PG6ImFSLA2PEaz
w6=BUBTKjIiTscM1
JIEzfX)1ufQBhPfU
s&Fa6&MwcVcNPC~i
<LPYBUAzIlaEp6KO
gAJ`NAjeq13EPVWX
QIK3NxYLNAC)aHPh
mjff8%AEAxB*!4MW

即使拿到了 flag 还是高兴不起来(不会逆向的苦逼人力操作)

失败的逆向

所以我并没有逆向成功
下载 generator.exe 可以发现是 UPX 加壳了,我们先解压,解压后有 15M+了
然后我使用了免费开源的 ghidra 而不是 ida pro 来逆向这一题,看到了 rdata 也看到了函数(FUN_00bc9660:00bc966e),不过还是逆向不出来算法(还得先学 c 差不多)。
然后改掉获得时间的 API 也不会,最后就还是采用了上面的手动穷举办法做。

总结

今年终于满足了19年时候的愿望,还算是挺开心的,不过 writeup 花了我将近两天时间来写,要累死了。

然而今年一个 Math 题都没做出来,太为难我这个高考数学__分的废物了,彻底失败😭。

明年如果有机会的话应该还会继续参加 hackergame (毕竟比较有意思
如果有时间的话,明年 汇编+逆向+C 也学点,不至于像现在一样这么憋屈)
数学就算了

最后,学习无止境,总之可以的话多碰点其它领域的东西吧。