160个CrackMe系列-001-Acid_burn

selph
selph
发布于 2020-09-23 / 637 阅读
0
0

160个CrackMe系列-001-Acid_burn

我的个人博客:https://www.kn0sky.com
微信公众号:我可是会飞的啊

前言

初学逆向,目前以这网上盛传的“适合破解新手的160个CreakMe”为目标,来进行实践练习与学习

本次我所使用的环境和工具如下:

操作系统:Windows 10 2004(物理机)

工具:x32dbg

程序分析

image-20200917140950556

第1个程序是一个验证的程序,提供了两种验证方式,分别是:通过序列号和用户名验证,和单独通过序列号验证

这次分析的目标是:实现验证的破解,以及分析出序列号的算法

分析目标01:暴力破解两种验证方式

1.1 用户名/序列号验证暴力破解

根据字符串搜索定位到弹窗那块的代码部分

修改判断逻辑

0042FAF8 | 8B55 F0                  | mov edx,dword ptr ss:[ebp-10]           | [ebp-10]:"Enter your serial here !"
0042FAFB | 8B45 F4                  | mov eax,dword ptr ss:[ebp-C]            | [ebp-C]:"CW-6560-CRACKED"
0042FAFE | E8 F93EFDFF              | call acid burn.4039FC                   |
;和正确的序列号进行对比
0042FB03 | 75 1A                    | jne acid burn.42FB1F                    |
;这里改成|0x9090,也就是nop填充这个751A即可强行跳过判断
0042FB05 | 6A 00                    | push 0                                  |
0042FB07 | B9 CCFB4200              | mov ecx,acid burn.42FBCC                | 42FBCC:"Congratz !!"
0042FB0C | BA D8FB4200              | mov edx,acid burn.42FBD8                | 42FBD8:"Good job dude =)"
0042FB11 | A1 480A4300              | mov eax,dword ptr ds:[430A48]           |
0042FB16 | 8B00                     | mov eax,dword ptr ds:[eax]              |
0042FB18 | E8 53A6FFFF              | call acid burn.42A170                   |
0042FB1D | EB 18                    | jmp acid burn.42FB37                    |
0042FB1F | 6A 00                    | push 0                                  |
0042FB21 | B9 74FB4200              | mov ecx,acid burn.42FB74                | 42FB74:"Try Again!"
0042FB26 | BA 80FB4200              | mov edx,acid burn.42FB80                | 42FB80:"Sorry , The serial is incorect !"
0042FB2B | A1 480A4300              | mov eax,dword ptr ds:[430A48]           |
0042FB30 | 8B00                     | mov eax,dword ptr ds:[eax]              |
0042FB32 | E8 39A6FFFF              | call acid burn.42A170                   |
0042FB37 | 33C0                     | xor eax,eax                             |

1.2 序列号单独验证暴力破解

搜索字符串,定位到弹窗的代码部分

修改判断逻辑:跟上面那个判断方法和修改方法一样

0042F4CA | 8B45 F0                  | mov eax,dword ptr ss:[ebp-10]           | [ebp-10]:"666"
0042F4CD | 8B55 F4                  | mov edx,dword ptr ss:[ebp-C]            | [ebp-C]:"Hello Dude!"
0042F4D0 | E8 2745FDFF              | call acid burn.4039FC                   |

0042F4D5 | 75 1A                    | jne acid burn.42F4F1                    |
; 用nop填充这个75 1A即可无条件判断成功

0042F4D7 | 6A 00                    | push 0                                  |
0042F4D9 | B9 64F54200              | mov ecx,acid burn.42F564                | ecx:"Failed!", 42F564:"Congratz!"
0042F4DE | BA 70F54200              | mov edx,acid burn.42F570                | 42F570:"God Job dude !! =)"
0042F4E3 | A1 480A4300              | mov eax,dword ptr ds:[430A48]           |
0042F4E8 | 8B00                     | mov eax,dword ptr ds:[eax]              |
0042F4EA | E8 81ACFFFF              | call acid burn.42A170                   |
0042F4EF | EB 18                    | jmp acid burn.42F509                    |
0042F4F1 | 6A 00                    | push 0                                  |
0042F4F3 | B9 84F54200              | mov ecx,acid burn.42F584                | ecx:"Failed!", 42F584:"Failed!"
0042F4F8 | BA 8CF54200              | mov edx,acid burn.42F58C                | 42F58C:"Try Again!!"
0042F4FD | A1 480A4300              | mov eax,dword ptr ds:[430A48]           |
0042F502 | 8B00                     | mov eax,dword ptr ds:[eax]              |
0042F504 | E8 67ACFFFF              | call acid burn.42A170                   |
0042F509 | 33C0                     | xor eax,eax                             |

