香山杯2023初赛 Reverse WriteUp

  • 有个人博客后好久没在CSDN上更新了,看着空空的,复制几篇个人博客上的文章过来hh

一. RE

1.URL从哪儿来

  • 看main函数

  • int __cdecl main(int argc, const char **argv, const char **envp)
    {
      HMODULE ModuleHandleW; // eax
      HMODULE v4; // eax
      HMODULE v5; // eax
      _BYTE *v7; // [esp+4h] [ebp-28Ch]
      HGLOBAL hResData; // [esp+8h] [ebp-288h]
      HRSRC hResInfo; // [esp+Ch] [ebp-284h]
      _BYTE *lpAddress; // [esp+10h] [ebp-280h]
      FILE *Stream; // [esp+1Ch] [ebp-274h]
      DWORD dwSize; // [esp+20h] [ebp-270h]
      DWORD i; // [esp+28h] [ebp-268h]
      struct _PROCESS_INFORMATION ProcessInformation; // [esp+30h] [ebp-260h] BYREF
      struct _STARTUPINFOA StartupInfo; // [esp+40h] [ebp-250h] BYREF
      CHAR Buffer[260]; // [esp+84h] [ebp-20Ch] BYREF
      CHAR TempFileName[260]; // [esp+188h] [ebp-108h] BYREF
    
      ModuleHandleW = GetModuleHandleW(0);
      hResInfo = FindResourceW(ModuleHandleW, (LPCWSTR)0x65, L"e_ou");
      v4 = GetModuleHandleW(0);
      hResData = LoadResource(v4, hResInfo);
      v7 = LockResource(hResData);
      v5 = GetModuleHandleW(0);
      dwSize = SizeofResource(v5, hResInfo);
      lpAddress = VirtualAlloc(0, dwSize, 0x1000u, 4u);
      if ( !lpAddress )
        return 1;
      for ( i = 0; i < dwSize; ++i )
      {
        if ( v7[i] && v7[i] != 120 )
          lpAddress[i] = v7[i] ^ 0x78;
        else
          lpAddress[i] = v7[i];
      }
      if ( !GetTempPathA(0x104u, Buffer) )
        return 1;
      if ( !GetTempFileNameA(Buffer, "ou.exe", 0, TempFileName) )
        return 1;
      Stream = fopen(TempFileName, "wb");
      if ( !Stream )
        return 1;
      if ( fwrite(lpAddress, 1u, dwSize, Stream) == dwSize )
      {
        fclose(Stream);
        VirtualFree(lpAddress, 0, 0x8000u);
        memset(&StartupInfo, 0, sizeof(StartupInfo));
        memset(&ProcessInformation, 0, sizeof(ProcessInformation));
        StartupInfo.cb = 68;
        GetStartupInfoA(&StartupInfo);
        StartupInfo.wShowWindow = 0;
        CreateProcessA(TempFileName, 0, 0, 0, 1, 0, 0, 0, &StartupInfo, &ProcessInformation);
        CloseHandle(ProcessInformation.hProcess);
        CloseHandle(ProcessInformation.hThread);
        DeleteFileA(TempFileName);
        return 0;
      }
      else
      {
        fclose(Stream);
        return 1;
      }
    }
    
  • 第一种方法(最简单的):直接动调,在CreateProcessA函数处下断点即可,导出ou.26B6.tmp文件,查壳,拖入IDA分析,逻辑很简单,-30后base64就行

  • 第二种方法:将原来的exe拖入resource hack里导出bin文件,再写脚本进行异或,输出bin文件,再IDA分析即可

  • 下面是bin文件输出方法:

  • arr = 0x78
    with open('C:\\Users\\lenovo\\Desktop\\E_OU101.bin', 'rb') as f:
        b = f.read()
    b = bytearray(b)
    for i in range(len(b)):
        if b[i]!=0 & b[i]!=120:
            b[i] = b[i] ^ arr
        else:
            b[i]=b[i]
    with open('C:\\Users\\lenovo\\Desktop\\COD_de1', 'wb') as f:
        f.write(b)
    
  • 脚本简单不在此赘述

