看雪 2016 CTF 第十二题 点评和解析
看雪版主&评委
netwind点评
出题者简介
作者:PeSafe,现任北京深思数盾科技股份有限公司软安实验室安全专家职位。
设计思路
下述截图和用例并非比赛数据,切勿对号入座,只是为了说明设计思路。
1.算法
为了保证唯一序列号的要求,采用迷宫算法。通过程序生成随机16*16的迷宫,且保证该迷宫有唯一路径。
迷宫从@到$拥有唯一路径,走迷宫的步骤即为序列号,方向由0-3表示,并且用2bit编码。
使用key对迷宫进行加密,其中key为base24解码表,同序列号的解码共用一张表。
加密算法为:
intencode_maze(unsignedcharbox[N][N],unsignedchar*key)
{
unsignedchar*k =key+ (N*N)-1;
for(inti = 0; i <N; i++)
{
for(intj = 0; j <N; j++)
{
unsignedcharch =box[i][j] ^ *(k--);
box[i][j] = ~ROR(ch, 3);
}
}
return0;
}
加密后的迷宫为
上述算法设计完成后,需要将走迷宫的步骤编码为可见字符。由于可以向4个方向走所以用2bit表示方向,之后采用base24算法将序列号映射到可见字符,base24码表为:
staticconstunsignedcharg_b24_map[] = {
/* 0 */'B','C','D','F','G',
/* 5 */'H','J','K','M','P',
/* 10 */'Q','R','T','V','W',
/* 15 */'X','Y','2','3','4',
/* 20 */'6','7','8','9'
};
Base24解码码表为上述key,关键位置加入随机数变换到256字节,并且进行了加法操作。经过编码的序列号为:
3. 组装
将迷宫和key放入crackme,写个程序边解密边走迷宫即可。
voiduser_register(HWNDhDlg,LPTSTRserial)
{
char*_in = to_ansi(serial);
VCProtectBegin(0);
int_len = strlen(_in);
if(_len >= 128)
return;
unsignedcharbuf[128];
intlen = base24_decode((unsignedchar*)buf, (unsignedchar*)_in, _len);
if(len <= 12)
return;
unsignedcharok = run_maze(box, buf);
//unsignedchar t = '$'^'@';
if((ok^DECODE_MAZE(box, 0,0)) == 0x64)
{
MessageBox(hDlg, L"序列号正确,注册成功!", L"CrackMe", 0);
}
VCProtectEnd();
return;
}
run_maze(box, buf)需要返回 $ 并且与 @ 异或后为 0x64 表示注册成功。程序编译时采用强制内联方便后期加壳处理。
4. 加壳
拿出自己写的VCProtect虚拟机加壳工具(这里说明一下本人拥有该虚拟机的完整代码及版权,且该工具完全由本人独立开发),将上述关键代码加壳:
同时将导入表和程序入口做了一些保护,因此想爆破也不容易。由于采用了内联函数所以算法全部在虚拟机中运行,想要逆出算法必须先还原代码。
破解思路
1. 还原虚拟机
对于没有解过虚拟机的人是一个考验,这里提供一个解虚拟机的思路。
首先定位vm_loop,找到vm_handler表,对每个handler进行反混淆处理,根据对应的加密方式对vm_code进行解密,对vm_code进行逻辑上的还原。
具体还原思路请参考大牛的分析。
2.还原算法
还原虚拟机指令后进一步还原走迷宫算法,根据算法解码迷宫矩阵,对照迷宫将每步进行2bit的编码。
之后解密base24码表,对应码表生成序列号。
正确的序列号为:
H4G3P2H3H4B6T8C9X9G4BMRRXMRVJ3JTWWHVRMT6XRX6B9B8J2JWPVX6T9
看雪安全 · 看雪众测
持续关注安全16年,专业为您服务!
快,关注这个公众号,一起涨姿势~