KCTF2025
第四题 血色试炼
TlsCallBack分析
TlsCallback_0初始化了两个Key,以及一个Base64表,并且有一个标准Sbox和一个标准InvSbox赋值。
这边从ntdll.dll中寻找所有Zw开头API,然后储存到自定义链式结构中。
这边加载了个加密字符串,自解密出来是ZwQueryInformationProcess,然后通过第二处红框去上面的结构里面找到对应API的syscall服务号。
赋值给全局服务号变量,然后调用syscall,判断返回值是否>=0,若>=0则触发异常,说明这边可能是一个反调试。
直接在这个call后面设置条件断点将返回值改成负数即可绕过。
Main函数分析
main函数开头处有设置一个VEH,
VEH里面分成两部分代码执行。
动调发现输出完”UserName:”后会触发异常,然后进入第一个箭头call处,这里面是进行了一个输入流的启动,下面syscall后就是等待用户输入。
用户名输入完后会返回到这边,然后底下进行三次循环处理,循环内第一个call会触发第二种异常,进入VEH函数的第二部分代码。
红框处就是我们输入的用户名,下面对用户名进行加密,加密结果是一个Base64,和序列号的格式一样,循环三次会对UserName加密三次。
加密完直接输出FAILED,这边解题完后也不知道为什么。
通过观察decstr和encstr两个Call,猜测大概是相互转换的加解密函数。
断点在TlsCallBack的随便一个API名解密处,将第一个参数改成刚刚上面加密一次的数据,单步运行解密函数,发现返回的解密文本就是刚刚输入的字符串,说明猜想成立。
尝试将readme.txt里面的序列号patch进去进行解密。
发现可以解密出32个字节,但是后32字节暂时看不到。vaEW2QqcchrlmpysQuRAzZoCdbPvDxhx
当输入readme.txt的示例用户名时,在VEH函数这边断点,三次加密后查看第三次加密完的用户名,发现和上面我们解密序列号的前三十二字节一样。
结论
序列号 = 四次加密(用户名)
获取KCTF序列号
将Main函数这边对用户名加密三次循环改成4次。
第四次加密时候断下,查看箭头处字符串,即是用户名对应的序列号。
tSzQkyqcvZLgkwDltPF9RpInibA5fTpH/bJni2yvLzTKao2uL5eLZ5QIxPj8bsYWe48ZohC5/Jw3cNNAaX8/rA==