-
-
[原创]看雪CTF2016--23、24、26、27题破文(补)
-
发表于: 2016-12-27 10:04 3240
-
一下补4题的破文...
25题没做出来,涉及密码学我一般都跪,但多谢HHHso提点找到了突破口,这也便于我解28题,即算法中的常量是加密或解密算法的特征。
不多说了开始进入破文,主要以原理为主
23题爆破点找窗口过程函数的WM_COMMAND消息,断下来有2个函数,第一个是图片识别(看不出来没有关系,看返回结果,他把我们输入的字符等价替换为整数而且呈一一对应关系),第二个函数就是验证函数,验证成功则提示注册成功;
验证函数是分段验证,后面的就是个对比,把整数翻译回来得到IsSerialNumber,而前面只有4位懒得逆推就穷举吧,穷举算法如下:
BYTE ary_btDes[0x4] = "\0";
DWORD backToGood(DWORD dwSoc)
{
DWORD dwDes;
do
{
ary_btDes[1] = 0;
do
{
ary_btDes[2] = 0;
do
{
ary_btDes[3] = 0;
do
{
__asm {
mov esi, offset ary_btDes
mov al, byte ptr [esi]
movzx edx, byte ptr [esi+1]
not al
movzx ecx, byte ptr [esi+2]
movzx eax, al
mov ebx, dword ptr dword_4121C0[eax*4]
xor ebx, 0FFFh
movzx eax, bl
xor edx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[edx*4]
movzx eax, bl
xor ecx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[ecx*4]
movzx ecx, byte ptr [esi+3]
movzx eax, bl
xor ecx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[ecx*4]
mov dwDes, ebx
}
if (dwDes == ~dwSoc) {
char szBuf[0x16] = "\0";
char* pTmp = (char*)&dwDes;
pTmp[0] = ary_btDes[3];
pTmp[1] = ary_btDes[2];
pTmp[2] = ary_btDes[1];
pTmp[3] = ary_btDes[0];
wsprintf(szBuf, "%X", dwDes);
MessageBox(NULL, szBuf, szBuf, NULL);
return dwDes;
}
ary_btDes[3]++;
} while (ary_btDes[3] <= 0x3D);
ary_btDes[2]++;
} while (ary_btDes[2] <= 0x3D);
ary_btDes[1]++;
} while (ary_btDes[1] <= 0x3D);
ary_btDes[0]++;
} while (ary_btDes[0] <= 0x3D);
return dwDes;
}
int main(int argc, char* argv[])
{
DWORD dwSoc = 0xA466EEEF;
printf("%X", backToGood(dwSoc));
return 0;
}
注意dword_4121C0在程序里面找出来,最终得到注册码为thisIsSerialNumber
24题,首先必须是以pediy开头,反正我记得整个sn够长的,下面是逆推的C代码,如下:
BYTE g_ary_btSoc[0x9c] = {
0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04,
0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04,
0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08,
0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04,
0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x00, 0x00, 0x00
};
void backToGoodMy(BYTE* pbtDes)
{
DWORD dwCount = 0;
DWORD dwCOuntDes = 0;
BYTE ary_btDes[0x9c];
while ( dwCount < 0x9c )
{
BYTE btTmp = 0;
if (!(g_ary_btSoc[dwCount] & 0xF0))
btTmp = 16 * g_ary_btSoc[dwCount];
else
int nTmp = 0;
dwCount++;
btTmp = (g_ary_btSoc[dwCount] | btTmp);
ary_btDes[dwCount - 1] = ((btTmp >> 4) + 48);
ary_btDes[dwCount - 1] = ary_btDes[dwCount - 1] ^ 0x86;
ary_btDes[dwCount] = ((btTmp & 0x0F) + 48);
ary_btDes[dwCount] = ary_btDes[dwCount] ^ 0x86;
++dwCount;
}
dwCount = 0;
dwCOuntDes = 0;
while (dwCOuntDes < 0x9c * 2) {
pbtDes[dwCOuntDes] = ary_btDes[dwCount] >> 4;
pbtDes[dwCOuntDes + 1] = ary_btDes[dwCount] & 0x0F;
if (pbtDes[dwCOuntDes] > 9) {
pbtDes[dwCOuntDes] = 55 + pbtDes[dwCOuntDes];
} else {
pbtDes[dwCOuntDes] = 48 + pbtDes[dwCOuntDes];
}
if (pbtDes[dwCOuntDes + 1] > 9) {
pbtDes[dwCOuntDes + 1] = 55 + pbtDes[dwCOuntDes + 1];
} else {
pbtDes[dwCOuntDes + 1] = 48 + pbtDes[dwCOuntDes + 1];
}
dwCOuntDes++;
dwCOuntDes++;
dwCount++;
}
}
int main(int argc, char* argv[])
{
BYTE ary_btDes[0x9c * 2];
backToGoodMy(ary_btDes);
return 0;
}
算出来的结果保存在ary_btDes中,拼上pediy就得到正确的sn了pediyB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB6B6B6
26题,和我自己的题思路很像,大家同门调试器是必写项目。。。
但相对来说不是太复杂,这题关键还是复原代码,大体结构分为
1.输入sn后首先在调试器中加密一次,
2.然后启动被调试程序并挂起,将加密后的sn和一段实现准备好的字节码写入被调试程序
3.修改EIP到写入的字节码位置,恢复被调试程序主线程
4.int3断点第1次来是系统断点,第2次来就是写入的字节码中间产生,这里把加号修改成为了减号
5.第3次断点就是最终验证
加密程序有2段,一段在调试器中,一段在被调试程序中,dump出来注意加号的位置,由于其是逐位加密,穷举发现第一位怎么都不是EF,后来通过hook原程序,才发现k、h等可以得到EF,但是除了k外其他的后面的加密数据就不对了。
这题的算法存在漏洞,都用第一位加密,而且其他位去撞加密库没撞到就用固定数值,那只要其他位是无法撞加密库成功的数据就可以验证成功,这也是其多解的原因
另外,为什么第一位加密后是EF,这个原因我没分析,HHHso做了详细分析,建议大家去认真看下,这也是个以后可以用到的招数
sn就不列举了第一位是k其他位全1好像就行
27题,没法找到爆破点,后来发现他写一个内存地址,然后跳到那去运行,那这个程序应该是加密了字节码,然后解密成功后也就是那段字节码提示成功,但是当我随便输入一个够位长的数据后3j1l2njlfjl发现验证成功了。。。
这题也就破解了,分析原因是由于其解密逐位字符多个异或,多个异或如果字符不是足够特殊的话,可以不同字符得到同一结果的,这也是其多解的原因
25题没做出来,涉及密码学我一般都跪,但多谢HHHso提点找到了突破口,这也便于我解28题,即算法中的常量是加密或解密算法的特征。
不多说了开始进入破文,主要以原理为主
23题爆破点找窗口过程函数的WM_COMMAND消息,断下来有2个函数,第一个是图片识别(看不出来没有关系,看返回结果,他把我们输入的字符等价替换为整数而且呈一一对应关系),第二个函数就是验证函数,验证成功则提示注册成功;
验证函数是分段验证,后面的就是个对比,把整数翻译回来得到IsSerialNumber,而前面只有4位懒得逆推就穷举吧,穷举算法如下:
BYTE ary_btDes[0x4] = "\0";
DWORD backToGood(DWORD dwSoc)
{
DWORD dwDes;
do
{
ary_btDes[1] = 0;
do
{
ary_btDes[2] = 0;
do
{
ary_btDes[3] = 0;
do
{
__asm {
mov esi, offset ary_btDes
mov al, byte ptr [esi]
movzx edx, byte ptr [esi+1]
not al
movzx ecx, byte ptr [esi+2]
movzx eax, al
mov ebx, dword ptr dword_4121C0[eax*4]
xor ebx, 0FFFh
movzx eax, bl
xor edx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[edx*4]
movzx eax, bl
xor ecx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[ecx*4]
movzx ecx, byte ptr [esi+3]
movzx eax, bl
xor ecx, eax
shr ebx, 8
xor ebx, dword ptr dword_4121C0[ecx*4]
mov dwDes, ebx
}
if (dwDes == ~dwSoc) {
char szBuf[0x16] = "\0";
char* pTmp = (char*)&dwDes;
pTmp[0] = ary_btDes[3];
pTmp[1] = ary_btDes[2];
pTmp[2] = ary_btDes[1];
pTmp[3] = ary_btDes[0];
wsprintf(szBuf, "%X", dwDes);
MessageBox(NULL, szBuf, szBuf, NULL);
return dwDes;
}
ary_btDes[3]++;
} while (ary_btDes[3] <= 0x3D);
ary_btDes[2]++;
} while (ary_btDes[2] <= 0x3D);
ary_btDes[1]++;
} while (ary_btDes[1] <= 0x3D);
ary_btDes[0]++;
} while (ary_btDes[0] <= 0x3D);
return dwDes;
}
int main(int argc, char* argv[])
{
DWORD dwSoc = 0xA466EEEF;
printf("%X", backToGood(dwSoc));
return 0;
}
注意dword_4121C0在程序里面找出来,最终得到注册码为thisIsSerialNumber
24题,首先必须是以pediy开头,反正我记得整个sn够长的,下面是逆推的C代码,如下:
BYTE g_ary_btSoc[0x9c] = {
0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04,
0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04,
0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08,
0x06, 0x04, 0x08, 0x07, 0x04, 0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x07, 0x04,
0x08, 0x08, 0x04, 0x08, 0x09, 0x04, 0x08, 0x01, 0x04, 0x08, 0x02, 0x04, 0x08, 0x03, 0x04, 0x08, 0x04, 0x04, 0x08, 0x05, 0x04, 0x08, 0x06, 0x04, 0x08, 0x00, 0x00, 0x00
};
void backToGoodMy(BYTE* pbtDes)
{
DWORD dwCount = 0;
DWORD dwCOuntDes = 0;
BYTE ary_btDes[0x9c];
while ( dwCount < 0x9c )
{
BYTE btTmp = 0;
if (!(g_ary_btSoc[dwCount] & 0xF0))
btTmp = 16 * g_ary_btSoc[dwCount];
else
int nTmp = 0;
dwCount++;
btTmp = (g_ary_btSoc[dwCount] | btTmp);
ary_btDes[dwCount - 1] = ((btTmp >> 4) + 48);
ary_btDes[dwCount - 1] = ary_btDes[dwCount - 1] ^ 0x86;
ary_btDes[dwCount] = ((btTmp & 0x0F) + 48);
ary_btDes[dwCount] = ary_btDes[dwCount] ^ 0x86;
++dwCount;
}
dwCount = 0;
dwCOuntDes = 0;
while (dwCOuntDes < 0x9c * 2) {
pbtDes[dwCOuntDes] = ary_btDes[dwCount] >> 4;
pbtDes[dwCOuntDes + 1] = ary_btDes[dwCount] & 0x0F;
if (pbtDes[dwCOuntDes] > 9) {
pbtDes[dwCOuntDes] = 55 + pbtDes[dwCOuntDes];
} else {
pbtDes[dwCOuntDes] = 48 + pbtDes[dwCOuntDes];
}
if (pbtDes[dwCOuntDes + 1] > 9) {
pbtDes[dwCOuntDes + 1] = 55 + pbtDes[dwCOuntDes + 1];
} else {
pbtDes[dwCOuntDes + 1] = 48 + pbtDes[dwCOuntDes + 1];
}
dwCOuntDes++;
dwCOuntDes++;
dwCount++;
}
}
int main(int argc, char* argv[])
{
BYTE ary_btDes[0x9c * 2];
backToGoodMy(ary_btDes);
return 0;
}
算出来的结果保存在ary_btDes中,拼上pediy就得到正确的sn了pediyB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB1B2BEBEB2BEBFB2BEB7B2BEB4B2BEB5B2BEB2B2BEB3B2BEB0B2BEB6B6B6
26题,和我自己的题思路很像,大家同门调试器是必写项目。。。
但相对来说不是太复杂,这题关键还是复原代码,大体结构分为
1.输入sn后首先在调试器中加密一次,
2.然后启动被调试程序并挂起,将加密后的sn和一段实现准备好的字节码写入被调试程序
3.修改EIP到写入的字节码位置,恢复被调试程序主线程
4.int3断点第1次来是系统断点,第2次来就是写入的字节码中间产生,这里把加号修改成为了减号
5.第3次断点就是最终验证
加密程序有2段,一段在调试器中,一段在被调试程序中,dump出来注意加号的位置,由于其是逐位加密,穷举发现第一位怎么都不是EF,后来通过hook原程序,才发现k、h等可以得到EF,但是除了k外其他的后面的加密数据就不对了。
这题的算法存在漏洞,都用第一位加密,而且其他位去撞加密库没撞到就用固定数值,那只要其他位是无法撞加密库成功的数据就可以验证成功,这也是其多解的原因
另外,为什么第一位加密后是EF,这个原因我没分析,HHHso做了详细分析,建议大家去认真看下,这也是个以后可以用到的招数
sn就不列举了第一位是k其他位全1好像就行
27题,没法找到爆破点,后来发现他写一个内存地址,然后跳到那去运行,那这个程序应该是加密了字节码,然后解密成功后也就是那段字节码提示成功,但是当我随便输入一个够位长的数据后3j1l2njlfjl发现验证成功了。。。
这题也就破解了,分析原因是由于其解密逐位字符多个异或,多个异或如果字符不是足够特殊的话,可以不同字符得到同一结果的,这也是其多解的原因
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: