首页
社区
课程
招聘
[原创]看雪WiFi万能钥匙第二题解析---lelfeiCM
发表于: 2017-12-30 18:53 2890

[原创]看雪WiFi万能钥匙第二题解析---lelfeiCM

2017-12-30 18:53
2890
  1. 先放入IDA中观察看看,通过view->String找到“well done”等提示字样,向上翻发现fget()函数用于接收输入                                                                            
  2. 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;
    }


  3. 此次算法的大体思路是,输入一个数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)


  4. 得到结果12345679, 传说中的缺八数 ,但是因为程序是逆序存储的, 先输入的数字表示低位,所以答案是97654321。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//