010破解(二)之算法分析一

本文深入分析了010Edit软件的注册算法,通过逆向工程解析了关键函数,揭示了用户名和序列号的加密过程。文章详细描述了如何通过IDA和OD工具跟踪并理解算法流程,最终实现序列号的自动生成。

010破解(二)之算法分析一

010破解(一)之暴力破解
暴力破解后,仍然想写一个010Edit的注册机,这篇接着上一篇分析

算法分析

分析关键函数:

  • 单步跟踪,找到访问用户名密码的代码,
  • 一步一步分析,加注释,
  • 反复推敲,找规律
  • 写代码验证

在关键函数出下断点,然后注册用户名和密码,监视程序运行
在这里插入图片描述
在这里插入图片描述
函数参数确定:

  • 参数一:0x9
  • 参数二:0x4389
  • 参数三:ecx---------ecx传参,应该是一个对象指针

内存窗口观察ecx对象内容,我们可以发现:

  • 字段一:QT数组偏移(不知道有啥用)
  • 字段二:用户名字符串地址
  • 字段三:序列号字符串地址
  • 字段四:一堆乱码,(没看出有什么用)
    1562729024551
    在这里插入图片描述

在这里插入图片描述
通过上篇可以知道sub_40A826是算法的关键函数,逐行分析这个函数,逆算法

