selph
selph
发布于 2022-07-23 / 227 阅读
1
0

新160个CrackMe练习:026-KeygenMe

算法难度:⭐⭐⭐

爆破难度:⭐

信息收集

运行情况:

image

查壳与脱壳:

识别的是UPX壳,实际上则无壳,识别错误应该是

image

调试分析

通过MessageBox函数交叉引用定位校验的位置,就分析主要逻辑吧,前面那些初始化无关紧要

前面省略了给Name和Serial赋值的API调用,直接从校验逻辑开始看

这里获取Name字符串长度遍历计算使用

然后一个循环,计算一个累加值到esi

每一轮取一个字符到ebx,累加ebx平方,累加ebx右移一位后+3后乘以ebx再减去ebx,然后esi乘以2(这样描述不清楚,具体看下文反汇编注释和注册机代码)

最后算出一个累加值和序列号字符串进行对比,没看错,是直接和字符串对比,输入的字符串会直接当成数字对待

image

输入的字符,这里esi是计算出来的累加和,下面那一行是输入的序列号,直接用ascii当数字来对比了!!!

image

注册机

注册码生成算法:因为对输入的用户名有要求,太长太短都不行,还得是能满足要求的字符,所以这里就使用随机字符串来生成满足要求的Name:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <time.h>
#define Random(x)(rand()%x)	// 生成x以内的随机数

// 参数:生成随机字符串长度
void GetRandomString(LPSTR str,SIZE_T nLength) {
	srand((int)time(NULL));	// 随机数种子
	for (size_t i = 0; i < nLength; i++) {
		switch (Random(2)){
		case 0: str[i] = 'a' + Random(26); break;
		case 1: str[i] = 'A' + Random(26); break;
		default: break;
		}
	}
}

bool SerialCheck(char* serial) {
	bool res = true;
	for (int i = 0; i < 3; i++) {
		char tmp = serial[i];
		if (0x21 <= tmp && tmp <= 0x7E) continue;
		res = false;
		break;
	}
	return res;
}

int main()
{
	int esi = 0;
	char name[20] = { 0 };
	char serial[20] = { 0 };
	bool flag = false;

	while (!flag) {
		memset(name, 0, sizeof(name));
		GetRandomString(name, 7);
		esi = 0;
		for (int i = 0; name[i]; i++)
		{
			int ebx = name[i];
			esi += ebx * ebx;
			esi += ebx * ((ebx >> 1) + 2);
			esi += esi;
		}
		*(int*)&serial = esi;
		if (SerialCheck(serial)) {
			std::cout << name << std::endl;
			std::cout << serial;
			break;

		}
	}
}

效果:

image

总结

算法不难,但写注册机还是有些麻烦的


评论