关于《加密与解密》的读后感----对dump脱壳的一点思考

偶然翻了一下手机日历,原来今天是夏至啊,时间过的真快。ISCC的比赛已经持续了2个多月了,我也跟着比赛的那些题目学了2个月.......虽然过程很辛苦,但感觉还是很幸运的,能在大三的时候遇到ISCC,不管怎样,对我来说都会是一个很好的锻炼机会。

在做综合关的逆向破解的题目,遇到了很多蛋疼的问题,磕磕碰碰把《加密与解密》看完了。还是老习惯,把这1个多星期以后学到的,想到的做一个总结,希望能有一个思想上的提高,也希望能对其他人有一个借鉴的效果吧。这里想起lenus大神的一句话:

Hacker的精神里面除了学习(learn)的第一精神以外,还应该是共享(share)的精神。

好,废话不多说,开始吧。

1. 逆向?破解?

一开始接触逆向,我直接是使用的一些加壳脱壳工具,都是鼠标点一下就出来了,这段时间深入了解了逆向的原理之后,我感觉逆向是一个综合性很强的技术,中间还需要windows编程,PE结果,汇编等知识。

对于脱壳,我的理解是:脱壳不意味着它字面上的意思,把外壳直接拿掉,然后就扔掉(像吃水果一样),脱壳本质上是一个高级的crack技术,也就是绕过破解。

这是一张关于壳的图示:

而我们不管用lordPE, procDump, 还是手工脱壳也好,其实本质上就是要改变程序的执行流,让程序跳过头部(实际中不一定是头部,这里只是逻辑上的)的一段壳代码,而直接来到原程序的入口代码处,也就是常说的OEP,那问题就来了,那脱壳就这么简单?

当然不是,我个人感觉,脱壳并不仅仅是改变程序执行流这么简单,因为被加过壳的程序往往IAT,重定位表,节区都被加密,压缩处理过了。如果你直接强行跳转过来,程序也是不能执行的。所以,网上常说的找OEP的原理就在这里。为什么要找OEP呢?其实本质上来说,这并不是最重要的,不要被这些形式上的东西框住了思维,我们从原理上思考,一个程序要运行,无非需要几种东西:

1. 可以执行的机器码

2. IAT

3. 重定位表(DLL)

4. 资源代码(不太重要就是了,因为并不影响逆向的结果)

转换了思维之后,我就豁然开朗了,我们要做的就是找到代码中的某一行,恰好已经全部完成了代码的解压缩和解密,IAT的重写,重定位表的重写,做好这些事后,我们就可以dump下来了,至于是不是OEP其实不是很重要,所谓的找OEP是因为,一般情况下在OEP的时候恰好都满足了上面的条件,而且也比较好找到,所以,OEP是dump的首选,如果你理解了原理,这么做也没有错。

 

第二点就是:为什么要用ImportREC来重建IAT表呢?它的原理又是什么呢?

这里首先要从加壳的原理说起,不管你是UPX压缩壳,ASProtect,穿山甲加密壳,还是VMP虚拟机壳...基本上来说都会破坏原本的IAT,将原本的IAT移到一个新的节区里面并加密等处理,然后构建自己的IAT表,并修改PE头。为什么要这么做呢?

因为壳必须保证它能调用到自己需要的函数,但是原程序并不能保证这点,所以壳必须自己构造自己的IAT三剑客:

调用LoadLibrary将dll文件映射到调用进程的地址空间中
调用GetModualHandle获得dll模块句柄
调用GetProcAddress获取输入函数的地址

一般壳程序都会构造这三个API,然后重建自己的IAT表,这样壳程序就能保证调用到自己需要的所有winAPI了。

而我们脱壳的是内存中的映像,即使这时候原程序的IAT已经恢复过来了(解压缩,解密),但是位置已经变了,原程序无法直接调用了。而ImportREC的作用就是找到这里API对用RVA,然后在一段空的地方重建这些结构,逆向构造出一个IAT出来,然后修改PE头,完成IAT的修复。明白了原理,自己手工修复IAT也是一样的。

修复重定位表和资源表的原理也是一样的,就不在详述了。

 

下面,我们通过一个dump实例的编程过程来深刻的理解一个dump的思想。

接下来的程序是我们对看雪上的一篇帖子的学习笔记,是lenus大神的脱壳教程,我这里就当是做一个学习笔记。

 

一:dump小程序的目标

对于dump来说,他的英文翻译就是“转存”。也就是说把内存中或者其他的输入转存到另一个位置,当然对于我们现在说的dump就是把内存中运行的PE进程的数据,从内存中抓取出来,然后在用文件的形式保存下来。

根据上面的分析我们基本上得到了一个这样的思维。Dump程序要做的事分几个基本的步骤:

