【文章标题】: 无壳无花无VMP玩具分析
【文章作者】: 蛭魔毅
【作者QQ号】: 5353737
【下载地址】: http://www.x64asm.com/AsmBbs/viewthread.php?tid=1491&extra=page%3D1
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
无壳无花无VMP玩具分析
原连接: http://www.x64asm.com/AsmBbs/viewthread.php?tid=1491&extra=page%3D1
一, 试试水有多深
先看作者说明
作者说了是无壳、无花、无VMP,不过我们还是用PEID、LordPE查看一下吧。
PEID:
LordPE 看一下PE头信息、目录信息
ImageBase: 7FFF0000;(这么奇怪的基址,能如愿加载到此地址呢?疑问1)
目录信息真是清爽,基本什么都没有;什么都没有,那这玩具怎么使用API呢?难道什么API都不用?真是这样就饶了吾等菜鸟吧。
运行一下试试(不放心的就丢虚拟机里),有弹出一个窗口:
既然有窗口弹出,肯定调用了API,可能玩具会自己定位API并调用,那怎么定位调用API呢?(疑问2)
关闭窗口时会出错,强行关闭程序时windows的桌面会有重启现象。(疑问3)
二, 上OD,动态调试
先看OEP的地址000133A8,没有加载到原定的IamgeBase,再看最后的retn 33C,看来不是正常的CALL调用,一条条指令看吧,看的时候注意1)ESP(最后retn了那么多);2)注意
是否有重定位。
注释里的c_esp表示当前指令执行后的ESP相对值
000133A8 > $ 55 push ebp
000133A9 . 8BEC mov ebp, esp
000133AB . 81C4 B4FEFFFF add esp, -14C
000133B1 . 53 push ebx
000133B2 . 52 push edx
000133B3 . 56 push esi
000133B4 . 57 push edi
000133B5 . E8 10000000 call 000133CA //call后一指令地址,像是为了重定位
000133BA . 64100100 dd 旓旓.00011064
000133BE . 64100100 dd 旓旓.00011064
000133C2 . 64100100 dd 旓旓.00011064
000133C6 . 64100100 dd 旓旓.00011064
000133CA /$ 8B0424 mov eax, dword ptr [esp] //取000133BA
000133CD |. 83C4 04 add esp, 4
000133D0 |. 2D BA330100 sub eax, 000133BA //计算偏移差,很可能是重定位了
000133D5 |. 93 xchg eax, ebx //ebx 保存偏移差;这里的偏移差是0,如果加载到其它地址,偏移值会不同
000133D6 |. B8 90340100 mov eax, 00013490
000133DB |. 03C3 add eax, ebx // 1 ) 00013490 + 偏移差; 2 ) 假设这里的c_esp为0
000133DD |. 81EC 3C030000 sub esp, 33C // esp : - 33c ; c_esp = -33c
000133E3 |. 8BCC mov ecx, esp // ecx : - 33c
000133E5 |. 83EC 0C sub esp, 0C // c_esp: -33c - 0c = -348
000133E8 |. 896C24 04 mov dword ptr [esp+4], ebp //保存ebp,WHY? [esp+4]: -344;
000133EC |. 81C1 BC020000 add ecx, 2BC // ecx : - 33C + 2BC = -80
000133F2 |. 91 xchg eax, ecx // 1, eax : -80 ; 2, ecx: 00013490 + 偏移差;
000133F3 |. 81E9 BA000000 sub ecx, 0BA // 00013490 - 0BA + 偏移差;
000133F9 |. 81C1 34000000 add ecx, 34 //00013490 - 0BA + 34 + 偏移差 = 1340A+ 偏移差
000133FF |. 8988 40FDFFFF mov dword ptr [eax-2C0], ecx // 1) eax-2C0 = -80 -2c0 = -340; 2) ecx = 1340A + 偏移差
00013405 |. 58 pop eax // c_esp: -348 + 4 = -344
00013406 |. 5D pop ebp // 1)恢复[-344]给ebp; 2)c_esp: -344 + 4 = -340
00013407 \. C2 3C03 retn 33C //1) retn到的地址 : [esp: -340]: 133A2+ 偏移差;再看看133A2的地址,就是当前retn指令的后一指令
地址,坑爷啊…;2) c_esp = + 4 + 33c = +340 ; 正好和000133DD 前一指令的c_esp平衡,既然retnXXX后堆栈平衡,那么这些堆栈平衡指令都是干扰指令,继续坑爷啊…
总算看完第一个CALL了,有点晕。
总结下,第一个CALL主要作用:
1, 取了偏移差,保存到ebx中;
2, 通过ebx 计算要跳转到的地址,然后用retn的方式跳转到某一地址,而且这个地址就是retn指令的后一指令地址
3, 还有一个问题,期间为什么要保存恢复ebp呢?
接下去看
0001340A . 85ED test ebp, ebp //检测ebp是否为0,ebp怎么会为0呢?
0001340C . 75 05 jnz short 00013413 //正常情况下跳走
0001340E . E8 37010000 call 0001354A //跟进去看看,发现是个会引起异常的CALL,之前也没有安装SEH,说明正常情况下不会来到这里。难道这
就是作者说明里所说的随机失败?
00013413 > 55 push ebp //正常情况下来到这里
00013414 . 8D83 89390100 lea eax, dword ptr [ebx+13989] //用到了ebx,来重定位[13989]
0001341A . 50 push eax
0001341B . 8D83 EC240100 lea eax, dword ptr [ebx+124EC] //用到了ebx,来重定位[124EC]
00013421 . 50 push eax
00013422 . 64:FF35 00000>push dword ptr fs:[0]
00013429 . 64:8925 00000>mov dword ptr fs:[0], esp //安装SEH
00013430 . B8 B2340100 mov eax, 000134B2 //之后这一段代码看着怎么这么眼熟,对了,就是用retn的方式跳转到某一地址
00013435 . 03C3 add eax, ebx
00013437 . 83EC 0C sub esp, 0C
0001343A . 8BCC mov ecx, esp
0001343C . 83EC 0C sub esp, 0C
0001343F . 896C24 04 mov dword ptr [esp+4], ebp
00013443 . 81C1 6C030000 add ecx, 36C
00013449 . 91 xchg eax, ecx
0001344A . 81E9 82000000 sub ecx, 82
00013450 . 81C1 31000000 add ecx, 31
00013456 . 8988 90FCFFFF mov dword ptr [eax-370], ecx
0001345C . 58 pop eax
0001345D . 5D pop ebp
0001345E . C2 0C00 retn 0C ;
0001354A /$ 8D85 B4FEFFFF lea eax, dword ptr [ebp-14C] //进入此CALL的条件是ebp为0
00013550 |. 50 push eax
00013551 |. FF75 FC push dword ptr [ebp-4]
00013554 |. FF55 C4 call dword ptr [ebp-3C] //既然ebp为0,那么这里肯定出错,说明这个CALL就是为了让程序引起异常,终止运行
……………………
看完2个CALL,对此玩具有初步的认识了
1, 作者虽说此玩具无花、无VM,但是有代码变形(貌似也叫扭曲);
2, 此玩具自身有实现重定位;
三, 扭曲了,咱就捋捋直
玩具的扭曲比较规律
1, 对于用retn方式进行跳转的扭曲;
因为跳转到的地址就是retn指令的后一指令地址,并且用来扭曲的指令组是相同的,所以长度定长,都是0x31个字节。
因此,我们只要找到扭曲指令组的开始地址,然后用0x31个NOP替代原指令就捋直了
定位指令组的开始地址:
用OD的搜索指令序列
mov eax,const
add eax, ebx
sub esp, const
mov ecx, esp
sub esp, const
mov dword ptr [esp+const], ebp
add ecx, const
xchg eax, ecx
添0x31个NOP大家都会的
还原了跳转变形,IDA就可以正常识别函数了。
2, 变量重定位指令还原,
1)
8D83 89390100 lea eax, dword ptr [ebx+13989]
还原成
8D05 89390100 lea eax, dword ptr [13989]
lea R32,dword ptr [ebx+ CONST]
还原成
lea R32,dword ptr [CONST]
2)
8B83 70110100 mov eax, dword ptr [ebx+11170] ; |
还原成
A1 70110100 mov eax, dword ptr [11170]
mov R32, dword ptr [ebx+CONST] ; |
还原成
mov R32, dword ptr [CONST]
mov dword ptr [ebx+CONST], R32 ; |
还原成
mov dword ptr [CONST] , R32
3)
0001220B |. 83BB 78110100 03 cmp dword ptr [ebx+11178], 3
还原成
0001220B 833D 78110100 03 cmp dword ptr [11178], 3
cmp dword ptr [ebx+const], const
还原成
cmp dword ptr [const], const
怎么用脚本来还原,大家也练练手,一起提高
四, 开始分析,IDA出手
还原了retn方式的跳转扭曲,IDA载入,发现还是有问题,很多函数不是正确的识别。
原因1,IDA会遇到CALL指令后,会将CALL XXXX中的调用地址XXXX记录为某个函数的地址,而玩具中有很多无效的CALL指令
例如之前分析过的
0001340A . 85ED test ebp, ebp //
0001340C . 75 05 jnz short 00013413 //
0001340E . E8 37010000 call 0001354A //
IDA会记录0001354A为某个函数的起始地址,下次分析到0001354A时,就将其作为另一个函数的起始位置,而玩具中有很多这样的无效CALL指令,这样就造成很多函数无法正确分析。
解决办法: 把无效CALL指令都NOP掉,搜索85ED7505E8 机器码,然后把E8 XXXXXXXX用5个NOP代替。
原因2,玩具的重定位代码中有4个dword干扰码,IDA遇到这些干扰码就无法正常识别了
000133B5 . E8 10000000 call 000133CA //重定位开始
000133BA . 64100100 dd 旓旓.00011064 //干扰码
000133BE . 64100100 dd 旓旓.00011064 //干扰码
000133C2 . 64100100 dd 旓旓.00011064 //干扰码
000133C6 . 64100100 dd 旓旓.00011064 //干扰码
000133CA /$ 8B0424 mov eax, dword ptr [esp]
000133CD |. 83C4 04 add esp, 4
000133D0 |. 2D BA330100 sub eax, 000133BA
000133D5 |. 93 xchg eax, ebx //重定位完毕
解决办法:为了让IDA正确识别所有函数,直接把重定位代码都NOP掉。
搜索E810000000,然后用0x21字节NOP掉重定位代码。
解决干扰码后, OD再次载入时,你会发现这些干扰码会死灰复燃…这是因为玩具将这些干扰码放入了重定位节内,每次玩具被系统的PE载入器装载时,会自动重定位干扰码,造成干扰码死灰复燃。既然如此,那我们就把重定位节也清了
解决了这些问题后,IDA就可以正常识别所有函数了,可以开始好好分析了
五, 玩具的具体功能
1,start函数
1) 载入模块,得到所有API地址,并用0 - 0x100的CRC值与每个API地址进行运算,生成每个API对应的KEY。之后玩具调用API时都是通过KEY来定位API的。
保存所有API和KEY到 *(_DWORD *)(11170)所指向的空间;
每个DLL用0x10000空间来存放
2) 检测一组API , 并保存对应的系统调用号
0012FE34 7C92D23E >覓| ntdll.ZwDeleteFile 0x18
0012FE38 7C92D92E .賿| ntdll.ZwQuerySystemInformation
0012FE3C 7C92DCAE 抾 ntdll.ZwSetInformationThread
0012FE40 7C92D7FE 抾 ntdll.ZwQueryInformationProcess
0012FE44 7C92DE7E ~迴| ntdll.ZwTerminateThread
0012FE48 7C92DE6E n迴| ntdll.ZwTerminateProcess
3)anti : ZwQuerySystemInformation_7_DbgHandle , ZwSetInformationThread_11_HideThreadFormDbg
4) 查找explorer进程, 获得PID,并提权
5) 将自身整个PE写入explorer进程;再创建explorer进程的远程线程;线程基址为 0x11000 + 0x20F0 = 130F0
6) 卸载模块,貌似卸载不了 -____-!
2,remote_thread_sub_130EE 远程线程函数
1)、2)同start函数
3)自删除,删除自身PE,这个delete_self_exe_sub_120FF()函数内有一个堆栈不平衡的BUG,可能是作者故意留着的,随机失败?
4) RegisterClass_CreateWindow_sub_12F7C() 注册创建窗口
5) 卸载模块
3, window_produce_sub_1261C 窗口过程,这个函数大家自己分析分析吧
另外提一下,因为这个玩具自身有重定位,所以我们可以直接修改OEP为remote_thread_sub_130EE,就可以直接运行远程线程函数,但是运行到delete_self_exe_sub_120FF会出错,我们只需改下[11178]的值为3就不会出错了
000135B7 180 C7 83 78 11 05 00 03 00+mov dword ptr [ebx+51178h], 3 ; 标志位;
六, 回顾
分析到这里,对此玩具应该有比较深刻的了解了,回头看看刚开始遇到的3个疑问,是否都解决了?
附件有idb分析文件,可以对照着分析。
最后感谢作者提供玩具,祝大家技术越来越好!
版主申请邀请码~~
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2012年04月30日 下午 11:05:51
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!