攻防世界 Replace Reverse二星題

demo41發表於2024-04-03

解題過程中,雖然解出來了,但是磕磕絆絆犯了一些錯誤,記錄一下


分析過程

PE查殼

有一個upx殼,最下面給了脫殼提示: upx.exe -d Replace.exe

脫殼結束,丟到IDA裡,SHIF+F12,檢視字串,看到一個可疑的”Well Done!\n“,點進去看看,發現就是主函式,反彙編主函式

  • Buffer很明顯是輸入,且長度要<=37
  • 判斷函式==1就成功,顯然這就是關鍵

sub_401090函式(改了一點名稱)

 1 // 最後要輸出1
 2 // a1是輸入,a2是輸入的長度且為35
 3 int __fastcall sub_401090(int a1, int a2)
 4 {
 5   int v4; // edx
 6   char char_v5; // al
 7   int high4; // esi
 8   int low4; // edi
 9   char char_v8; // al
10   int v9; // eax
11   char char_v10; // cl
12   int v11; // eax
13   int v12; // ecx
14 
15   if ( a2 != 35 )
16     return -1;
17   v4 = 0;
18   while ( 1 )
19   {
20     char_v5 = *(_BYTE *)(v4 + a1);
21     high4 = (char_v5 >> 4) % 16;                // 先右移4位,相當於把原來8位的高4位數移動到低4位,新高4位全都置零——再%16,相當於取出變換後新8位的低4位——也就是取出原來數的高4位
22     low4 = ((16 * char_v5) >> 4) % 16;          // 先*16,相當於左移4位,原來的低4位移動到高4位,新的低4位置零——再右移4位,相當於移回來了,不過高4位都是0——再%16,取出低4位——原就是取出原來數的低4位
23     char_v8 = byte_402150[2 * v4];
24     if ( char_v8 < 48 || char_v8 > 57 )
25       v9 = char_v8 - 87;
26     else
27       v9 = char_v8 - 48;
28     char_v10 = byte_402151[2 * v4];
29     v11 = 16 * v9;                              // v9*16,相當於左移4位————為後面組成新的8位做準備,作為新數的高4位
30     if ( char_v10 < 48 || char_v10 > 57 )
31       v12 = char_v10 - 87;
32     else
33       v12 = char_v10 - 48;                      // 作為新數的低4位
34     if ( (unsigned __int8)byte_4021A0[16 * high4 + low4] != ((v11 + v12) ^ 25) )// 16 * high4 + low4——操作的目的是組成新的8位數——其實就是原來輸入的數
35                                                 // ((v11 + v12) ^ 25)——將上面獲得v11與v12組成新的數,與25異或
36                                                 // 最後進行比較,相等才能return 1
37       break;
38     if ( ++v4 >= 35 )
39       return 1;
40   }
41   return -1;
42 }

總而言之,從byte_402150取一個元素作為char_v8,變換後獲得組成高位的v11;從byte_402151取一個元素作為char_v10,變換後獲得組成低位的v12,最後組成一個新的數去異或

【異或後得到的值】要與 【將輸入作為下標,從byte_4021A0中取出的數】 相等

byte_402150與byte_402151

!!在這裡我就犯了一個錯誤,我還以為是我IDA又出現問題了....byte_402150陣列的32h後面沒有 0 表示結束,所以要繼續往下讀取,一直到出現 0 ,即byte_402150是50+byte_402151

unsigned char ida_chars[] =
{
   5097,  52,  57, 102,  54,  57,  99,  51,  56,  51, 
   57,  53,  99, 100, 101,  57,  54, 100,  54, 100, 
  101,  57,  54, 100,  54, 102,  52, 101,  48,  50, 
   53,  52,  56,  52,  57,  53,  52, 100,  54,  49, 
   57,  53,  52,  52,  56, 100, 101, 102,  54, 101, 
   50, 100,  97, 100,  54,  55,  55,  56,  54, 101, 
   50,  49, 100,  53,  97, 100,  97, 101,  54,   0, 
    0,   0,   0,   0,   0,   0,   0,   0,   0
};
unsigned char ida_chars[] =
{
   97,  52,  57, 102,  54,  57,  99,  51,  56,  51, 
   57,  53,  99, 100, 101,  57,  54, 100,  54, 100, 
  101,  57,  54, 100,  54, 102,  52, 101,  48,  50, 
   53,  52,  56,  52,  57,  53,  52, 100,  54,  49, 
   57,  53,  52,  52,  56, 100, 101, 102,  54, 101, 
   50, 100,  97, 100,  54,  55,  55,  56,  54, 101, 
   50,  49, 100,  53,  97, 100,  97, 101,  54,   0, 
    0,   0,   0,   0,   0,   0,   0,   0,   0
};