;解密序列号
013BDBC0    55         PUSH EBP
013BDBC1    8BEC       MOV EBP,ESP
013BDBC3    6A FF      PUSH -0x1
013BDBC5    68 595E880>PUSH 010Edito.01885E59
013BDBCA    64:A1 0000>MOV EAX,DWORD PTR FS:[0]
013BDBD0    50         PUSH EAX
013BDBD1    83EC 18    SUB ESP,0x18
013BDBD4    53         PUSH EBX
013BDBD5    56         PUSH ESI
013BDBD6    57         PUSH EDI
013BDBD7    A1 F0CFE60>MOV EAX,DWORD PTR DS:[0x2E6CFF0]
013BDBDC    33C5       XOR EAX,EBP
013BDBDE    50         PUSH EAX
013BDBDF    8D45 F4    LEA EAX,DWORD PTR SS:[EBP-0xC]
013BDBE2    64:A3 0000>MOV DWORD PTR FS:[0],EAX
013BDBE8    8BF9       MOV EDI,ECX                               ; this
013BDBEA    8D5F 04    LEA EBX,DWORD PTR DS:[EDI+0x4]            ; ebx=this->username
013BDBED    C745 F0 00>MOV DWORD PTR SS:[EBP-0x10],0x0
013BDBF4    8BCB       MOV ECX,EBX                               ; ecx=this->username
013BDBF6    C747 30 00>MOV DWORD PTR DS:[EDI+0x30],0x0
013BDBFD    C747 18 00>MOV DWORD PTR DS:[EDI+0x18],0x0
013BDC04    C747 34 00>MOV DWORD PTR DS:[EDI+0x34],0x0
013BDC0B    FF15 B424E>CALL DWORD PTR DS:[<&Qt5Core.?isEmpty@QSt>; 判断用户名是否为空?
013BDC11    84C0       TEST AL,AL
013BDC13    0F85 75020>JNZ 010Edito.013BDE8E                     ; 为空跳到函数末尾
013BDC19    8D4F 08    LEA ECX,DWORD PTR DS:[EDI+0x8]            ; ecx=this->password
013BDC1C    FF15 B424E>CALL DWORD PTR DS:[<&Qt5Core.?isEmpty@QSt>; 判断密码是否为空?
013BDC22    84C0       TEST AL,AL
013BDC24    0F85 64020>JNZ 010Edito.013BDE8E                     ; 为空跳到函数末尾
013BDC2A    8D45 DC    LEA EAX,DWORD PTR SS:[EBP-0x24]           ; 局部变量var1
013BDC2D    8BCF       MOV ECX,EDI                               ; ecx=this
013BDC2F    50         PUSH EAX
013BDC30    E8 3ABF04F>CALL 010Edito.00409B6F                    ; var1=password
013BDC35    BE 1446E60>MOV ESI,010Edito.02E64614                 ; 获取999字符串地址
013BDC3A    8D9B 00000>LEA EBX,DWORD PTR DS:[EBX]                ; ebx=username
013BDC40    FF36       PUSH DWORD PTR DS:[ESI]
013BDC42    8BCB       MOV ECX,EBX
013BDC44    FF15 E82CE>CALL DWORD PTR DS:[<&Qt5Core.??8QString@@>; ???观察以下密码后面的数据有变化吗
013BDC4A    84C0       TEST AL,AL
013BDC4C    0F85 23020>JNZ 010Edito.013BDE75
013BDC52    83C6 04    ADD ESI,0x4                               ; esi=02E64614+4=02E64618=指向字符串0-9A-F
013BDC55    81FE 1846E>CMP ESI,010Edito.02E64618                 ; 比较02E64618 和 02E64618???
013BDC5B  ^ 7C E3      JL SHORT 010Edito.013BDC40                ; 循环
013BDC5D    8A5D DF    MOV BL,BYTE PTR SS:[EBP-0x21]             ; BL=k[3]
013BDC60    8A7D E1    MOV BH,BYTE PTR SS:[EBP-0x1F]             ; BH=k[5]
013BDC63    80FB 9C    CMP BL,0x9C
013BDC66    75 70      JNZ SHORT 010Edito.013BDCD8               ; 比较k[3]=0x9C FC AC

;下面都是当 k[3]=0x9C要执行的代码
013BDC68    8A45 DC    MOV AL,BYTE PTR SS:[EBP-0x24]             ; AL=k[0]
013BDC6B    3245 E2    XOR AL,BYTE PTR SS:[EBP-0x1E]             ; AL=k[0]^k[6]
013BDC6E    8845 E8    MOV BYTE PTR SS:[EBP-0x18],AL             ; [ebp-0x18]=al
013BDC71    8A45 DD    MOV AL,BYTE PTR SS:[EBP-0x23]             ; AL=k[1]
013BDC74    3245 E3    XOR AL,BYTE PTR SS:[EBP-0x1D]             ; AL=k[1]^k[7]
013BDC77    FF75 E8    PUSH DWORD PTR SS:[EBP-0x18]
013BDC7A    0FB6C8     MOVZX ECX,AL                              ; ECX=K[1]^k[7]&0xFF
013BDC7D    B8 0001000>MOV EAX,0x100
013BDC82    66:0FAFC8  IMUL CX,AX                                ; CX=(K[1]^k[7]&0xFF)*0x100
013BDC86    8A45 DE    MOV AL,BYTE PTR SS:[EBP-0x22]             ; AL=k[2]
013BDC89    32C7       XOR AL,BH                                 ; AL=k[2]^k[5]
013BDC8B    0FB6C0     MOVZX EAX,AL                              ; EAX=(K[2]^k[5]&0xFF)
013BDC8E    66:03C8    ADD CX,AX
013BDC91    0FB7F1     MOVZX ESI,CX                              ; esi=((K[2]^k[5]&0xFF)+(K[1]^k[7]&0xFF)*0x100)&0xFFFF
013BDC94    E8 AB9904F>CALL 010Edito.00407644                    ; 计算 k[0]和k[6] 结果不能等于0
013BDC99    0FB6C0     MOVZX EAX,AL                              ; AL=(k[0]^k[6]^0x18+0x3D)^0xA7
013BDC9C    56         PUSH ESI
013BDC9D    8947 1C    MOV DWORD PTR DS:[EDI+0x1C],EAX
013BDCA0    E8 23A704F>CALL 010Edito.004083C8                    ; EAX=((esi^0x7892+0x4D30)^0x3421&0xFFFF)/0xB
013BDCA5    8B4F 1C    MOV ECX,DWORD PTR DS:[EDI+0x1C]           ; ECX=(k[0]^k[6]^0x18+0x3D)^0xA7
013BDCA8    83C4 08    ADD ESP,0x8
013BDCAB    0FB7C0     MOVZX EAX,AX                              ; eax=eax&0xFFFF
013BDCAE    8947 20    MOV DWORD PTR DS:[EDI+0x20],EAX
013BDCB1    85C9       TEST ECX,ECX
013BDCB3    0F84 BC010>JE 010Edito.013BDE75                      ; ecx!=0第一个call的返回值不能等于0
013BDCB9    85C0       TEST EAX,EAX
013BDCBB    0F84 B4010>JE 010Edito.013BDE75                      ; eax!=0
013BDCC1    3D E803000>CMP EAX,0x3E8
013BDCC6    0F87 A9010>JA 010Edito.013BDE75                      ; eax<=0x3e8

验证用户名

013BDD8B    8D45 EC    LEA EAX,DWORD PTR SS:[EBP-0x14]
013BDD8E    50         PUSH EAX
013BDD8F    8D4F 04    LEA ECX,DWORD PTR DS:[EDI+0x4]
013BDD92    FF15 782BE>CALL DWORD PTR DS:[<&Qt5Core.?toUtf8@QStr>; 返回用户名ASCII
013BDD98    FF77 20    PUSH DWORD PTR DS:[EDI+0x20]              ; sub_4083C8的返回值
013BDD9B    33C0       XOR EAX,EAX
013BDD9D    C745 FC 00>MOV DWORD PTR SS:[EBP-0x4],0x0
013BDDA4    80FB FC    CMP BL,0xFC
013BDDA7    8D4D EC    LEA ECX,DWORD PTR SS:[EBP-0x14]           ; 用户名
013BDDAA    56         PUSH ESI                                  ; 0
013BDDAB    0F95C0     SETNE AL
013BDDAE    50         PUSH EAX                                  ; 1
013BDDAF    FF15 8C24E>CALL DWORD PTR DS:[<&Qt5Core.?data@QByteA>; Qt5Core.?data@QByteArray@@QAEPADXZ
013BDDB5    50         PUSH EAX                                  ; 返回用户名字符串
013BDDB6    E8 955004F>CALL 010Edito.00402E50                    ; 用户名转一个加密值
013BDDBB    8BD0       MOV EDX,EAX
013BDDBD    83C4 10    ADD ESP,0x10
013BDDC0    3855 E0    CMP BYTE PTR SS:[EBP-0x20],DL             ; CMP k[4] Ret&0xFF
013BDDC3    0F85 81000>JNZ 010Edito.013BDE4A                     ; 说明K[4]=Ret&0xFF
013BDDC9    8BCA       MOV ECX,EDX
013BDDCB    C1E9 08    SHR ECX,0x8                               ; ecx=Ret>>8
013BDDCE    3AF9       CMP BH,CL                                 ; k[5]
013BDDD0    75 78      JNZ SHORT 010Edito.013BDE4A               ; 说明k[5]=(Ret>>8)&0xFF
013BDDD2    8BCA       MOV ECX,EDX
013BDDD4    C1E9 10    SHR ECX,0x10
013BDDD7    384D E2    CMP BYTE PTR SS:[EBP-0x1E],CL
013BDDDA    75 6E      JNZ SHORT 010Edito.013BDE4A               ; 说明k[6]=(Ret>>0x10)%0xFF
013BDDDC    C1E8 18    SHR EAX,0x18                              ; ret>>0x18
013BDDDF    3845 E3    CMP BYTE PTR SS:[EBP-0x1D],AL
013BDDE2    75 66      JNZ SHORT 010Edito.013BDE4A               ; k[7]=(Ret>>0x18)&0xFF
013BDDE4    80FB 9C    CMP BL,0x9C
013BDDE7    75 0F      JNZ SHORT 010Edito.013BDDF8               ; k[4]=0x9C
013BDDE9    8B45 08    MOV EAX,DWORD PTR SS:[EBP+0x8]            ; 第一个参数0x9
013BDDEC    3B47 1C    CMP EAX,DWORD PTR DS:[EDI+0x1C]           ; [edi+1c]=sub_407644函数返回值

;到这里,满足条件后返回值就是0x2D了,算法分析到这也就算完了
013BDDEF    76 52      JBE SHORT 010Edito.013BDE43				;满足条件跳转,eax=0x2D返回

分析完代码你会发现算法最开始有一个比较k[3] (序列号第四个)与0x9C 0xFC 0xAC,不满足直接就返回E7,这里就可以猜测
k[3] =0x9C 0xFC 0xAC 任意一个都可以,也就是说他可能有三种不同的算法,下面我们先分析第一种k[3]=0x9C的情况在这里插入图片描述
一路分析下来,做一个总结:
算法:两个关键的序列号计算函数
在这里插入图片描述
用户名:通过分析我们可以发现sub_00402E50 函数是把用户名做了个加密,进去看了一下,也是做一些计算,我们用IDA看一下.
在这里插入图片描述
通过IDAF5后分析用户名是通过一个数组进行加密转换的
在这里插入图片描述
用OD把数组拷贝出来,为后面的编写注册机做准备
在这里插入图片描述

最后,用户名加密值与k4 k5 k6 k7有对应关系,都满足后判断第一个参数与sub_407644函数返回值比较,返回值大于等于第一个参数就成功了.
在这里插入图片描述
在这里插入图片描述
至此算法一就分析完成了

代码验证

思路:

  • 0x9C系列序列号是八位
  • 根据用户名的加密值可以得到 k4 k5 k6 k7
  • 第一个序列号计算函数:参数相关k0 k6 可以枚举出符合条件的k0
  • 第二个序列号计算函数:参数相关k1 k2 k5 k7 可以枚举出符合条件的k1 k2
VOID CalPassword::CrackKey1(CHAR* pUserName,CString& Password,DWORD arg)
{
	srand(time(NULL));
	BYTE Key[10] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA };
	BYTE k0 = 0;
	BYTE k1 = 0;
	BYTE k2 = 0;
	BYTE k3 = 0;
	BYTE k4 = 0;
	BYTE k5 = 0;
	BYTE k6 = 0;
	BYTE k7 = 0;
	BYTE k8 = 0;
	BYTE k9 = 0;

	//Key[3] = 0x9C 0xAC 0xFC
	//0x9C是8位序列号
	Key[3] = 0x9C;

	//计算用户名加密值与k4 k5 k6 k7有对应关系
	//k4 k5 k6 k7 逆用户名加密值
	//说明K[4]=Ret&0xFF
	//说明k[5]=(Ret>>8)&0xFF
	//说明k[6]=(Ret>>0x10)%0xFF
	//说明k[7]=(Ret>>0x18)&0xFF
	DWORD dwRetValue = rand() % 0x3e8 + 1;		//指定商的范围 0-0x3e8
	DWORD dwValue = sub_EncodeName(pUserName, 1, 0, dwRetValue);
	Key[4] = dwValue & 0xFF;
	Key[5] = (dwValue >> 8) & 0xFF;
	Key[6] = (dwValue >> 16) & 0xFF;
	Key[7] = (dwValue >> 24) & 0xFF;

	//计算k0和k6
	while (TRUE)
	{
		k0 = rand() % 0xFF;
		BYTE AL = ((k0 ^ Key[6] ^ 0x18) + 0x3D) ^ 0xA7;
		if (AL >= arg)
		{
			Key[0] = k0;
			//Key[6] = k6;
			break;
		}
	}
	while (TRUE)
	{
		k1 = rand() % 0xFF;
		k2 = rand() % 0xFF;
		//esi=((K[2]^k[5]&0xFF)+(K[1]^k[7]&0xFF)*0x100)&0xFFFF
		//EAX=((esi^0x7892+0x4D30)^0x3421&0xFFFF)/0xB
		//判断余数是否为0,为0返回商,不为0返回0
		//商<=0x3e8
		DWORD dwTemp1 = (k2 ^ Key[5]) & 0xFF;
		DWORD dwTemp2 = ((k1 ^ Key[7]) & 0xFF) * 0x100;
		DWORD dwESI = (dwTemp1 + dwTemp2) & 0xFFFF;
		DWORD dwEAX = ((((dwESI ^ 0x7892) + 0x4D30) ^ 0x3421) & 0xFFFF);
		if (dwEAX % 0xB == 0 && dwEAX/0xB == dwRetValue)
		{
			Key[1] = k1;
			Key[2] = k2;
			break;
		}
	}
	Password.Format(L"%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X", Key[0], Key[1],
		Key[2], Key[3], Key[4], Key[5], Key[6], Key[7], Key[8], Key[9]);
}

sub_EncodeName直接从IDA拷贝出来的,数组也是拷贝的

int __cdecl CalPassword::sub_EncodeName(const char *pUserName, int a2, char a3, int a4)
{
	const char *v4; // edx
	signed int v5; // esi
	signed int v6; // edi
	unsigned __int8 v7; // bl
	int v8; // eax
	int v9; // ecx
	int v10; // ecx
	int result; // eax
	unsigned __int8 v12; // [esp+8h] [ebp-10h]
	unsigned __int8 v13; // [esp+Ch] [ebp-Ch]
	unsigned __int8 v14; // [esp+10h] [ebp-8h]
	int v15; // [esp+14h] [ebp-4h]

	v4 = pUserName;
	v15 = 0;
	v5 = strlen(pUserName);
	v6 = 0;
	if (v5 <= 0)
		return 0;
	v12 = 0;
	v13 = 0;
	v7 = 15 * a4;
	v14 = 17 * a3;
	do
	{
		v8 = toupper((unsigned __int8)v4[v6]);
		v9 = v15 + m_EncodeArray[v8];
		if (a2)
			v10 = m_EncodeArray[v13]
			+ m_EncodeArray[v7]
			+ m_EncodeArray[v14]
			+ m_EncodeArray[(unsigned __int8)(v8 + 47)] * (m_EncodeArray[(unsigned __int8)(v8 + 13)] ^ v9);
		else
			v10 = m_EncodeArray[v12]
			+ m_EncodeArray[v7]
			+ m_EncodeArray[v14]
			+ m_EncodeArray[(unsigned __int8)(v8 + 23)] * (m_EncodeArray[(unsigned __int8)(v8 + 63)] ^ v9);
		result = v10;
		v15 = v10;
		v13 += 19;
		++v6;
		v14 += 9;
		v7 += 13;
		v12 += 7;
		v4 = pUserName;
	} while (v6 < v5);
	return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值