常年不去上早八课的人,今天居然在八点前就醒了,要问为什么?这比赛从早上八点打到晚上八点 >…<(悲)

不过,咱们队伍在下午就把逆向题 ak 了~居然没有坐牢到晚上 (●’◡’●)

题目附件: 点击下载

# careful

动态调试,题目里有个 inline hook, 在这里打个断点

image-20230604155520790

那么网址就是 Just_An_APIH00k11.com

# babyre

die 查一下壳

image-20230604155708500

有 sleep 反调试,把 sleep nop 掉

image-20230604160528205

这里读取了名称为 cod 的资源,用 resource hacker 把资源复制下来

image-20230604170253987

然后向下执行,这里是一个对 cod 资源进行解密的地方

image-20230604170321641

这里要注意的是如果检测到调试器,那么 byte_7FF6DA64F000[3] 将会被赋值为 36

image-20230604170357225

所以要把这个 if 语句通过修改 ZF 标志位的方式来绕过反调试

cod 资源解密脚本如下

arr = [0x18, 0x57, 0x68, 0x64]
with open('COD101.bin', 'rb') as f:
    b = f.read()
b = bytearray(b)
for i in range(len(b)):
    b[i] = b[i] ^ arr[i % 4]
with open('COD_de.bin', 'wb') as f:
    f.write(b)

用 ida 打开,看到有花指令

image-20230604170448531

nop 一下,主要的改动有这几处

image-20230604170942267

image-20230604172257775

image-20230604172306573

于是得到如下的伪代码

image-20230604172324746

看算法是魔改的 RC4,exp 如下

class RC4:
    def __init__(self, key) -> None:
        self.key = key
        self.S = 0
        self.__rc4_init__()
    def __rc4_init__(self):
        S = [i for i in range(256)]
        j = 0
        for i in range(256):
            j = (2 * j + S[i] + key[i % len(key)]) % 256
            S[i], S[j] = S[j], S[i]
        self.S = S
    def rc4_encrypt(self, plain) -> list:
        i = 0
        j = 0
        cipher = []
        cnt = 0
        for p in plain:
            p = (p + 256 - cnt % 0xd) % 256
            cnt += 1
            i = (i + j) % 256
            j = (j + self.S[i]) % 256
            self.S[i], self.S[j] = self.S[j], self.S[i]
            tmp = self.S[(self.S[i] + self.S[j] + j) % 256]
            k = p ^ tmp
            cipher.append(k)
        return cipher
key = [0x5D , 0x42 , 0x62 , 0x29 , 0x3, 0x36 , 0x47 , 0x41 , 0x15, 0x36]
data = [0xF7, 0x2E, 0x34, 0xF0, 0x72, 0xCF, 0x5E, 0x0A, 0xBB, 0xEC, 0xB1, 0x2B, 0x70, 0x88, 0x88, 0xED,
0x46, 0x38, 0xDB, 0xDA, 0x6C, 0xBD, 0xD4, 0x06, 0x77, 0xF2, 0xCF, 0x56, 0x88, 0xC6, 0x31, 0xD2,
0xB7, 0x5A, 0xC1, 0x42, 0xB0, 0xF4, 0x48, 0x37, 0xF5, 0x2C, 0xF5, 0x58]
rc4 = RC4(key)
plain = rc4.rc4_encrypt(data)
print(''.join(map(chr,plain)))

# ez_exe

查个壳,是 python 逆向

image-20230604080525484

pyinstxtractor 脱一下

image-20230604080836146

