出题团队简介
赛题设计思路
公开的用户名及序列号:
用户名 : D1BD4E3EC707C7CA
序列号 : 84616AC140435FEE
题目答案
用户名 : KCTF
序列号 : 6FB1CE80914403C4
题目设计:此题使用了X86的代码混淆。
1、题目介绍
这次提交的题目是windows32位的应用程序,作战思路是将算法高级代码和汇编进行混淆、膨胀
2、整体设计
(1)用户名加密部分
使用修改过的DES算法,对用户名进行多次加密,将结果进行hash、拼接,再进行hash,得到一个值。
(2)序列号加密部分
使用修改过的DES算法,对序列号进行多次解密,密钥来自于(1)的中间结果,最后得到一个值,与(1)最后生成的值进行比较
3、混淆
(1)32位混淆:对常见汇编指令进行替换、膨胀,增加垃圾代码,乱序
技术要点
(1)X86的代码混淆
(2)算法设计
5、破解时可能会遇到的问题
总体思路:
通过调试器的trace功能或者模拟执行的方式拿到完整的解密算法,最后分析解密算法得到加密算法。
6、需要解决的问题
(1)拿到完整的代码之后,需要写脚本将垃圾指令除去,以便最后的分析
(2)通过分析解密算法得到加密的算法。
(3)需要正面对抗大量冗余代码
7、预期的破解思路
(1)找到混淆规律,提升分析速度
(2)根据关键算法代码写逆算法
赛题解析
本题解析由看雪论坛会员【mb_mgodlfyn】提供:
个人主页:https://bbs.pediy.com/user-home-925585.htm
// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
memset(v70, 0, 0x1C2u);
memset(v69, 0, sizeof(v69));
qmemcpy(v116, "abcdefgh", sizeof(v116));
memset(name, 0, 0x32u);
*(_DWORD *)Buffer = 0;
v406 = 0;
v207 = 0;
v208 = 0;
v209 = 0;
v210 = 0;
v326 = 0;
v327 = 0;
v328 = 0;
printf(&Format); // "请输入用户名:n"
gets((int)name);
v390 = name;
v293 = &name[1];
v390 += strlen(v390);
v289 = ++v390 - &name[1];
Value = sub_5AE2D3(name, v390 - &name[1]);
itoa(Value, Buffer, 16);
sub_5AD9F9(v116, Buffer, &v207, &v326);
*(_DWORD *)v70 = v207;
*(_DWORD *)&v70[4] = v208;
*(_DWORD *)v69 = v326;
*(_DWORD *)&v69[4] = v327;
qmemcpy(v115, "ijklmnop", sizeof(v115));
v231 = 0;
v232 = 0;
v233 = 0;
v234 = 0;
v323 = 0;
v324 = 0;
v325 = 0;
...
sub_593D03(v126, Buffer, v77, v277);
qmemcpy(v127, "abcdefgh", sizeof(v127));
memset(v76, 0, sizeof(v76));
v275[0] = 0;
v275[1] = 0;
v276 = 0;
sub_5937B3(v127, Buffer, v76, v275);
memset(v365, 0, sizeof(v365));
v366 = 0;
v3 = sub_5AE2D3(v69, 192);
sprintf(v365, "%08x", v3); // v365
v401[0] = 0;
v401[1] = 0;
v402 = 0;
v403 = 0;
v404 = 0;
v391[0] = 0;
v391[1] = 0;
v392 = 0;
v393 = 0;
v394 = 0;
sub_592CB9(v70, v365, v401);
v368 = malloc(8u);
v4 = v368;
v5 = v403;
*v368 = v402;
v4[1] = v5;
sub_592CB9(&v70[9], v401, v391);
...
v44 = malloc(8u);
v387 = v44;
v45 = v403;
*v44 = v402;
v44[1] = v45;
sub_592CB9(&v70[189], v401, v391); // here, v391 is the correct serial
v44 = malloc(8u);
v387 = v44;
v45 = v403;
*v44 = v402;
v44[1] = v45;
sub_592CB9(&v70[189], v401, v391);
memset(serial, 0, sizeof(serial));
v288 = 0;
memset(v399, 0, sizeof(v399));
v400 = 0;
memset(v395, 0, sizeof(v395));
v396 = 0;
v397 = 0;
v398 = 0;
printf(&byte_5D0848); // "请输入序列号:n"
gets((int)serial);
v389 = serial;
v291 = &serial[1];
v389 += strlen(v389);
v292 = ++v389 - &serial[1];
if ( v389 - &serial[1] == 16 )
{
sub_592C10(&v70[189], v399, serial);
v47 = v387[1];
*(_DWORD *)&v399[8] = *v387;
*(_DWORD *)&v399[12] = v47;
...
sub_592C10(&v70[9], v399, v395);
v67 = v368[1];
*(_DWORD *)&v399[8] = *v368;
*(_DWORD *)&v399[12] = v67;
sub_592C10(v70, v395, v399);
for ( i = 0; i < 8; ++i )
{
if ( v395[i] != v365[i] )
{
printf(&fail_1); // "登录失败!n"
system(aPause_0);
return 0;
}
}
printf(&success); // "登录成功!n"
system(aPause_1);
return 0;
}
else
{
printf(&fail_2); // "登录失败!n"
system(Command);
return 0;
}
}
在输入serial之前以v365为初始值正向遍历v70进行了22次迭代计算得到v391;在输入serial之后以serial为初始值反向遍历v70进行了22次迭代计算得到v395。
这两块计算给人感觉很像互逆的。输入公开的name,尝试在0x5B6364下断点查看正向迭代22次后的v391,发现此处的v391正好就是公开的serial。
C:>CrackMe.exe
请输入用户名:
KCTF
请输入序列号:
6FB1CE80914403C4
登录成功!
请按任意键继续. . .
1、2022 KCTF 秋季赛【新思路奖】,快为你支持的战队投票吧!
https://bbs.pediy.com/thread-275535.htm
2、2022 KCTF 秋季赛【最佳人气奖】,快为你喜欢的战队投票吧!
https://bbs.pediy.com/thread-275534.htm
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):看雪2022 KCTF 秋季赛 | 第十一题设计思路及解析