新手破解练习Crackme160之091 - crueme

  1. 去NAG
    程序一运行就有个密钥文件验证提示框, 我们可以先跳过~, OD载入程序一眼就看到了文件读取并弹框的代码, 前面这些代码看, 并没有对文件进行校验, 所以我们可以直接创建一个CRUEME.DAT的空文件, 也可以将00401065处的jnz改为jmp, 达到跳过提示框的操作~
     

  2. 暴力破解
    搜索关键字"False password - Try again" 定位到 004012DE, 将push 0x402254 改为 push 0x402236 失败的字符串改成成功的字符串, 保存搞定~
     

  3. 正常破解
    在004012DE上面看到了004012BE处 user32.GetDlgItemTextA, 下个断点, 看来下一行就是关键算法了, 进入方法
    1). 判断是不是8位, 不是的话, 置空8位并返回失败(eax=0)
    2). 我们输入8位abc12345来验证,
    3). 每一位循环与0x50异或, 得123abcde
    4). 再将结果每一位循环加上循环变量(倒序), 得999fffff
    5). 结果再每2位异或得005F0000(开)
    6). 得到的4位再分别两位异或结果再异或得到最后一位((00^5F) ^ (00^00))=5F
    7). 结果如果是0则返回失败
    8). 取出第4).步得到的结果999fffff, 循环异或6).步的结果5F, 得fff99999
    9). fff99999 与固定串 "???罾... 异或得 D軺???...
    10). 与固定串 籇3酞U"?? 对比, 如果不相等失败退出, 相等则成功退出
    退出算法方法, 来到004012C7关键跳, 进入成功分支后又马上进入到失败分支了~ 这个有点懵了, 这是不是说明显上面的算法是没用的?
    看着好像没办法了~ 从头再看一下代码, 发现有个方法到处都是, 它就是 call dword ptr ds:[0x402364] , 我们进入这个方法看下(对应行上按Enter键即可进入方法), 方法入口下个断点,
    重新按check按钮, 看是否会进这个方法, 果然 还是有进来~ 看到这个方法后面有非常多的系统方法的调用~ 看来这个方法是一个公共方法, 单步跟踪每次进入这个方法与退出这个方法回到的地方, 最终可以知道按验证按钮后, 第一个调用这个方法的系统方法是<jmp.&USER32.SendDlgItemMessageA>, 返回到00401239, 这行又是一个方法call 00401B73, 我们进入方法看下, 看到是一堆的加减乘除运算, 嗯~ 还有0xFF * 0xFF次的循环(65025次), 代码如下:

	00401B73   .  33FF          xor edi,edi
	00401B75   .  33F6          xor esi,esi
	00401B77   .  85C0          test eax,eax
	00401B79   .  0F84 0F010000 je crueme.00401C8E
	00401B7F   .  A3 72214000   mov dword ptr ds:[0x402172],eax
	00401B84   .  BB 1D214000   mov ebx,crueme.0040211D                          ;  输入串
	00401B89   >  B9 FF000000   mov ecx,0xFF						; 第一层循环
	00401B8E   >  8B03          mov eax,dword ptr ds:[ebx]				; 第二层循环
	00401B90   .  05 87D61200   add eax,0x12D687
	00401B95   .  0343 08       add eax,dword ptr ds:[ebx+0x8]
	00401B98   .  2D 672B0000   sub eax,0x2B67
	00401B9D   .  33D2          xor edx,edx
	00401B9F   .  F725 72214000 mul dword ptr ds:[0x402172]
	00401BA5   .  8B53 04       mov edx,dword ptr ds:[ebx+0x4]
	00401BA8   .  81C2 EAF48F04 add edx,0x48FF4EA
	00401BAE   .  33C2          xor eax,edx
	00401BB0   .  8B53 08       mov edx,dword ptr ds:[ebx+0x8]
	00401BB3   .  81EA 015CBC00 sub edx,0xBC5C01
	00401BB9   .  2B13          sub edx,dword ptr ds:[ebx]
	00401BBB   .  81C2 672B0000 add edx,0x2B67
	00401BC1   .  33C2          xor eax,edx
	00401BC3   .  0D E2599302   or eax,0x29359E2
	00401BC8   .  03F8          add edi,eax
	00401BCA   .  233D 62214000 and edi,dword ptr ds:[0x402162]
	00401BD0   .  8B03          mov eax,dword ptr ds:[ebx]
	00401BD2   .  2D 87D61200   sub eax,0x12D687
	00401BD7   .  2B43 08       sub eax,dword ptr ds:[ebx+0x8]
	00401BDA   .  05 CE560000   add eax,0x56CE
	00401BDF   .  33D2          xor edx,edx
	00401BE1   .  F735 72214000 div dword ptr ds:[0x402172]
	00401BE7   .  8B53 04       mov edx,dword ptr ds:[ebx+0x4]
	00401BEA   .  81EA EAF48F04 sub edx,0x48FF4EA
	00401BF0   .  33C2          xor eax,edx
	00401BF2   .  8B53 08       mov edx,dword ptr ds:[ebx+0x8]
	00401BF5   .  81C2 015CBC00 add edx,0xBC5C01
	00401BFB   .  0313          add edx,dword ptr ds:[ebx]
	00401BFD   .  81EA CE560000 sub edx,0x56CE
	00401C03   .  33C2          xor eax,edx
	00401C05   .  25 E2599302   and eax,0x29359E2
	00401C0A   .  03F0          add esi,eax
	00401C0C   .  0B35 66214000 or esi,dword ptr ds:[0x402166]
	00401C12   .  E2 02         loopd short crueme.00401C16
	00401C14   .  EB 05         jmp short crueme.00401C1B
	00401C16   >^ E9 73FFFFFF   jmp crueme.00401B8E
	00401C1B   >  81C7 11090000 add edi,0x911
	00401C21   .  81EE 11090000 sub esi,0x911
	00401C27   .  FE0D 33214000 dec byte ptr ds:[0x402133]
	00401C2D   .  803D 33214000>cmp byte ptr ds:[0x402133],0x0
	00401C34   .^ 0F85 4FFFFFFF jnz crueme.00401B89
	00401C3A   .  C605 33214000>mov byte ptr ds:[0x402133],0xFF
	00401C41   .  BB 1D214000   mov ebx,crueme.0040211D                          ;  输入串
	00401C46   .  B9 0E000000   mov ecx,0xE
	00401C4B   >  C603 00       mov byte ptr ds:[ebx],0x0
	00401C4E   .  43            inc ebx                                          ;  crueme.0040211D
	00401C4F   .^ E2 FA         loopd short crueme.00401C4B
	00401C51   .  81C7 AFE3FDEF add edi,0xEFFDE3AF
	00401C57   .  75 35         jnz short crueme.00401C8E
	00401C59   .  81C6 238D94A4 add esi,0xA4948D23
	00401C5F   .  75 2D         jnz short crueme.00401C8E
	00401C61   .  BE 2C224000   mov esi,crueme.0040222C                          ;  for input
	00401C66   .  83C6 0A       add esi,0xA
	00401C69   .  BF 6F224000   mov edi,crueme.0040226F                          ;  ASCII "Correct password - Good work!"
	00401C6E   .  B9 1D000000   mov ecx,0x1D
	00401C73   .  F3:A4         rep movs byte ptr es:[edi],byte ptr ds:[esi]
	00401C75   .  68 6F224000   push crueme.0040226F                             ;  ASCII "Correct password - Good work!"
	00401C7A   .  68 F2030000   push 0x3F2
	00401C7F   .  FF75 08       push dword ptr ss:[ebp+0x8]
	00401C82   .  B0 15         mov al,0x15
	00401C84   .  FF15 64234000 call dword ptr ds:[0x402364]                     ;  crueme.00401493
	00401C8A   .  33C0          xor eax,eax
	00401C8C   .  EB 05         jmp short crueme.00401C93
	00401C8E   >  B8 01000000   mov eax,0x1
	00401C93   >  C3            retn