在线网站看一下 ez_py.pyc 的源代码

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.11
import ctypes
from time import *
from ctypes import *
from ctypes import wintypes
from hashlib import md5
class _STARTUPINFO(Structure):
    _fields_ = [
        ('cb', c_ulong),
        ('lpReserved', c_char_p),
        ('lpDesktop', c_char_p),
        ('lpTitle', c_char_p),
        ('dwX', c_ulong),
        ('dwY', c_ulong),
        ('dwXSize', c_ulong),
        ('dwYSize', c_ulong),
        ('dwXCountChars', c_ulong),
        ('dwYCountChars', c_ulong),
        ('dwFillAttribute', c_ulong),
        ('dwFlags', c_ulong),
        ('wShowWindow', c_ushort),
        ('cbReserved2', c_ushort),
        ('lpReserved2', c_char_p),
        ('hStdInput', c_ulong),
        ('hStdOutput', c_ulong),
        ('hStdError', c_ulong)]
class _PROCESS_INFORMATION(Structure):
    _fields_ = [
        ('hProcess', c_void_p),
        ('hThread', c_void_p),
        ('dwProcessId', c_ulong),
        ('dwThreadId', c_ulong)]
StartupInfo = _STARTUPINFO()
ProcessInfo = _PROCESS_INFORMATION()
key1 = bytes(md5(b'bin1bin1bin1').hexdigest().encode())
file = open('bin1', 'rb').read()
arr = range(len(file))()
open('bin1', 'wb').write(bytes(arr))
sleep(0)
bet = ctypes.windll.kernel32.CreateProcessA(b'bin1', ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), byref(StartupInfo), byref(ProcessInfo))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ProcessInfo.hProcess), ctypes.c_int(-1))
open('bin1', 'wb').write(file)

用 ida 反编译 bin1 失败,看来是被加密了

用这个代码看一下字节码

import marshal, dis
f = open("ez_py.pyc", "rb").read()
code = marshal.loads(f[16:])			#这边从 16 位开始取因为是 python3 python2 从 8 位开始取
dis.dis(code)

在最后面得到了这个

Disassembly of <code object <listcomp> at 0x00000297CC7F8E70, file "ez_py.py", line 59>:
 59           0 RESUME                   0
              2 BUILD_LIST               0
              4 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                50 (to 108)
              8 STORE_FAST               1 (i)
             10 LOAD_GLOBAL              0 (key1)
             22 LOAD_FAST                1 (i)
             24 LOAD_GLOBAL              3 (NULL + len)
             36 LOAD_GLOBAL              0 (key1)
             48 PRECALL                  1
             52 CALL                     1
             62 BINARY_OP                6 (%)
             66 BINARY_SUBSCR
             76 LOAD_GLOBAL              4 (file)
             88 LOAD_FAST                1 (i)
             90 BINARY_SUBSCR
            100 BINARY_OP               12 (^)
            104 LIST_APPEND              2
            106 JUMP_BACKWARD           51 (to 6)
        >>  108 RETURN_VALUE

那么解密代码如下

from hashlib import md5
key1 = bytes(md5(b'bin1bin1bin1').hexdigest().encode())
# print(key1)
file = open('bin1', 'rb').read()
arr = [key1[i % len(key1)] ^ file[i] for i in range(len(file))]
# open('bin1', 'wb').write(bytes(arr))
with open('bin1__','wb') as f:
    f.write(bytes(arr))

反编译出来是这个

image-20230604091113130

那根据提示我们把上面的解密脚本稍作修改

from hashlib import md5
key1 = bytes(md5(b'bin2bin2bin2').hexdigest().encode())
# print(key1)
file = open('bin2', 'rb').read()
arr = [key1[i % len(key1)] ^ file[i] for i in range(len(file))]
# open('bin1', 'wb').write(bytes(arr))
with open('bin2__','wb') as f:
    f.write(bytes(arr))

然后用 ida 反编译 bin2__

image-20230604091158113

那么这就是正常的逆向题了

btea 函数里面是这个,这是一个 xxtea 算法

image-20230604091222886

写一下 exp

