-
-
[原创]看雪WiFi万能钥匙第二题解析---lelfeiCM
-
发表于: 2017-12-30 18:53 2890
-
- 先放入IDA中观察看看,通过view->String找到“well done”等提示字样,向上翻发现fget()函数用于接收输入
-
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; }
简化后得到伪代码: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。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: