Ha1cyon-CTF 芜湖 题解

警告
本文最后更新于 2020-04-21,文中内容可能已过时。

源代码

先上源代码:

#include <stdio.h>

#include <iostream>
#include <vector>
using namespace std;

string oOoOo00[35];
char o0ooOo0oo0O[35][42] = {
    {
        84, 84, 24, 89, 84, 56, 12, 47, 87, 56, 4,  47, 84, 0,  50, 47,
        84, 84, 16, 36, 87, 0,  46, 46, 84, 17, 12, 23, 45, 38, 92, 92,
    },
    {
        84, 19, 8,  48, 84, 19, 8,  48, 84, 16, 8,  9, 84, 86, 46, 42,
        84, 45, 16, 38, 84, 86, 16, 12, 84, 0,  87, 0, 45, 46, 92, 92,
    },
    {
        84, 17, 8,  7,  84, 87, 12, 87, 84, 45, 8,  45, 84, 19, 54, 35,
        84, 19, 54, 16, 84, 84, 16, 36, 84, 45, 83, 6,  45, 41, 92, 92,
    },
    {
        84, 45, 20, 47, 84, 85, 50, 83, 84, 87, 4, 56, 84, 0,  74, 38,
        84, 84, 16, 36, 87, 45, 4,  5,  84, 87, 0, 86, 45, 32, 92, 92,
    },
    {
        84, 19, 8,  17, 84, 3,  16, 12, 84, 19, 0, 40, 84, 0,  50, 25,
        84, 84, 16, 36, 84, 84, 87, 18, 87, 59, 4, 81, 45, 41, 92, 92,
    },
    {
        84, 17, 4,  6,  84, 19, 46, 55, 87, 42, 4,  12, 84, 17, 38, 85,
        84, 84, 16, 36, 84, 17, 8,  46, 84, 0,  50, 17, 45, 39, 92, 92,
    },
    {
        84, 19, 42, 9,  84, 17, 24, 43, 84, 3,  24, 55, 84, 56, 16, 3,
        84, 84, 16, 36, 84, 45, 8,  54, 84, 84, 54, 44, 45, 38, 92, 92,
    },
    {
        84, 19, 42, 9,  84, 17, 24, 43, 87, 40, 50, 0,  84, 56, 83, 22,
        84, 84, 16, 36, 84, 56, 54, 43, 84, 3,  12, 81, 45, 37, 92, 92,
    },
    {
        87, 45, 74, 56, 84, 59, 24, 14, 84, 87, 83, 43, 84, 84, 83, 32,
        84, 45, 83, 6,  84, 56, 4,  87, 84, 85, 87, 22, 45, 41, 92, 92,
    },
    {
        84, 17, 4,  13, 84, 17, 4,  13, 84, 0,  50, 2,  84, 0,
        50, 2,  87, 40, 4,  16, 87, 45, 83, 18, 84, 84, 16, 36,
        87, 42, 38, 44, 84, 17, 8,  7,  45, 36, 92, 92,
    },
    {
        84, 56, 8,  22, 84, 0,  50, 36, 87, 56, 38, 20, 84, 19,
        20, 9,  84, 56, 8,  19, 84, 45, 16, 87, 84, 84, 16, 36,
        87, 40, 46, 44, 84, 3,  83, 25, 45, 38, 92, 92,
    },
    {
        87, 42, 87, 17, 87, 0,  46, 46, 84, 59, 34, 84, 84, 17,
        54, 11, 84, 19, 4,  82, 84, 45, 12, 25, 84, 84, 16, 36,
        84, 59, 38, 89, 84, 59, 34, 85, 45, 38, 92, 92,
    },
    {
        84, 3,  74, 19, 84, 3,  74, 19, 84, 19,
        8,  39, 87, 56, 0,  50, 7,  15, 92, 92,
    },
    {
        87, 59, 83, 59, 87, 59, 83, 59, 84, 85, 54, 15, 84, 45,
        16, 20, 84, 56, 87, 7,  84, 17, 83, 13, 84, 84, 16, 36,
        87, 40, 4,  16, 84, 3,  4,  25, 45, 45, 92, 92,
    },
    {
        84, 0,  50, 17, 84, 87, 12, 87, 84, 19, 50, 50, 84, 19,
        20, 9,  84, 3,  74, 88, 84, 85, 50, 83, 84, 84, 16, 36,
        84, 56, 54, 43, 84, 17, 8,  46, 45, 36, 92, 92,
    },
    {
        84, 84, 24, 89, 84, 45, 8,  21, 84, 56, 74, 16, 87, 42,
        0,  35, 84, 86, 20, 0,  84, 85, 46, 34, 84, 84, 16, 36,
        84, 0,  50, 17, 87, 59, 12, 39, 45, 38, 92, 92,
    },
    {
        84, 56,  0, 47, 87,  0, 46,  4, 87, 42, 38, 44, 40, 54, 92, 92,
    },
    {
        84, 14, 8,  51, 84, 56, 20, 41, 84, 17, 54, 8,  84, 59, 24, 22,
        84, 14, 16, 18, 87, 45, 54, 82, 84, 0,  50, 81, 45, 44, 92, 92,
    },
    {
        84, 84, 24, 45, 84, 84, 83, 32, 87, 40, 24, 19, 87, 40, 24, 19,
        84, 84, 16, 36, 84, 0,  87, 41, 84, 0,  87, 59, 45, 41, 92, 92,
    },
    {
        84, 0,  50, 0,  84, 3,  34, 51, 84, 17, 24, 16, 84, 84, 74, 13,
        84, 84, 16, 36, 84, 17, 8,  7,  84, 84, 34, 37, 45, 43, 92, 92,
    },
    {
        84, 17, 24, 43, 84, 19, 42, 9,  84, 17, 24, 43, 87, 56,
        34, 0,  84, 59, 34, 51, 84, 17, 24, 16, 84, 17, 83, 13,
        87, 45, 4,  23, 84, 56, 74, 11, 45, 37, 92, 92,
    },
    {
        84, 45, 16, 24, 84, 85, 8,  25, 84, 84, 16, 36,
        84, 45, 24, 59, 84, 45, 24, 81, 45, 35, 92, 92,
    },
    {
        87, 42, 87, 17, 84, 14, 8,  51, 84, 45, 20, 18, 84, 45, 8,  32,
        87, 45, 54, 82, 84, 85, 42, 84, 84, 85, 4,  37, 45, 38, 92, 92,
    },
    {
        84, 56, 20, 41, 84, 19, 34, 52, 84, 59, 42, 44,
        84, 45, 74, 9,  84, 3,  74, 80, 45, 46, 92, 92,
    },
    {
        84, 59, 24, 14, 87, 56, 38, 13, 87, 45, 74, 2,  84, 84,
        16, 36, 84, 0,  50, 17, 87, 45, 87, 84, 45, 38, 92, 92,
    },
    {
        87, 59, 46, 83, 84, 19, 42, 27, 87, 45,
        87, 84, 84, 86, 24, 56, 45, 41, 92, 92,
    },
    {
        84, 17, 24, 43, 84, 45, 8,  32, 84, 85, 12, 41, 84, 87,
        54, 4,  84, 0,  54, 41, 84, 84, 16, 36, 84, 3,  83, 17,
        87, 43, 12, 84, 84, 19, 54, 82, 45, 34, 92, 92,
    },
    {
        84, 59, 42, 44, 84, 14, 8,  51, 84, 45, 8,  32, 87, 45,
        54, 82, 84, 56, 0,  50, 87, 59, 12, 17, 45, 35, 92, 92,
    },
    {
        87, 0,  46, 4,  84, 59, 34, 51, 84, 56, 74, 12, 84, 45, 8,  32,
        84, 45, 8,  16, 84, 45, 8,  54, 84, 84, 54, 44, 45, 34, 92, 92,
    },
    {
        84, 59, 24, 14, 87, 56, 38, 13, 87, 45, 74, 2,  84, 84,
        16, 36, 84, 0,  50, 17, 87, 45, 87, 84, 45, 35, 92, 92,
    },
    {
        87, 59, 46, 83, 84, 19, 42, 27, 87, 45,
        87, 84, 84, 86, 24, 56, 45, 34, 92, 92,
    },
    {
        84, 17, 24, 43, 84, 45, 8,  32, 84, 85, 12, 41, 84, 87,
        54, 4,  84, 0,  54, 41, 84, 84, 16, 36, 84, 3,  83, 17,
        87, 43, 12, 84, 84, 19, 54, 82, 45, 35, 92, 92,
    },
    {
        84, 59, 42, 44, 84, 14, 8,  51, 84, 45, 8,  32, 87, 45,
        54, 82, 84, 56, 0,  50, 87, 59, 12, 17, 45, 41, 92, 92,
    },
    {
        87, 0,  46, 4,  84, 59, 34, 51, 84, 56, 74, 12, 84, 45, 8,  32,
        84, 45, 8,  16, 84, 45, 8,  54, 84, 84, 54, 44, 45, 47, 92, 92,
    },
    {
        2,  82, 55, 22, 59, 57, 40, 6,  3,  54, 39, 15, 0,  54,
        44, 6,  5,  83, 88, 24, 3,  38, 51, 74, 7,  6,  92, 92,
    },
};
int o000O0ooo0ooOoOo[] = {
    32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 20, 40, 40, 40, 16, 32,
    32, 32, 40, 24, 32, 24, 28, 20, 40, 28, 32, 28, 20, 40, 28, 32, 28,
};
int o0o0o(int o00ooo000, int o00OoO0O) { return !(o00ooo000 & o00OoO0O); }

