1. 逆向分析过程
先上手把玩一下, 从外观上看感觉和Chafe.1.exe差不了多少, 还是那个界面
 
 找到RegisterClassEx从而找到其对应的窗口过程
 
 找到对应的WM_COMMAND分支
 
 首先其修改了代码中的4个字节, 将其修改成0x00584554, 然后通过GetDlgItemInt获取了serial值
 
 这里其实原本就是这个值, 只不过作者为了确保是0x00584554, 再次写入
 
 获取了输入的serial值后, 将其压入栈内, 接着获取输入的name值, 将name进行hash运算, 最后获取了一个DWORD值, 这里可以看出有Chafe.2.exe的痕迹。其会将name遍历并每次取4个字节与0x58455443进行16次异或操作后, 得到一个hash值。
 
 接下来他从栈内获取了之前输入的serial值, 并与上一步生成的hash值相加生成了hashX。将hashX与FixCodePlace(也就是直接被修复成0x54, 0x45, 0x58, 0x00的位置, 即0x4012D9)进行异或操作。之后获取了hashX的高位WORD部分并将其与FixCodePlace的低位WORD进行相减。
 
 来看一下FixCodePlace到底在哪:
 
 接着作者把从窗口过程函数开始的248个字节(没错, 是代码段内的代码), 每次以4个字节为步长, 一共执行3E次, 将其与0不断的做异或操作, 最终生成的一个值FinalX与0xAFFCCFFB进行比较, 如果相同那就代表是最终的serial key
 注意, 这里从从窗口过程函数开始的248个字节中包含了那个FixCodePlace的位置, 因为这里本身就是窗口过程函数, 也就是说这个最终的值FinalX是会随着FixCodePlace的值变动而不停变化的。
整个算法非常清晰, 下面来写注册机
2. 编写注册机
这个注册机的编写重点在于如何倒推回去。首先可以确定的是我们必须要得到FixCodePlace这个FinalX到底是什么, 由于这个FinalX是由窗口过程函数的前248个字节不停的异或取得的。并且异或是具有还原性的, 所以这个值是可以得到的, 首先我们把这窗口过程的248个字节提取出来。并且将FinalX的值全部设置成0(这是为了取消这个值的影响), 进行还原。
 
 接着编写代码将FinalX的值还原出来, 这里解释一下第123条语句
 由于我们的计算机使用的是小端序, 所以高位存放在高地址内存, 低位存放在低地址内存
 这里计算FinalX的操作是按照4个字节为1组进行异或的。
 这样FinalX的值会被截断, 分成了3字节和1字节, 3字节放在低位内存, 而1字节放在高位内存
 我们要将其重新组合并放在一个DWORD里面
 所以1字节要放在DWORD的最高位也就是首位, 3字节放在DWORD剩下的3个位置
 
 完成这一步后就对其进行逆推即可, 首先计算出通过name获取的hash这里取名为NameHash, 原本的逻辑是:
DWORD NameAddSerial = NameHash + Serial;
DWORD FixCodePlace = 0x00584554;
FixCodePlace ^= NameAddSerial;
DWORD dwHighPartOfNameAddSerial = (NameAddSerial >> 0x10);  // 也就是HIWORD(NameAddSerial)
FixCodePlace -= dwHighPartOfnameAddSerial; // 也就是LOWORD(FixCodePlace) -= HIWORD(NameAddSerial)
// 接着计算窗口过程的248字节的hash值获取FinalX
现在我们要把逻辑改一下, 逆推回去最终计算出NameAddSerial这个值, 这个值被计算出来只要再减去NameHash就能求出Serial了
DWORD dwOrginFixCodePlace = 0x00584554;
// 利用异或的还原性, 由于低位值还进行了减法运算, 所以这里先把高位还原
WORD wHighPart = HIWORD(FinalX) ^ HIWORD(dwOrginFixCodePlace);
// 由于有LOWORD(FixCodePlace ) -= HIWORD(NameAddSerial)存在, 所以要加上(因为还原是逆过程)
// 由于NameAddSerial的高位部分我们已经做过还原了也就是wHighPart, 所以可以直接使用
WORD wLowPart = LOWORD(FinalX) + wHighPart;
// 别忘了还有异或还原
wLowPart ^= LOWORD(dwOrginFixCodePlace);
// 然后把这两部分组合到一起, 别忘了减去Serial值, 这就是最终的serial key了
MAKELONG(wLowPart, wHighpart) - NameHash;

 最终核心代码如下:
