关于《加密与解密》的读后感----对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,一经查实,立即删除!

相关文章

java vector_Java Vector elements()方法与示例

java vector向量类elements()方法 (Vector Class elements() method) elements() method is available in java.util package. elements()方法在java.util包中可用。 elements() method is used to get an enumeration of the elements that exist in this Vector. elements()方…

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

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

动态添加,删除行之心理测试系统

动态添加&#xff0c;删除行之考试系统 数据库设计&#xff1a; xl_option 题目选项 20090105134755404(编号) 20090105134904421(外键) 比较符合(选项内容) ②(选项标号) 2&#xff08;选项分值&#xff09; xl_subject 题目信息 20090105134943608&#xff08;编号&#xff…

android bitmap裁剪中间,Android裁剪中心位图

虽然上面的大多数答案提供了一种方法来实现这一点&#xff0c;但已经有一种内置的方法来实现这一点&#xff0c;它是一行代码(ThumbnailUtils.extractThumbnail())int dimension getSquareCropDimensionForBitmap(bitmap);bitmap ThumbnailUtils.extractThumbnail(bitmap, di…

二、request请求库

一、requests介绍与安装 1&#xff0c;requests介绍 答&#xff1a;requests是一个优雅且简单的Python HTTP请求库 2&#xff0c;requests作用 答&#xff1a;requests的作用是发送请求获取响应数据 3&#xff0c;requests安装 答&#xff1a;pip install requests 二、…

Java Vector Capacity()方法与示例

向量类的Capacity()方法 (Vector Class capacity() method) capacity() method is available in java.util package. Capacity()方法在java.util包中可用。 capacity() method is used to return the current capacity (i.e. initially, how many object exists) of this Vecto…

MFC和GTK的区别

关键技术 http://blog.csdn.net/master_max/article/details/1540204 MFC和GTK的区别&#xff1f;&#xff1f; 1.  两者都是基于面向对象设计的。尽管MFC是用C写的&#xff0c;而GTK是用C写的&#xff0c;但思想都是面向对象的。GTK使用glib的对象机制&#xff0c;由于用C写…

视频图像质量评价

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

鸿蒙科技与文化,数字阅读 | “华为鸿蒙”:当现代科技遇到古典文化

华为事件愈演愈烈。海思芯片 20 年 " 备胎 " 终转正&#xff0c;那么操作系统呢&#xff1f;最近&#xff0c;华为为自主研发的操作系统注册商标—— " 鸿蒙 "&#xff0c;引发了关于华为注册整本《山海经》的热烈讨论&#xff0c;很多人的朋友圈&#xff…

三、Beautiful Soup解析库

一、Beautiful Soup介绍与安装 1&#xff0c;Beautiful Soup介绍 答&#xff1a;Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库 2&#xff0c;Beautiful Soup安装 答&#xff1a;安装Beautiful Soup 4&#xff1a;pip install bs4 安装lxml&#xff1a;pip…

strictmath_Java StrictMath sqrt()方法与示例

strictmathStrictMath类sqrt()方法 (StrictMath Class sqrt() method) sqrt() Method is available in java.lang package. sqrt()方法在java.lang包中可用。 sqrt() Method is used to find the square root of the given parameter in the method. Here, "sqrt" st…

recovery编译问题汇总

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

言语理解每日学习及精解20110831

【例题】天气预报一般要考虑气温、气压、温度、风力等因素&#xff0c;这些都是大气层本身变化的结果&#xff0c;只要掌握这些因素&#xff0c;通过计算机的计算就能准确地预报天气变化的趋势。沙尘暴作为一种特殊的天气现象&#xff0c;同样要考虑上述气象因素。据气象学家分…

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

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

四、正则表达式

一、正则表达式的概念和作用 正则表达式概念&#xff1a;一种字符串匹配的模式 正则表达式作用&#xff1a; 可以检查一个字符串中是否包含某种字串替换匹配的字串提取某个字符串中匹配的字串 二、正则表达式中常见的语法 字符描述原样字符匹配字符一般字符匹配自身beyondb…

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

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

java treemap_Java TreeMap lastKey()方法与示例

java treemapTreeMap类lastKey()方法 (TreeMap Class lastKey() method) lastKey() method is available in java.util package. lastKey()方法在java.util包中可用。 lastKey() method is used to return the last highest key element value exists in this TreeMap. lastKey…

网上看来的

http://blog.163.com/dong_xiao_yang/blog/static/216138205201321114659430/ http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20compile%20FFmpeg%20for%20Raspberry%20Pi%20%28Raspbian%29#FFmpegwithlibaacpluslibx264andalsa-lib 编译环境 Ubuntu 12.04 w64-mingw32下载lib…

阅读iPhone.3D.Programming(O'Reilly.2010-05) 英文版 第一感觉

最近开始阅读iPhone.3D.Programming(OReilly.2010-05)&#xff0c;英文版此书&#xff0c;我阅读到P21了&#xff0c;中间讲了一个样例&#xff0c;HelloArrow在这个过程中&#xff0c;我想简单点&#xff0c;少打点字&#xff0c;直接拿书中配套来学习&#xff0c;发现一个问题…

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

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