[160CRACKME]Chafe.2

(一)分析

运行一下这个程序发现,这个程序没有确认按钮,推测是实时获取用户输入,然后在文本框中显示输入是否正确。
OD中打开,自动定位到了主函数。
在这里插入图片描述
GetModuleHandle获取窗口句柄,ExitProcess函数是退出程序的进程,中间那个401023函数就是程序运行的主体部分,F7步入,看到一些加载图标,加载光标,注册窗口的api函数等等,
在这里插入图片描述
直到ShowWindow函数,才显示出窗口,前面一直是在进行一些窗口的初始化操作
在这里插入图片描述
后面一个大循环,在循环里面看到GetMessage函数,这个函数可以实现获取源源不断的消息,因为所有在窗口上的输入消息,都会放到应用程序的消息队列里,然后再发送给窗口回调函数处理。
在这里插入图片描述
目的是了解程序如何对用户输入的文本进行操作,势必要跳出循环。
Ctrl+F2重新加载,这次直接按F9让程序跑飞,这样才能输入测试数据。
在这里插入图片描述
输入完测试数据之后,可以注意到在循环的下一步,程序开始获取文本框的输入,有个GetDlgItemInt函数,给它下个断点,点一下文本框,程序便停在了这个函数的位置,这样就可以看程序对数据做了什么处理。
首先要弄清楚输入文本都被存入了哪里。
在这里插入图片描述
MSDN文档查找一下GetDlgItemInt函数,这个函数意在实现int型读取用户输入文本框的内容并返回。pSuccess指向用户输入的序列号所在的内存单元的地址,即0x0018F920。
GetWindowTextA函数就是获取文本框的文本,把文本存入了Buffer指向的内存(从OD看出Buffer指向的地址是0x0040316C)
在这里插入图片描述
下拉发现在对用户名跟序列号处理的代码下面一些奇怪的汇编指令,且没有跳转指向输入正确的提示。推测这里是SMC。在这里插入图片描述
先分析一下程序对数据的处理。在这里插入图片描述

004012A3    .  A1 0B304000     mov     eax, dword ptr [40300B];常量0x58455443存入eax
004012A8    . BB6C314000       mov     ebx,0040316C           ;ASCII "123456"

004012AD    >  0303            add     eax, dword ptr [ebx]
004012AF    .  43              inc     ebx
004012B0    .  81FB 7C314000   cmp     ebx, 0040317C
004012B6    .^ 75 F5           jnz     short 004012AD;输入的用户名“123456”循环16次每次读取4个字节累加到常量0x58455443

004012B8    .  5B              pop     ebx;输入的序列号存入ebx
004012B9    .  03C3            add     eax, ebx
004012BB    .  3105 D9124000   xor     dword ptr [4012D9], eax;常量0x5845540x4012D9处的字节码)跟累加后的值异或【SMC004012C1    .  C1E8 10         shr     eax, 10;异或后右移16004012C4    .  66:2905 D9124  >sub     word ptr [4012D9], ax;异或后的值减去累加后的值,可以注意到0x4012D9指向的不是数据,而是那一段奇怪的代码的地址,明显在对0x4012D9的代码进行修改。【SMC

现在分析一下这部分SMC,之所以没有跳转指令跳转到提示正确的字符的位置,推测是因为SMC自修改出来的代码就是缺失的跳转语句。
但跳转到自修改出的跳转语句之前,还有一部分bx跟ax异或0x3E次的,
这边有个lods 取串指令(把源串中的元素(字或字节)逐一装入AL或AX中),从si寄存器存放的地址处,也就是0x4011EC处开始取串,然后必须要异或出固定的值0xAFFCCFFB才会有后来的跳转。

这里的循环后期写注册机的时候其实是可以忽略的,因为这个循环主要的作用是进一步验证SMC出的代码是不是jmp到yes字符串,而破解的话只要前面的数据处理就能爆破出来了。
在这里插入图片描述
理清程序的大体过程之后可以写注册机。

(二)注册机

源代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
using namespace std;
int main()
{	
	char usrname[20] = { 0 };
	int serial = 0x58455443, temp = 0, temp1 = 0,n;
	printf("username:");
	scanf("%s", usrname);
	for (int i = 0; i < 16; i++)
		serial += *(int *)(usrname + i);

	for (int j = 0; j < 0xFFFFFFFF; j++)
	{
		temp = (0x584554 ^ (serial + j))-((serial + j) >> 16);
		if (temp == 0x585426EB)
		{
			printf("serial:%u", j);
			break;
		}
	}
	return 0;
}

后来发现temp = (0x584554 ^ (serial + j))-((serial + j) >> 16);这一步可以优化,用分步运算,
先算高四位的值

(0x58 ^ ((serial + j)>>16))==0x5854

后算第四位的值,

(0x4554 ^ ((serial + j)&0x0000FFFF))-((serial + j) >> 16)==0x26EB

可以看出最后序列号与累加后的用户名的和其实等于一个常数,优化之后的程序源码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
using namespace std;
int main()
{	
	char usrname[20] = { 0 };
	int serial = 0x58455443, temp = 0, temp1 = 0,n;
	printf("username:");
	scanf("%s", usrname);
	for (int i = 0; i < 16; i++)
		serial += *(int *)(usrname + i);

	printf("Serial:%u\n", 0x580C3BA3 - serial);
	return 0;
}

(三)实现

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Em0s_Er1t

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值