BUGKU Reverse bingo题解

 

题目下载后得到一张图片

刚开始的时候没看分类,以为是 MISC 题,然后找了半天隐写内容,都没做找到,但是这个文件这么大,肯定是有问题。

然后又仔细找了一下,结果发现了这样一段内容,

看上去内容好像是 EXE 程序中才会有的,于是看了一下分类,好家伙,原来是 re 题。

百度了一下 png 文件尾

PNG (png),

文件头:89504E47 文件尾:AE 42 60 82

搜索 AE 42 60 82

这不就是熟悉的 MZ 文件头吗,用 010 Editor 提取出这一段内容。

并重命名为 bingo.exe

结果报错了,随便找了一个 exe 文件,比对文件内容是否确实,发生少了 PE 文件头标识。

补上这一段内容。

数据补上后直接打开运行,发现可以显示黑框,但是运行后直接退出,于是打开 IDA 分析一下

ida 打开后似乎认不出来文件的其他内容,动态调试后发现这一段内容会出现异常的情况。

pusha
mov     ecx, 3E000h
mov     ebx, 1000h
mov     ebx, 400000h
add     ebx, edx
xor     byte ptr [ebx], 22h
inc     ebx
popa
jmp     loc_408BE0

原因是 edx 的内容也是 400xxxh,相加之后到了 800xxx,超出了范围。
这里不知道是作者预期还是怎么的,反正应该是这个解密函数出现了问题。

但是看到这里应该就是一个 xor 解密 (xor 0x22),所以直接在外部解密吧。

编写解密脚本:

#define MAXKEY 5000
#define MAXFILE 1000
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
    char xor_key[MAXKEY], file_dir[MAXFILE];
    char* buf;
    //printf("xor key: ");
    //scanf("%s", xor_key);
    xor_key[0] = 0x22;
    xor_key[1] = 0;
    printf("file: ");
    scanf("%s", file_dir);
    FILE* fp = fopen(file_dir, "rb");
    strcat(file_dir, ".xor");
    FILE* fpw = fopen(file_dir, "wb+");
    if (fp && fpw)
    {
        fseek(fp, 0, SEEK_END);
        size_t size = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        buf = new char[size];
        fread(buf, sizeof(char), size, fp);
        for (size_t i = 0, keySize = strlen(xor_key); i < size; i++)
            buf[i] ^= xor_key[i % keySize];
        fwrite(buf, sizeof(char), size, fpw);
    }
    if (fp) fclose(fp);
    if (fpw) fclose(fpw);
    return 0;
}

解密文件后,得到文件bingo.exe.xor,观察文件信息,发现实际上只有.text段进行了异或加密,其他内容都没有加密。
从文件中大量的 0x22 内容也可以看出来 (因为 0x00 ^ 0x22 = 0x22)

从 0xCC 就可以知道,应该是解密对了,因为 0xCC 对应的是 INT3 断点,也就是当段未初始化的时候,vs debug 模式下会赋值的内容。vs 中的烫烫烫也是这么来的。

替换.text 段的内容。

替换后得到的 exe 程序,直接运行当然还是不可以的,但是可以放到 ida 中解析各个函数了。

但是没有任何的符号信息,难以阅读。所以我还是打算调整程序让其可以正常运行。

打开 ida 后,定位到 start 处

直接用 Keypatch(ida 插件)进行修改,让其直接跳到程序真正的入口点 (jmp sub_408BE0)。

修改后进行保存

发现程序以及可以成功运行。

重新载入后发现,接下来发现程序的符号信息就有了。

可以看出,程序对输入内容进行加密后与程序中 off_443DC0 (zaciWjV!Xm [_XSqeThmegndq) 进行比对。

这里的加密方法就是对你输入值 (c) 进行平方,然后再加上一个参数 (b),最后解出来 a。

满足关系式: a^2 + b^2 = c^2。

本来以为直接解密就好了,没想到这里还有一个函数_strrev (v6);

他的作用是把字符串信息倒置,所以最后显示的顺序也会变换。

由于这里 sqrt 还有个精度问题,我这里就不进行逆运算了,也就是

c = sqrt(a^2 + b^2)

直接编写程序爆破 c 的内容。

解密程序

#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
int main()
{
    char s[] = "zaciWjV!Xm[_XSqeThmegndq";
    char e[] = "                        ";
    char* v6 = (char*)operator new(strlen(s) + 1);
    memset(v6, 0, strlen(s) + 1);
    for (int i = 0; i < strlen(s); i++)
    {
        v6[i] = 'a' + i;
        _strrev(v6);
    }
    for (int i = 0; i < strlen(s); i++) e[v6[i] - 'a'] = s[i];
    printf("%s\n", v6);
    printf("%s\n", e);
    int a2 = 0x34;
    for (int i = 0; i < strlen(s); ++i)
    {
        for (char t = 1; t < 0xFF; t++)
        {
            int v2 = (signed __int64)pow((double)a2, 2.0);
            signed int v3 = (unsigned __int64)(signed __int64)pow((double)t, 2.0);
            v3 -= v2;
            v6[i] = (signed __int64)(sqrt((double)v3) + 0.5);
            if (v6[i] == e[i])
            {
                printf("%c", t);
                break;
            }
        }
        --a2;
    }
    return 0;
}

运行后可以得到:flag {woc_6p_tql_moshifu}

(完)