首页
社区
课程
招聘
[原创]南冥神功
2021-5-12 11:32 5056

[原创]南冥神功

2021-5-12 11:32
5056

南冥神功

目录

定位Main函数,还原代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char g_szStr[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int g_StrLen = 0x24;
 
char byte_4B7080[] =
{
  0x53, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
  0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00
};
 
//计算cFlag在g_szStr中的下标
bool LuoCalcStrIndex(char cFlag, int& nStrIndex)
{
    bool bFlag = false;
    for (int i = 0; i < g_StrLen; i++)
    {
        if (g_szStr[i] == cFlag)
        {
            bFlag = true;
            nStrIndex = i;
        }
 
    }
 
    return bFlag;
}
 
int main()
{
    char szFlag[80] = {};
    memset(szFlag, 0, sizeof(szFlag));
 
    printf("Input your code: \r\n");
    scanf("%s", szFlag);
 
    int nStrIdx = 0;
    int nFlagIdx = 0;
    char cFlag = szFlag[0];
    int nSwitch = 0;
 
    int v7 = 0;
    int v8 = 0;
    int v12 = 0;
    char* v13 = NULL;
    int v17 = 0;
    int v18 = 0;
    int v19 = 0;
    int v20 = 0;
    int v22 = 0;
 
    unsigned int nCol = 0;
    unsigned int nRow = 0;
 
    int nFlagLen = strlen(szFlag);
    if (nFlagLen > 0x30)
    {
        printf("Try again...\r\n");
        return 0;
    }
 
    int i = 0;
    for (nFlagIdx = 0; nFlagIdx < nFlagLen; )
    {
        //计算cFlag在g_szStr中的下标
        if (!LuoCalcStrIndex(cFlag, nStrIdx))
        {
            printf("cFlag = %c 不在g_szStr数组中\r\n", cFlag);
            system("pause");
            return 0;
        }
 
        v20 = (nFlagIdx + nStrIdx / 6) % 6;
        nCol = v22;
        nSwitch = 5 - (nFlagIdx + nStrIdx) % 6;
 
        for (int i = 0; ;i = 1)
        {
            switch (nSwitch)
            {
            case 1:
                ++nCol;                              //++
                break;
            case 2:
                v17 = (nRow++ & 1) == 0;             //偶数行, 行++ ++
                nCol += v17;                         //奇数行, 行++ 列不变
                break;
            case 3:
                v12 = (nRow++ & 1) != 0;            //奇数行, 行++ --
                nCol -= v12;                        //偶数行, 行++ 列不变
                break;
            case 4:
                --nCol;                             //--
                break;
            case 5:
                v19 = (nRow-- & 1) != 0;            //奇数行, 行-- --
                nCol -= v19;                        //偶数行, 行-- 列不变
                break;
            default:
                v18 = (nRow-- & 1) == 0;            //偶数行, 行-- ++
                nCol += v18;                        //奇数行, 行-- 列不变
                break;
            }
 
            if (nCol > 9)
            {
                printf("Try again...\r\n");
                return 0;
            }
 
            if (nRow > 8)
            {
                printf("Try again...\r\n");
                return 0;
            }
 
            v13 = &byte_4B7080[0xA * nRow + nCol];
            if (*v13)
            {
                printf("Try again...\r\n");
                return 0;
            }
            *v13 = 1;
 
            if (i == 1)
            {
                ++nFlagIdx;
                v22 = nCol;
                cFlag = szFlag[nFlagIdx];
                break;
            }
 
            nSwitch = v20;
        }
 
    }
 
    int nSize = sizeof(byte_4B7080);
    bool bFlag = true;
    for (int i = 0; i < nSize; i++)
    {
        if (byte_4B7080[i] == 0)
        {
            bFlag = false;
        }
    }
 
    if (bFlag == true)
    {
        printf("Good job!\r\n");
    }
    else
    {
        printf("Try again...\r\n");
    }
 
    system("pause");
    return 0;
}

通过阅读上述代码,我们知道这个算法的流程如下:

  1. 遍历我们输入的Flag值,每次取1个字母

  2. 计算取出的字母在g_szStr数组中的下标值,为nStrIdx

  3. 按照以下公式计算出两个Switch值

    1
    2
    nSwitch = 5 - (nFlagIdx + nStrIdx) % 6; //nFlagIdx为当前取出的字母在我们输入的Flag中的下标
    v20 = (nFlagIdx + nStrIdx / 6) % 6;
  4. 根据Swicth中的值,去改变byte_4B7080二维数组中的行列坐标值

    1
    2
    需满足 byte_4B7080[0xA * nRow + nCol] == 0,若不为0,则失败
    若byte_4B7080[0xA * nRow + nCol] == 0, 则将其修改为1
  5. 最后若byte_4B7080数组中的值全部不为0,则说明Flag有效

    逆向分析Flag

确定最终目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//这个数组中的值,为0的地方,要全部修改为1
char byte_4B7080[] =
{
  0x53, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
  0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00
};
`

明确我们输入的Flag中的单个字母的意义

通过阅读源代码得知,我们输入的Flag中的每个单个字母会影响Switch的值,进而影响在byte_4B7080数组中取出的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
unsigned int nRow = 0;//
unsigned int nCol = 0;//
 
switch (nSwitch)
    {
    case 1:
        ++nCol;                              //++
        break;
    case 2:
        v17 = (nRow++ & 1) == 0;             //偶数行, 行++ ++
        nCol += v17;                         //奇数行, 行++ 列不变
        break;
    case 3:
        v12 = (nRow++ & 1) != 0;            //奇数行, 行++ --
        nCol -= v12;                        //偶数行, 行++ 列不变
        break;
    case 4:
        --nCol;                             //--
        break;
    case 5:
        v19 = (nRow-- & 1) != 0;            //奇数行, 行-- --
        nCol -= v19;                        //偶数行, 行-- 列不变
        break;
    default:
        v18 = (nRow-- & 1) == 0;            //偶数行, 行-- ++
        nCol += v18;                        //奇数行, 行-- 列不变
        break;
    }
 
    if (nCol > 9)
    {
        printf("Try again...\r\n");
        return 0;
    }
 
    if (nRow > 8)
    {
        printf("Try again...\r\n");
        return 0;
    }

确定要修改byte_4B7080数组的起点以及路线

根据Switch修改的nRow nCol,要满足

1
2
3
1. nCol <= 9
2. nRow <= 8
3.byte_4B7080[0xA * nRow + nCol] == 0

1

计算flag

1
2
unsigned int nRow = 0;//
unsigned int nCol = 0; //

每个字母,会计算出两个Switch值,进而得到两个坐标

 

先来计算Flag的第一个字母

 

如行走路线为上图,则前两个坐标为(0, 1), (1, 2)

 

根据坐标初始值为(0, 0), 以及上述Switch影响坐标的规则,可以逆向推出两个Switch的值分别为 1 2

 

根据下述代码,得到Flag的第一个字母

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char g_szStr[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int g_StrLen = 0x24;
 
char byte_4B7080[] =
{
  0x53, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
  0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
  0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00
};
 
void LuoCalcFlag(int nFlagIdx, int nSwitch1, int nSwitch2)
{
    int nStrLen = strlen(g_szStr);
    for (int i = 0; i < nStrLen; i++)
    {
        int n1 = 5 - (nFlagIdx + i) % 6;
        int n2 = (nFlagIdx + i / 6) % 6;
 
        if (((n1 == nSwitch1) && (n2 == nSwitch2)))
        {
            printf("cFlag: %c\r\n", g_szStr[i]);
        }
    }
}
 
int main()
{
    //计算Flag
    LuoCalcFlag(0, 1, 2);
 
    system("pause");
    return 0;
}

最后按照上述路线图全部走完,利用上述代码计算单个Flag值,将其拼接起来就是有效的Flag

1
Flag: GJ0V4LA4VKEVQZSVCNGJ00N

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

最后于 2021-5-12 11:34 被夏洛魂编辑 ,原因:
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (1)
雪    币: 385
活跃值: (90)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
三只跳蚤 2021-5-12 20:19
2
0
闫总牛逼
游客
登录 | 注册 方可回帖
返回