常年不去上早八课的人,今天居然在八点前就醒了,要问为什么?这比赛从早上八点打到晚上八点>...<(悲)
不过,咱们队伍在下午就把逆向题ak了~居然没有坐牢到晚上(●'◡'●)
动态调试,题目里有个inline hook,在这里打个断点
 
那么网址就是Just_An_APIH00k11.com
die查一下壳
 
有sleep反调试,把sleep nop掉
 
这里读取了名称为cod的资源,用resource hacker把资源复制下来
 
然后向下执行,这里是一个对cod资源进行解密的地方
 
这里要注意的是如果检测到调试器,那么byte_7FF6DA64F000[3]将会被赋值为36
 
所以要把这个if语句通过修改ZF标志位的方式来绕过反调试
cod资源解密脚本如下
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      | arr =[0x18, 0x57, 0x68, 0x64]with open('COD101.bin', 'rb') as f:    b =f.read()b =bytearray(b)fori inrange(len(b)):    b[i] =b[i] ^ arr[i %4]with open('COD_de.bin', 'wb') as f:    f.write(b) | 
用ida打开,看到有花指令
 
nop一下,主要的改动有这几处
 
 
 
于是得到如下的伪代码
 
看算法是魔改的RC4,exp如下
| 
      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
      | classRC4:    def__init__(self, key) -> None:        self.key =key        self.S =0        self.__rc4_init__()    def__rc4_init__(self):        S =[i fori inrange(256)]        j =0        fori inrange(256):            j =(2*j +S[i] +key[i %len(key)]) %256            S[i], S[j] =S[j], S[i]        self.S =S    defrc4_encrypt(self, plain) -> list:        i =0        j =0        cipher =[]        cnt =0        forp inplain:            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)        returncipherkey =[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))) | 
查个壳,是python逆向
 
用pyinstxtractor脱一下
 
用看一下ez_py.pyc的源代码
| 
      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
      | #!/usr/bin/env python# visit https://tool.lu/pyc/ for more information# Version: Python 3.11importctypesfromtime import*fromctypes import*fromctypes importwintypesfromhashlib importmd5class_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失败,看来是被加密了
用这个代码看一下字节码
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      | importmarshal, disf =open("ez_py.pyc", "rb").read()code =marshal.loads(f[16:])            #这边从16位开始取因为是python3 python2从8位开始取dis.dis(code) | 
在最后面得到了这个
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      
      12
      
      13
      
      14
      
      15
      
      16
      
      17
      
      18
      
      19
      
      20
      
      21
      | Disassembly of <code object<listcomp> at 0x00000297CC7F8E70, file"ez_py.py", line 59>: 590RESUME                   0              2BUILD_LIST               0              4LOAD_FAST                0(.0)        >>    6FOR_ITER                50(to 108)              8STORE_FAST               1(i)             10LOAD_GLOBAL              0(key1)             22LOAD_FAST                1(i)             24LOAD_GLOBAL              3(NULL +len)             36LOAD_GLOBAL              0(key1)             48PRECALL                  1             52CALL                     1             62BINARY_OP                6(%)             66BINARY_SUBSCR             76LOAD_GLOBAL              4(file)             88LOAD_FAST                1(i)             90BINARY_SUBSCR            100BINARY_OP               12(^)            104LIST_APPEND              2            106JUMP_BACKWARD           51(to 6)        >>  108RETURN_VALUE | 
那么解密代码如下
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      | fromhashlib importmd5key1 =bytes(md5(b'bin1bin1bin1').hexdigest().encode())# print(key1)file=open('bin1', 'rb').read()arr =[key1[i %len(key1)] ^ file[i] fori inrange(len(file))]# open('bin1', 'wb').write(bytes(arr))with open('bin1__','wb') as f:    f.write(bytes(arr)) | 
反编译出来是这个
 
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      | fromhashlib importmd5key1 =bytes(md5(b'bin2bin2bin2').hexdigest().encode())# print(key1)file=open('bin2', 'rb').read()arr =[key1[i %len(key1)] ^ file[i] fori inrange(len(file))]# open('bin1', 'wb').write(bytes(arr))with open('bin2__','wb') as f:    f.write(bytes(arr)) | 
然后用ida反编译bin2__
 
那么这就是正常的逆向题了
btea函数里面是这个,这是一个xxtea算法
 
写一下exp
| 
      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
      | #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, intn, 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);    }    elseif(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);    }}intmain(){    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(inti =0; i < 11; i++) {        for(intj =0; j < 4; j++)        {            printf("%c", sent[i] & 0xff);            sent[i] >>=8;        }    }    return0;}//DASCTF{7eb20cb2-deac-11ed-ae42-94085339ce84} | 
在这个地方动调
 
可以发现数组的下标在0~12之间循环
我们随便打开一个BMP类型的文件,用010看看
 
对于BMP类型的文件前两个字节必定是43 4D
既然这个加密的bmp的每一个字节进行的都是异或,那我们可以将前两个字节异或看看
 
n和c是密钥enc_by_dasctf的第2个和第3个字符,按照这个序列,我们向后将密钥向后延申看看后面的情况如何
 
所以我们写个脚本,从密钥的第二位开始,循环异或
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      | key ="enc_by_dasctf"with open('cap.bin', 'rb') as f:    s =bytearray(f.read())fori inrange(len(s)):    s[i] ^=ord(key[(i+1) %len(key)])with open('flag.bmp', 'wb') as f:    f.write(s) | 
得到flag
 
查一下壳,是go逆向
 
用这个脚本恢复一下go符号https://github.com/renshareck/IDAGolangHelper_SupportGo1.20
依次点击如下按钮
 
首先判断key正确与否,看来这是个rsa
 
用yafu解一下p和q
 
然后解出密钥
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      
      12
      
      13
      | importgmpy2fromCrypto.Util.number importlong_to_bytesn =0x1d884d54d21694ccd120f145c8344b729b301e782c69a8f3073325b9c5p =37636318457745167234140808130156739q =21154904887215748949280410616478423c =0xfad53ce897d2c26f8cad910417fbdd1f0f9a18f6c1748faca10299dc8e =0x10001phi =(p -1) *(q -1)d =gmpy2.invert(e, phi)m =pow(c, d, n)print(long_to_bytes(m))# E@sy_RSA_enc7ypt | 
再往后看,
 
动调了一下看到iv和key都是一样的
所以直接写个exp把加密的文件解密
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      | fromCrypto.Cipher importAESpassword =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了
 
更多【DASCTF 2023六月挑战赛 二进制专项 RE writeup】相关视频教程:www.yxfzedu.com