2023 羊城杯 Reverse

mi1itray.axe published on
8 min, 1434 words

Categories: Reverse

没打,王哥发了我三题,我就看了一下,稍微多花了时间。

CSGO

这题golang写的,打开看到一个base64表和密文,直接解码不对,估计换表了,然后有反调试。没怎么找,直接就运行后attach上去

拿到base表就直接秒了

vm_co

虚拟机题,不是很难,这是赛后看的,就完整翻译了一遍

Rip = 0

def function_17(x, y):
    global Rip
    if x == 0:
        Rip = y
    return f"cmp x, 0\n jz {Rip}"


def function_18(x, y):
    global Rip
    if x:
        Rip = y
    return f"cmp x, 0\n jnz {Rip}"


def function_19(x, _):
    global Rip
    Rip = x
    return f"jmp {x}"


vm_opcode = {
    "0": lambda x, y: f"swap(vm_body[{x}], vm_body[{y}])",
    "1": lambda x, y: f"vm_body[{x}] ^= vm_body[{y}]",
    "2": lambda x, y: f"vm_body[{x}] += {y}",
    "3": lambda x, y: f"vm_body[{x}] += vm_body[{y}]",
    "4": lambda x, y: f"vm_body[{x}] -= {y}",
    "5": lambda x, y: f"vm_body[{x}] -= vm_body[{y}]",
    "6": lambda x, y: f"vm_body[{x}] *= {y}",
    "7": lambda x, y: f"vm_body[{x}] *= vm_body[{y}]",
    "8": lambda x, y: f"vm_body[{x}] = vm_body[{x}] / {y}",
    "9": lambda x, y: f"vm_body[{x}] = vm_body[{x}] / vm_body[{y}]",
    "10": lambda x, y: f"vm_body[{x}] = vm_body[{x}] % {y}",
    "11": lambda x, y: f"vm_body[{x}] = vm_body[{x}] % vm_body[{y}]",
    "12": lambda x, y: f"vm_body[{x}] <<= {y}",
    "13": lambda x, y: f"vm_body[{x}] = vm_body[0] << {y}",
    "14": lambda x, _: f"vm_body[Var1 + 16] = vm_body[{x}]\n Var1 += 1",
    "15": lambda x, _: f"print(vm_body[{x}])",
    "16": lambda _, __: f"Var1 -= 1\nprint(vm_body[Var + 16])",
    "17": function_17,
    "18": function_18,
    "19": function_19,
    "20": lambda x, _: f"t = vm_body[ vm_body[{x}] ]\n vm_body[Var + 16] = t\nVar += 1",
    "21": lambda _, __: f"Var1 -= 1\n vm_body[0] = vm_body[Var + 16]",
    "22": lambda x, _: f"vm_body[Var + 16] = {x}\nVar += 1",
    "23": lambda _, __: f"if Rip >= 15:\n\tbreak\ncontinue",
    "24": lambda _, __: f"vm_body[0] = vm_body[2] | vm_body[1]",
    "25": lambda x, y: f"vm_body[{x}] = vm_body[0] >> {y}",
    "26": lambda x, y: f"vm_body[{x}] = {y}",
}


def interpre_byte_code(opcodes: list, len: int):
    global Rip
    Rip = 0
    while Rip < len:
        # print(f"rip => {Rip}")
        print(vm_opcode[str(opcodes[Rip])](opcodes[Rip + 1], opcodes[Rip + 2]))
        Rip += 3


opcode1 = list(0x20D01011903001A.to_bytes(8, "little"))
opcode2 = list(0x300010201180702.to_bytes(8, "little"))
opcode3 = opcode1[:7] + opcode2
opcode3[2] = "flag[i]"
print(opcode3)
interpre_byte_code(opcode3, 15)

opcode1 = list(0x20D02011903001A.to_bytes(8, "little"))
opcode2 = list(0x400010201180602.to_bytes(8, "little"))
opcode3 = opcode1[:7] + opcode2
opcode3[2] = "vm_body[0]"
interpre_byte_code(opcode3, 15)

opcode1 = list(0x20D03011903001A.to_bytes(8, "little"))
opcode2 = list(0x500010201180502.to_bytes(8, "little"))
opcode3 = opcode1[:7] + opcode2
opcode3[2] = "vm_body[0]"
interpre_byte_code(opcode3, 15)

opcode1 = list(0x20D04011903001A.to_bytes(8, "little"))
opcode2 = list(0x600010201180402.to_bytes(8, "little"))
opcode3 = opcode1[:7] + opcode2
opcode3[2] = "vm_body[0]"
interpre_byte_code(opcode3, 15)

翻译出来的结果

[26, 0, 'flag[i]', 25, 1, 1, 13, 2, 7, 24, 1, 2, 1, 0, 3]
vm_body[0] = flag[i]
vm_body[1] = vm_body[0] >> 1
vm_body[2] = vm_body[0] << 7
vm_body[0] = vm_body[2] | vm_body[1]
vm_body[0] ^= vm_body[3]
vm_body[0] = vm_body[0]
vm_body[1] = vm_body[0] >> 2
vm_body[2] = vm_body[0] << 6
vm_body[0] = vm_body[2] | vm_body[1]
vm_body[0] ^= vm_body[4]
vm_body[0] = vm_body[0]
vm_body[1] = vm_body[0] >> 3
vm_body[2] = vm_body[0] << 5
vm_body[0] = vm_body[2] | vm_body[1]
vm_body[0] ^= vm_body[5]
vm_body[0] = vm_body[0]
vm_body[1] = vm_body[0] >> 4
vm_body[2] = vm_body[0] << 4
vm_body[0] = vm_body[2] | vm_body[1]
vm_body[0] ^= vm_body[6]

