selph
selph
发布于 2022-08-15 / 202 阅读
0
0

新160个CrackMe练习:044-tsrh-crackme

算法难度:⭐⭐⭐⭐

爆破难度:⭐

信息收集

运行情况:

一开始就是Nag:

image

然后进入界面:

image

查壳与脱壳:

image

调试分析

首先是一个创建互斥量,然后接收一个错误,如果是特定错误就不执行程序,这是一种简单有效的防多开手段,然后就是创建窗口了,这里跟进窗口函数

image

去除Nag

窗口过程的开头:

image

第一个0x110号消息的分支里,存在一个MessageBoxA的调用,查阅资料[1]可知,创建对话框的时候,会有一个初始化消息会发送到消息循环,这个时候窗口还未显示出来,这个消息WM_INITDIALOG就是0x110号消息

Nag存在于这个消息里,而这里MessageBox调用前面有一个跳过的条件,这里如果要消除Nag,就直接改这个跳转语句jz为jmp即可

分析校验算法

进入之后就是switch-case里的多分支语句了,使用xray查看check按钮id=22b,按钮按下的消息码是0x111,直接找111消息码控件id为22b的分支:

首先是先获取Name到全局变量里(长度需要大于等于5)

image

接下来是Serial校验过程

这里调用了三个生成真码的函数,这一段反汇编的主要功能就是这三个函数,中间的这些对比可以忽视,是提升效率用的

image

先看第一个生成函数:

获取了Name长度,保存到全局变量里

然后拼接字符串:tsrh-一个数字-

image

第二个生成函数:

获取生成Serial的长度,遍历用户名,对于每个用户名字节,都进行一系列计算得到一个新的数值,以十六进制的形式拼接到生成Serial的后面

image

第三个生成函数:

这里分别从Name和生成Serial里取一个字节,异或(Name从头开始取,Serial从12偏移处开始取)

把结果变成大写字母,然后把该大写字母以int型赋值的形式,赋值到Serial第10字节处

最后调用对比函数

image

对比函数:逐字节对比,如果全都一样,则返回1,返回1则会跳转到成功提示弹窗处

image

注册机

注册码生成算法:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

#pragma region check
int midValue = 0;
char serial[100];
char name[100];
void GenSerial1() {
	midValue = strlen(name);
	sprintf(serial,"tsrh-%d-",midValue+0x7d3);
}
void GenSerial2() {
	int len = strlen(serial);
	int tmp = midValue;
	for (int i = 0; i < midValue; i++)
	{
		int tmp1 = name[i] + 0xc;
		int tmp2 = tmp1 - 0x11 + tmp1 - len;
		tmp1 ^= tmp2;
		sprintf(&serial[len], "%X", tmp1);

		len = strlen(serial);
	}
}
void GenSerial3() {
	for (int i = 0; i < 0x10; i++)
	{
		if (name[i] == 0)break;
		char tmpn = name[i]+1;
		char tmps = serial[i + 12];
		char c = tmpn ^= tmps;
		while (c < 'A')c += 8;
		while (c > 'Z')c -= 3;

		*(int*)&serial[i + 10] = c;
	}
	std::cout << serial;
}
#pragma endregion 校验函数

int main()
{
	std::cin >> name;
	GenSerial1();
	GenSerial2();
	GenSerial3();
}

效果:

image

总结

分析这类的窗口程序,如果知道常见的窗口消息是什么,那就很好定位到控件操作的事件

参考资料


评论