This page looks best with JavaScript enabled

HGAME_2021 Re WriteUp

 ·  ☕ 3 min read · 👀... views

又到了一年Hgame

Reverse:

apacha

好家伙 第一道就xxTEA

 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
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

#define MX (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))
#define DELTA 0x9e3779b9


static DWORD* xxtea_uint_encrypt(DWORD* data, size_t len, DWORD* key) {
    DWORD n = (DWORD)len - 1;
    DWORD z = data[n], y, p, q = 6 + 52 / (n + 1), sum = 0, e;

    if (n < 1) return data;

    while (0 < q--) {
        sum += DELTA;
        e = sum >> 2 & 3;

        for (p = 0; p < n; p++) {
            y = data[p + 1];
            z = data[p] += MX;
        }

        y = data[0];
        z = data[n] += MX;
    }

    return data;
}

static DWORD* xxtea_uint_decrypt(DWORD* data, size_t len, DWORD* key) {
    DWORD n = (DWORD)len - 1;
    DWORD z, y = data[0], p, q = 6 + 52 / (n + 1), sum = q * DELTA, e;

    if (n < 1) return data;

    while (sum != 0) {
        e = sum >> 2 & 3;

        for (p = n; p > 0; p--) {
            z = data[p - 1];
            y = data[p] -= MX;
        }

        z = data[n];
        y = data[0] -= MX;
        sum -= DELTA;
    }

    return data;
}
int main(){
    BYTE data[] =
    {
      0x23, 0xB3, 0x4E, 0xE7, 0x36, 0x28, 0xA7, 0xB7, 0xE2, 0x6F,
      0xCA, 0x59, 0xC1, 0xC5, 0x7C, 0x96, 0x74, 0x26, 0x80, 0xE7,
      0xE6, 0x54, 0x2D, 0x3D, 0x56, 0x03, 0x9D, 0x8A, 0x9C, 0xC3,
      0xDC, 0x99, 0xED, 0xD8, 0x26, 0x70, 0xAD, 0xFD, 0x33, 0x6A,
      0x0A, 0x55, 0x96, 0xF4, 0x9E, 0x6F, 0x9C, 0x5C, 0x4C, 0xD0,
      0xE5, 0x1B, 0x17, 0xAE, 0x23, 0x67, 0xC2, 0xA5, 0x70, 0x52,
      0x0A, 0x13, 0x42, 0xAC, 0xB2, 0x67, 0xBE, 0x84, 0x79, 0xC7,
      0x5C, 0x70, 0x98, 0x3D, 0x51, 0x5C, 0x2D, 0xDA, 0x36, 0xFB,
      0x45, 0x96, 0x17, 0x22, 0x9D, 0x52, 0xE3, 0x5C, 0xFB, 0xE1,
      0x89, 0xD1, 0x89, 0xD4, 0x5B, 0xE8, 0x1F, 0xD1, 0xC8, 0x73,
      0x96, 0xC1, 0xB5, 0x54, 0x90, 0xB4, 0x7C, 0xB6, 0xCA, 0xE4,
      0x17, 0x21, 0x94, 0xF9, 0xE3, 0x9D, 0xAA, 0xA1, 0x5A, 0x2F,
      0xFD, 0x01, 0xE8, 0xA7, 0xAB, 0x6E, 0x0D, 0xC3, 0x9C, 0xDC,
      0xAD, 0x1B, 0x4A, 0xB0, 0x53, 0x34, 0xF9, 0x06, 0xA4, 0x92
    };
    int key[] = { 1,2,3,4 };
    DWORD * x = xxtea_uint_decrypt((DWORD*)data, 35, (DWORD*)key);

    for (int i = 0; i < 35; i++) {
        printf("%c", x[i]);
    }
    return 0;
}

helloRe

helloRe

整活了一点小操作,关闭一个不存在的句柄来反调试,不过代码逻辑非常简单,也懒得去绕这个反调了

1
2
3
4
5
6
7
8
9
cipher = [
    0x97, 0x99, 0x9C, 0x91, 0x9E, 0x81, 0x91, 0x9D, 0x9B, 0x9A, 
    0x9A, 0xAB, 0x81, 0x97, 0xAE, 0x80, 0x83, 0x8F, 0x94, 0x89, 
    0x99, 0x97
]
flag = ""
for i in range(len(cipher)):
    flag += chr( cipher[i] ^ (0xFF-i) )
print(flag)

pypy

和去年的Hgame一样也是出现了一道Py字节码题,先对照着修复出源代码然后逆

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

import dis
def encrypto():
    raw_flag = input('give me your flag:\n')
    cipher = list(raw_flag[6:-1])       # 去除 "hgame{"
    length = len(cipher)

    for i in range(length//2):
        cipher[i*2], cipher[i*2+1] = cipher[i*2+1], cipher[i*2]


    res = []
    for i in range(length):
        res.append( ord(cipher[i])^i )
    res = bytes(res).hex()
    print('your flag: '+res)

# print(dis.dis(encrypto))
encrypto()

只要源代码修对了,很快就能写出逆向脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
res = "30466633346f59213b4139794520572b45514d61583151576638643a"
res = [int('0x'+res[i:i+2], 16) for i in range(0, len(res), 2)]
for i in range(len(res)):
    res[i] ^= i
for i in range(len(res)//2):
    res[i*2], res[i*2+1] = res[i*2+1], res[i*2]
print("hgame{" + "".join([chr(x) for x in res])+'}')   

# https://www.cnblogs.com/blili/p/11804690.html
# https://docs.python.org/2/library/dis.html

PS:这题flag一堆的特殊字符,一波解出来一看怎么这么多特殊字符一口咬定自己解错了,反反复复检查汇编和脚本,最后发现原来没错 感觉自己像傻逼一样

fake_debugger beta

好家伙 一开始没看懂这个 ‘Step’ by ‘Space’ 到底是什么鬼 题目模拟了一个小调试器 大概是这样的

![fake_debugger beta](/images/CTF/Hgame_2021/fake_debugger beta.png)

每按一次空格+回车就相当于是步过了一下,可以看到它的输出信息很简单,只有4个值,通过名字可以猜到,eax和ebx两个寄存器中的值一定与运算和运算结果有关,而观察ecx的值可以确定是一个计数器,而zf标志位标志当前是否完成运算。 然后我们观察可以发现,eax和ebx的值,似乎都不是我们输入的值,但是考虑到这个输出过与简单,但题目一定又是可逆的,猜测是其中一个值和输入做了异或,然后再比较,观察整理出下面的逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for i in range(len(input)):
    zf = 0
    eax = input[i]^box[i]
    ebx = box[i]
    ecx = i

    zf = 1
    ebx = cipher[i]
    if eax != ebx:
        print("Wrong Flag! Try again!")
        break

看懂这逻辑后就会发现当zf=0和zf=1时,ebx分别保存box和cipher的值,抠出来异或一下就是明文的值了

1
2
3
4
5
6
7
8
box = [23, 45, 67, 89, 13, 24, 35, 46, 57, 35, 46, 57, 37, 48, 38, 13, 16, 37, 58, 63, 41, 73, 52, 94]
cipher = [127, 74, 34, 52, 104, 99, 122, 65, 76, 124, 101, 87, 21, 71, 121, 105, 117, 71, 79, 120, 78, 122, 70, 35]

flag = ""
for i in range(len(box)):
    flag += chr( box[i]^cipher[i] )

print(flag)
Share on

Qfrost
WRITTEN BY
Qfrost
CTFer, Anti-Cheater, LLVM Committer