int Oo0O(int o00ooo000, int o00OoO0O) {
    string o00o0o0oo00ooooo00o0o = "真真假假假假真真, 位运算的世界如此奇妙~";
    return o0o0o(o0o0o(o00OoO0O, o0o0o(o00ooo000, o00OoO0O)),
                 o0o0o(o00ooo000, o0o0o(o00ooo000, o00OoO0O)));
}
typedef unsigned char uchar;
static const std::string o00OoO0O =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static std::string o0o0OO(const std::string &o00ooO0O) {
    std::string o00o0oOOOo;
    string o0oo0o0oo00oo0o0 = "俗话说, 最危险的地方就是最安全的地方.";
    std::vector<int> o00o0O(256, -1);
    for (int o00o0o0o0O = 0; o00o0o0o0O < 64; o00o0o0o0O++)
        o00o0O[o00OoO0O[o00o0o0o0O]] = o00o0o0o0O;

    int o00oOoO0O = 0, o00OO00ooo0O = -8;
    for (uchar o00o0oO0Oo : o00ooO0O) {
        if (o00o0O[o00o0oO0Oo] == -1) break;
        o00oOoO0O = (o00oOoO0O << 6) + o00o0O[o00o0oO0Oo];
        o00OO00ooo0O += 6;
        if (o00OO00ooo0O >= 0) {
            o00o0oOOOo.push_back(char((o00oOoO0O >> o00OO00ooo0O) & 0xFF));
            o00OO00ooo0O -= 8;
        }
    }
    return o00o0oOOOo;
}
void o00oo0o0o0o() {
    for (int o00o0o0o0O = 0; o00o0o0o0O < 35; o00o0o0o0O++) {
        for (int o00o0o0o00 = 0; o00o0o0o00 < o000O0ooo0ooOoOo[o00o0o0o0O];
             o00o0o0o00++) {
            char o00o0o0oo = o0ooOo0oo0O[o00o0o0o0O][o00o0o0o00];
            char o0oooOO = 0;
            for (int o00o0o0o0o = 0; o00o0o0o0o < 8; o00o0o0o0o++) {
                o0oooOO <<= 1;
                o0oooOO ^= Oo0O((o00o0o0oo >> 8 - o00o0o0o0o - 1) & 1,
                                ('a' >> 8 - o00o0o0o0o - 1) & 1) &
                           1;
            }
            oOoOo00[o00o0o0o0O] += o0oooOO;
        }
        cout << o0o0OO(oOoOo00[o00o0o0o0O]) << endl;
    }
    cout << endl;
}

