新CrackMe160之041 - crackme.2

与旧版032相同

  1. 运行程序有个提示界面阻挡了去路, 看来是要先把这个界面干掉, 提示信息说是需要reg.dat那我们就创建一个, 果然不再提示了直接进入到主界面了
    既然是去NAG, 那我们直接跳过这个文件验证, 004383EC处改为 jmp 00438428, 搞定~
    主界面什么作用都没有, 我们也把它当作NAG干掉吧, 因为程序是delphi写的, 我们这里需要了解delphi窗体的加载原理, 在delphi中, 第一个创建的窗体就是第一个加载的, 所以我们找到窗体的创建在哪里, OD中, 00438447, 00438459, 00438471, 三处即为三个窗体创建, 用IDR来分析更容易看出来
 EntryPoint
 004383DC    push       ebp
 004383DD    mov        ebp,esp
 004383DF    add        esp,0FFFFFFF4
 004383E2    mov        eax,4382E4
 004383E7    call       @InitExe
>004383EC    jmp        00438428
 004383EE    nop
 004383EF    nop
 004383F0    nop
 004383F1    mov        eax,43A760; gvar_0043A760:TFileRec
 004383F6    call       @Assign
 004383FB    mov        edx,80
 00438400    mov        eax,43A760; gvar_0043A760:TFileRec
 00438405    call       @ResetFile
 0043840A    call       IOResult
 0043840F    test       eax,eax
>00438411    je         00438428
 00438413    push       0
 00438415    push       4384A0; 'Missing!'
 0043841A    push       4384AC; 'Hey, where is my little Reg.dat\rI love it so much that I can't\rlive without it. Return it back! Arrgh!'
 0043841F    push       0
 00438421    call       user32.MessageBoxA
>00438426    jmp        00438488
 00438428    mov        eax,[00439A90]; ^Application:TApplication
 0043842D    mov        eax,dword ptr [eax]
 0043842F    call       TApplication.Initialize
 00438434    mov        ecx,dword ptr ds:[439B0C]; ^gvar_0043A758:TForm1
 0043843A    mov        eax,[00439A90]; ^Application:TApplication
 0043843F    mov        eax,dword ptr [eax]
 00438441    mov        edx,dword ptr ds:[4380C8]; TForm1
 00438447    call       TApplication.CreateForm
 0043844C    mov        ecx,dword ptr ds:[439B20]; ^gvar_0043A728:TForm2
 00438452    mov        eax,[00439A90]; ^Application:TApplication
 00438457    mov        eax,dword ptr [eax]
 00438459    mov        edx,dword ptr ds:[4379C0]; TForm2
 0043845F    call       TApplication.CreateForm
 00438464    mov        ecx,dword ptr ds:[439A6C]; ^gvar_0043A750:TForm3
 0043846A    mov        eax,[00439A90]; ^Application:TApplication
 0043846F    mov        eax,dword ptr [eax]
 00438471    mov        edx,dword ptr ds:[437F38]; TForm3
 00438477    call       TApplication.CreateForm
 0043847C    mov        eax,[00439A90]; ^Application:TApplication
 00438481    mov        eax,dword ptr [eax]
 00438483    call       TApplication.Run
 00438488    call       @Halt0
 那我们就把TForm1 和 TForm2的地址互换一下, 即让程序先创建TForm2, 在OD中我们找到下面这四行, 
 00438434    mov        ecx,dword ptr ds:[439B0C]; ^gvar_0043A758:TForm1
 00438441    mov        edx,dword ptr ds:[4380C8]; TForm1
 0043844C    mov        ecx,dword ptr ds:[439B20]; ^gvar_0043A728:TForm2
 00438459    mov        edx,dword ptr ds:[4379C0]; TForm2
 将它们改为
 00438434    mov        ecx,dword ptr ds:[439B20]; ^gvar_0043A758:TForm2
 00438441    mov        edx,dword ptr ds:[4379C0]; TForm2
 0043844C    mov        ecx,dword ptr ds:[439B0C]; ^gvar_0043A728:TForm1
 00438459    mov        edx,dword ptr ds:[4380C8]; TForm1

保存, 第二个NAG也去掉了

  1. 暴力破解, 4个选择框的change事件都到00437BD8这个方法来验证数据, 我们就下断点跟踪, 首先, 判断name长度有没有5位, 记录这边关键跳位置004383EC, 然后一路向下我们看到了算法, 看到了很多对比, 我们都先不管, 一直执行到最后的关键跳00437CC9, 这时我们4位数是乱选的, 到这边就跳过了, , 本次调试就结束了, 说明, 不跳过就是成功分支, 所以我们让第一次判断直接跳入成功分支, 将004383EC改为jmp 00437CCB, 保存测试~ 随便选择一个数ok亮起, 暴破完成~

  2. 正常破解, 由上一步, 我们进入00437BD8这个方法详细分析, 用户名输入abcde, 太长也没用, 程序只取了0234这4位来验证的, 验证码第一位选1, 即当前为1, 0, 0, 0, 单步跟踪, 到00437C16处, 取用户名第一位a=97, 除于10 = 9, 同理得到其它3位为c/10=9, d/10=10, e/10=10, 4位取值计算完成, 进入校验环节, 分别判断计算结算有没有大于10的, 有的话继续除于10, 接着与输入的4位验证码进行对比, 由此, 算法就已经清晰了, 做了正反向计算, c代码如下:

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

int main(){
	while(1==1){
		char rlt[5] = {0};
		char user[20] = {0};
		printf("请选择 1.用户名(5~20位) 2.系列号(4位数字): ");
		int inp;
		scanf("%d",&inp);
		if(inp == 2){
			printf("请输入系列号(4位): ");
			scanf("%s", rlt);
			int len = strlen(rlt);
			if(len != 4){
				printf("系列号必须为4位");
				continue; 
			}
			int a,i,j;
			srand((unsigned int)time(0));
			for(i=0,j=0; i<4; i++,j++){
				a = rlt[i] - 0x30; //字符数字转整形数字 
				if(a == 1) { a = 10; }
				if(a == 0) { a = 1; } else  //不可见符号
				if(a == 2) { a = 21; } else  //不可见符号
				if(a == 3) { a = 30 + rand() % 8 + 2; } else //0~7 +2 = 2~9
				if(a >= 4 && a <= 10) { //40~109 可见符号
					a *= 10;
					a += rand() % 10; //0~9
				}
				user[j] = a;
				if(i == 0){ j++; }
			}
			user[1]='?'; //第二位任意 
			printf("用户名为: %d,%d,%d,%d=%s\n", user[0],user[2],user[3],user[4],user);
		} else {
			printf("请输入用户名(5~20位): ");
			scanf("%s", user);
			int len = strlen(user);
			if(len < 5){
				printf("用户名长度小于5位");
				continue; 
			}
			int i, j, a = 0;
			for(i=0,j=0; i<5; i++,j++){
				a = user[i] / 10;
				while(a >= 10){ a = a / 10; }
				rlt[j] = a+0x30; //整形数字转字符数字 
				if(i == 0) i++;
			}
			printf("系列号为: %s\n", rlt);
		}
	}
	return 0;
}

运行示例:
请输入用户名(5~20位): abcde
系列号为: 9911
注意: 用户名输入完后就算当前系列号是对的, 也需要改变一下再改回来OK按钮才会亮起

 
 
本节高手录制的视频,点击前往查看

 
 
 

使用的工具连接(工具有点多有点大,可以先下OD,其它的后面慢慢下) 点击前往下载

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

posted @ 2024-12-19 13:25  hankerstudio  阅读(4)  评论(0)    收藏  举报