首页
社区
课程
招聘
[旧帖] [原创]无壳无花无VMP玩具分析 0.00雪花
发表于: 2012-4-29 23:12 2134

[旧帖] [原创]无壳无花无VMP玩具分析 0.00雪花

2012-4-29 23:12
2134
【文章标题】: 无壳无花无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

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 33
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
一早上起来就看到好文章,哈哈、

这才叫破文嘛、 其他人写的那个根本看不懂,又不说清、

呵呵、谢谢楼主、
2012-4-30 07:18
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
总算还有人支持。
2012-4-30 23:19
0
雪    币: 32
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
看不懂啊  看无字天书一样......................
2012-5-1 07:31
0
雪    币: 384
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我来支持了,版主给他转正吧,呵呵
2012-5-1 10:34
0
雪    币: 80
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
知其然,知其所以然,好文章。。

谢谢楼主分享,楼主辛苦了。。
2013-2-24 22:40
0
游客
登录 | 注册 方可回帖
返回
//