L3HCTF-re复现
这个比赛的题看起来难,但是考察的知识点比较浅,属于那种不会是真的不会,会了秒起来很快的。。。而我刚好什么都不会,当时只做出来一道QAQ。还有一个安卓,一个CPP虚拟机和迷宫,不想复现了QAQ
TemporalParadox
进入主函数发现有花指令:
去掉,然后成功反编译主函数:
逻辑很清楚,time为时间戳,如果时间戳在1751990400和1752052051就使用哈希函数对时间加密并输出字符串类似于salt=tlkyeueq7fej8vtzitt26yl24kswrgm5&t=1751994277&r=101356418&a=1388848462&b=441975230&x=1469980073&y=290308156
并输出最后的hex值,如果不在,则直接要求输入这个字符串并令其哈希后的hex结果与8a2fc1e9e2830c37f8a7f51572a640aa比较。
因此,可以通过idapython,爆破每步的时间戳,在时间戳处于1751990400和1752052051的情况下,程序本身就会打印字符串和hex结果,通过比较该结果即可获取正确字符串(先将时间比较patch掉,方便爆破)
exp:
1 | import idaapi |
snake
一个GO,很久没有做过go相关的了,go和c的调用约定不一样。将start函数的调用约定改为__golang
然后尝试找runtime_newproc函数,该函数第一个传参为runtime_main
点进这个off_3c3790就是runtime_main指针
进入后找到检查双标志位的地方,里面就是main函数
找到后,单步调试找到游戏主循环
该处有很明显的校验逻辑,由于有基于时间的反调,所以先尝试直接改这里的判断吃没吃到。去掉if保存patch
1 | L3HCTF{ad4d5916-9697-4219-af06-014959c2f4c9} |
obfuscate
使用signsrch可以在sub_1250找到RC5轮密钥,使用d810去混淆。同时有一堆反调试,找到对exit的引用然后force jmp所有的条件跳转。
动调发现1250这里是生成S盒
1 | [0x122F2C9C, 0xE3BCCAE7, 0xD0FFC0F2, 0xD9A12544, 0x8A27992F, 0x55B1B935, 0x9110B161, 0x92811564, 0x5CE9B359, 0x77C79A51, 0x4265527A, 0x8AB57C4B, 0x11529FA4, 0x9D9F63FF, 0xA970B936, 0xC8EABA0D, 0x9A0EB4AA, 0xB0BC6E7F, 0x9784B100, 0x70DCD3AE, 0x6057A44E, 0x89187658, 0xE00098A8, 0x45773540, 0xF9374F1A, 0x913FA548] |
找check函数可以调试得最后的数据
1 | [0xF2A1BB1B, 0x21877CE9, 0x0AFD378A, 0xBC811A94, 0xAAE31E40, 0x3FD82E73, 0x4271B884, 0x398B35CC, 0xE9977D00, 0x00007FFD] |
然后网上找个RC5脚本对着代码改一下:
1 |
|
1 | L3HCTF{5fd277be39046905ef6348ba89131922} |
这个题要是像我当时比赛的时候一直跟混淆,那就废了。左边函数列表不多,可以先看看有没有加密函数之类的。
TheFinalGate
这个程序使用了raylib库,且在开始的时候,执行了
1 | sub_7FF7D3CA1100(&v26, 0i64, byte_7FF7D3D13380); |
这种函数,这是装载着色器片段,byte_7FF7D3D13380是字符串格式的代码,这个代码会交给GPU运行
调试到这里,然后dump,得到代码
1 |
|
可以看出是一个栈虚拟机
最上面6行是定义的常量,在main中由:
1 | v21 = sub_7FF7D3C8EFF0(0x2A0u, (__int64)&unk_7FF7D3D130E0, 0x88EAu); |
这几个定义,即:
1 | v21 = sub_7FF7D3C8EFF0(0x2A0u, (__int64)&code, 0x88EAu); |
尝试生成这个栈虚拟机,同时可以发现这个虚拟机是两位两位校验的,所以尝试直接爆破:
1 | from prism import * |