第一題解決以後,火燒眉毛地進入第二題,但是這猝不及防的難度提高,直接耗費了三天的時間來分析。這題是純算法的大數運算,很容易就找到算法關鍵點,可是分析算法須要耗費些時間。Talk is cheap, show you the process...python
int __cdecl main(int argc, const char **argv, const char **envp) { signed int v3; // ecx@1 int v4; // esi@3 signed int v5; // edx@3 char v6; // al@4 int v7; // esi@11 int v8; // esi@11 signed int v9; // eax@13 int v10; // edi@13 int v11; // eax@13 char *v12; // ecx@13 int v13; // ST10_4@14 int v14; // esi@14 int v15; // eax@14 int v16; // esi@14 int v17; // eax@14 char v19[260]; // [sp+8h] [bp-4130h]@1 char v20; // [sp+10Ch] [bp-402Ch]@10 char v21; // [sp+211Ch] [bp-201Ch]@11 int v22; // [sp+4134h] [bp-4h]@10 sub_401BE0(aPediyCtf2017); sub_401BE0(aCrackmeByLelfe); sub_401BE0(aPleaseInputKey); fgets(v19, 260, &stru_4090E0); v3 = strlen(v19) - 1; if ( v3 < 8 || v3 > 20 ) { sub_401BE0(aKeyLenErrorD__); return 0; } v4 = 0; v5 = 0; v19[v3] = 0; if ( v3 > 0 ) { do { v6 = v19[v5]; if ( v6 <= 48 || v6 > 57 ) ++v4; ++v5; } while ( v5 < v3 ); if ( v4 ) { sub_401BE0(aKeyFormatError); return 0; } } sub_4012C0(&v20, v5); v22 = 0; sub_4014E0(v19); nullsub_1(&v20); sub_401730(9); nullsub_1(&v20); while ( 1 ) { sub_401350(v19); LOBYTE(v22) = 1; v7 = sub_401840(&v21); v8 = sub_401730(9) + v7; nullsub_1(&v20); if ( v8 || sub_4013A0(&v20) % 2 != 1 ) goto LABEL_16; v9 = sub_4013A0(&v20); v10 = sub_4013B0(v9 >> 1); v11 = sub_4013B0(0); v12 = &v21; if ( v10 == v11 ) break; LABEL_17: LOBYTE(v22) = 0; sub_401390(v12); if ( v8 ) { sub_401BE0(aWrongKey___); goto LABEL_19; } } v13 = sub_4013A0(&v21) - 1; v14 = 1 - sub_4013A0(&v21); v15 = sub_4013A0(&v20); v16 = sub_4013E0(&v21, v15 + v14, 1, v13, 0); v17 = sub_4013A0(&v21); if ( sub_4013E0(&v21, 0, 1, v17 - 1, 1) + v16 ) { v8 = 0; LABEL_16: v12 = &v21; goto LABEL_17; } sub_401BE0(aWellDone); LOBYTE(v22) = 0; sub_401390(&v21); LABEL_19: v22 = -1; sub_401390(&v20); return 0; }
這是F5得出的反編譯結果,太過於複雜,經過分析將之優化獲得算法
int __cdecl main(int argc, const char **argv, const char **envp) { signed int key_len; // ecx@1 int v4; // esi@3 signed int v5; // edx@3 char v6; // al@4 int v7; // eax@10 int v8; // edx@10 int v9; // edx@11 int v10; // eax@11 int v11; // edx@11 int v12; // esi@11 int v13; // eax@13 signed int v14; // edi@13 signed int v15; // eax@13 char *v16; // ecx@13 int v17; // ST10_4@14 int v18; // esi@14 int v19; // eax@14 unsigned int v20; // esi@14 int v21; // eax@14 char key[260]; // [sp+8h] [bp-4130h]@1 char big_num1; // [sp+10Ch] [bp-402Ch]@10 char big_num2; // [sp+211Ch] [bp-201Ch]@11 int v26; // [sp+4134h] [bp-4h]@10 sub_401BE0(aPediyCtf2017); sub_401BE0(aCrackmeByLelfe); sub_401BE0(aPleaseInputKey); fgets(key, 260, &stru_4090E0); key_len = strlen(key) - 1; if ( key_len < 8 || key_len > 20 ) { sub_401BE0(aKeyLenErrorD__); return 0; } v4 = 0; v5 = 0; key[key_len] = 0; if ( key_len > 0 ) { do { v6 = key[v5]; if ( v6 <= 48 || v6 > 57 ) ++v4; ++v5; } while ( v5 < key_len ); if ( v4 ) { sub_401BE0(aKeyFormatError); return 0; } } random_init_big_num(&big_num1); v26 = 0; init_big_num(&big_num1, key); nullsub_1(); multiple(v7, v8, &big_num1, 9); nullsub_1(); while ( 1 ) { init(&big_num2, key); LOBYTE(v26) = 1; v10 = multiple_big_num(&big_num2, v9, &big_num1, &big_num2); v12 = multiple(v10, v11, &big_num1, 9) + v10; nullsub_1(); if ( v12 || get_len(&big_num1) % 2 != 1 ) goto END_LABEL; v13 = get_len(&big_num1); v14 = get_ch(&big_num1, v13 >> 1); v15 = get_ch(&big_num2, 0); v16 = &big_num2; if ( v14 == v15 ) break; LABEL_17: LOBYTE(v26) = 0; sub_401390(v16); if ( v12 ) { sub_401BE0(aWrongKey___); goto LABEL_19; } } v17 = get_len(&big_num2) - 1; v18 = 1 - get_len(&big_num2); v19 = get_len(&big_num1); v20 = compare_big_num(&big_num1, &big_num2, v19 + v18, 1, v17, 0); v21 = get_len(&big_num2); if ( compare_big_num(&big_num1, &big_num2, 0, 1, v21 - 1, 1) + v20 ) { v12 = 0; END_LABEL: v16 = &big_num2; goto LABEL_17; } sub_401BE0(aWellDone); LOBYTE(v26) = 0; sub_401390(&big_num2); LABEL_19: v26 = -1; sub_401390(&big_num1); return 0; }
再次通過簡化獲得僞代碼dom
BOOL Verify(const char *key) { //在這次算法中,DWORD *big_num指向一個大緩衝區 //big_num[0]存儲0x4080C8(magic number),big_num[1]存儲大數位數 //big_num[1026]處開始存儲索引,即big_num[2+big_num[1026]]存儲第0位數,big_num[2+big_num[1026+i]]存儲第i位數 //big_num[2050]和big_num[2051]處存儲GetTickCount()獲得的隨機數種子 DWORD *big_num1=0x12BF58; DWORD *big_num2=0x12BF68; //tmp_garbage表明在調試時不用注意的多餘參數 int tmp_garbage[5]; signed int key_len = strlen(key) - 1; if (key_len < 8 || key_len>20) return FALSE; key[key_len] = 0; if (key_len > 0) { for (int i = 0; i < key_len; i++) { if (key[i] <= '0' && key[i]>'9') return FALSE; } } //經過GetTickCount()得到隨機種子,同時對緩衝區內容進行亂序排列 random_init_big_num(big_num1); //將key中的數字存儲於big_num1中,輸入7521,反序存儲爲1257(一千二百五十七) init_big_num(big_num1, key); //big_num1*=9 multiple(tmp_garbage[0], tmp_garbage[1], big_num1, 9); while (true) { random_init_big_num(big_num2); init_big_num(big_num2, key); //big_num1*=big_num2,此處result一定爲0 //函數內部原理爲x*1586=x*10^3*1+x*10^2*5+x*10^1*8+x*10^0*6 int reslut = multiple_big_num(big_num2, tmp_garbage[2], big_num1, big_num2); //big_num1*=9,同時由於最後一個參數是9,因此一定返回0 reslut += multiple(tmp_garbage[3], tmp_garbage[4], big_num1, 9); //判斷big_num1的位數是否爲奇數 if (reslut || big_num1[1] % 2 != 1) return FALSE; //判斷big_num1的中間位數和輸入key的第一個數字(此時big_num2存儲key) if (big_num1[big_num1[1026 + big_num1[1] / 2] + 2] == big_num2[big_num2[1026+0]+2]) break; } //PS:低位在前,高位在後;compare_big_num最後return時,會計算big_num1[2051]^big_num2[2051]來驗證調試器是否存在 //比較big_num1高big_num2[1]-1位,與big_num2中的高big_num2[1]-1位是否順序相同 //即假設big_num2有8位數字,比較big_num1的高7位與big_num2的高7位是否順序相同 int com_result=compare_big_num(big_num1, big_num2, big_num1[1] - big_num2[1] + 1, 1, big_num2[1]-1, 0); //比較big_num1低big_num2[1]-1位,與big_num2中的低big_num2[1]-1位是否逆序相同 //即假設假設big_num2有8位數字,比較big_num1的低7位與big_num2的低7位是否逆序相同 com_result += compare_big_num(big_num1, big_num2, 0, 1, big_num2[1]-1, 1); if (com_result) return FALSE; return TRUE; }
具體過程須要進入函數內部觀察,才能推出函數做用,當函數過於模糊的時候可動態調試觀察先後結果來推導。函數
這次算法的大致思路是,輸入一個數X,程序計算Y=X*X*9*9,X的位數有n個,Y的中間數字須要等於X的第一位(最低位)數字,同時X的高n-1位和Y的高n-1位須要順序相同,X的低n-1位和Y的低n-1位須要逆序相同。經過Python簡化Verify獲得代碼:優化
def verify(key): #檢測數字位數長度 if len(key)<8 or len(key)>20: print("Wrong Number") return False #大數中不能有0 for i in range(0, len(key)): if ord(key[i])<=48 or ord(key[i])>57: print("Wrong Number") return False #計算的到key*key*9*9 alter_key = int(key)*int(key)*9*9; alter_key = str(alter_key) #逆序放置,低位在前,高位在後 alter_key=alter_key[::-1] key = key[::-1] #判斷alter_key中間位數與key的最低位 middle_idx = int(len(alter_key)/2) if alter_key[middle_idx] != key[0]: print("Wrong Number") return False #判斷alter_key的高len(key)-1位和key的高len(key)-1位是否順序相同 x = len(alter_key)-len(key)+1 y = 1 for i in range(0,len(key)-1): if alter_key[x]!=key[y]: print("Wrong Number") return False x+=1 y+=1 #判斷alter_key的低len(key)-1位和key的低len(key)-1位是否逆序相同 for i in range(0,len(key)-1): if alter_key[i] != key[len(key)-1-i]: print("Wrong Number") return False return True i = 11111111 while verify(str(i)) != True: i += 1 print(i)
獲得結果12345679,傳說中的缺八數,但是程序是逆序存儲的,先輸入的數字表示低位,因此答案是97654321。驗證結果,正確無誤
調試