1.  在系统中找到目标进程
2.  在进程中确定进程的大小imagesize
3.  把进程中的数据保存到文件

 

Code:

resource.h

#define ID_FLESH                        2
#define IDD_DIALOG1                     101
#define ICO_MAIN                        103
#define IDC_PROCESS                     1028
#define IDC_CORRECT                     1033

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1034
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

 

#include <windows.h>
#include <tlhelp32.h>
#include "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL GetProcessListFunc(HWND hDlg,HWND hWindList); // 列出各个进程的函数,刷新时也用到
LPCTSTR SaveAsFunc(HWND hDlg);                     //通用对话框函数
BOOL DumpFunc(HWND hDlg,HWND hWindList);           // 主要的dump函数,调用其他的小功能函数实现其功能
BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem);    //生成dump文件函数,把dump的东西写到磁盘上
HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess);      //读取目标进程空间,放置到本空间申请的堆中
int GetSizeOfImage(HWND hDlg,DWORD IDProcess);       // 获取pe文件的SizeOfImage
BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess);        //检查pe文件的完整性
BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList);                   //纠正文件的大小
LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess);//获取目标exe的绝对路径
BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name);//修改文件的节表使其RA=RVA ,RS=RVS
BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name);   //把原来PE文件的头部复制到dump文件中

全局变量/
int sizeoffile=0;
int sizeofimage=0;              //当使用了CorrectSizeFunc后这个有了具体数值,就不需要再次获取了
int BaseAddress=0x400000;      
DWORD ID=0;                   //这个是用来控制进程的切换的
///

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                                            PSTR szCmdLine, int iCmdShow)
{
    DialogBoxParam (hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc,(LPARAM)hInstance);
    return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{
        static HINSTANCE hInstance;
        static HWND hWindList;

         switch (message)
         {
        case    WM_CLOSE:
                EndDialog (hDlg, 0) ;
                   return TRUE ;
         case     WM_INITDIALOG :
                hInstance = (HINSTANCE) lParam;
                SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM)LoadIcon(hInstance,MAKEINTRESOURCE(ICO_MAIN)));
                hWindList = GetDlgItem(hDlg,IDC_PROCESS);
                if(!GetProcessListFunc(hDlg,hWindList))
                {
                    MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
                    EndDialog(hDlg,0);            
                }
                return TRUE ;
                
         case     WM_COMMAND :
                  switch (LOWORD (wParam))
                  {
                      case     IDOK :
                                   DumpFunc(hDlg,hWindList);
                                   return TRUE ;
                    case    ID_FLESH:
                                if(!GetProcessListFunc(hDlg,hWindList))
                                {
                                    MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
                                    EndDialog(hDlg,0);            
                                }
                                return TRUE;

                    case    IDC_CORRECT:
                                if(!CorrectSizeFunc(hDlg,hWindList))
                                    MessageBox(hDlg,TEXT("The correct size function is faile..."),TEXT("Fail..."),MB_OK | MB_ICONWARNING);
                                return TRUE;


                }


                  break ;
     }
     return FALSE ;
}


BOOL GetProcessListFunc(HWND hDlg,HWND hWindList)
{
    //此函数是进程列表的作用,在此不作过多介绍
    HANDLE hProcessSnap = NULL;
    PROCESSENTRY32 pe32   = {0};
    SendMessage(hWindList,LB_RESETCONTENT,0,0);
    pe32.dwSize = sizeof(PROCESSENTRY32);


    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)  
    return FALSE;
    

    if (!Process32First(hProcessSnap, &pe32))
    {
       CloseHandle (hProcessSnap);
       return FALSE;
    }
    do
    {
        WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_ADDSTRING,0,(LPARAM)pe32.szExeFile);
        SendMessage(hWindList,LB_SETITEMDATA,tmp,(LPARAM)pe32.th32ProcessID);
    }
    while (Process32Next(hProcessSnap, &pe32));
    CloseHandle (hProcessSnap);
    
    return TRUE;

}

BOOL DumpFunc(HWND hDlg,HWND hWindList)
{
    HGLOBAL hMem;
    LPCTSTR Dump_Name=NULL;                    //感觉有点问题,这个只是指针没有开辟足够的空间。
                                              //从数组(规定好的)到指针(未规定好的)就可以,
                                              //从指针到数组就不可以
    WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
    if (tmp==LB_ERR)
    {
        MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
        return FALSE;
    }
    DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //获得此列单里面的进程ID
    ID=IDProcess;
    hMem=ReadProcess(hDlg,IDProcess);
    if(hMem)                                   //如果返回的hMen不正确说明没有正确的申请到空间
    {
        if(sizeoffile!=0)                     //没有大小的dump 是没有意义的
        {
            Dump_Name=SaveAsFunc(hDlg);       //要保存的文件名
            if(Dump_Name)                     //如果得到的文件名是空就不继续执行
            {
                CreateDumpFile(hDlg,Dump_Name,hMem); //把数据写入文件中
                GlobalFree(hMem);                    //资源是可贵的,释放空间
            }
        }
    }
        
        return TRUE;
}