int main() { o00oo0o0o0o(); }

这是出题人编写的源代码.

解题思路

这道题我们用IDA打开之后, 可以看见:

main函数里面直接跳转到了这里. 经过查看, 第二层for循环中的判断条件和第一层for循环的i结合起来, 应该是在遍历一个二维数组, 而第二层for中的那个变量数组应该表示二维数组中不同维度的元素的长度. 再往下看, v3就是在提取这个二维数组中的每个元素了. 我们点进这个数组, 可以看到一大片数据.

接下来对v3进行处理, 结果存进v2:

for ( k = 0; k <= 7; ++k )
    v2 = Oo0O((v3 >> (7 - k)) & 1, (97 >> (7 - k)) & 1) & 1 ^ 2 * v2;

可以看出这是一个按位操作, 而Oo0O()函数有点迷惑. 它里面的长这样:

__int64 __fastcall Oo0O(int a1, int a2)
{
  int v2; // eax
  int v3; // ebx
  int v4; // eax
  int v5; // eax
  _BOOL4 v6; // ebx
  char v8; // [rsp+1Fh] [rbp-41h]
  char v9; // [rsp+20h] [rbp-40h]
  unsigned __int64 v10; // [rsp+48h] [rbp-18h]

  v10 = __readfsqword(0x28u);
  std::allocator<char>::allocator(&v8);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v9, &unk_3010, &v8);
  std::allocator<char>::~allocator(&v8);
  v2 = o0o0o(a1, a2);
  v3 = o0o0o(a1, v2);
  v4 = o0o0o(a1, a2);
  v5 = o0o0o(a2, v4);
  v6 = o0o0o(v5, v3);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v9);
  return (unsigned int)v6;
}

