第一题解决以后,火烧眉毛地进入第二题,但是这猝不及防的难度提高,直接耗费了三天的时间来分析。这题是纯算法的大数运算,很容易就找到算法关键点,可是分析算法须要耗费些时间。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。验证结果,正确无误
调试