好家伙~ 这个肯定没办法逆运算了吧? 单步跟踪也没办法了, 这得跟踪到猴年马月去, 写个程序来暴破, 看是否能写出来, 代码中有几处是取注册码的4位, 8位的, 所以我们为了方便, 规定注册码为4位, 8位 或 12位, 先试下4位的, 看能否有答案,
接下来开始写代码, 分别对 1). 纯数字 2). 纯字母 3).字母+数字 4). 纯符号 5).符号+数字 6). 符号+字母 7).符号+数字+字母 来写暴破代码
经验证, 纯数字与纯字母, 纯符号都没有找到符合条件的, 符号+数字与没有,
符号+小写字母找到了很多, 例:

    时间: 6.756秒, 注册码为: "*q`
    时间: 6.757秒, 注册码为: "*q@
    时间: 6.761秒, 注册码为: "*q (注意后面一个是空格)
    时间: 21.779秒, 注册码为: "j"?
    时间: 21.830秒, 注册码为: "j@*
    时间: 21.835秒, 注册码为: "j@j
    时间: 22.163秒, 注册码为: "jh}
    时间: 22.248秒, 注册码为: "jp`
    时间: 22.249秒, 注册码为: "jp@
    时间: 22.254秒, 注册码为: "jp (注意后面一个是空格)