这是每一个字符单独的加密过程,非常简单,就是字节的bit变换,逆过来就是exp

exp:

c = [
    0xDF,
    0xD5,
    0xF1,
    0xD1,
    0xFF,
    0xDB,
    0xA1,
    0xA5,
    0x89,
    0xBD,
    0xE9,
    0x95,
    0xB3,
    0x9D,
    0xE9,
    0xB3,
    0x85,
    0x99,
    0x87,
    0xBF,
    0xE9,
    0xB1,
    0x89,
    0xE9,
    0x91,
    0x89,
    0x89,
    0x8F,
    0xAD,
]
d = list(0xBEEDBEEF.to_bytes(4, "little"))


def enc(x: int):
    t = ((x >> 1) | (x << 7)) & 0xFF
    t ^= d[0]
    t = ((t >> 2) | (t << 6)) & 0xFF
    t ^= d[1]
    t = ((t >> 3) | (t << 5)) & 0xFF
    t ^= d[2]
    t = ((t >> 4) | (t << 4)) & 0xFF
    t ^= d[3]
    t = ((t >> 5) | (t << 3)) & 0xFF
    return t


def dec(x: int):
    t = ((x >> 3) | (x << 5)) & 0xFF
    t ^= d[3]
    t = ((t >> 4) | (t << 4)) & 0xFF
    t ^= d[2]
    t = ((t >> 5) | (t << 3)) & 0xFF
    t ^= d[1]
    t = ((t >> 6) | (t << 2)) & 0xFF
    t ^= d[0]
    t = ((t >> 7) | (t << 1)) & 0xFF
    return t


c = [dec(i) for i in c]
print(bytes(c))

ez_加密

中间加了很多混淆,很怪的混淆,我抽象出来大概是这样的一个形式

/* return flag + 1 + input; */
int core(int flag, int input)
{
    int tmp1, mask, tmp2, tmp;
    mask = 1;
    do {
        tmp1 = mask;
        tmp2 = input;
        tmp  = input & mask;
        input ^= mask;
        mask = tmp << 1;
    } while (mask);

    do {
        if (tmp1 != tmp2) {
            mask = flag;
            flag ^= input;
            input = (input & mask) << 1;
        }
    } while (input);

    return flag;
}

实际可以替换成lambda x,y: x + y + 1

实际上这题结构是这样,要求输入code和flag,先检查code和flag的长度与格式

我照这还原了一下,是code长度6字节,范围0-9,flag长度40,开头结尾是DASCTF{}

直接照着伪代码,抽象写了一下

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* return flag + 1 + input; */
int core(int flag, int input)
{
    int tmp1, mask, tmp2, tmp;
    mask = 1;
    do {
        tmp1 = mask;
        tmp2 = input;
        tmp  = input & mask;
        input ^= mask;
        mask = tmp << 1;
    } while (mask);

    do {
        if (tmp1 != tmp2) {
            mask = flag;
            flag ^= input;
            input = (input & mask) << 1;
        }
    } while (input);

    return flag;
}

int verification_len()
{
    int x = 0;

    for (int i = 0;; i++) {
        x = core(i, -7);
        if (x == 0) {
            return i;
        }
    }
}

char* verification_range(int len)
{
    int   x;
    char* r   = calloc(sizeof(char), 100);
    int   ptr = 0;

    for (int i = 32; i < 127; i++) {
        x = core(i, -49);
        if (x < 0xa && x >= 0) {
            // printf("%d\n", i);
            r[ptr] = i;
            ptr++;
        }
    }

    return r;
}

int flag_len()
{
    int x = 0;

    for (int i = 0;; i++) {
        x = core(i, -41);
        if (x == 0) {
            return i;
        }
    }
}

char* flag_part1()
{
    char  t[] = "DASCTF{}";
    int   x;
    char* r = calloc(sizeof(char), 9);

    for (int i = 0; i < 8; i++) {
        for (int j = 32; j < 127; j++) {
            x = core(j, ~t[i]);
            if (x == 0) {
                r[i] = j;
            }
        }
    }

    return r;
}

int main()
{
    int      code_len;
    char*    ver_code;
    char*    ver_rang;

    code_len = verification_len();
    printf("verification code len: %d\n", code_len);

    ver_rang = verification_range(code_len);
    printf("verification range: %s\n", ver_rang);

    code_len = flag_len();
    printf("flag len: %d\n", code_len);

    ver_code = flag_part1();
    printf("flag part1: %s\n", ver_code);
}

得到结果是

verification code len: 6
verification range: 0123456789
flag len: 40
flag part1: DASCTF{}

然后code经过了一个base64换表的函数,很明显的换表,没什么好说,把6字节的code变成8字节

然后8字节的code和flag过DES加密,这个DES没改动,加了混淆后很难看,但是DES的表没变,所以很好认出来

DES加密中间还对密文处理了一下

所以整个过程就如下

解密直接爆破code就可以,code6位数,范围0-9,两秒出了

exp:

from base64 import b64encode
import itertools
from string import digits
from Crypto.Cipher import DES
from binascii import unhexlify


def des_descrypt(s, key):
    e = DES.new(key, DES.MODE_ECB)
    return e.decrypt(unhexlify(s))


def base64_encode(text):
    outtab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    intab = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/="
    text = b64encode(text).decode()
    return text.translate(text.maketrans(outtab, intab))


enc = "0723105D5C12217DCDC3601F5ECB54DA9CCEC2279F1684A13A0D716D17217F4C9EA85FF1A42795731CA3C55D3A4D7BEA"

for i in itertools.product(digits, repeat=6):
    key = "".join(i)
    msg = base64_encode(key.encode())
    s = des_descrypt(enc, msg.encode())
    if b"DASCTF" in s:
        print(key)
        print(s)