#include <iostream>
#include <stdio.h>
using namespace std;
#include <stdint.h>
#define DELTA 0x7937B99E
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t* v, int n, uint32_t const key[4]) {
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1) {          /* Coding Part */
        rounds = /*6 + */52 / n;
        sum = 0;
        z = v[n - 1];
        do {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p = 0; p < n - 1; p++) {
                y = v[p + 1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n - 1] += MX;
        } while (--rounds);
    }
    else if (n < -1) {  /* Decoding Part */
        n = -n;
        rounds = /*6 + */52 / n;
        sum = rounds * DELTA;
        y = v[0];
        do {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--) {
                z = v[p - 1];
                y = v[p] -= MX;
            }
            z = v[n - 1];
            y = v[0] -= MX;
        } while ((sum -= DELTA) != 0);
    }
}
int main()
{
    uint32_t const key[4] = { 0x4B5F, 0xDEAD, 0x11ED, 0xB3CC };
    uint32_t data[11] = { 0xCC45699D, 0x683D5352,0xB8BB71A0,0xD3817AD,0x7547E79E,0x4BDD8C7C,0x95E25A81,0xC4525103,0x7049B46F,0x5417F77C,0x65567138 };
    uint32_t* sent = data;
    //btea(sent, 11, key);
    //printf("coded:%x  %x\n", sent[0], sent[1]);
    btea(sent, -11, key);
    //printf("decoded:%x  %x\n", sent[0], sent[1]);
    for (int i = 0; i < 11; i++) {
        for (int j = 0; j < 4; j++)
        {
            printf("%c", sent[i] & 0xff);
            sent[i] >>= 8;
        }
    }
    return 0;
}
//DASCTF{7eb20cb2-deac-11ed-ae42-94085339ce84}

# cap

在这个地方动调

image-20230604175543351

可以发现数组的下标在 0~12 之间循环

我们随便打开一个 BMP 类型的文件,用 010 看看

image-20230604181159971

对于 BMP 类型的文件前两个字节必定是 43 4D

既然这个加密的 bmp 的每一个字节进行的都是异或,那我们可以将前两个字节异或看看

image-20230604181040985

n 和 c 是密钥 enc_by_dasctf 的第 2 个和第 3 个字符,按照这个序列,我们向后将密钥向后延申看看后面的情况如何

image-20230604181917246

所以我们写个脚本,从密钥的第二位开始,循环异或

key = "enc_by_dasctf"
with open('cap.bin', 'rb') as f:
    s = bytearray(f.read())
for i in range(len(s)):
    s[i] ^= ord(key[(i+1) % len(key)])
with open('flag.bmp', 'wb') as f:
    f.write(s)

得到 flag

image-20230604182002579

# unsym

查一下壳,是 go 逆向

image-20230604093542315

用这个脚本恢复一下 go 符号 https://github.com/renshareck/IDAGolangHelper_SupportGo1.20

依次点击如下按钮

image-20230604185203790

首先判断 key 正确与否,看来这是个 rsa

image-20230604135543069

用 yafu 解一下 p 和 q

image-20230604151321851

然后解出密钥

import gmpy2
from Crypto.Util.number import long_to_bytes
n = 0x1d884d54d21694ccd120f145c8344b729b301e782c69a8f3073325b9c5
p = 37636318457745167234140808130156739
q = 21154904887215748949280410616478423
c = 0xfad53ce897d2c26f8cad910417fbdd1f0f9a18f6c1748faca10299dc8
e = 0x10001
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
# E@sy_RSA_enc7ypt

再往后看,

image-20230604150237185

动调了一下看到 iv 和 key 都是一样的

所以直接写个 exp 把加密的文件解密

from Crypto.Cipher import AES
password = b'E@sy_RSA_enc7ypt'  # 秘钥必须为 16 字节或者 16 字节的倍数的字节型数据
iv = b'E@sy_RSA_enc7ypt'  # iv 偏移量,bytes 类型
with open('encrypted.bin','rb') as f:
    en_text = f.read()
aes = AES.new(password, AES.MODE_CBC, iv)  # CBC 模式下解密需要重新创建一个 aes 对象
de_text = aes.decrypt(en_text)
with open('decrypt.exe','wb') as f:
    f.write(de_text)

运行一下解密出的 exe, 就得到 flag 了

image-20230604152619966