符号+大写字母找到了很多, 例:

    时间: 6.597秒, 注册码为: "*A*
	时间: 6.603秒, 注册码为: "*AJ
	时间: 17.248秒, 注册码为: "B#?
	时间: 17.672秒, 注册码为: "BS}

符号+大小写字母一起的也找到了很多(与上面去重后), 例: (没等全跑完, 后面时间太长了)

    时间: 14.459秒, 注册码为: "*Aj
	时间: 47.063秒, 注册码为: "j@J

符号+数字+字母(从上面测试可知这几个符号("*@}?#`)容易成功, 所以把它们放最前面去加快查找速度), 找到特别多, 例:

    时间: 49.384秒, 注册码为: "2s@
	时间: 49.385秒, 注册码为: "2s`
	时间: 49.390秒, 注册码为: "2s (注意后面一个是空格)
	时间: 49.560秒, 注册码为: "2C*
	时间: 49.569秒, 注册码为: "2Cj
	时间: 49.574秒, 注册码为: "2CJ
	时间: 75.615秒, 注册码为: "j8*
	时间: 75.624秒, 注册码为: "j8j
	时间: 75.629秒, 注册码为: "j8J

得到正确的注册码后,验证成功~ 提示框也显示了"Correct password - Good work!", 程序就不会进入到上面那段没用的算法中了, 看关键代码,如下:

    00401239   .  E8 35090000   call crueme.00401B73                        ;真正算法
    0040123E   .  50            push eax                                    ;将验证结果存起来了,下面这段代码是没用了,作者用来迷惑我们的,看起来也像是算法
    0040123F   ?  BF 01000000   mov edi,0x1
    00401244   .  E8 FD080000   call crueme.00401B46
    00401249   .  B9 08000000   mov ecx,0x8
    0040124E   .  BF 85214000   mov edi,crueme.00402185
    00401253   .  BE 34214000   mov esi,crueme.00402134
    00401258   >  8A07          mov al,byte ptr ds:[edi]
    0040125A   .  8A1E          mov bl,byte ptr ds:[esi]
    0040125C   .  32C3          xor al,bl
    0040125E   .  D0C0          rol al,1
    00401260   .  8806          mov byte ptr ds:[esi],al
    00401262   .  47            inc edi
    00401263   .  46            inc esi
    00401264   .^ E2 F2         loopd short crueme.00401258
    00401266   .  58            pop eax                                          ;  crueme.0040123E 直到这里又把上面的存的结果取出来了
    00401267   .  85C0          test eax,eax
    00401269   .  0F84 84000000 je crueme.004012F3                               ; 这边才是真正的关键跳, 失败就会进入最前面那段没用的假算法去

到此就算正常破解完成了, 4位以上的就不玩了, 4位都需要那么久时间才能暴破出来, 再长就没办法等了~
这里是暴破代码: keygen.c

	#include <stdio.h>
	#include <string.h>
	#include <time.h>

	int chk(char data[5]);
	int genPassword(int index, int len, char * testPassword);

	int main(int argc, char** argv) {
		clock_t s = clock(), e;
		char *data = "\"@*`?#~!$%^&()-+{}|:;'<>,./\\ abcdefghijklmnopqrstuvwxyz"; // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; //"0123456789"; //这里的符号只验证的键盘上有的符号, 其它特殊符号就没考虑了
		char rlt[5] = {0};
		int ret = 1, len = strlen(data);
		
		for(int i=0; i<len; i++){
			rlt[0] = data[i];
			for(int j=0; j<len; j++){
				rlt[1] = data[j];
				for(int k=0; k<len; k++){
					rlt[2] = data[k];
					for(int l=0; l<len; l++){
						rlt[3] = data[l];
						ret = chk(rlt);
						//if(ret == 0) goto end; //只找一个 
						if(ret == 0){ //找所有 
							e = clock();
							double t = ((double)(e - s)) / CLOCKS_PER_SEC;
							printf("时间: %.3f秒, 注册码为: %s\n", t, rlt);
						}
					}
				}
			}
		}
		
		//genPassword(0, 4, rlt); //递归,300多秒才能出结果 "*A* 
		
		/*end:
		if(ret == 0) printf("时间: %.5f秒, 注册码为: %s", t, rlt);
		else */
		e = clock();
		double t = ((double)(e - s)) / CLOCKS_PER_SEC;
		printf("时间: %.3f秒, 结束~", t);
		getchar();getchar();
		return 0;
	}

	int genPassword(int index, int len, char * testPassword){ //递归写法 从大佬那边复制来的
		int ret = 1;
		if(index == len){
			//printf("%s,", testPassword);
			ret = chk(testPassword);
			if(ret == 0){
				printf("注册码为: %s\n", testPassword);
				return 0;
			}
			return 1;
		}
		for(int i=0x20; i<0x7F; i++) { //字符区域
			testPassword[index] = (char)i;
			ret = genPassword(index+1, len, testPassword);
			if(ret == 0){
				return 0;
			 }
		}
		return 1;
	}

	int chk(char* data){
		long long eax = 0, edx = 0, edi = 0, esi = 0, tmp1 = 0, tmp2 = 0;
		long d4 = 0;
		for(int i=3; i>=0; i--){
			d4 = d4 << 8;
			d4 |= data[i];
		}
		eax = d4;
		eax += 0x12D687;
		eax += 0; //因为4位,所以8位后的4位地址是0
		eax -= 0x2B67;
		edx = 0;
		eax *= 4; //长度是4位
		
		edx = 0; //取4位后的4位,也是0
		edx += 0x48FF4EA;
		eax ^= edx;
		
		edx = 0; //取8位后的4位地址 0
		edx -= 0xBC5C01;
		edx -= d4;
		edx += 0x2B67;
		eax ^= edx;
		eax |= 0x29359E2;
		tmp1 = eax;

		eax = d4;
		eax -= 0x12D687;
		eax -= 0; //8位后0
		eax += 0x56CE;
		edx = 0;
		edx = eax % 4;
		eax /= 4; //长度4
		
		edx = 0; //4位后0
		edx -= 0x48FF4EA;
		eax ^= edx;
		
		edx = 0; //8位后0
		edx += 0xBC5C01;
		edx += d4;
		edx -= 0x56CE;
		eax ^= edx;
		eax &= 0x29359E2;
		tmp2 = eax;
		
		for(int i=0; i<0xFF; i++){
			for(int j=0; j<0xFF; j++){
				edi += tmp1;
				edi &= 0x15263748;
				
				esi += tmp2;
				esi |= 0x596A7B8C; 
			}
			edi += 0x911;
			esi -= 0x911;
		}
		edi += 0xEFFDE3AF;
		esi += 0xA4948D23;
		edi &= 0xFFFFFFFF;
		esi &= 0xFFFFFFFF;
		if(edi > 0 || esi > 0) return 1;
		return 0;
	}

 
 
 
这是160个软件part1
这是160个软件part2

1~160每个破解过程,在吾爱破解论坛都有高手破解过了,也有整理好现成的, 我这边主要就是自己动手操作的过程,与他们的不太一样
附上高手们的连接: 点击前往查看
使用的工具连接(工具有点多有点大,可以先下OD,其它的后面慢慢下) 点击前往下载

新人入门教程"玩玩破解,写给新人看" 点击前往查看
我就是从这里开始的,对我这样的小白感觉超级友好~

下面是我的OD的界面布局,我觉得这4个是最常用的界面,其它的我基本上没用到~
OD界面布局

posted @ 2024-07-05 10:18  hankerstudio  阅读(50)  评论(0)    收藏  举报