首页
社区
课程
招聘
[原创]看雪.京东 2018CTF-第四题分析
2018-6-23 10:28 2590

[原创]看雪.京东 2018CTF-第四题分析

2018-6-23 10:28
2590
        一、去掉花指令
        程序通过构造类似如下跳转指令让IDA分析失效。可以将下面指令直接nop掉,就可以去掉花指令。由于相关花指令不多,手动就可以修改,在修改过程中注意堆栈平衡。
text:00403156 E8 05 00 00 00                                                  call    00403160
.text:0040315B E8                                                              db 0E8h
.text:0040315C                                                 loc_40315C:                             
.text:0040315C EB 07                                                           jmp     short loc_403165
.text:0040315E 00                                                              db    0
.text:0040315F 00                                                              db    0
.text:00403160 E8 F7 FF FF FF                                                  call    loc_40315C
.text:00403165
.text:00403165                                                 loc_403165:                             
.text:00403165                                                                                         
.text:00403165 83 C4 08                                                        add     esp, 8
                 下面是去掉花指令后的main函数
int main_0()
{
  unsigned __int64 v0; // rax
  int v1; // ST08_4
  unsigned __int64 v2; // rax
  int key3Len; // eax
  int v4; // eax
  int result; // eax
  char v6; // [esp+4Ch] [ebp-3Ch]
  __int16 v7; // [esp+4Dh] [ebp-3Bh]
  char inputKey; // [esp+50h] [ebp-38h]
  char key3Buf[4]; // [esp+51h] [ebp-37h]
  int v10; // [esp+55h] [ebp-33h]
  int v11; // [esp+59h] [ebp-2Fh]
  int v12; // [esp+5Dh] [ebp-2Bh]
  int v13; // [esp+61h] [ebp-27h]
  __int16 v14; // [esp+65h] [ebp-23h]
  char v15; // [esp+67h] [ebp-21h]
  char errorString[4]; // [esp+68h] [ebp-20h]
  int v17; // [esp+6Eh] [ebp-1Ah]
  char successString[4]; // [esp+74h] [ebp-14h]
  __int16 v19; // [esp+7Ch] [ebp-Ch]
  int v20; // [esp+80h] [ebp-8h]
  BOOL v21; // [esp+84h] [ebp-4h]

  v21 = 0;
  v20 = 0;
  do
  {
    v0 = __rdtsc();
    v1 = v0;
    v2 = __rdtsc();
  }
  while ( (unsigned int)(v2 - v1) > 0xFFF );
  strcpy(successString, "rw`g`ut");
  v19 = 0;
  strcpy(errorString, "dpqkw");
  v17 = 0;
  inputKey = 0;
  *(_DWORD *)key3Buf = 0;
  v10 = 0;
  v11 = 0;
  v12 = 0;
  v13 = 0;
  v14 = 0;
  v15 = 0;
  v6 = 0;
  v7 = 0;
  sub_4011E0();
  sub_40100A();
  j_stringDecode((int)successString);
  j_stringDecode((int)errorString);
  scanf("%s", &inputKey, 24);
  if ( strlen(&inputKey) > 0x17 )
  {
    printf(errorString);
    exit(0);
  }
  key3Len = strlen(&key3Buf[2]);
  hextoString((int)&key3Buf[2], (int)&g_key3Buf, key3Len);
  v21 = sub_40125D();
  memcpy(&v6, &inputKey, 3u);
  if ( sub_40108C((int)&v6) )
  {
    sub_40128F((int)&v6);
    v20 = v4;
    if ( v20 + v21 == 2 )
      printf(successString);
    else
      printf(errorString);
    system("pause");
    result = 0;
  }
  else
  {
    printf(errorString);
    result = 0;
  }
  return result;
}
二、main函数分析
        首先通过memcpy将2个看似加密的字符串调用401078函数进行处理,401078函数实际上是个字符串解密函数,将花指令去掉如下:
size_t __cdecl stringDecode(char *a1)
{
  size_t result; // eax
  size_t i; // [esp+4Ch] [ebp-8h]
  signed int v3; // [esp+50h] [ebp-4h]

  v3 = 1;
  for ( i = 0; ; ++i )
  {
    result = strlen(a1);
    if ( i >= result )
      break;
    a1[i] ^= v3++;
  }
  return result;
}
        可以看出,第一个字符异或  0x01第2个字符 异或 0x02,依次类推。
        实际解密出来的字符串是:“success” 和"error"。在这里我们可以根据successString字符串看那些函数是用于key比较的。回到main函数继续分析。
        main在开始的时候调用了sub_4011E0和sub_40100A函数, sub_4011E0函数用于输出提示字符,而 sub_40100A函数比较复杂,但是貌似和key无关,暂时先不要分析它,先找到存储我们输入key的位置。可以看到如下代码为处理输入key:
  scanf("%s", &inputKey, 24);
  if ( strlen(&inputKey) > 0x17 )
  {
    printf(errorString);
    exit(0);
  }
        输入的字符串长度不能超过0x17,否则将结束进程。
        然后调用hextoString将输入key从第四个字符开始转换成字符串,并将其存储到全局变量g_key3Buf中。然后调用sub_40125D, 在 sub_40125D 中有对 g_key3Buf处理。后面再说。V21为 sub_40125D的返回值。
        接着调用sub_40108C函数判断输入key前三个字符是否是数字,如果不是数字则直接输出key错误字符串,程序结束。如果输入的key是数字,调用sub_40128F函数,并将其返回赋给V20, 如果v20+v1 == 2 则输出key成功字符串。
        因此我们可以看出key被分成2部分,前3个字节是数字,并且通过函数 sub_40128F进行校验,其它的 key字符通过函数 sub_40125D进行校验,2个校验函数都返回1 则表示key是正确的。
       下面开始分析 sub_40125D函数。

 二、RSA 加密函数sub_40125D
      
