算法难度:⭐⭐⭐⭐
爆破难度:⭐
信息收集
运行情况:
查壳与脱壳:
Delphi程序,无壳
调试分析
通过IDR找到按钮事件的函数,在IDA里分析:
开始是初始化操作,读取Name和Serial,然后复制了一串奇怪的东西到变量里
接下来对刚刚复制的内容的值全部进行+1操作
然后判断了下用户名长度,必须是1-10字符
往下是一个循环:功能是复制字符串,把那个奇怪的东西复制了一份
然后往刚刚复制完成的后面间隔一个00,复制我们的UserName:
然后遍历这个新生成的str2字符串,进入一个大循环:当值分别是这7种情况的时候分别单独进行处理
具体计算方式如果看反汇编不太清楚,可以看下面的注册机代码
不同情况的处理基本上是两个一组:
3C和3E的处理分别是对一个索引值进行–和++操作
2B和2D的操作则是将使用索引取到的值分别进行++和–操作
对于5B和5D:
- 如果索引的值是0,5B是向后搜索5D,
- 如果索引的值是非0,5D则是向前搜索5B
对于21分支,则是设置跳出条件
大循环之后,还有个小循环:
累加原Name所在位置开始往后10个字节的值,累加到edi里,这个edi是个固定值:0x012F73C
然后比对,如果相同则弹窗成功
注册机
注册码生成算法:
#include <iostream>
#include <string>
char str[100] = ";;;;;;;;;;;;;**====,,=,,========*=**=*=**=*=**=*=*=* ";
char str2[100] = { 0 };
int main()
{
std::string name = "";
std::cin >> name;
for (int i = 0; i < strlen(str); i++) {
str[i] = str[i] + 1;
str2[i] = str[i];
}
for (int i = 0; i < name.length(); i++)
str2[strlen(str) + i + 1] = name[i];
// n索引指向的是Name
int n = strlen(str) + 2-1;
int flag = 0;
for (int i = 0; flag != 1; i++)
{
switch (str2[i])
{
case 0x3E:n++; break;
case 0x3C:n--; break;
case 0x2B:str2[n]++; break;
case 0x2D:str2[n]--; break;
case 0x21:flag = 1; break;
case 0x5B: {
if (str2[n] == 0)
do i++; while (str2[i] != 0x5D);
break;
}
case 0x5D: {
if (str2[n] != 0)
do i--; while (str2[i] != 0x5B);
break;
}default:
break;
}
}
int sum = 0x012F73C;
for (int i = 0; i < 10; i++)
{
int len = strlen(str) + i + 1;
sum += str2[len];
}
std::cout << sum << std::endl;
}
效果:
总结
是个不错的switch-case语句的反汇编练习