PEID查看你程序有没有壳,发现是汇编语言程序
打开程序,我们发现程序是由Nag提示窗口的。
我们先进行去Nag提示。
进入回调函数,进行分析
保存修改到文件。
重新打开文件,我们发现没有Nag窗口。
再次使用OD进行附加进行分析。
我们通过分析发现,这个注册框是一个模态框,我们找到它的回调函数,简单分析它的回调函数,发现并没有我们需要的关键算法。
继续分析,我们发现输入用户名和密码之后跳转到这里进行执行。
进入这个004013E8的call,进行分析
简单模拟这个算法,其实就是判断的过程,对小于0x39的字符,也就是’9’进行处理,放到Buffer缓冲区里。但是通过程序单步调试发现,如果我们输入全是大于’9’的字符,程序不能到达第二个关键算法。
for (int i = 0; i < nLen; i++)
{
int nTemp = szName[i];
if (nTemp > 0x39) continue;
nTemp -= 0x30;
if (nTemp <= 0) continue;
szBuffer[j++] = nTemp;
}
继续往下分析,来到第二个算法,经过第二个算法之后把结果放到0x402125这个地址里。
之后进行一个比较,我们发现,上面计算出来的结果,如果和0x3039相等,则可以通过验证。
总结:
这个算法就是把name字符串小于0x39的减去0x30,再放到另一个缓冲区地址里。当然如果一个都没有,转化后的字符串长度为0,直接无法通过验证。
之后再把转换字符串每一位的ascii码取出来,经过一个计算的算法。
我们可以设计一个随机的字符串,为了方便通过验证计算,字符串的所有字符都小于0x39,大于0,通过程序的单步调试发现,name字符串并不会太长,我们随机一个字符串长度,为10以内,通过逆向的思维,我们构造一个结果字符串,并且回推回去。
算法KeyGen:
#include <iostream>
#include <time.h>
using namespace std;
/*
第一个算法就是
for (int i = 0; i < nLen; i++)
{
int nTemp = szName[i];
if (nTemp > 0x39) continue;
nTemp -= 0x30;ec
if (nTemp <= 0) continue;
szBuffer[j++] = nTemp;
}
通过第一个算法计算出来的结果字符串、
每一位的ascii码*8之和是0x3039
*/
int main()
{
srand((unsigned int)time(NULL));
unsigned int nSum = 0;
char szName[10] = { 0 };
unsigned int nEbx = 0;
int nRandomLen = 0;
while (1)
{
//得到一个随机长度
nRandomLen = rand() % 10 + 1;
//根据随机长度,随机一个字符串
for (int i = 0; i < nRandomLen; i++)
{
nSum <<= 1;
nEbx = nSum;
nSum <<= 2;
//由于要满足循环条件,我们直接让它在0-9之间循环
szName[i] = rand()%9;
nEbx += szName[i];
nSum += nEbx;
}
if (nSum == 0x3039) break;
nSum = 0;
}
char szRealName[10] = { 0 };
//得到真实名字 根据第一段算法
for (int i = 0; i < nRandomLen; i++)
{
szRealName[i] = szName[i] + 0x30;
}
cout << szRealName << endl;
return 0;
}
执行结果:
我们发现每次运行注册机,结果都是12345字符串。所有这就是答案。