BOOL sub_402630()
{
  int v0; // eax
  _DWORD *big_inputKey; // [esp+4Ch] [ebp-334h]
  _DWORD *big_M; // [esp+50h] [ebp-330h]
  int big_e; // [esp+54h] [ebp-32Ch]
  _DWORD *big_n; // [esp+58h] [ebp-328h]
  char n; // [esp+5Ch] [ebp-324h]
  char v7; // [esp+5Dh] [ebp-323h]
  char v8; // [esp+5Eh] [ebp-322h]
  char v9; // [esp+5Fh] [ebp-321h]
  char v10; // [esp+60h] [ebp-320h]
  char v11; // [esp+61h] [ebp-31Fh]
  char v12; // [esp+62h] [ebp-31Eh]
  char v13; // [esp+63h] [ebp-31Dh]
  char v14; // [esp+64h] [ebp-31Ch]
  char v15; // [esp+65h] [ebp-31Bh]
  char v16; // [esp+66h] [ebp-31Ah]
  char v17; // [esp+67h] [ebp-319h]
  char v18; // [esp+68h] [ebp-318h]
  char v19; // [esp+69h] [ebp-317h]
  char v20; // [esp+6Ah] [ebp-316h]
  char v21; // [esp+6Bh] [ebp-315h]
  char v22; // [esp+6Ch] [ebp-314h]
  char v23; // [esp+6Dh] [ebp-313h]
  char v24; // [esp+6Eh] [ebp-312h]
  char v25; // [esp+6Fh] [ebp-311h]
  char v26; // [esp+70h] [ebp-310h]
  char v27; // [esp+71h] [ebp-30Fh]
  char v28; // [esp+72h] [ebp-30Eh]
  char v29; // [esp+73h] [ebp-30Dh]
  char v30; // [esp+74h] [ebp-30Ch]
  char v31; // [esp+75h] [ebp-30Bh]
  char v32; // [esp+76h] [ebp-30Ah]
  char v33; // [esp+77h] [ebp-309h]
  char v34; // [esp+78h] [ebp-308h]
  char v35; // [esp+79h] [ebp-307h]
  char v36; // [esp+7Ah] [ebp-306h]
  char v37; // [esp+7Bh] [ebp-305h]
  char v38; // [esp+7Ch] [ebp-304h]
  char v39; // [esp+7Dh] [ebp-303h]
  char v40; // [esp+7Eh] [ebp-302h]
  char v41; // [esp+7Fh] [ebp-301h]
  char v42; // [esp+80h] [ebp-300h]
  char v43; // [esp+81h] [ebp-2FFh]
  char v44; // [esp+82h] [ebp-2FEh]
  char v45; // [esp+83h] [ebp-2FDh]
  char v46; // [esp+84h] [ebp-2FCh]
  char v47; // [esp+85h] [ebp-2FBh]
  char v48; // [esp+86h] [ebp-2FAh]
  char v49; // [esp+87h] [ebp-2F9h]
  char v50; // [esp+88h] [ebp-2F8h]
  char v51; // [esp+89h] [ebp-2F7h]
  char v52; // [esp+8Ah] [ebp-2F6h]
  char v53; // [esp+8Bh] [ebp-2F5h]
  char v54; // [esp+8Ch] [ebp-2F4h]
  char v55; // [esp+8Dh] [ebp-2F3h]
  char v56; // [esp+8Eh] [ebp-2F2h]
  char v57; // [esp+8Fh] [ebp-2F1h]
  char v58; // [esp+90h] [ebp-2F0h]
  char v59; // [esp+91h] [ebp-2EFh]
  char v60; // [esp+92h] [ebp-2EEh]
  char v61; // [esp+93h] [ebp-2EDh]
  char v62; // [esp+94h] [ebp-2ECh]
  char v63; // [esp+95h] [ebp-2EBh]
  char v64; // [esp+96h] [ebp-2EAh]
  char v65; // [esp+97h] [ebp-2E9h]
  char v66; // [esp+98h] [ebp-2E8h]
  char v67; // [esp+99h] [ebp-2E7h]
  char v68; // [esp+9Ah] [ebp-2E6h]
  char v69; // [esp+9Bh] [ebp-2E5h]
  char v70; // [esp+9Ch] [ebp-2E4h]
  char v71; // [esp+124h] [ebp-25Ch]
  char v72; // [esp+125h] [ebp-25Bh]
  __int16 v73; // [esp+1E9h] [ebp-197h]
  char v74; // [esp+1EBh] [ebp-195h]
  char v75; // [esp+1ECh] [ebp-194h]
  char v76; // [esp+1EDh] [ebp-193h]
  __int16 v77; // [esp+2B1h] [ebp-CFh]
  char v78; // [esp+2B3h] [ebp-CDh]
  char orgKeyDataString; // [esp+2B4h] [ebp-CCh]
  char v80; // [esp+2B5h] [ebp-CBh]
  char v81; // [esp+2B6h] [ebp-CAh]
  char v82; // [esp+2B7h] [ebp-C9h]
  char v83; // [esp+2B8h] [ebp-C8h]
  char v84; // [esp+2B9h] [ebp-C7h]
  char v85; // [esp+2BAh] [ebp-C6h]
  char v86; // [esp+2BBh] [ebp-C5h]
  char v87; // [esp+2BCh] [ebp-C4h]
  char v88; // [esp+2BDh] [ebp-C3h]
  char v89; // [esp+2BEh] [ebp-C2h]
  char v90; // [esp+2BFh] [ebp-C1h]
  char v91; // [esp+2C0h] [ebp-C0h]
  char v92; // [esp+2C1h] [ebp-BFh]
  char v93; // [esp+2C2h] [ebp-BEh]
  char v94; // [esp+2C3h] [ebp-BDh]
  char v95; // [esp+2C4h] [ebp-BCh]
  char v96; // [esp+2C5h] [ebp-BBh]
  char v97; // [esp+2C6h] [ebp-BAh]
  char v98; // [esp+2C7h] [ebp-B9h]
  char v99; // [esp+2C8h] [ebp-B8h]
  char v100; // [esp+2C9h] [ebp-B7h]
  char v101; // [esp+2CAh] [ebp-B6h]
  char v102; // [esp+2CBh] [ebp-B5h]
  char v103; // [esp+2CCh] [ebp-B4h]
  char v104; // [esp+2CDh] [ebp-B3h]
  char v105; // [esp+2CEh] [ebp-B2h]
  char v106; // [esp+2CFh] [ebp-B1h]
  char v107; // [esp+2D0h] [ebp-B0h]
  char v108; // [esp+2D1h] [ebp-AFh]
  char v109; // [esp+2D2h] [ebp-AEh]
  char v110; // [esp+2D3h] [ebp-ADh]
  char v111; // [esp+2D4h] [ebp-ACh]
  char v112; // [esp+2D5h] [ebp-ABh]
  char v113; // [esp+2D6h] [ebp-AAh]
  char v114; // [esp+2D7h] [ebp-A9h]
  char v115; // [esp+2D8h] [ebp-A8h]
  char v116; // [esp+2D9h] [ebp-A7h]
  char v117; // [esp+2DAh] [ebp-A6h]
  char v118; // [esp+2DBh] [ebp-A5h]
  char v119; // [esp+2DCh] [ebp-A4h]
  char v120; // [esp+2DDh] [ebp-A3h]
  char v121; // [esp+2DEh] [ebp-A2h]
  char v122; // [esp+2DFh] [ebp-A1h]
  char v123; // [esp+2E0h] [ebp-A0h]
  char v124; // [esp+2E1h] [ebp-9Fh]
  char v125; // [esp+2E2h] [ebp-9Eh]
  char v126; // [esp+2E3h] [ebp-9Dh]
  char v127; // [esp+2E4h] [ebp-9Ch]
  char v128; // [esp+2E5h] [ebp-9Bh]
  char v129; // [esp+2E6h] [ebp-9Ah]
  char v130; // [esp+2E7h] [ebp-99h]
  char v131; // [esp+2E8h] [ebp-98h]
  char v132; // [esp+2E9h] [ebp-97h]
  char v133; // [esp+2EAh] [ebp-96h]
  char v134; // [esp+2EBh] [ebp-95h]
  char v135; // [esp+2ECh] [ebp-94h]
  char v136; // [esp+2EDh] [ebp-93h]
  char v137; // [esp+2EEh] [ebp-92h]
  char v138; // [esp+2EFh] [ebp-91h]
  char v139; // [esp+2F0h] [ebp-90h]
  char v140; // [esp+2F1h] [ebp-8Fh]
  char v141; // [esp+2F2h] [ebp-8Eh]
  char v142; // [esp+2F3h] [ebp-8Dh]
  char v143; // [esp+2F4h] [ebp-8Ch]
  int mr_mip; // [esp+37Ch] [ebp-4h]

  mr_mip = mirsys(500, 0x10u);
  orgKeyDataString = 51;
  v80 = 50;
  v81 = 59;
  v82 = 71;
  v83 = 71;
  v84 = 68;
  v85 = 48;
  v86 = 75;
  v87 = 77;
  v88 = 60;
  v89 = 78;
  v90 = 79;
  v91 = 78;
  v92 = 56;
  v93 = 59;
  v94 = 37;
  v95 = 32;
  v96 = 36;
  v97 = 87;
  v98 = 36;
  v99 = 34;
  v100 = 82;
  v101 = 46;
  v102 = 47;
  v103 = 33;
  v104 = 92;
  v105 = 46;
  v106 = 90;
  v107 = 45;
  v108 = 40;
  v109 = 39;
  v110 = 17;
  v111 = 103;
  v112 = 23;
  v113 = 16;
  v114 = 16;
  v115 = 96;
  v116 = 103;
  v117 = 99;
  v118 = 26;
  v119 = 26;
  v120 = 31;
  v121 = 111;
  v122 = 25;
  v123 = 110;
  v124 = 26;
  v125 = 22;
  v126 = 113;
  v127 = 117;
  v128 = 118;
  v129 = 4;
  v130 = 6;
  v131 = 113;
  v132 = 4;
  v133 = 115;
  v134 = 122;
  v135 = 1;
  v136 = 14;
  v137 = 11;
  v138 = 120;
  v139 = 8;
  v140 = 13;
  v141 = 15;
  v142 = 116;
  memset(&v143, 0, 0x88u);
  v75 = 0;
  memset(&v76, 0, 0xC4u);
  v77 = 0;
  v78 = 0;
  v71 = 0;
  memset(&v72, 0, 0xC4u);
  v73 = 0;
  v74 = 0;
  n = 54;
  v7 = 102;
  v8 = 98;
  v9 = 55;
  v10 = 60;
  v11 = 98;
  v12 = 98;
  v13 = 62;
  v14 = 63;
  v15 = 58;
  v16 = 58;
  v17 = 58;
  v18 = 57;
  v19 = 57;
  v20 = 56;
  v21 = 114;
  v22 = 32;
  v23 = 115;
  v24 = 117;
  v25 = 119;
  v26 = 38;
  v27 = 114;
  v28 = 116;
  v29 = 32;
  v30 = 124;
  v31 = 41;
  v32 = 43;
  v33 = 37;
  v34 = 121;
  v35 = 125;
  v36 = 43;
  v37 = 18;
  v38 = 24;
  v39 = 64;
  v40 = 22;
  v41 = 64;
  v42 = 64;
  v43 = 30;
  v44 = 18;
  v45 = 29;
  v46 = 79;
  v47 = 26;
  v48 = 79;
  v49 = 26;
  v50 = 28;
  v51 = 24;
  v52 = 75;
  v53 = 2;
  v54 = 3;
  v55 = 7;
  v56 = 81;
  v57 = 1;
  v58 = 2;
  v59 = 6;
  v60 = 85;
  v61 = 14;
  v62 = 1;
  v63 = 88;
  v64 = 3;
  v65 = 4;
  v66 = 92;
  v67 = 11;
  v68 = 7;
  v69 = 117;
  memset(&v70, 0, 0x88u);
  j_stringDecode((int)&orgKeyDataString);
  j_stringDecode((int)&n);
  *(_DWORD *)(mr_mip + 564) = 0x10;
  big_n = (_DWORD *)mirvar(0);
  big_e = mirvar(0);
  big_inputKey = (_DWORD *)mirvar(0);
  big_M = (_DWORD *)mirvar(0);
  cinstr((int)big_inputKey, (int)&g_key3Buf);   //  将大数字符串转换成大数
  cinstr((int)big_n, (int)&n);                  // 初始化n
  cinstr(big_e, (int)"3e9");                    // 初始化 e
  if ( mr_compare(big_inputKey, big_n) != -1 )  // 比较输入的key是否小于 N,如果不小于则直接返回0
    return 0;
  powmod((int)big_inputKey, big_e, (int)big_n, (int)big_M);// 模幂运算
  big_to_bytes(0, big_M, &v75, 0);
  mirkill((int)big_n);
  mirkill(big_e);
  mirkill((int)big_inputKey);
  mirkill((int)big_M);
  mirexit();
  v0 = strlen(&v75);
  sub_40100F((int)&v75, (int)&v71, v0);         // 密文转换成字符串
  return strcmp(&orgKeyDataString, &v71) == 0;  // 判断密文是否正确
}

        程序中存在如下“MIRACL not initialised - no call to mirsys()”字符串,可以看到用到了 MIRACL大数库。知道是 MIRACL库,可以通过下面指令判断相关运算函数 :mov dword ptr [esi+eax*4+20h],XXh  。如上面的代码。
        分析40125D函数可以看出其实际上是个RSA加密函数,其中输入的key为明文
         N = 7DA39DE66016477B1AFC3DC8E309DC429B5DE855F0D616D225B570B68B88A585
         E = 3E9
         然后将加密后的数据与 字符串“”208CBB7CD6ECC64516D07D978F5F0681F534EAD235D5C49ADD72D2DB840D5304“比较
         因此将字符串208CBB7CD6ECC64516D07D978F5F0681F534EAD235D5C49ADD72D2DB840D5304经过RSA解密后就可以得到明文,但是需要知道D,可以用RSATOOLS来推算出D来:如下图:
         
     
        可知 D = 2E70A649E6A648F78A9D2C1074A7D51F0099C13F7F9BCBB78BAD2C1B1B1D96F1
         用RSA解密工具就可以得到明文:


        因此可知对应秘钥为: iamahandsomeguyhaha1
        此时还差前面3个数字。下面分析sub_40128F函数
