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

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章