分析目标02:分析出两种序列号的算法

2.1 用户名/序列号的序列号分析

搜索字符串Sorry,发现存在两处地方用到了这个字符串:一个一个判断

第一个字符串是在登录是否成功这里判断的,这一块完整代码在1.1部分有,这里就只放关键部分了:

0042FAF8 | 8B55 F0                  | mov edx,dword ptr ss:[ebp-10]           | [ebp-10]:"123456"
0042FAFB | 8B45 F4                  | mov eax,dword ptr ss:[ebp-C]            | [ebp-C]:"CW-8528-CRACKED"
0042FAFE | E8 F93EFDFF              | call acid burn.4039FC                   |
0042FB03 | 75 1A                    | jne acid burn.42FB1F                    | 用户名/序列号验证

这里进行对比的那个字符串应该就是正确的值,用这个序列号去登录,即可登录成功

另一个字符串的位置在:

0042FA4D | A1 6C174300              | mov eax,dword ptr ds:[43176C]           | 0043176C:&"hello"
0042FA52 | E8 D96EFDFF              | call acid burn.406930                   | 获取字符串长度
0042FA57 | 83F8 04                  | cmp eax,4                               | 如果大于4就跳转进行序列号验证,如果小于4则直接Sorry
0042FA5A | 7D 1D                    | jge acid burn.42FA79                    |
0042FA5C | 6A 00                    | push 0                                  |
0042FA5E | B9 74FB4200              | mov ecx,acid burn.42FB74                | 42FB74:"Try Again!"
0042FA63 | BA 80FB4200              | mov edx,acid burn.42FB80                | edx:&"d稝", 42FB80:"Sorry , The serial is incorect !"
0042FA68 | A1 480A4300              | mov eax,dword ptr ds:[430A48]           |
0042FA6D | 8B00                     | mov eax,dword ptr ds:[eax]              |
0042FA6F | E8 FCA6FFFF              | call acid burn.42A170                   | MessageBox
0042FA74 | E9 BE000000              | jmp acid burn.42FB37                    |

判断用户名长度的,长度大于4则进行验证,长度小于4则直接返回验证失败

将jge改成jmp即可绕过用户名长度限制

经过反复测试发现,序列号的生成与用户名的首字符有关,比如1:4018,2:4100,a:7954,等

以及发现计算的数字会出现在内存地址0019E71E的位置上

继续测试,找到该地址是在哪里被填充的

image-20200921185113576

是在这里,4018被填充到0019E71E的,这里又是movsb指令,是从0019E6AC地方复制来的,下面来找找这个地址的值是哪里来的

经过又一遍的步进,发现上面不远处的这个函数执行完会在0019E6AC填充中间数字:

image-20200921185401368

进入函数接着步进发现了一个循环在填充中间数字:

image-20200921185505582

这里的EAX里的值就是4018,中间数值依然是之前就算好的,这里的代码是通过对10取余一个一个数字填到内存中去,要找到中间数字是怎么算的还得往前找,找EAX的值是哪来的:

image-20200921190429513

值是ESI地址里传给EAX 的,接着往上找ESI的地址(0019F72C)的值是哪来的:

image-20200921191156443

生气,往里面找了半天,结果这个值就在最外面一层就算好了,计算方法是:

最终结果(十六进制) = 首字符的ASCII码(十六进制) * 29h * 2h

这么算的话,2开头的用户名应该是 32h * 29h * 2h = 1004h = 4100,和之前测试结果吻合

注册机代码:

#include<stdio.h>
#include<windows.h>
#include<string.h>
int main() {
	char Username[10];
	printf_s("请输入用户名:");
	scanf_s("%s",Username,(unsigned int)sizeof(Username));

	int FirstASCII = Username[0];
	int Midcode = FirstASCII * 82;

	char* Front = (char*)"序列号为: CW";
	char Middle[10] = {0};
	sprintf_s(Middle,"%d",Midcode);
	char* Behind = (char*)"CRACKED";

	char Res[50] = {0};
	strcpy_s(Res,Front);
	strcat_s(Res, "-");
	strcat_s(Res, Middle);
	strcat_s(Res, "-");
	strcat_s(Res, Behind);
	printf("%s",Res);

	Sleep(10000);
	return 0;
}

效果展示:

image-20200921200106905

2.2 序列号单独验证序列号分析

通过修改不同的序列号进行验证,看见反汇编窗口中都跟同样的值进行比较,所以判断这个序列号单独验证是一个固定的值,是硬编码进去的验证,值为:Hello Dude!

总结

第一次独自分析破解CreakMe,遇到了挺多坑,这次让我学到了要去找某一个数值是怎么冒出来的,不妨先看看函数调用之前的参数是什么,也许这会有帮助(对应2.1部分)


评论