DRKCTF 2024

flower_tea

  1. call与retn的花指令
    img|720x208

    call loc_140001224 //call到loc_140001224位置,并将栈顶设置为返回地址0x14000121F
    pop rax //将栈顶元素(返回地址)弹到rax中
    add rax, 0Ch //将rax加上0x0C
    push rax //将rax入栈,即将当前rax的值作为返回地址
    retn //读取栈顶地址(0x14000121F+0x0C)返回

  2. 除了IsDebuggerPresent API外,另一个反调试检测:
    在x64下,调试标志位(BeingDebugged flag)在PEB表(Process Environment Block)偏移0x2的位置;通过获取gs寄存器找到peb表(gs:[0x60])的位置;readgsqword(0x62)得到调试标志位,读取该值如果该值为1,表示当前进程正在被调试;如果为0,则表示当前进程没有被调试。
    img

  3. 之前提过,又hook的地方肯定存在virtualprotect,通过异或把virtualprotect函数名字改了也是逆天
    img

    FARPROC GetProcAddress(
    	HMODULE hModule,
    	LPCSTR  lpProcName
    );
    

    参数
    hModule:这是一个模块句柄,指向包含函数或变量的 DLL 模块。这个句柄通常由 LoadLibrary 或 GetModuleHandle 函数返回。
    lpProcName:这是一个指向以空结尾的字符串,包含要检索的函数或变量的名称,或者包含函数的序号(由 MAKEINTRESOURCE 宏生成)。
    返回值
    成功时,返回值是名称为lpProName的函数或变量的地址。
    失败时,返回值为 NULL。可以通过调用 GetLastError 函数获取更多的错误信息。
    而对于ModuleName,可能是反编译的问题,不细究了,下图是题解的显示
    img
    所以GetProcAddress()通过在kernel32.dll用到的virtualprotect获得了virtualprotect函数的地址
    所以函数返回virtualprotect函数的地址

  4. 对virtualprotect函数的认识
    img

    BOOL VirtualProtect(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flNewProtect,
    PDWORD lpflOldProtect
    );
    参数
    lpAddress:指向要更改保护属性的内存区域的起始地址。必须是系统分配的页面中的地址。
    dwSize:要更改保护属性的内存区域的大小(以字节为单位)。
    flNewProtect:新的保护属性。可以是以下值之一(或它们的组合):
    PAGE_NOACCESS (0x01):禁止所有访问。
    PAGE_READONLY (0x02):允许读取访问。
    PAGE_READWRITE (0x04):允许读写访问。
    PAGE_WRITECOPY (0x08):允许写入时复制访问。
    PAGE_EXECUTE (0x10):允许执行访问。
    PAGE_EXECUTE_READ (0x20):允许执行和读取访问。
    PAGE_EXECUTE_READWRITE (0x40):允许执行. 读取和写入访问。
    PAGE_EXECUTE_WRITECOPY (0x80):允许执行和写入时复制访问。
    PAGE_TARGETS_INVALID:指定目标区域内的所有代码地址都无效。
    PAGE_TARGETS_NO_UPDATE:禁止更新目标区域内的代码地址。
    lpflOldProtect:指向一个变量,该变量接收之前的保护属性。
    返回值
    如果函数成功,返回值为非零值。
    如果函数失败,返回值为零。可以调用 GetLastError 函数获取扩展错误信息。
    题中第三个参数便是64(0x40),允许执行. 读取和写入访问。

  5. 依旧是call,retn花指令
    img
    rsp(Register Stack Pointer)寄存器包含了当前栈的顶部地址(64位)
    而esp(Extended Stack Pointer)寄存器包含了当前栈的顶部地址(32位)
    与第一点相同,call后会将返回地址0x1400014BA压栈,而

    sub     qword ptr [rsp+0], 5Ah      //则将栈顶地址减去了0x5A,即将返回地址变为了0x1400014BA-0x5A=0x140001460
    mov     rdx, 1      //rdx=1
    test    rdx, rdx        //rdx进行&操作,rdx还是1
    jz      loc_140001489       //因为rdx为1所以不会跳转
    retn
    

    所以我们要在0x140001460位置重新反汇编,call loc_140001489与loc_140001489里的内容直接nop掉即可
    此处为qword ptr [rsp+0],突然又翻到一个文章,让我对此处加深了理解 https://c10udlnk.top/p/reSkillsOn-ALLaboutJunkCode/

    ...:00815023 038 E8 00 00 00 00          call    $+5
    ...:00815028 03C 55                      push    ebp
    ...:00815029 040 8B EC                   mov     ebp, esp
    ...:0081502B                             db      36h
    ...:0081502B 040 36 83 45 04 0A          add     dword ptr [ebp+4], 0Ah
    ...:00815030 040 5D                      pop     ebp
    ...:00815031 03C C3                      retn  
    

    此处栈上内容(图取自上述链接)
    img|555x221
    dword ptr [ebp+4]就指向retn addr,注意点就是这个+4
    从大佬的文章还知道一点,需要注意有没有破坏寄存器,需要更改寄存器的值(当然如果这两个寄存器后面都没用到的话当我没说x 不过保险起见,建议还是补上最好)。

  6. call,pop,jmp花指令
    img|656x248
    call后将0x140001648存入栈顶,pop rax将栈顶传给rax,又jmp rax,相当于直接retn了

  7. stack frame is too big
    成因:分析栈帧时有异常出现
    解决方案:找到明显不合常理的stack 双击进入栈帧界面,按U键盘删除对应的stack
    有可能加壳
    可能由花指令导致,手动或自动检查并去掉花指令
    img
    此题是因为去花导致分析异常,用解决方法1,双击进入stack界面
    img
    按U将异常出undefine

  8. 对于int64转为int32

    delta[0] = 0xE1C49E7259578627ui64;
    delta[1] = 0x8C3DA26BBC24167Fui64;
    unsigned int delta[4]={0x59578627,0xE1C49E72,0xBC24167F,0x8C3DA26B}  
    
  9. 魔改tea,可以将delta的值魔改成数组,每次对应的delta不同

    //encode:
    for (int i = 0; i <15 ; i+=4) {
        for (int j = 0; j <= 32; ++j) {
            sum += delta[flag[i]%4];
            flag[i] += (sum >> 3) ^ (4 * ke[j%4]) ^ sum ^ flag[i+3];
    
            sum += delta[flag[i+1]%4];
            flag[i+1] += (16 * sum) ^ (ke[(j+1)%4] >> 1) ^ sum ^ flag[i];
    
            sum += delta[flag[i+2]%4];
    	    flag[i+2] += (sum >> 2) ^ (8 * ke[(j+2)%4]) ^ sum ^ flag[i+1];
    
    	    sum +=delta[flag[i+3]%4];
            flag[i+3] += (2 * sum) ^ (ke[(j+3)%4] >> 2) ^ sum ^ flag[i+2]
        }
    }
    
    	//偷的decode,我写的感觉和以这个一样啊,但是就是结果不对
        //decode:
        for (i = 0; i < len; i += 4) {
            uint32_t* c[4] = { &flag[(len - (i + 3)) % len],&flag[(len - (i + 2))
                                                                  % len],&flag[(len - (i + 1)) % len],&flag[(len - i) % len] };
            for (j = 32; j >= 0; j--) {
                *c[3] -= ((e ^ *c[2]) ^ (key[(j + 3) % 4] >> 2)) ^ (e << 1);
                e -= delta[*c[3] % 4];
                *c[2] -= ((e ^ *c[1]) ^ (key[(j + 2) % 4] << 3)) ^ (e >> 2);
                e -= delta[*c[2] % 4];
                *c[1] -= ((e ^ *c[0]) ^ (key[(j + 1) % 4] >> 1)) ^ (e << 4);
                e -= delta[*c[1] % 4];
                *c[0] -= ((e ^ *c[3]) ^ (key[j % 4] << 2)) ^ (e >> 3);
                e -= delta[*c[0] % 4];
            }
            
    

elec_go

Electron(原名为Atom Shell)是GitHub开发的一个开源框架。它通过使用Node.js(作为后端)和Chromium的渲染引擎(作为前端)完成跨平台的桌面GUI应用程序的开发。Electron现已被多个开源Web应用程序用于前端与后端的开发,著名项目包括GitHub的Atom和微软的Visual Studio Code。
一个基础的Electron包含三个文件:package.json(元数据). main.js(代码)和index.html(图形用户界面)。框架由Electron可执行文件(Windows中为electron.exe. macOS中为electron.app. Linux中为electron)提供。开发者可以自行添加标志. 自定义图标. 重命名或编辑Electron可执行文件。

Debug

  1. die没查出壳,但是直接拖到ida中发现,函数非常少,很可能是进行了某些操作,对其加密
    img
    既然die不行,那就多试试别的,就此,我顺便把其他几个更新一下
    查是查出来了,怎么脱呢,研究研究特征码之类的 https://www.52pojie.cn/thread-326995-1-1.html
    根据文章得到
    (1)修改区段名(UPX0...)和修改标识(3.-.-UPX!)后,die,PEid,Exeinfope都能查到
    (2)去掉特征码后,只有Exeinfope可以识别出来
    (3)加垃圾区段与移动PE头后,都识别不出了
    (4)添加花指令还没看
  2. 借助x64dbg及Scylla的UPX手工脱壳
    参考文章:https://bbs.kanxue.com/thread-268159.htm
    就此题的学习文章: https://www.cnblogs.com/Un1corn/p/18221363
posted @ 2024-06-02 17:51  Un1corn  阅读(69)  评论(0)    收藏  举报