byte_4021A0

選中全部,SHIF+E-->選中‘C unsigned char array (decimal)’,得到陣列的值(上面也是這麼得到的)

unsigned char ida_chars[] =
{
   99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 
  103,  43, 254, 215, 171, 118, 202, 130, 201, 125, 
  250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
  114, 192, 183, 253, 147,  38,  54,  63, 247, 204, 
   52, 165, 229, 241, 113, 216,  49,  21,   4, 199, 
   35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
  235,  39, 178, 117,   9, 131,  44,  26,  27, 110, 
   90, 160,  82,  59, 214, 179,  41, 227,  47, 132, 
   83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
  190,  57,  74,  76,  88, 207, 208, 239, 170, 251, 
   67,  77,  51, 133,  69, 249,   2, 127,  80,  60, 
  159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
  188, 182, 218,  33,  16, 255, 243, 210, 205,  12, 
   19, 236,  95, 151,  68,  23, 196, 167, 126,  61, 
  100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 
  144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 
  224,  50,  58,  10,  73,   6,  36,  92, 194, 211, 
  172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
  141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 
  174,   8, 186, 120,  37,  46,  28, 166, 180, 198, 
  232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
  181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 
  134, 193,  29, 158, 225, 248, 152,  17, 105, 217, 
  142, 148, 155,  30, 135, 233, 206,  85,  40, 223, 
  140, 161, 137,  13, 191, 230,  66, 104,  65, 153, 
   45,  15, 176,  84, 187,  22
};

解題指令碼(自己寫的常規版本)

 1 key1=[
 2     50,97,  52,  57, 102,  54,  57,  99,  51,  56,  51,
 3    57,  53,  99, 100, 101,  57,  54, 100,  54, 100,
 4   101,  57,  54, 100,  54, 102,  52, 101,  48,  50,
 5    53,  52,  56,  52,  57,  53,  52, 100,  54,  49,
 6    57,  53,  52,  52,  56, 100, 101, 102,  54, 101,
 7    50, 100,  97, 100,  54,  55,  55,  56,  54, 101,
 8    50,  49, 100,  53,  97, 100,  97, 101,  54
 9 ]
10 key2=[
11 97,  52,  57, 102,  54,  57,  99,  51,  56,  51,
12    57,  53,  99, 100, 101,  57,  54, 100,  54, 100,
13   101,  57,  54, 100,  54, 102,  52, 101,  48,  50,
14    53,  52,  56,  52,  57,  53,  52, 100,  54,  49,
15    57,  53,  52,  52,  56, 100, 101, 102,  54, 101,
16    50, 100,  97, 100,  54,  55,  55,  56,  54, 101,
17    50,  49, 100,  53,  97, 100,  97, 101,  54
18 ]
19 str=[
20 99, 124, 119, 123, 242, 107, 111, 197,  48,   1,
21   103,  43, 254, 215, 171, 118, 202, 130, 201, 125,
22   250,  89,  71, 240, 173, 212, 162, 175, 156, 164,
23   114, 192, 183, 253, 147,  38,  54,  63, 247, 204,
24    52, 165, 229, 241, 113, 216,  49,  21,   4, 199,
25    35, 195,  24, 150,   5, 154,   7,  18, 128, 226,
26   235,  39, 178, 117,   9, 131,  44,  26,  27, 110,
27    90, 160,  82,  59, 214, 179,  41, 227,  47, 132,
28    83, 209,   0, 237,  32, 252, 177,  91, 106, 203,
29   190,  57,  74,  76,  88, 207, 208, 239, 170, 251,
30    67,  77,  51, 133,  69, 249,   2, 127,  80,  60,
31   159, 168,  81, 163,  64, 143, 146, 157,  56, 245,
32   188, 182, 218,  33,  16, 255, 243, 210, 205,  12,
33    19, 236,  95, 151,  68,  23, 196, 167, 126,  61,
34   100,  93,  25, 115,  96, 129,  79, 220,  34,  42,
35   144, 136,  70, 238, 184,  20, 222,  94,  11, 219,
36   224,  50,  58,  10,  73,   6,  36,  92, 194, 211,
37   172,  98, 145, 149, 228, 121, 231, 200,  55, 109,
38   141, 213,  78, 169, 108,  86, 244, 234, 101, 122,
39   174,   8, 186, 120,  37,  46,  28, 166, 180, 198,
40   232, 221, 116,  31,  75, 189, 139, 138, 112,  62,
41   181, 102,  72,   3, 246,  14,  97,  53,  87, 185,
42   134, 193,  29, 158, 225, 248, 152,  17, 105, 217,
43   142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
44   140, 161, 137,  13, 191, 230,  66, 104,  65, 153,
45    45,  15, 176,  84, 187,  22
46 ]
47 flag = ""
48 for i in range(35):
49     v8 = key1[2*i]
50     if v8 < 48 or v8 > 57:
51         v11 = 16 * (v8 - 87)
52     else:
53         v11 = 16 * (v8 - 48)
54     v10 = key2[2 * i]
55     if v10 < 48 or v10 > 57:
56         v12 = v10 - 87
57     else:
58         v12 = v10 - 48
59     v4=((v11 + v12) ^ 25)
60     flag += chr(str.index(v4))
61 
62 print(flag)