2.hello_py

  • 该题运用chaquopy来调用python代码,根据题目提示我们找到python源码,在app.imy里找到hello.py,代码如下:

  • from java import jboolean ,jclass #line:1
    import struct #line:3
    import ctypes #line:4
    def MX (O0O00OOO00OO00O00 ,O0OO0O00OO0O000OO ,OO000OO000000O0O0 ,OOO00O00OOO000OOO ,OO0OOO0OOO0OOOO0O ,O0OO000O0000O000O ):#line:7
        OOO000O0O0OO00000 =(O0O00OOO00OO00O00 .value >>5 ^O0OO0O00OO0O000OO .value <<2 )+(O0OO0O00OO0O000OO .value >>3 ^O0O00OOO00OO00O00 .value <<4 )#line:8
        OOO0OOOOOO0O0OO00 =(OO000OO000000O0O0 .value ^O0OO0O00OO0O000OO .value )+(OOO00O00OOO000OOO [(OO0OOO0OOO0OOOO0O &3 )^O0OO000O0000O000O .value ]^O0O00OOO00OO00O00 .value )#line:9
        return ctypes .c_uint32 (OOO000O0O0OO00000 ^OOO0OOOOOO0O0OO00 )#line:11
    def encrypt (OO0OO0O0O0O0O0OO0 ,OOO0O0OO0O0OOO000 ,OO0OOOO0OO0OOO0O0 ):#line:14
        O0OOO0OO00O0000OO =0x9e3779b9 #line:15
        OOOO0OOOO00O0OOOO =6 +52 //OO0OO0O0O0O0O0OO0 #line:16
        O00OO00000O0OO00O =ctypes .c_uint32 (0 )#line:18
        OO0OOOO0O0O0O0OO0 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ])#line:19
        OOOOO00000OOOOOOO =ctypes .c_uint32 (0 )#line:20
        while OOOO0OOOO00O0OOOO >0 :#line:22
            O00OO00000O0OO00O .value +=O0OOO0OO00O0000OO #line:23
            OOOOO00000OOOOOOO .value =(O00OO00000O0OO00O .value >>2 )&3 #line:24
            for OO0O0OOO000O0000O in range (OO0OO0O0O0O0O0OO0 -1 ):#line:25
                OOO0OO00O0OO0O000 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O +1 ])#line:26
                OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]=ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]+MX (OO0OOOO0O0O0O0OO0 ,OOO0OO00O0OO0O000 ,O00OO00000O0OO00O ,OO0OOOO0OO0OOO0O0 ,OO0O0OOO000O0000O ,OOOOO00000OOOOOOO ).value ).value #line:27
                OO0OOOO0O0O0O0OO0 .value =OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]#line:28
            OOO0OO00O0OO0O000 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [0 ])#line:29
            OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]=ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]+MX (OO0OOOO0O0O0O0OO0 ,OOO0OO00O0OO0O000 ,O00OO00000O0OO00O ,OO0OOOO0OO0OOO0O0 ,OO0OO0O0O0O0O0OO0 -1 ,OOOOO00000OOOOOOO ).value ).value #line:30
            OO0OOOO0O0O0O0OO0 .value =OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]#line:31
            OOOO0OOOO00O0OOOO -=1 #line:32
        return OOO0O0OO0O0OOO000 #line:34
    
    def check (O0000000000O0O0O0 ):#line:63
        print ("checking~~~: "+O0000000000O0O0O0 )#line:64
        O0000000000O0O0O0 =str (O0000000000O0O0O0 )#line:65
        if len (O0000000000O0O0O0 )!=36 :#line:66
            return jboolean (False )#line:67
        O00OO00000OO0OOOO =[]#line:69
        for O0O0OOOOO0OOO0OOO in range (0 ,36 ,4 ):#line:70
            OO0OO0OOO000OO0O0 =O0000000000O0O0O0 [O0O0OOOOO0OOO0OOO :O0O0OOOOO0OOO0OOO +4 ].encode ('latin-1')#line:71
            O00OO00000OO0OOOO .append (OO0OO0OOO000OO0O0 )#line:72
        _O00OO0OOOOO00O00O =[]#line:73
        for O0O0OOOOO0OOO0OOO in O00OO00000OO0OOOO :#line:74
            _O00OO0OOOOO00O00O .append (struct .unpack (",O0O0OOOOO0OOO0OOO )[0 ])#line:75
        print (_O00OO0OOOOO00O00O )#line:77
        OO0OO0OOO000OO0O0 =encrypt (9 ,_O00OO0OOOOO00O00O ,[12345678 ,12398712 ,91283904 ,12378192 ])#line:78
        OOOOO0OOO0OO00000 =[689085350 ,626885696 ,1894439255 ,1204672445 ,1869189675 ,475967424 ,1932042439 ,1280104741 ,2808893494 ]#line:85
        for O0O0OOOOO0OOO0OOO in range (9 ):#line:86
            if OOOOO0OOO0OO00000 [O0O0OOOOO0OOO0OOO ]!=OO0OO0OOO000OO0O0 [O0O0OOOOO0OOO0OOO ]:#line:87
                return jboolean (False )#line:88
        return jboolean (True )#line:90
    def sayHello ():#line:92
        print ("hello from py")#line:93
    
    
  • 虽然混淆了,但是可以看出来是XXTea加密

  • 后面直接套脚本解密即可

  • import ctypes  
    def MX(z, y, total, tea_key, p, e):  
        temp1 = (z .value >> 5 ^ y .value << 2)+(y .value >> 3 ^ z .value << 4)   
        temp2 = (total .value ^ y .value)+(tea_key[(p & 3) ^ e .value] ^ z .value)  
        return ctypes.c_uint32(temp1 ^ temp2)  
    
    def decrypt(n, v, key):
        delta = 0x9e3779b9
        rounds = 6 + 52//n 
        
        total = ctypes.c_uint32(rounds * delta)
        y = ctypes.c_uint32(v[0])
        e = ctypes.c_uint32(0)
    
        while rounds > 0:
            e.value = (total.value >> 2) & 3
            for p in range(n-1, 0, -1):
                z = ctypes.c_uint32(v[p-1])
                v[p] = ctypes.c_uint32((v[p] - MX(z,y,total,key,p,e).value)).value
                y.value = v[p]
            z = ctypes.c_uint32(v[n-1])  
            v[0] = ctypes.c_uint32(v[0] - MX(z,y,total,key,0,e).value).value
            y.value = v[0]  
            total.value -= delta
            rounds -= 1
    
        return v 
    
    key_str = [689085350, 626885696, 1894439255, 1204672445, 1869189675, 475967424, 1932042439, 1280104741, 2808893494]
    res = encrypt_input = decrypt(9, key_str, [12345678, 12398712, 91283904, 12378192])
    for i in res:
        print(hex(i)[2:])
    
  • 手动恢复大端序,然后16进制转字符串即可

  • flag{c1f8ace6-4b46-4931-b25b-a1010a89c592}

3. nesting

  • 竟然是一道vm题,与最近的研究方向撞上了哈哈

  • 双重vm,大致有两种方法:手搓+脚本还原或是用intel pin进行爆破

  • 首先介绍第一种方法:intel pin爆破

  • 找到程序的输入点,再找到程序的cmp点,有两个case:0x1E21,0x1EC9,通过输入fl,fi等不同字符串发现,两处执行次数不同,用pin去统计执行次数,这样我们可以逐字节爆破。

  • 首先在 source/tools/ManualExamples 路径下 make obj-intel64/inscount0.so TARGET=intel64,编译生成计数器的so文件(即为一种pintool),可以记录程序执行的指令的条数

  • 注:想知道pintool功能可以:$ pin -t your_pintool -h – your_application

  • 同样的,编译32位pintool使用:make obj-ia32/inscount0.so

  • 可以用 make all TAEGET=intel64 make all TAEGET=ia32 来编译所有的pintool

  • 编译完以后生成输出文件 …/…/…/pin -t obj-intel64/inscount0.so -o inscount0.log – 文件路径

  • 这时可以再去写脚本进行爆破,另外的,在编译pintool之前可以把.c源码文件中的一些源码改掉以适应情况

  • 对于此题:

  • 首先我们只想统计指定行代码的执行次数,所以将inscount0.c中:

  • VOID docount() { icount++; }
    
  • 改为:

  • VOID docount(void *ip) 
    {
    	if ((long long int)ip == 0x0000555555555E21)
    	 icount++; 
    }
    
  • 并将

    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
    
  • 改为

  • INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);
    
  • 加入IARG_INST_PTR参数,是为了检测指令的地址,传入ip

  • 注意用

    sudo su
    echo 0 > /proc/sys/kernel/randomize_va_space
    
  • 将ALSR保护关掉

  • 改完后在将inscount0进行64位编译,生成输出文件,之后编写脚本如下:

  • from pwn import *
    import subprocess
    def run(msg):
        cmd = [
        "/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/pin",
        "-t", "/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/obj-intel64/inscount0.so",
        "-o", "/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/inscount0.log",
        "--",
        "/home/tanggerr/nesting"
    ]
        # 启动进程,并设置 stdin, stdout, stderr 为 PIPE,以便与进程交互
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
        # 发送数据到进程
        p.stdin.write(msg.encode())
        p.stdin.flush()
        # 从进程接收数据
        output = p.stdout.readline()
        # print(output)
        # 结束进程
        p.terminate()
        return int(read("/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/inscount0.log").split(" ")[1])
    
    
    def read(fname):
        with open(fname) as f:
            return f.read()
    # print(run("fl"))
    
    
    charset = string.printable
    
    l = []
    flag = ""
    counter = 0
    
    while(True):
        max_chr = 0
        first_iteration = True  # 初始化标志为 True
        for chr in charset:
            tmp = run(flag + chr)
            if first_iteration:  # 只在第一次迭代时执行
                max_value = tmp
                first_iteration = False  # 设置标志为 False
            if tmp > max_value:
                max_chr = chr
                max_value = tmp
                break
        print(max_chr)
        flag += str(max_chr)
        print(flag)
        #flag{2c7c093b-f648-11ed-a716-701ab8caaafe}
    
  • 等待其爆破出flag{2c7c093b-f648-11ed-a716-701ab8caaafe}

  • 接着看第二种方法:

  • 手搓两层vm,这里我学习了LaoGong战队的解法,请见‌⁢‬⁡⁣⁤⁣‬⁡‍‌‌⁡⁡⁣‬‍⁤⁢‬⁢⁢⁤‬⁣‍‍‌⁡‍‬2023 10.15 香山杯 wp - LaoGong - 飞书云文档 (feishu.cn)

  • 只能说手搓是真的nb(tql,orz

你可能感兴趣的:(安全)