[CrackMe]160个CrackMe之23
该题目算法可能不是麻烦,但是由于是汇编编写,循环这里使用栈不容易看出,并且成立条件这里分析时需要拓展下思路,因此花费了一些时间,再次记录一下。
1. 通过字符串搜索来找到关键位置
直接定位到关键位置,如下图,其是与一个全局变量来进行比较是否为eax。
现在分析存在两种思路:
1)直接下内存断点通过OD动态分析;
2)如果没壳,则可以通过IDA中的交叉索引(x)查看该值在哪里调用。
2. 使用IDA交叉索引功能查看判断位置
如下图,我们对关键变量进行交叉索引,这里明显存在四处,只要我们把这四处分析完毕,则就能分析出算法来。
1)sub_401023+70 分析
如下图,我们结合动态与静态分析,发现这是输入用户名,当输入成功之后会+4。
2)sub_40146F+24 分析
如下图,结合动态与静态分析,是获取Serial
3)sub_40149c+E 分析
其函数分析如下,我们可以计算出满足serial时需要的值
4)sub_401325+73 分析
该函数是加密算法,因为其同时出现name和serial的循环运算,这里的循环运算不容易看出,说实话我也不清楚其如何实现循环的(应该通过修改esp)实现的。
但是通过分析全局变量i,其从0->15时,将add key,4 满足条件,从这里就很容易推断出循环条件。
另外需要注意一点,其xor并不是单个字符,而是四个字节,(PULONG)&name[i],这也就解释了为什么只循环16次但需要20个字节,否则就会不满足..
3. 加密算法推测与注册机生成
如下推测出其加密算法
因此我们可以实现注册机编写:
#include<windows.h>
#include <iostream>
using namespace std;
int main() {
char name[21];
unsigned long* p;
memset(name, 0, 21);
cout << "输入name:" << endl;
cin >> name;
unsigned int serial = 0xF6EEDB88;
for (int i = 15; i >=0; i--) {
p = (unsigned long*)&name[i];
serial = (serial ^ *p)-1;
}
cout << serial << endl;
}