這裡我犯了一個弱智錯誤

  • 最初寫指令碼的時候,想照著反彙編程式碼的思路去寫,在後面寫了一個 for j in range(len(str_data)): if str[i]==((v11 + v12) ^ 25):v4=((v11 + v12) ^ 25)
  • 結果導致執行沒有報錯,也沒有結果
  • str[i]==((v11 + v12) ^ 25)這一行程式碼在每次迴圈中都會遍歷str列表來查詢是否存在相等的值,這會導致程式在執行時花費很長時間,因為它的時間複雜度是 O(n),其中 n 是str列表的長度。由於列表長度非常大,因此這種線性搜尋的方法會非常慢,尤其是在內層迴圈中還巢狀了一個迴圈。這也解釋了為什麼程式碼沒有輸出也沒有報錯,因為它可能需要很長時間才能完成執行

解題指令碼(列舉正向爆破)

這個版本是我參考別的大佬的wp時候學到的,記錄一下

由於用了取餘 % 運算,所以採用列舉正向爆破的方法,讓flag中的每一個字元遍歷常用的字元(ascii碼錶中32-126),帶入加密演算法,如果成功,就把這個flag存入

list1=[50, 97, 52, 57, 102, 54, 57, 99, 51, 56, 51, 57, 53, 99, 100, 101, 57, 54, 100, 54, 100, 101, 57, 54, 100, 54, 102, 52, 101, 48, 50, 53, 52, 56, 52, 57, 53, 52, 100, 54, 49, 57, 53, 52, 52, 56, 100, 101, 102, 54, 101, 50, 100, 97, 100, 54, 55, 55, 56, 54, 101, 50, 49, 100, 53, 97, 100, 97, 101, 54]
list2=[97, 52, 57, 102, 54, 57, 99, 51, 56, 51, 57, 53, 99, 100, 101, 57, 54, 100, 54, 100, 101, 57, 54, 100, 54, 102, 52, 101, 48, 50, 53, 52, 56, 52, 57, 53, 52, 100, 54, 49, 57, 53, 52, 52, 56, 100, 101, 102, 54, 101, 50, 100, 97, 100, 54, 55, 55, 56, 54, 101, 50, 49, 100, 53, 97, 100, 97, 101, 54]
list3=[99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
v4=0
flag=""
for i in range(35):
    v8=list1[2*i]
    if v8 < 48 or v8 > 57:
        v9=v8 - 87
    else:
        v9=v8 - 48
    v10=list2[2*i]
    v11=16*v9
    if v10 <48 or v10 > 57:
        v12=v10-87
    else:
        v12=v10-48
    v4=((v11+v12)^25)
    for a in range(32,127):
        v5=a
        v6=(v5>>4)%16
        v7=((16*v5)>>4)%16
        if list3[16*v6+v7] == v4:
            flag+=chr(a)
print(flag)

flag

flag{Th1s_1s_Simple_Rep1ac3_Enc0d3}