Press "Enter" to skip to content

2020 安洵杯 EasyCM WriteUp

Last updated on 2022年6月18日

初步分析

这是一个运行在x86 Windows上的可执行文件,无壳。

我们运行一下观察它的功能:

EasyCM.exe

它读取一行输入然后判断。

逆向分析

我们在IDA打开它,查看反编译的代码:

主流程

它读取输入到input字符串,然后在sub_411578进行预处理,最后在check进行检查对比。

sub_411578

sub_411578

我们看到,它将输入字符串末尾补符号-,使得字符串的长度为3的整数倍。但是这里的反编译其实是不完整的,我们需要在汇编代码中去花指令从而看到完整的流程。

sub_411578去花

这里我们可以发现,后面还对输入进行循环做了一个处理,但是sub_411546sub_4113A2中的

hEventhHandle事件我们还不知道是什么,因此这里我们先去看其他处理的部分。

sub_4112AD

sub_4112AD

这里没什么好说的,就是比较并输出。有一个这里IDA默认的std::ostream::operator<<只有一个参数,我们要将它修改为两个参数的(第二个参数是输出格式参数),否则这里反编译会是下面的样子:

TLS

我们注意到在段表中有一个.tls段,我们基本可以预料是使用tls的回调函数做了一些操作:

不知道tls的可以参考这一篇文章:TLS回调函数

在函数列表中搜索TLS,可以看到如下这么几个函数:

其中,带_0后缀的是对应回调函数的调用函数。

TlsCallback_0_0

TlsCallback_0_0

sub_411212sub_4111AE就是进程开始前执行的内容(DLL_PROCESS_ATTACH)。

sub_411212

这个函数有花指令,去花之后代码如下:

sub_411212

它使用CheckRemoteDebuggerPresent检查是否有调试器,若有则对table进行干扰操作。

sub_4111AE

这个函数同样有花指令,去花之后如下:

sub_4111AE

这段代码是一段SMC,它在进程的段表中查找一个叫.cyzcc的段,然后对段内容异或操作解密。

我们使用IDAPython脚本对.cyzcc段进行patch:

from ida_bytes import get_byte, patch_byte

addr = 0x41E000
end  = 0x41F200

x = 'D0g3'

for i in range(end - addr):
    b = get_byte(addr + i)
    bb = b ^ ord(x[i%4])
    patch_byte(addr + i, bb)

print("Done")

TlsCallback_1_0

这里我们看到了需要的hHandlehEvent事件信号,但这里只是创建,并不能满足前面循环处理的需求。

TlsCallback_2_0

这个回调函数创建了两个子线程StartAddresssub_41109B

StartAddress

这个线程是对table进行解密,然后接收hHandle信号,还有一部分处理被花指令隐藏了,去花之后如下:

它有一个循环,接收hHandle信号和触发hEvent信号,刚好和之前的sub_411578对应起来。这里的处理相当于把table当作环形的,然后每次移动指针位置。

flag代码

#include 
unsigned char table[128] = {
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
    0x20, 0x21, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
    0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
    0x15, 0x16, 0x17, 0x18, 0x19, 0xFA, 0xFB, 0xFC,
    0xFD, 0xFE, 0xFF, 0x00, 0xB6, 0xB7, 0xB8, 0xB9,
    0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xB1, 0xB5};
char dest[] = {
    0x23, 0x7A, 0x3D, 0x60, 0x34, 0x07, 0x11, 0x36,
    0x2C, 0x05, 0x0C, 0x20, 0x0B, 0x22, 0x3F, 0x6F,
    0x16, 0x00, 0x37, 0x0D, 0x36, 0x0F, 0x1E, 0x20,
    0x37, 0x14, 0x02, 0x09, 0x02, 0x0F, 0x1B, 0x39, 
    0x00};
char fake_flag[] = "D0g3{cyzcc_have_ten_girlfriends}";
char input[64];
unsigned char* table_ptr;
void decode_table()
{
    for (int i = 0; i < 64; ++i)
    {
        if (i >= 26)
        {
            if (i >= 45)
                table[i] += 122;
            else
                table[i] += 90;
        }
        else
        {
            table[i] += 57;
        }
    }
}
void get_dest()
{
    for (int i = 0; i < 32; ++i)
        dest[i] ^= fake_flag[i];
}
int indexof(char ch)
{
    for (int i = 0; i < 64; ++i)
        if (table_ptr[i] == ch)
            return i;
    return -1;
}
int main()
{
    decode_table();
    get_dest();
    table_ptr = table;
    for (int i = 0; i < 8; ++i)
    {
        table_ptr[64] = table_ptr[0];
        table_ptr++;
        int a = indexof(dest[i * 4]);
        int b = indexof(dest[i * 4 + 1]);
        int c = indexof(dest[i * 4 + 2]);
        int d = indexof(dest[i * 4 + 3]);
        input[i * 3 + 0] = (a << 2) & 0xC0 | (c << 2) & 0x30 | (d >> 2) & 0xC | b & 0x3;
        input[i * 3 + 1] = (b << 2) & 0xC0 | (a << 2) & 0x30 | (d >> 0) & 0xC | c & 0x3;
        input[i * 3 + 2] = (c << 2) & 0xC0 | (b << 2) & 0x30 | (d << 2) & 0xC | a & 0x3;
    }
    puts(input);
}

flag

d0g3{d0g3_1sA_b1gf4mily}
发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注