本帖最后由 ZhangYixiSuccee 于 2026-3-5 00:29 编辑
1、 OllyDbg 调试破解
首先运行程序,密码可以随便输入,然后出现一下提示信息,提示长度不对。
然后根据字符串查找到关键password输入以及检查位置,双击可以跳转过去,- password 字符串和界面信息一致,所以这里就是关键输入信息的位置,
- 双击就可以跳转到输入password的位置处,然后可以下断点,点击运行就可以运行到输入密码处。
运行到如下图即可输入字符串:随便输入字符,按回车即可。
然后一路回车,发现运行结果是:长度不对。
继续分析,[!] Hint: The length is your first real challenge.,向上分析,可以看到,- 一个je跳转,然后不符合条件,然后就会提示长度错误
- strlen的函数调用,所以这里必然是判断长度,
- cmp eax,0x1f,即是目标长度,flag是31个字符。
基于以上分析,我们把flag的长度设置成31个字符,可以通过长度检查
然后接着继续分析,- 002CD364 E8 6743F3FF call 【2026春.002016D0,调用这个函数之后,会有test al,al,然后je 会跳转到错误。
- 所以这个函数里面必然会对password 字符串进行检查
- 这里可以先nop掉,
根据字符串提示,可以看到这里是对字符串进行checksum校验,- 校验的最终结果是0xAEA5,而我们输入的字符串校验结果是5EF0,则会提示校验结果不对,
继续分析这里- 可以看到下面又一个跳转,002CD3A3 /0F84 A7000000 je 【2026春.002CD450,这个会跳转到正确位置
- 这里我们将Z Flag置为1,即可跳转到正确结果
基于以上分析,我们的分析是正常的,所以需要分析call 【2026春.002016D0- 单步进入该函数,持续跳过运行,
- 即可看到flag字符已经出来,52pojie!!!_2026_Happy_new_year!
所以整体流程应该是这样:- 先检查长度
- 再进行password正确性检查
- password checksum检查
通过以上全部检查,则会得到正确flag:52pojie!!!_2026_Happy_new_year!所以基于以上分析,可以将关键点nop掉,就可以无论输入什么程序,均可以成功了。
2、IDA 静态分析破解
程序拖到IDA里面,找到字符串所在函数(F5 将汇编转成C语言)
先看401740 函数- 返回结果是v3,v3 又赋值v5
- v5 结果为0,则进入检查,否则返回[!] Nice try, but not quite right...
- 字符串是52pojie_2026_HappyNewYear
- 意思就是先对一个错误的flag进行检查, 如果完全匹配,则是报错,提示不完全对,
- 障眼法检查,迷惑用户
函数里面是对输入字符串进行比较
接下来又是一个障眼法检查,假的字符串检查 - 字符串市byte_4D3032,对应字符串:2pojie2026Happy
- 如果输入该字符串,会提示[!] You're getting closer...
由于我们没有尝试这两张字符串,所以没有碰到这种错误,接下来是长度检查
接着就是关键字符串检查函数,- 因为从代码来看,如果这个函数返回false,则会跳转打印失败:[X] Access Denied!
- 且返回true之后,会对字符串进行校验,所以这个是关键函数
- sub_4016D0(str,31),这里可以看里面的实现
sub_4016D0(str,31)- a1 为字符串,a2为长度,31
- Block像是申请的一块memory,申请长度是:0x64
- 而调用过sub_401620(Block)函数之后,就可以进行对比,则sub_401620(Block)该函数是填充函数
接着看一下sub_401620是如何赋值的,- 先赋值一堆数字
- 然后再对这个数组数据进行异或,则得到字符串,
- 可以想到如果直接对这个数组进行字符串串赋值,则很明细可以通过查找str来得到,所以这也是一种隐藏字符串的方法
汇编如下:
这里可以用C语言编写测试一下:- 通过这个函数可以生成flag
- 通过异或可以生成对应的编码,然后就可以填充数据
[C] 纯文本查看 复制代码 #include "stdio.h"
​
typedef unsigned char _BYTE;
typedef unsigned int _DWORD;
typedef unsigned short int _WORD;
​
_BYTE * sub_401620(_BYTE* a1)
{
_BYTE *result; // eax
​
*(_DWORD *)a1 = 0x2D327077;
*(_DWORD *)(a1 + 4) = 0x63272B28;
*(_DWORD *)(a1 + 8) = 0x701D6363;
*(_DWORD *)(a1 + 12) = 0x1D747072;
*(_DWORD *)(a1 + 16) = 0x3232230A;
*(_DWORD *)(a1 + 20) = 0x272C1D3B;
*(_DWORD *)(a1 + 24) = 0x273B1D35;
*(_BYTE *)(a1 + 30) = 0x63;
*(_WORD *)(a1 + 28) = 0x3023;
result = (_BYTE *)a1;
do
{
*result++ ^= 0x42u;
printf("%c \t",*result);
}
while ( result != (_BYTE *)(a1 + 31) );
*(_BYTE *)(a1 + 31) = 0;
return a1;
}
​
​
void process(_BYTE* a1, _DWORD len )
{
_DWORD i = 0;
while(i < len)
{
printf("%x \t", a1[i] ^ 0x42);
i++;
}
printf("\n");
}
​
_BYTE str[0x64];
int main()
{
_BYTE* string = sub_401620(str);
printf("string:%s\n", str, string,string);
process(string,31);
return 0;
} |