DRKCTF 2024
flower_tea
-
call与retn的花指令

call loc_140001224 //call到loc_140001224位置,并将栈顶设置为返回地址0x14000121F
pop rax //将栈顶元素(返回地址)弹到rax中
add rax, 0Ch //将rax加上0x0C
push rax //将rax入栈,即将当前rax的值作为返回地址
retn //读取栈顶地址(0x14000121F+0x0C)返回 -
除了IsDebuggerPresent API外,另一个反调试检测:
在x64下,调试标志位(BeingDebugged flag)在PEB表(Process Environment Block)偏移0x2的位置;通过获取gs寄存器找到peb表(gs:[0x60])的位置;readgsqword(0x62)得到调试标志位,读取该值如果该值为1,表示当前进程正在被调试;如果为0,则表示当前进程没有被调试。

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

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

所以GetProcAddress()通过在kernel32.dll用到的virtualprotect获得了virtualprotect函数的地址
所以函数返回virtualprotect函数的地址 -
对virtualprotect函数的认识

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),允许执行. 读取和写入访问。 -
依旧是call,retn花指令

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此处栈上内容(图取自上述链接)

dword ptr [ebp+4]就指向retn addr,注意点就是这个+4
从大佬的文章还知道一点,需要注意有没有破坏寄存器,需要更改寄存器的值(当然如果这两个寄存器后面都没用到的话当我没说x 不过保险起见,建议还是补上最好)。 -
call,pop,jmp花指令

call后将0x140001648存入栈顶,pop rax将栈顶传给rax,又jmp rax,相当于直接retn了 -
stack frame is too big
成因:分析栈帧时有异常出现
解决方案:找到明显不合常理的stack 双击进入栈帧界面,按U键盘删除对应的stack
有可能加壳
可能由花指令导致,手动或自动检查并去掉花指令

此题是因为去花导致分析异常,用解决方法1,双击进入stack界面

按U将异常出undefine -
对于int64转为int32
delta[0] = 0xE1C49E7259578627ui64; delta[1] = 0x8C3DA26BBC24167Fui64; unsigned int delta[4]={0x59578627,0xE1C49E72,0xBC24167F,0x8C3DA26B} -
魔改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
- die没查出壳,但是直接拖到ida中发现,函数非常少,很可能是进行了某些操作,对其加密

既然die不行,那就多试试别的,就此,我顺便把其他几个更新一下
查是查出来了,怎么脱呢,研究研究特征码之类的 https://www.52pojie.cn/thread-326995-1-1.html
根据文章得到
(1)修改区段名(UPX0...)和修改标识(3.-.-UPX!)后,die,PEid,Exeinfope都能查到
(2)去掉特征码后,只有Exeinfope可以识别出来
(3)加垃圾区段与移动PE头后,都识别不出了
(4)添加花指令还没看 - 借助x64dbg及Scylla的UPX手工脱壳
参考文章:https://bbs.kanxue.com/thread-268159.htm
就此题的学习文章: https://www.cnblogs.com/Un1corn/p/18221363

浙公网安备 33010602011771号