LPCTSTR SaveAsFunc(HWND hDlg)
{
    //获取你要保存的文件名,默认为dumped.exe
    HANDLE hFile;
    static    char szFileName[MAX_PATH]="dumped";
    OPENFILENAME stOF={0};
    stOF.hwndOwner=hDlg;
    stOF.lStructSize=sizeof(stOF);
    stOF.lpstrFilter="*.*";
    stOF.lpstrDefExt="exe";
    stOF.nMaxFile=MAX_PATH;    
    stOF.lpstrFile=szFileName;
    if(!GetSaveFileName(&stOF))
            return FALSE;
    char szBuffer[100];
    char szMsg[]="%s 已存在。要替换它吗?";
    wsprintf(szBuffer,szMsg,szFileName);
    hFile=CreateFile(szFileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile != INVALID_HANDLE_VALUE)
    {
       if(IDNO==MessageBox(hDlg,szBuffer,TEXT("另存为"),MB_YESNO | MB_ICONWARNING))
       {
           CloseHandle(hFile);
           return FALSE;
       }
    }

    CloseHandle(hFile);
    return szFileName;
}

BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem)
{
    //创建一个新的dump文件
    HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE)
    {

        MessageBox(hDlg,TEXT("Maybe you have alreadly had a this name file:("),
                   TEXT("Can't create a file"),MB_OK | MB_ICONWARNING);
        GlobalFree(hMem);
        return FALSE;
    }
    int NumberOfBytesWritten;
    WriteFile(hFile,hMem,sizeoffile,(LPDWORD)&NumberOfBytesWritten,NULL);    //注意这个函数第三个参数是必要的!
    CloseHandle(hFile);
    if(!CopyThePEHead(hDlg,Dump_Name))
    {
        //复制PE头
        MessageBox(hDlg,TEXT("复制PE头失败了"),TEXT("失败了"),MB_OK | MB_ICONWARNING);
    }    
    if(!ModifySectionFunc(hDlg,Dump_Name))
    {
        //节表对齐
        MessageBox(hDlg,TEXT("修改节表失败了"),TEXT("失败了"),MB_OK | MB_ICONWARNING);
    }
    MessageBox(hDlg,TEXT("文件已经dump成功"),TEXT("Lenus'ExeDump"),MB_OK | MB_ICONINFORMATION);//胜利的号角!    
    return TRUE;
}




HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess)
{
    //此函数是读取目标进程的空间,并把他写入到自己内存空间里面的一个内存块中
    
    HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,IDProcess);//使用上面获得的进程id
    if(!hProcess)
    {
        MessageBox(hDlg,TEXT("I can't open the process:("),TEXT("oh my god.."),MB_OK);
        return FALSE;
    }
    if(sizeofimage==0 || ID!=IDProcess)                 
    //当更换当前的进程或者没有使用修正的功能的时候需要重新的获取
    //由于不知道在更换选项的时候会发出什么消息,所以只能这么干 so foolish!!
    {
            sizeofimage=GetSizeOfImage(hDlg,IDProcess);
    }
    
    if(!sizeofimage)
    {
        return FALSE;
    }
    //为了以防万一,让sizeofimage增加一个文件对齐度。

    if(!(sizeofimage%0x1000))                          //如果是文件对齐度的整数倍的时候就不处理
        sizeoffile=sizeofimage;
    else
        sizeoffile=(sizeofimage/0x1000+1)*0x1000;     //如果不是就增加一个文件对齐度
    
    //申请一个文件空间的内存块
    static HGLOBAL hMem=0;
    hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeoffile);
    if(!hMem)
    {
        MessageBox(hDlg,TEXT("I think i have enough space to get!:("),TEXT("Wrong!!!"),MB_OK | MB_ICONSTOP);
        return FALSE;
    }
    //将这个pe文件在内存中的大小全部读到申请的块中
    
    DWORD NumberOfBytesReadorWrite;
    if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,hMem,sizeofimage,&NumberOfBytesReadorWrite))
    {
        MessageBox(hDlg,TEXT("I can't read the process:("),TEXT("oh my god.."),MB_OK);
        return FALSE;
    }

    CloseHandle(hProcess);   //有开始就,有关闭
    Sleep(200);               //等待一会
    return hMem;

}