其中又涉及到了o0o0o()函数, 这个函数原型十分简单, 就只有一句话: return (a2 & a1) == 0;

如果选手对数字电路有些许了解的话, 应该很轻松的可以辨认出, 这个函数是与非门(NAND门). 结合hint, 我们可以大致画出Oo0O()函数的结构:

这个结构走一遍, 实际上就是异或门(xor).

异或大家就很熟悉了. 稍微改一下IDA反编译出来的函数, 我们可以得到一堆Base64字符串.

其实没有学过模电问题也不大, 我们使用gdb, 在o0o0OO()函数前面下个断点, 就像这样:

然后你就可以看见寄存器指向的运算结果了, 继续跟踪, 我们可以把所有经过运算的字符串全部抓出来, 得到了一堆Base64.

这个时候你把Base64全部拿出去解码, 只能得到和程序运行之后的输出一样的一堆歌词, 并没有flag. 我们观察这些歌词的结尾大部分都是逗号, 然后结合hint的提示, 相同字符经过Base64编码后的结果是相同的, 但是你所抓出来的这一大串base64, 他们的结尾甚至都不相同. ,经过编码后是LA==, 但是这些base64结尾不全都是这个. 这个时候你需要对Base64的解码过程有一定的理解, Base64解码过程中会舍弃=所对应的padding位, 这也是Base64隐写的原理.

因此我们把那一堆Base64用隐写提取脚本提取一下就可以了.

ICTFE中关于Base64隐写提取的函数如下:


def base64_stego(lines):
    alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    flag = ''
    temp = 0
    digit = 0
    for i in lines:
        if i[-1] != '=':
            continue
        elif i[-2] != '=':
            digit += 2
            temp = (temp << 2) + (alphabet.find(i[-2]) & 0x3)
        else:
            digit += 4
            temp = (temp << 4) + (alphabet.find(i[-3]) & 0xf)
        if digit == 8:
            digit = 0
            flag += chr(temp)
            temp = 0
        elif digit > 8:
            digit = 2
            flag += chr(temp >> 2)
            temp = temp & 0x3
    return flag
0%