三、直接修改程序逻辑,穷举出前三位KEY
        由于我们已经知道前3位为000-999,  就不用再分析sub_40128F直接暴力穷举吧,直接在OD中将下面改成如下:
00403278  |.  8D45 C4            lea eax,[local.15]
0040327B  |.  50                 push eax
0040327C  |.  E8 0EE0FFFF        call CrackMe.0040128F
00403281  |.  83C4 04            add esp,0x4
00403284  |.  8945 F8            mov [local.2],eax
00403287  |.  EB 10              jmp short CrackMe.00403299
00403289  |>  8D4D E0            lea ecx,[local.8]
0040328C  |.  51                 push ecx
0040328D  |.  E8 AEA40200        call CrackMe.0042D740
00403292  |.  83C4 04            add esp,0x4
00403295  |.  33C0               xor eax,eax
00403297  |.  EB 34              jmp short CrackMe.004032CD
00403299  |>  8B55 FC            mov edx,[local.1]
0040329C  |.  0355 F8            add edx,[local.2]
0040329F  |.  83FA 02            cmp edx,0x2
004032A2  |.  75 0E              jnz short CrackMe.004032B2
004032A4  |.  8D45 EC            lea eax,[local.5]                        ;  设置断点,断下表示得到key
004032A7  |.  50                 push eax
004032A8  |.  E8 93A40200        call CrackMe.0042D740
004032AD  |.  83C4 04            add esp,0x4
004032B0      EB 0C              jmp short CrackMe.004032BE
004032B2      3E:8A45 C4         mov al,byte ptr ds:[ebp-0x3C]            ;  获取第一个字符(修改开始地址)
004032B6      FEC0               inc al                                   ;  将第一个字符+1
004032B8      3C 3A              cmp al,0x3A
004032BA      74 06              je short CrackMe.004032C2                ;  是否等于3A
004032BC      3E:8845 C4         mov byte ptr ds:[ebp-0x3C],al            ;  不等于3A,递增数据
004032C0    ^ EB B6              jmp short CrackMe.00403278               ;  继续进行KEY判断
004032C2      3E:C645 C4 30      mov byte ptr ds:[ebp-0x3C],0x30          ;  等于3A,第一个字符设置成0x30
004032C7      3E:8A45 C5         mov al,byte ptr ds:[ebp-0x3B]            ;  获取第2个字符
004032CB      40                 inc eax                                  ;  将第二个字符+1
004032CC      3C 3A              cmp al,0x3A
004032CE      74 06              je short CrackMe.004032D6                ;  第二个字符等于3A,递增第三个字符
004032D0      3E:8845 C5         mov byte ptr ds:[ebp-0x3B],al            ;  第二个字符不等于3A,递增数据
004032D4    ^ EB A2              jmp short CrackMe.00403278               ;  继续进行KEY判断
004032D6      3E:C645 C5 30      mov byte ptr ds:[ebp-0x3B],0x30          ;  先将第2个字符设置成0x30
004032DB      3E:8A45 C6         mov al,byte ptr ds:[ebp-0x3A]            ;  递增第3个字符
004032DF      40                 inc eax
004032E0      3E:8845 C6         mov byte ptr ds:[ebp-0x3A],al
004032E4    ^ EB 92              jmp short CrackMe.00403278               ;  继续判断(修改结束地址)


 输入"000iamahandsomeguyhaha1"  程序立刻立刻中断在4032A7位置,看数据为'520'

四、flag 为:520iamahandsomeguyhaha1


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2018-6-23 11:44 被oooAooo编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回