int GetSizeOfImage(HWND hDlg,DWORD IDProcess)
{
    //这个函数的作用是获取SizeOfImage的数值
    //当函数执行失败返回的是0
    //成功返回的是非0
    HANDLE hModuleSnap = NULL;
    MODULEENTRY32 stModE  = {0};
    stModE.dwSize = sizeof(MODULEENTRY32);
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);  //快照,对本进程中所有的模块进行snap

    if (hModuleSnap == INVALID_HANDLE_VALUE)
    {
        MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
        return FALSE;    //返回0
    }
    if (!Module32First(hModuleSnap, &stModE))
    {
       MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
       CloseHandle (hModuleSnap);
       return FALSE;
    }
    CloseHandle (hModuleSnap);
    return stModE.modBaseSize;//初始化为0
}

BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess)
{
    //检查pe文件,检查两个标志
    //如果不是pe文件那么会迫使GetSizeOfImage直接返回
    //下面不是重点,所以不介绍了
    int BaseAddress=0x400000;
    IMAGE_DOS_HEADER DosHead;
    _IMAGE_NT_HEADERS NtHead;

    if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,&DosHead.e_magic,2,NULL))
    {
        MessageBox(hDlg,TEXT("I can't read the IMAGE_DOS_SIGNATURE:("),TEXT("oh my god.."),MB_OK);
        return FALSE;
    }

    if(DosHead.e_magic != IMAGE_DOS_SIGNATURE)
    {
        return FALSE;
    }


    if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+0x3c),&DosHead.e_lfanew,4,NULL))
    {
        MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
        return FALSE;
    }

    if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+DosHead.e_lfanew),&NtHead.Signature,4,NULL))
    {
        MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
        return FALSE;
    }

    if(NtHead.Signature != IMAGE_NT_SIGNATURE)
    {
        return FALSE;
    }
    return TRUE;    

}


BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList)
{
    //函数能获取文件的PE头部的SizeOfImage,作为正确的SizeOfImage
    LPCTSTR File_Name=NULL;                   
    WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
    if (tmp==LB_ERR)
    {
        MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
        return FALSE;
    }
    DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //获得此列单里面的进程ID
    ID=IDProcess;//全局变量ID的作用是控制在不同的进程的切换
    File_Name=GetFilePath(hDlg,IDProcess);
    if(!File_Name)
        return FALSE;
    //打开文件
    HANDLE  hFile;
    hFile=CreateFile(File_Name,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

    if (hFile == INVALID_HANDLE_VALUE )
    {
        return FALSE ;
    }

    //创建文件映射内核对象
    HANDLE hMapping;
    hMapping =    CreateFileMapping (hFile, NULL, PAGE_READONLY,0,0,NULL);
    if (hMapping == NULL )
    {
        CloseHandle (hFile ) ;
        return FALSE;
    }
    //创建文件视图
    LPVOID ImageBase ;
    ImageBase =MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0) ;
    if (ImageBase == NULL)
    {
        CloseHandle (hMapping) ;
        return FALSE;
    }
    //下面的代码就是从文件的PE头找到SizeOfImage的
    PIMAGE_DOS_HEADER DosHead = NULL ;
    PIMAGE_NT_HEADERS32 pNtHeader = NULL ;
    PIMAGE_FILE_HEADER pFileHeader = NULL ;
    PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL ;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL ;
    DosHead=(PIMAGE_DOS_HEADER)ImageBase;
    pNtHeader = ( PIMAGE_NT_HEADERS32 ) ((DWORD)ImageBase + DosHead->e_lfanew ) ;
    pOptionalHeader = &pNtHeader->OptionalHeader;    
    sizeofimage=(int)pOptionalHeader->SizeOfImage;
    //找到了以后,输出结果
    char szBuffer[100];
    char szMsg[]="原来的image size是:%08X\n修整的image size是:%08X";
    wsprintf(szBuffer,szMsg,GetSizeOfImage(hDlg,IDProcess),sizeofimage);
    MessageBox(hDlg,szBuffer,TEXT("纠正结果"),MB_OK );
    CloseHandle (hMapping);
    CloseHandle (hFile) ;
    Sleep(200);
    return TRUE;
}

LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess)
{
    //此函数获得目标进程的绝对路径
    //如果获取失败返回NULL
    HANDLE hModuleSnap = NULL;
    MODULEENTRY32 a   = {0};
    a.dwSize = sizeof(MODULEENTRY32);
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);  //快照,对本进程中所有的模块进行snap

    if (hModuleSnap == INVALID_HANDLE_VALUE)
    {
        MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
        return FALSE;    //返回0
    }
    if (!Module32First(hModuleSnap, &a))
    {
       MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
       CloseHandle (hModuleSnap);
       return FALSE;
    }
    CloseHandle (hModuleSnap);
    return a.szExePath;

}

BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name)
{
    //此函数的将修改dump下来的exe,使其RA=RVA ,RS=RVS
    //首先是打开dump文件
    HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE)
    {

        MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
        return FALSE;
    }
    //下面移动到节表前面
    IMAGE_DOS_HEADER myDosHeader;
    DWORD NumberOfBytesReadorWrite;    
    ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
    SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
    IMAGE_FILE_HEADER myNtHeader;
    ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
    int nSectionCount;
    nSectionCount = myNtHeader.NumberOfSections;             // 保存Section个数
    // 过了IMAGE_NT_HEADERS结构就是IMAGE_SECTION_HEADER结构数组了,注意是结构数组,有几个Section该结构就有几个元素
    // 这里动态开辟NumberOfSections个内存来存储不同的Section信息
    IMAGE_SECTION_HEADER *pmySectionHeader = (IMAGE_SECTION_HEADER *)calloc(nSectionCount, sizeof(IMAGE_SECTION_HEADER));
    SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
    ReadFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER)*nSectionCount,
             &NumberOfBytesReadorWrite,NULL);
    //移动回到节表的开始,准备写入
    SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
    for (int i = 0; i < nSectionCount; i++, pmySectionHeader++)
    {
        //将RA=RVA ,RS=RVS
        pmySectionHeader->SizeOfRawData=pmySectionHeader->Misc.VirtualSize;
        pmySectionHeader->PointerToRawData=pmySectionHeader->VirtualAddress;
        //将修改好的数值写回
        WriteFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
    }
    // 恢复指针
    pmySectionHeader -=nSectionCount;

    if (pmySectionHeader != NULL)          // 释放内存
    {
      free(pmySectionHeader);
      pmySectionHeader = NULL;
    }

    // 最后不要忘记关闭文件
    CloseHandle(hFile);
    return TRUE;
}

BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name)
{
    //此函数的作用是将原来PE文件的PE头部完整的copy到dump文件中
    HANDLE hFile=CreateFile(GetFilePath(hDlg,ID),GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE)
    {

        MessageBox(hDlg,TEXT("I can open the object file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
        return FALSE;
    }
    //下面移动到节表前面
    IMAGE_DOS_HEADER myDosHeader;
    DWORD NumberOfBytesReadorWrite;    
    ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
    SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
    IMAGE_FILE_HEADER myNtHeader;
    ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
    IMAGE_SECTION_HEADER mySectionHeader;
    SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
    ReadFile(hFile,(LPVOID)&mySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
    SetFilePointer(hFile,NULL,NULL,FILE_BEGIN);
    HGLOBAL hMem=0;
    //读出节表的第一个文件位置,以确PE头的大小
    //申请同样大小的空间
    hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,mySectionHeader.PointerToRawData);
    if(!hMem)
    {
        MessageBox(hDlg,TEXT("I can't get the Memory space!"),TEXT("Error!!!"),MB_OK | MB_ICONSTOP);
        return FALSE;
    }
    //将文件中的PE头部读取到申请的空间中
    ReadFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
    CloseHandle(hFile);
    //上面是读///
    //下面是写///
    hFile=CreateFile(Dump_Name,GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE)
    {
        MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
        return FALSE;
    }
    //下面是将空间中的数据写到dump文件的头部
    WriteFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
    CloseHandle(hFile);
    GlobalFree(hMem);
    return TRUE;
}

 

这里:

1. 获取当前进程列表使用的是:CreateToolhelp32Snapshot,Process32First, Process32Next 这三个函数,即创建一个进程快照,然后遍历它,之前做ring3的进程注入的时候用的也是这个技术,原理不是很难理解,本质上就是一个链表的操作。

2. 在进程中确定进程的大小imagesize 使用的是从磁盘的文件PE头中读取SizeOfImage这个字段来获取。这里就有一个很有趣的知识点了,我们先从原始的方法开始讲。

2.1 用ReadProcessMemory函数从从当前内存的进程映像工具PE格式的偏移获取到SizeOfImage的值的,这种方法不太可靠,要是加壳程序修改了映像中的PE头信息,就要出错了。

2.2 LordPE使用的方法,采用了对Module32Next来获取dump的进程的基本信息的。

BOOL WINAPI Module32First( 
HANDLE hSnapshot, //这是先前的CreateToolhelp32Snapshot函数返回的快照
  LPMODULEENTRY32 lpme //这个是指向MODULEENTRY32结构的指针
);

下面是MUDULEENTRY32结构:
typedef struct tagMODULEENTRY32 { 
  DWORD dwSize; 
  DWORD th32ModuleID; 
  DWORD th32ProcessID; 
  DWORD GlblcntUsage; 
  DWORD ProccntUsage; 
  BYTE *modBaseAddr; 
  DWORD modBaseSize;    //这个是是我们要获取的关键 
  HMODULE hModule; 
  TCHAR szModule[MAX_PATH]; 
  TCHAR szExePath[MAX_PATH]; 
  DWORD dwFlags;
} MODULEENTRY32, *PMODULEENTRY32, *LPMODULEENTRY32;

这种方法会受到anti-dump技术的影响。

因为MUDULEENTRY32是从内核中PEB(进程环境块)获取的数据。

下面是PEB的结构。
struct   _PEB (sizeof=488) 
+000 byte     InheritedAddressSpace 
+001 byte     ReadImageFileExecOptions 
+002 byte     BeingDebugged 
+003 byte     SpareBool 
+004 void     *Mutant 
+008 void     *ImageBaseAddress 
+00c struct   _PEB_LDR_DATA *Ldr 
+010 struct   _RTL_USER_PROCESS_PARAMETERS *ProcessParameters 
+014 void     *SubSystemData 
+018 void     *ProcessHeap 
+01c void     *FastPebLock 
+020 void     *FastPebLockRoutine 
+024 void     *FastPebUnlockRoutine 
+028 uint32   EnvironmentUpdateCount 
+02c void     *KernelCallbackTable 
+030 uint32   SystemReserved[2] 
+038 struct   _PEB_FREE_BLOCK *FreeList 
+03c uint32   TlsExpansionCounter 
+040 void     *TlsBitmap 
+044 uint32   TlsBitmapBits[2] 
+04c void     *ReadOnlySharedMemoryBase 
+050 void     *ReadOnlySharedMemoryHeap 
+054 void     **ReadOnlyStaticServerData 
+058 void     *AnsiCodePageData 
+05c void     *OemCodePageData 
+060 void     *UnicodeCaseTableData
+064 uint32   NumberOfProcessors 
+068 uint32   NtGlobalFlag 
+070 union    _LARGE_INTEGER CriticalSectionTimeout 
+070    uint32   LowPart 
+074    int32    HighPart 
+070    struct   __unnamed3 u 
+070       uint32   LowPart 
+074       int32    HighPart 
+070    int64    QuadPart 
+078 uint32   HeapSegmentReserve 
+07c uint32   HeapSegmentCommit 
+080 uint32   HeapDeCommitTotalFreeThreshold 
+084 uint32   HeapDeCommitFreeBlockThreshold 
+088 uint32   NumberOfHeaps 
+08c uint32   MaximumNumberOfHeaps 
+090 void     **ProcessHeaps 
+094 void     *GdiSharedHandleTable 
+098 void     *ProcessStarterHelper 
+09c uint32   GdiDCAttributeList 
+0a0 void     *LoaderLock 
+0a4 uint32   OSMajorVersion 
+0a8 uint32   OSMinorVersion 
+0ac uint16   OSBuildNumber 
+0ae uint16   OSCSDVersion 
+0b0 uint32   OSPlatformId 
+0b4 uint32   ImageSubsystem 
+0b8 uint32   ImageSubsystemMajorVersion 
+0bc uint32   ImageSubsystemMinorVersion 
+0c0 uint32   ImageProcessAffinityMask 
+0c4 uint32   GdiHandleBuffer[34] 
+14c function *PostProcessInitRoutine 
+150 void     *TlsExpansionBitmap 
+154 uint32   TlsExpansionBitmapBits[32] 
+1d4 uint32   SessionId 
+1d8 void     *AppCompatInfo 
+1dc struct   _UNICODE_STRING CSDVersion 
+1dc    uint16   Length 
+1de    uint16   MaximumLength 
+1e0    uint16   *Buffer

我们从FS:[30]就可以获得这个PEB的首地址。然后在0C处的_PEB_LDR_DATA *Ldr是一个关键通过它,我们能访问到

typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

该结构的后三个成员是指向LDR_MODULE链表结构中相应三条双向链表头的指针,分别是按照加载顺序、在内存中的地址顺序和初始化顺序排列的模块信息结构的指针。于是通过它,我们能访问到_LDR_MODULE结构,而这里面包括了本进程的SizeOfImage。
_LDR_MODULE结构如下:
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;                   //进程的image size
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;

所以,我们得到关键的代码就是:
  //这里的几个代码是修改PEB的关键
  __asm
  {
    mov eax,fs:[30h]                //获得PEB地址
    mov eax,[eax+0ch]               // +00c struct   _PEB_LDR_DATA *Ldr 
    mov eax,[eax+0ch]               // _LDR_MODULE的首地址
    mov dword ptr [eax+20h],1000h   //eax+20是保存image size的地方

  }
上面的代码的作用就是把image size的大小改为了1000h,这样我们用MODULEENTRY32得到的大小是不准确的。

 

2.3 针对上面的问题,必须使用LordPE的corect image size 技术了,它的原理很简单,就是从磁盘PE文件头中读取真实的image size来修正获取到的数据,从而避免了anti-dump的影响,当然,anti-dump技术还有很多,不止这一种,我也要慢慢去摸索。

 

3. 对齐节表问题

学过PE结构的朋友都指导,FileAlignment(磁盘对齐值)和SectionAlignment(内存对齐值)是不一样的。

FileAlignment:0x200h

SectionAlignment:0x1000h

在PE加载器加载文件映像的时候,就会在对齐值之间的差值中填0,当然我们dump的时候连着这些0也一并dump下来了。这也就是为什么脱壳的程序普遍都比源程序大很多的原理(当然实际情况不止这些,IAT重建也会造成大小扩大)。

另外,还造成了一个问题,就是 RA!=RVA ,RS!=RVS的问题:

因为我们是从内从中直接dump出来的数据,所以这时正常的情况应该是RA=RVA ,RS=RVS,这里可以手动修改,或者编程实现即可,原理上之前学PE的时候写的PEInfo差不多,主要考察的PE结构的知识。

这是修改后的。

到这里,dump就完成了,还差IAT没修复。

可以采取手工的方法在文件中找一块空位,逆向的重构IAT,在修改PE头。

或者用ImportREC来自动修复。

 

修复完成后,脱壳成功,至此,一个简单的脱壳工具就出来了。

 

这个学习笔记就当这段时间学习逆向脱壳的总结了,留下了几个问题没解决:

1. DLL脱壳后的重定位问题

2. VMP虚拟机壳寻找OEP问题。

留待以后解决了.............

信安的路果然还很长,希望以后能继续多多学习,思考,总结,分享,向看雪上那些大神看齐。

下一阶段准备研究一些缓冲区溢出的内核的知识点,继续看《0DAY》。离ISCC结束还有10天,继续加油啦,暑假一定要到北京ISCC决赛去。

 

 

-------------------------------分割线-----------------------

不知道怎么结尾,就这样了吧,  跑步去。

转载于:https://www.cnblogs.com/LittleHann/p/3148349.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构基础笔记】【链表】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、链表基础2、创建一个链表3、插入结点4、删除结点5、销毁链表6、实例分析前言 本章总结&#xff1a;链表的定义、创建、销毁&#xff0c;结点的插入与删除 1、链表基础 链表的物理存储结构是用一组地址任意的存储单元存储…

视频图像质量评价

目录1、人眼视觉特性1、眼的适应性2、对比灵敏度3、空间分辨率和时间分辨率4、马赫效应5、可见度阈值2、图像质量测度3、图像评价方法4、图像评价方法的优劣1、人眼视觉特性 1、眼的适应性 暗适应性&#xff1a;从亮环境到暗环境&#xff0c;适应暗环境的特性 亮适应性&#…

recovery编译问题汇总

1、修改支持USB大容量存储 &#xff08;1&#xff09;、首先需要查看手机lun位置 手机链接电脑&#xff0c;打开cmd命令行&#xff0c;依次输入以下命令: adb shell find /sys -name "lun" 输出以下结果&#xff1a; 发现手机输出结果有两个&#xff0c;需要进一步查…

【数据结构基础笔记】【栈】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、栈的定义2、创建一个栈3、入栈和出栈操作4、栈的清空、销毁、计算栈的当前容量5、实例分析前言 本章总结&#xff1a;栈的定义、创建栈&#xff0c;销毁栈&#xff0c;入栈出栈操作等操作。 1、栈的定义 栈是一种重要的…

用HTML语言制作list标记,html5 datalist标签的用法是什么?这里有datalist标签的用法实例...

本篇文章主要为大家讲述了关于html5 datalist标签的用法及html5 datalist标签的用法实例。本文说了两个常用的选项框的实例供大家选择观看&#xff0c;下面就让我们一起来看这篇文章吧我们先来看看html5 datalist标签的用法&#xff1a;标签定义选项列表。请与input元素配合使用…

【数据结构基础笔记】【队列】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、队列定义2、创建一个队列3、入队列4、出队列5、销毁一个队列6、循环队列的概念7、循环队列的实现8、实例分析前言 本章总结&#xff1a;链队列定义&#xff0c;创建&#xff0c;出队入队操作&#xff0c;销毁操作&#x…

html图片自动循环轮播图,js实现图片无缝循环轮播

本文实例为大家分享了js实现图片无缝循环轮播的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下代码如下Document#container{overflow:hidden;width:400px;height:300px;margin:auto;}#front,#container{display:flex;flex-direction:row;}#container img{width:400px…

五、json模块

一、json模块的介绍 json模块是Python自带的模块&#xff0c;用于json和Python数据之间的相互转换 Json与Python数据类型的对应关系 JsonPythonobjectdictarrayliststringstrnumber(int)int,longnumber(real)floattrueTruefalseFalsenullNone [#中括号括起来的&#xff0c;对…

【数据结构基础笔记】【树】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、树的概念2、二叉树3、二叉树的遍历4、创建二叉树5、实例分析前言 本章总结&#xff1a;树的概念、二叉树的创建、遍历 1、树的概念 树结构是以分支关系定义得一种层次结构。 树的定义&#xff1a;树是由n(n>0)个结点…

可以自动撑起的html样式,好好玩:CSS3抖动样式CSS Shake让你的网页酷炫起来

之前在一些网站发现了一个好玩的样式&#xff0c;就是鼠标移到网站LOGO上&#xff0c;logo会自动抖动起来&#xff0c;显得非常炫酷。我也是十分感兴趣。自从本站新添加了一个视觉设计的分类之后&#xff0c;我也是想起来有个抖动CSS样式CSS Shake&#xff0c;所以今天给小伙伴…

爬虫项目(一)---采集最近一日世界各国的疫情数据信息

该内容出自黑马程序员教程 采集最近一日世界各国疫情数据 步骤&#xff1a; 发送请求&#xff0c;获取疫情首页从疫情首页中提取最近一日各国疫情字符串从最近一日各国疫情字符串中提取json格式字符串把json格式字符串转换为Python类型把Python类型的数据&#xff0c;以json…

【数据结构基础应用】【顺序表】

代码参考《妙趣横生的算法.C语言实现》、《剑指OFFER 名企面试官精讲典型编程题 第2版》等 文章目录前言1、合并两个顺序表前言 本章总结在看书过程中的一些关于顺序表的算法题并可能含有一些自己的一些疑问。题目数量不定&#xff0c;随阅历增加而增加&#xff1b; 1、合并两…

html上下滚动切换顶端tab,jQuery实现Tab菜单滚动切换的方法

本文实例讲述了jQuery实现Tab菜单滚动切换的方法。分享给大家供大家参考。具体如下&#xff1a;这是一款jQuery实现让你的Tab菜单滚动的代码,先运行一下看看效果咋样?是不是超不错,让你的网页变得灵动起来,不再静止,学习jquery的朋友也可作为范例来参考吧.运行效果截图如下&am…

[转载]十四步实现拥有强大AI的五子棋游戏

又是本人一份人工智能作业……首先道歉&#xff0c;从Word贴到Livewrter&#xff0c;好多格式没了&#xff0c;也没做代码高亮……大家凑活着看……想做个好的人机对弈的五子棋&#xff0c;可以说需要考虑的问题还是很多的&#xff0c;我们将制作拥有强大AI五子棋的过程分为十四…

爬虫项目(二)---采集从03月02号以来的世界各国疫情数据

该内容出自黑马程序员教程 采集从03月02号以来的世界各国疫情数据 步骤&#xff1a; Ⅰ&#xff0c;重构项目(一)的代码&#xff0c;以提高扩展性 把功能封装到一个类中每一个小功能变成一个方法通过run方法启动爬虫 import requests import re import json from bs4 impor…

【原创】StreamInsight查询系列(二十)——查询模式之检测间隙事件

上篇文章介绍了查询模式中如何检测异常事件&#xff0c;这篇博文将介绍StreamInsight中如何检测间隙事件。 测试数据准备 为了方便测试查询&#xff0c;我们首先准备一个静态的测试数据源&#xff1a;// 创建数据源&#xff0c;要注意的是4:16和4:30之间存在的事件间隙 var sou…

【数据结构基础应用】【查找和排序算法】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、顺序查找2、折半查找3、直接插入排序4、选择排序5、冒泡排序6、希尔排序7、快速排序8、堆排序9、排序算法性能比较10、所有算法的code&#xff08;C语言&#xff09;前言 本章总结查找和排序算法&#xff1a;顺序查找、折…

爬虫项目(三)---采集最近一日全国各省疫情数据

该内容出自黑马程序员教程 采集最近一日全国各省疫情数据 当然&#xff0c;数据来源仍然是丁香园新型冠状病毒肺炎疫情实时动态首页 url&#xff1a;https://ncov.dxy.cn/ncovh5/view/pneumonia 思路&#xff1a;首先需要先确定全国各省疫情数据的位置 全国各省份的疫情数据…

计算机专业博士后排名,排名丨计算机专业领域TOP10,性价比超高!

原标题&#xff1a;排名丨计算机专业领域TOP10&#xff0c;性价比超高&#xff01;相信各位家长、同学已经看过太多专业的排名&#xff0c;我问过很多理科生将来想学什么专业&#xff0c;听到频率最高的还是计算机专业。似乎大家都知道&#xff0c;学计算机是比较挣钱的&#x…

js 命名规范

转载于:https://www.cnblogs.com/zjx2011/p/3165043.html