【2019看雪CTF】Q2赛季 第七题 部落冲突 脱壳小记
这一题真是不会做。只能做做前面的准备工作,脱下壳什么的。
壳简要分析
一下载文件就被杀软杀了,可能有壳。
先拖进ida看了眼,果然有壳。目前入口处的部分伪代码如下(asm代码量有点大):
get_api_addr_by_base_and_name_4D0564(l_kernel_base, &v141);// get LoadLibraryA
p_GetProcAddress = (DWORD (__stdcall *)(int, char *))get_api_addr_by_base_and_name_4D0564(l_kernel_base, &v113);// GetProcAddress
p_GetProcAddress(l_kernel_base, &v154); // VirtualAlloc
p_CheckRemoteDebuggerPresent = (void (__stdcall *)(signed int, int *, int))p_GetProcAddress(l_kernel_base, &v50);// CheckRemoteDebuggerPresent
p_GetProcAddress(l_kernel_base, &v77); // GetCurrentProcess
p_IsDebuggerPresent = (int (*)(void))p_GetProcAddress(l_kernel_base, &v95);// IsDebuggerPresent
p_ExitProcess = (void (__cdecl *)(_DWORD))p_GetProcAddress(l_kernel_base, &v167);// ExitProcess
p_CreateFileA = (int (__cdecl *)(char *, unsigned int, _DWORD, _DWORD, signed int, signed int, _DWORD))p_GetProcAddress(l_kernel_base, &v179);// CreateFileA
p_CreateMutexA = (int (__cdecl *)(_DWORD, _DWORD, char *))p_GetProcAddress(l_kernel_base, &v128);// CreateMutexA
p_DeleteFileA = (void (__cdecl *)(char *))p_GetProcAddress(l_kernel_base, &v191);// DeleteFileA
createMutex_lyd_4CE0C4(p_CreateMutexA);
if ( caller_none_param(p_IsDebuggerPresent) )
bug_4CE0A4();
v9 = 0;
((void (__stdcall *)(signed int, int *))p_CheckRemoteDebuggerPresent)(-1, &v9);
if ( v9 )
p_ExitProcess(0);
v204 = get_base_4D0304(); // get program base
v1 = v204;
v6 = *(_DWORD *)(v204 + 60) + v204 + 24 + *(unsigned __int16 *)(*(_DWORD *)(v204 + 60) + v204 + 20);
createfile_4CE0F4(p_CreateFileA, p_DeleteFileA);
if ( p_IsDebuggerPresent() )
p_ExitProcess(0);
v49 = (_DWORD *)(v204 + *(_DWORD *)(v6 + 52));
v203 = v49;
v4 = (int *)(v1 + *(_DWORD *)(v6 + 92));
v48 = (char *)v49 + *v49;
for ( i = v49[2]; i < v203[1]; ++i )
v48[i] ^= 0x55u;
if ( CheckRemoteDebuggerPresent_4CE044(p_CheckRemoteDebuggerPresent) )
bug_4CE0A4();
write_data_and_api_4CE864((int)v48, v4, v203, (int)v49);
主要流程如下:
- 取API
- 创建名为
lyd
的互斥体
- 创建名为'-'的文件
- 解码代码数据
- 将代码数据恢复到程序相应地址(类似原PE的载入),并构建api间接调用机制(write_data_and_api_4CE864)
期间还有IsDebuggerPresent
(strong OD无影响)、CheckRemoteDebuggerPr
等反调试手段。
此处取的API是壳运行所用,创建互斥体及创建文件是与原程序代码遥相呼应起到反调试作用(更改数据)。
解码的数据为原PE4个Section的数据,偏移为0x400-0x9CA00
,解码操作为与0x55
异或。其实原PE被删了IMPORT TABLE,编码了Section后存放于地址0x4D0824,即文件偏移0x2C24。
进入write_data_and_api_4CE864
函数后,先分别copy了原PE的头及4个Section到原虚拟地址处,再开始构建api调用机制。
原PE的IAT在虚拟地址0x421000,IT在0x42E034(基本被删完了)。为了阻止脱壳运行,在IAT部分也进行了点动作,壳不再在跳到OEP之前恢复IAT,而是构造了一种API间接调用的机制。此机制将call api变成了call 堆地址,IAT中存放的是堆地址,各堆中预设了代码,进行某些其它操作后,跳到API执行。预设代码有6种,随机分配,这样脚本恢复IAT就比较麻烦了,得先确定各代码堆对应的预设代码种类。此处用到了由原IT转换而来的表,位于虚拟地址0x59B000,文件偏移0xCC800,结构与IT有点类似,库文件名和API hash异或编码了。
此机制的构建过程比较清晰,不多说。组装好的中转代码其中一种典型样式如下:
006C0000 60 pushad
006C0001 9C pushfd
006C0002 68 24A05900 push 0x59A024
006C0007 68 00004000 push 0x400000
006C000C 68 940A4D00 push 0x4D0A94 ; ASCII ".rsrc"
006C0011 68 00E04C00 push 0x4CE000 ; ASCII "$("
006C0016 E8 A9FCE0FF call Tribalco.004CFCC4
006C001B 83C4 10 add esp,0x10
006C001E 9D popfd
006C001F 61 popad
006C0020 68 70671C76 push kernel32.VirtualProtect
006C0025 50 push eax ; Tribalco.00421004
006C0026 53 push ebx
006C0027 5B pop ebx ; Tribalco.<ModuleEntryPoint>
006C0028 58 pop eax ; Tribalco.<ModuleEntryPoint>
006C0029 C3 retn
006C002A C3 retn
6种代码样式中,跳到api均使用了push ;ret
的方式。
构建完API中转调用后,会调用sub_4CFD74
,实际操作为与0异或,开始地址为0x401000,大小为0x1D600,操作完后与改写0x4CE010处的参数,这一次操作实际是为后面解码作准备。此处也有反调试,如检测到调试,会改变异或值(加上0x85)。
sub_4CFD74
还会在间接调用API时被调用,进行解码,具体情况如下:
addr size op v
0x416e00 0x1000 xor 2
0x41f600 0x800 xor 4
0x41fe00 0x400 xor 6
0x420200 0x200 xor 8
0x420400 0x100 xor 10
0x420500 0x100 xor 12
write_data_and_api_4CE864
函数返回后就直接jmp到了OEP。
脱壳
做题时看得没这么细致,看了下壳的大概结构,直接在jmp oep处patch成了死循环,运行程序再attach,然后dump。然后走一步看一步。
先发现IAT有猫腻,尝试多次终于用OD脚本获得IAT表;然后又发现dump下来的代码中还有SMC,IAT修复后(增加区段,改了IAT地址),SMC之后的代码call api就出问题了。
后来一想,其实可以直接从文件中把编码后的原PE分离开,然后解码,最后再修复IAT应该好点,至少文件小一半。
我当时的完整做法是:
- 跳转OEP前,patch程序成死循环
- 运行程序并attach,然后dump bin
- 用OD脚本改写IAT中的堆地址为API地址,得到IAT表
- 半手动修复dump_bin的IAT表(增加区段写IMPORT TABLE)
- 010 editor实现
sub_4CFD74
进行的6段解码
- 发现SMC会检查区段,直接去除SMC
- 去反调试(比壳中多了花样,除了壳中用的三种,前呼后应的两种,还有检查窗口标题和检查窗体风格)
最后附上脱壳之后的文件。不知是否还有问题。
阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开
发者可享99元/年,续费同价!