吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 16715|回复: 21
收起左侧

[转贴] 谈谈VMP2.05的脱壳修复以及跨平台

  [复制链接]
stucky 发表于 2011-5-12 17:48
本帖最后由 wgz001 于 2011-5-12 21:35 编辑

VMP的外壳部分不能说难,只能说烦。特别是IAT的修复,由于函数调用的错位(并非是规则的call xxxx nop或者nop call xxxx系列)、寄存器使用的随机性、大量的乱序等,使得写脚本修复是个非常蛋疼的事情,而且往往会修复错,修复漏,而VM代码里的IAT更是无法得到修复。于是,目前公开的方法中,最简单的也是最治本的修复方式,就是先生成张调用关系的表,然后写个DLL去读,在程序运行前,修正一些值,来达到跨平台。其他的,还真想不到有更完美的方法。或许牛人间有更好的处理方法,我这种菜鸟实在想不到了。。

至于修复IAT,牛人的文章不少了,比如kissy牛的2个视频,以及aa1ss2牛的学习笔记,这里就不再多说了。

下面写下个人脱壳的记录,当算是个简单的笔记,跟步骤的总结,没什么技术含量,一切都是按牛人的脚步学习的。

试练品是个XP记事本,加上VMP外壳的全选项,顺便把OEP处的代码也给和谐掉。下面开始脱壳:

1.CTRL+G,VirtualProtect,在VirtualProtectEx的调用处下好断点:

7C801AE3    E8 75FFFFFF          call kernel32.VirtualProtectEx

接着,在数据窗口定位到代码段,比如,这个样本的为01001000.主要是为了观察是否已经解码,找返回的时机。

2.一直F9,一直到代码段解码,取消断点。ALT+F9,返回到程序代码区。

3.HOOK VM_RETN,找程序的入口。

先来看下VM_RETN这条handler长成啥样吧:
01071753 Main     pushad                                  //VM_Retn
01071754 Main     xchg ax,di                                ; EAX=0000F694, EDI=0006000E
01071756 Main     jmp NOTEPAD_.0106FB88
0106FB88 Main     sub ch,bl                                 ; ECX=01072F53
0106FB8A Main     mov esp,ebp
0106FB8C Main     movzx cx,al                               ; ECX=01070094
0106FB90 Main     xadd cl,ah                                ; EAX=00009494, ECX=0107008A
0106FB93 Main     call NOTEPAD_.0107152C
0107152C Main     mov ecx,dword ptr ss:[esp+4]              ; ECX=00000246
01071530 Main     xchg eax,esi                              ; EAX=01018E26, ESI=00009494
01071531 Main     mov eax,dword ptr ss:[esp+8]              ; EAX=0006FFB0
01071535 Main     jmp NOTEPAD_.0107147E
0107147E Main     bswap cx                                  ; ECX=00000000
01071481 Main     aaa                                       ; EAX=0006FF00
01071482 Main     mov eax,dword ptr ss:[esp+C]              ; EAX=F035F4BE
01071486 Main     pushfd
01071487 Main     adc esi,A8EF9681                          ; ESI=A8F02B15
0107148D Main     jmp NOTEPAD_.0107163C
0107163C Main     std
0107163D Main     mov esi,dword ptr ss:[esp+14]             ; ESI=0100CB51
01071641 Main     xor di,dx                                 ; EDI=00060248
01071644 Main     neg di                                    ; EDI=0006FDB8
01071647 Main     shld si,sp,cl
0107164B Main     btr bp,0E                                 ; EBP=0006BF84
01071650 Main     mov edi,dword ptr ss:[esp+18]             ; EDI=524DB0B9
01071654 Main     sub esi,734AB4D1                          ; ESI=8DB61680
0107165A Main     lea ebp,dword ptr ds:[edi+DB6A88FF]       ; EBP=2DB839B8
01071660 Main     mov esi,dword ptr ss:[esp+1C]             ; ESI=80A871EF
01071664 Main     inc bp                                    ; EBP=2DB839B9
01071667 Main     rcr bp,cl
0107166A Main     mov ebp,dword ptr ss:[esp+20]             ; EBP=E137F11A
0107166E Main     cmp bl,bh
01071670 Main     movzx bx,dl                               ; EBX=C71A0046
01071674 Main     mov cl,byte ptr ss:[esp+4]                ; ECX=00000098
01071678 Main     bt bx,0F
0107167D Main     mov ebx,dword ptr ss:[esp+24]             ; EBX=05B50161
01071681 Main     rol dh,cl
01071683 Main     mov ecx,dword ptr ss:[esp+28]             ; ECX=213EBC0B
01071687 Main     dec dx                                    ; EDX=00000245
0107168A Main     mov edx,dword ptr ss:[esp+2C]             ; EDX=DA74A7B8
0107168E Main     clc
0107168F Main     add esp,30
01071692 Main     je NOTEPAD_.0106FD6C
01071698 Main     popfd
01071699 Main     pushad
0107169A Main     mov dword ptr ss:[esp],AD22F63E
010716A1 Main     push dword ptr ss:[esp]
010716A4 Main     pushfd
010716A5 Main     push dword ptr ss:[esp+28]
010716A9 Main     retn 2C直接在010716A9下好断点,F9运行,断下后取消断点,F7,来到下面的地方:
0102A32B    9C                   pushfd
0102A32C    9C                   pushfd
0102A32D    8F0424               pop dword ptr ss:[esp]
0102A330    F5                   cmc
0102A331    81F5 EA0E31E1        xor ebp,E1310EEA
0102A337 ^ E9 29E3FEFF          jmp NOTEPAD_.01018665
0102A33C    AF                   scas dword ptr es:[edi]
4.继续F7几次,就可以来到下面的地方:
01007568    68 BA750001          push NOTEPAD_.010075BA
0100756D    64:A1 00000000       mov eax,dword ptr fs:[0]
01007573    50                   push eax
01007574    8B4424 10            mov eax,dword ptr ss:[esp+10]
01007578    896C24 10            mov dword ptr ss:[esp+10],ebp
0100757C    8D6C24 10            lea ebp,dword ptr ss:[esp+10]
01007580    2BE0                 sub esp,eax
01007582    53                   push ebx
01007583    56                   push esi
01007584    57                   push edi
01007585    8B45 F8              mov eax,dword ptr ss:[ebp-8]
01007588    8965 E8              mov dword ptr ss:[ebp-18],esp
0100758B    50                   push eax
0100758C    8B45 FC              mov eax,dword ptr ss:[ebp-4]
0100758F    C745 FC FFFFFFFF     mov dword ptr ss:[ebp-4],-1
01007596    8945 F8              mov dword ptr ss:[ebp-8],eax
01007599    8D45 F0              lea eax,dword ptr ss:[ebp-10]
0100759C    64:A3 00000000       mov dword ptr fs:[0],eax
010075A2    C3                   retn
这是多么熟悉的代码啊。因为VM并不支持CALL的嵌套,这就给我们可趁之机。先dump一份。

5.下面就来补代码:

此时看堆栈窗口:
0006FFB8   0101654F 返回到 NOTEPAD_.0101654F 来自 NOTEPAD_.0101BFA7 //CALL的出口,注意
0006FFBC   01001898 NOTEPAD_.01001898
0006FFC0   00000070
于是,在代码段,随便找个地址,补入如下代码:
01008890 > 6A 70                push 70
01008892    68 98180001          push dumped1.01001898
01008897    68 4F650101          push dumped1.0101654F
0100889C ^ E9 C7ECFFFF          jmp dumped1.01007568
010088A1    90                   nop
补完后,保存一下,顺便把OEP设置成8890.

OK,程序能够正常。不过此时还不能跨平台。

6.下面开始修复跨平台,这部分我就不说了,自己看牛人的文章跟视频,跑份IAT调用的对应关系表,部分如下:
1013360,DE02099D,comdlg32,PageSetupDlgW
102B3D6,E53997CF,comdlg32,ChooseFontW
1014ABD,4443359,comdlg32,PrintDlgExW
103107A,F8A5F191,comdlg32,ReplaceTextW
1019B4F,8F70D755,comdlg32,GetFileTitleW
101403B,AB122473,comdlg32,FindTextW
101CF16,7495EB47,comdlg32,CommDlgExtendedError
1016371,D8A0FB6B,comdlg32,GetOpenFileNameW
1014D62,BED81FBF,shell32,DragAcceptFiles
10230B9,A0524363,shell32,DragFinish
1022832,F10CB8C9,shell32,DragQueryFileW
101D8F2,4AD7BE0D,comdlg32,GetSaveFileNameW
10314C3,F6D99DC5,shell32,ShellAboutW
102A015,5B0DDC5B,winspool,GetPrinterDriverW
1023366,181A1537,winspool,OpenPrinterW
7.下面就简单的写个DLL,来完成工作,原理很简单,比如某个函数调用中,关键代码如下:
01031019    BA A2390001          mov edx,dump.010039A2
010328AE    8B92 FD430000        mov edx,dword ptr ds:[edx+43FD] //注意这里
010328BE    8D92 DB73ECD6        lea edx,dword ptr ds:[edx+D6EC73DB] //这句出真实的地址
ds:[01007D9F]=A594FBB8
edx=010039A2 (dump.010039A2)
找下对应的关系表:
1007D9F,D6EC73DB,kernel32,GetCommandLineW
可知,在地址01007D9F中存着解密的key,而解密的key为多少,即为真实的API地址-表中对用的key

其实也就是这句的逆过程:lea edx,dword ptr ds:[edx+D6EC73DB]

由于不同的平台,API的地址有差异,所有想要跨平台,就需要修正这个解密的key值,方法是在当前的用户平台上,先获取当前用户平台的API地址,再减去表中对应的那个KEY值即可。

知道了原理,就可以写代码了,代码写的很挫,仅供参考:
#include <windows.h>
#include <stdio.h>
extern "C" BOOL __declspec(dllexport) FkVMPIAT();
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call,LPVOID lpReserved)
{
if (ul_reason_for_call==DLL_PROCESS_ATTACH)
{
   FkVMPIAT();
}
return TRUE;
}
BOOL FkVMPIAT()
{
DWORD dwImageBase=(DWORD)GetModuleHandle(NULL);
PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)dwImageBase;
if (pDosHeader->e_magic!=IMAGE_DOS_SIGNATURE)
{
   return FALSE;
}
    PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)(dwImageBase+pDosHeader->e_lfanew);
if (pNtHeader->Signature!=IMAGE_NT_SIGNATURE)
{
   return FALSE;
}
UINT NumOfSection=pNtHeader->FileHeader.NumberOfSections;
    PIMAGE_SECTION_HEADER pSectionHeader=(PIMAGE_SECTION_HEADER)((UINT_PTR)pNtHeader+0x18+
   (UINT_PTR)(pNtHeader->FileHeader.SizeOfOptionalHeader));
for(int i=0;i<NumOfSection;i++)
{
   DWORD dwSecVA=pSectionHeader->VirtualAddress+dwImageBase;
   DWORD dwSecSize=pSectionHeader->Misc.VirtualSize;
   DWORD dwOldProtect;
   VirtualProtect((LPVOID)dwSecVA,dwSecSize,PAGE_EXECUTE_READWRITE,&dwOldProtect);
   pSectionHeader=(PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader+sizeof(IMAGE_SECTION_HEADER));
}
FILE *fp;
fp=fopen("FkIAT.txt","r");
    if (fp==NULL)
    {
   MessageBox(NULL,"请把fkiat.txt放当前目录下!","ximo",MB_OK|MB_ICONINFORMATION);
    }
char buf[256]={0};
while(!feof(fp))
{
       fgets(buf,256,fp);
    DWORD dwIATAddr,dwKey;
    char szIATAddr[10]={0};
    char szKey[10]={0};
    char szDllName[50]={0};
    char szFunName[100]={0};
    char *p;
    int i=0;
    if ((p=strchr(buf,','))==NULL)
    {
     continue;
    }
    while(buf!=',')
    {
     szIATAddr=buf;
     i++;
    }
    sscanf(szIATAddr,"%08x",&dwIATAddr);
    p=strchr(buf,',');
    i=0;
    while(p[i+1]!=',')
    {
     szKey=p[i+1];
     i++;
    }
    sscanf(szKey,"%08x",&dwKey);
   
    p=strchr(p+1,',');
    i=0;
    while(p[i+1]!=',')
    {
     szDllName=p[i+1];
     i++;
    }
    p=strchr(p+1,',');
    i=0;
    while(p[i+1]!=0x0A)
    {
     szFunName=p[i+1];
     i++;
    }
    //特殊处理下,不知道是否还有其他动态库也是如此的,想不出好的,只能硬编码了,汗
   if (strcmp(szDllName,"winspool")==0)
    {
     wsprintf(szDllName,"%s","winspool.drv");
    }
    //特殊处理2
    /*
    if ((p=strchr(szDllName,0x20))!=0)
    {
     wsprintf(szDllName,"%s",p+1);
    }*/
    if ((p=strstr(szDllName,"offset"))!=0)
    {
     wsprintf(szDllName,"%s",p+7);
    }
    HMODULE hdll=GetModuleHandle(szDllName);
    if (!hdll)
    {
     hdll=LoadLibrary(szDllName);
    }
   DWORD dwIAT=(DWORD)GetProcAddress(hdll,(LPCSTR)szFunName);
   DWORD dwWrite=dwIAT-dwKey;
   __asm
   {
    pushad
    mov eax,dwIATAddr
    mov ecx,dwWrite
    mov dword ptr [eax],ecx
    popad
   }
}
fclose(fp);
return TRUE;
}
8,最后就是用PE工具把这个DLL加到脱壳后文件的导入表中了。这个用LORDPE,CFF等工具都能搞定了。
然后把3个文件:脱壳后文件,DLL,以及对应关系的txt这3个文件放在一起,就OK了。放到其他平台测试一下,OK了。
脱壳也就到此结束了。

免费评分

参与人数 1热心值 +1 收起 理由
tianlinghu + 1 谢谢分享!吾爱有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

cu629944 发表于 2011-5-12 18:05
呃..这个我怎么觉的我看过哈~好像是超哥的~支持ximo大大~
小败 发表于 2011-5-12 18:11
闭幕 发表于 2011-5-12 18:12
Emil 发表于 2011-5-12 19:16
谢谢分享! 支持了
a408115319 发表于 2011-5-12 19:33
貌似超哥发过了呃···
xiaocainiaok 发表于 2011-5-12 19:37
给的很详细 学习~~
xiaocainiaok 发表于 2011-5-12 19:39
回复 stucky 的帖子

想知道 导入了dll后 他是怎么加载的  就算导入  dll也应该有导出函数吧  脱壳后的程序 怎么加载 导出函数修复iat的呢?
792404868@qq.co 发表于 2011-5-12 20:36
好东西。。支持+收藏了
李明1900 发表于 2011-5-13 21:30
很好很强大,给分吧,没了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-18 05:49

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表