// 237
BYTE data[248] = {
	0x55, 0x8B, 0xEC, 0x83, 0xC4, 0xFC, 0x8B, 0x45, 0x0C, 0x83, 0xF8, 0x10, 0x75, 0x0D, 0x6A, 0x00,
	0xE8, 0x6B, 0x02, 0x00, 0x00, 0x33, 0xC0, 0xC9, 0xC2, 0x10, 0x00, 0x83, 0xF8, 0x0F, 0x75, 0x0E,
	0x8B, 0x45, 0x08, 0xE8, 0x18, 0x01, 0x00, 0x00, 0x33, 0xC0, 0xC9, 0xC2, 0x10, 0x00, 0x83, 0xF8,
	0x01, 0x75, 0x06, 0x33, 0xC0, 0xC9, 0xC2, 0x10, 0x00, 0x3D, 0x11, 0x01, 0x00, 0x00, 0x0F, 0x85,
	0xE7, 0x00, 0x00, 0x00, 0x8B, 0x45, 0x14, 0x3B, 0x05, 0x60, 0x31, 0x40, 0x00, 0x75, 0x1A, 0x6A,
	0x00, 0x68, 0x96, 0x30, 0x40, 0x00, 0x68, 0xA7, 0x30, 0x40, 0x00, 0xFF, 0x75, 0x08, 0xE8, 0x17,
	0x02, 0x00, 0x00, 0x33, 0xC0, 0xC9, 0xC2, 0x10, 0x00, 0x3B, 0x05, 0x58, 0x31, 0x40, 0x00, 0x74,
	0x0C, 0x3B, 0x05, 0x54, 0x31, 0x40, 0x00, 0x0F, 0x85, 0xAE, 0x00, 0x00, 0x00, 0xC7, 0x05, 0xD9,
	0x12, 0x40, 0x00, 0x54, 0x45, 0x58, 0x00, 0x6A, 0x00, 0x8D, 0x45, 0xFC, 0x50, 0x6A, 0x64, 0xFF,
	0x35, 0x50, 0x31, 0x40, 0x00, 0xE8, 0xBC, 0x01, 0x00, 0x00, 0x83, 0x7D, 0xFC, 0x00, 0x74, 0x5F,
	0x50, 0x6A, 0x14, 0x68, 0x6C, 0x31, 0x40, 0x00, 0xFF, 0x35, 0x54, 0x31, 0x40, 0x00, 0xE8, 0xAF,
	0x01, 0x00, 0x00, 0x85, 0xC0, 0x74, 0x48, 0xA1, 0x0B, 0x30, 0x40, 0x00, 0xBB, 0x6C, 0x31, 0x40,
	0x00, 0x03, 0x03, 0x43, 0x81, 0xFB, 0x7C, 0x31, 0x40, 0x00, 0x75, 0xF5, 0x5B, 0x03, 0xC3, 0x31,
	0x05, 0xD9, 0x12, 0x40, 0x00, 0xC1, 0xE8, 0x10, 0x66, 0x29, 0x05, 0xD9, 0x12, 0x40, 0x00, 0xBE,
	0xEC, 0x11, 0x40, 0x00, 0xB9, 0x3E, 0x00, 0x00, 0x00, 0x33, 0xDB, 0xEB, 0x04, 0x00, 0x00, 0x00, /*Fix Code Place*/
	0x00, /*lodsd*/ 0xAD, 0x33, 0xD8, 0x49, 0x75, 0xFA, 0x81
};
DWORD CalcLostCode()
{
	// 0x54, 0x45, 0x58, 0x00 original
	DWORD dwCompareValue = 0xAFFCCFFB;
	DWORD dwTimes = 0x3E;
	PDWORD pdwCodeBuf = (PDWORD)data;
	do
	{
		dwCompareValue ^= *pdwCodeBuf;
		++pdwCodeBuf;
		--dwTimes;
	} while (dwTimes);
	dwCompareValue = (dwCompareValue >> 8) | (dwCompareValue << 24);
	return(dwCompareValue);
}
DWORD GetSerialKey(LPSTR lpszName, DWORD dwFinalCode)
{
	DWORD dwCipher = 0x58455443;
	for (size_t nIdx = 0; nIdx < 0x10; ++nIdx)
	{
		dwCipher += *(DWORD *)&lpszName[nIdx];
	}
	DWORD dwOriginalCode = 0x00584554;
	WORD wHighPart = HIWORD(dwFinalCode) ^ HIWORD(dwOriginalCode);
	WORD wLowPart = (LOWORD(dwFinalCode) + wHighPart) ^ LOWORD(dwOriginalCode);
	DWORD dwRes = MAKELONG(wLowPart, wHighPart);
	return(MAKELONG(wLowPart, wHighPart) - dwCipher);
}
void CKeyGenDlg::OnBnClickedBtnGen()
{
	// TODO: 在此添加控件通知处理程序代码
	char szBuf[20] = {0};
	int iLen = 0;
	CString strName;
	// 获取输入的name值
	GetDlgItemText(IDC_EDIT_NAME, strName);
	iLen = strName.GetLength();
	if (strName.IsEmpty())
	{
		MessageBox("Name can't be empty!");
		return;
	}
	strcpy_s(szBuf, sizeof(szBuf), strName.GetBuffer());
	char* pszName = szBuf;
	RtlZeroMemory(szBuf + iLen, sizeof(szBuf) - iLen);
	
	DWORD dwVal = CalcLostCode();
	DWORD dwSerialKey = GetSerialKey(szBuf, dwVal);
	SetDlgItemInt(IDC_EDIT_SERIAL, dwSerialKey, FALSE);
}
(完)


















