共计 1135 个字符,预计需要花费 3 分钟才能阅读完成。
Crackme023 的逆向分析
1. 程序观察
当输入的序列号正确的时候,下面的状态应该会变。
2. 简单查壳
使用汇编语言写的,没有壳。
3. 程序分析
使用 OD 载入程序,搜索字符串
可以看到状态栏的字符串出现在了里面,我们进入对应的代码空间
在地址 004012B3 处有一个比较语句,如果 eax 的值为 0x10,则程序跳转到正确提示处;如果 eax 的值不等于 0x10,程序则会跳转到错误提示处。
看来这里就是关键的比较了,那么 eax 的值是从哪里来的呢?
在地址 00401299 处可以看到,程序将内存 403166 处的值赋给了 eax。
那么问题又来了,内存 403166 处的值又是怎么来的呢?
我们在程序中查找常量 403166
和内存 403166 有关的语句共有 8 句,有 4 句命令是让内存 403166 的值加 4。
因为前面比较是让 403166 的值和 0x10 作比较,所以需要这四条命令全部执行才行。
这四处,分别是用来求得用户名,求得序列号,计算序列号,验证序列号。
求得用户名:
程序先得到输入的用户名,然后求得用户名长度。接着使用循环将用户名后面一定长度的值清零。最后进行判断,如果用户名长度为 0,则清零内存 403166 的值;如果不为 0,则将内存 403166 的值加 0x4。
求得序列号:
程序使用函数获取输入的序列号,如果函数返回值为 0,也就是序列号为空的话,就会直接返回;如果序列号不为空,将内存 403166 的值加 0x4。
计算序列号:
这是一个循环,循环次数要进行 0x10 次,内存 403166 处的值才会加 0x4。
在循环中:
- 程序得到用户名
- 用户名左移 i 位,i 为循环次数
- 求得输入的序列号
- 将序列号加 1
- 让序列号和移位后的用户名进行异或运算
- 将计算出来的值存入内存 403188 处
验证序列号:
将内存 401388 处序列号取出,加上 0x9112478,看结果是否为 0。如果为 0,就是正确的;如果非 0,就是错误的。所以计算出来的序列号一定是 0 – 0x9112478 = 0xF6EEDB88。
4. 注册机
#include <stdio.h>
#include <string.h>
#include <Windows.h>
int Key()
{
unsigned long serial = 0xF6EEDB88;
char szName[20] = {0};
unsigned long* p;
printf("请输入用户名:");
scanf_s("%s", szName, 20);
for (int i = 15; i >= 0; i--)
{p = (unsigned long*)& szName[i];
serial ^= *p;
serial--;
}
printf("%u", serial);
return 0;
}
int main(int argc, char* argv[])
{Key();
return 0;
}
相关文件在我的 Github