首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

2.4  如何获取PE文件中的OEP

OEP(Original Entry Point)是每个PE文件被加载时的起始地址,如何获得这个地址很重要,因为修改程序中的这个值是文件加壳和脱壳时的必须步骤,一些黑客程序也是通过修改OEP值来获得对目标程序的控制权从而实施攻击。下面分别介绍如何通过文件直接访问和通过内存映射访问读取OEP值的方法,并给出完整的程序代码。

2.4.1  通过文件读取OEP值

获得OEP值的最简单方法是,直接从一个PE文件中读取OEP。根据以上对PE文件结构的介绍可知,OEP是PE文件的IMAGE_OPTIONAL_HEADER结构的AddressOfEntryPoint成员,在偏移此结构头40个字节处。而IMAGE_OPTIONAL_ HEADER在PE文件的起始位置由IMAGE_DOS_HEADER的e_lfanew成员来计算。注意,以上两个结构在PE文件中不是紧跟在一起的,它之间是DOS Stub,而在每个PE文件DOS Stub的长度可能不一定相等。在PE文件的头部是IMAGE_ DOS_HEADER结构,读取这个结构可以得到e_lfanew的值,因而可以得到IMAGE_ OPTIONAL_HEADER在PE文件中的位置,也就得到了OEP值。以下是通过文件访问的方法读取OEP的程序代码,即:

l           

// 通过文件读取OEP值

BOOL ReadOEPbyFile(LPCSTR szFileName)

{

    HANDLE hFile;

   

    // 打开文件

    if ((hFile = CreateFile(szFileName, GENERIC_READ,

        FILE_SHARE_READ, 0, OPEN_EXISTING,

        FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)

    {

        printf("Can't not open file.\n");

        return FALSE;

    }

   

    DWORD dwOEP,cbRead;

    IMAGE_DOS_HEADER dos_head[sizeof(IMAGE_DOS_HEADER)];

    if (!ReadFile(hFile, dos_head, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)){

        printf("Read image_dos_header failed.\n");

        CloseHandle(hFile);

        return FALSE;

    }

   

    int nEntryPos=dos_head->e_lfanew+40;

    SetFilePointer(hFile, nEntryPos, NULL, FILE_BEGIN);

   

    if (!ReadFile(hFile, &dwOEP, sizeof(dwOEP), &cbRead, NULL)){

        printf("read OEP failed.\n");

        CloseHandle(hFile);

        return FALSE;

    }

   

    // 关闭文件

    CloseHandle(hFile);

   

    // 显示OEP地址

    printf("OEP by file:%d\n",dwOEP);

    return TRUE;

}

2.4.2  通过内存映射读取OEP值

获得OEP值的另一种方法是通过内存映射来实现,此方法也需要熟悉PE的文件结构。与直接访问PE的方法不同,内存映射的方法首先把PE文件映射到计算机的内存,再通过内存的基指针获得IMAGE_DOS_HEADER的头指针,由此再获得IMAGE_ OPTIONAL_HEADER指针,这样就可以得到AddressOfEntryPoint的值。下面是通过内存映射获得OEP值的方法:

l           

// 通过文件内存映射读取OEP值

BOOL ReadOEPbyMemory(LPCSTR szFileName)

{

    struct PE_HEADER_MAP

    {

        DWORD signature;

        IMAGE_FILE_HEADER _head;

        IMAGE_OPTIONAL_HEADER opt_head;

        IMAGE_SECTION_HEADER section_header[6];

    } *header;

    HANDLE hFile;

    HANDLE hMapping;

    void *basepointer;

   

    // 打开文件

    if ((hFile = CreateFile(szFileName, GENERIC_READ,

        FILE_SHARE_READ,0,OPEN_EXISTING,

        FILE_FLAG_SEQUENTIAL_SCAN,0)) == INVALID_HANDLE_VALUE)

    {

        printf("Can't open file.\n");

        return FALSE;

    }

   

    // 创建内存映射文件

   if (!(hMapping = CreateFileMapping(hFile,0,PAGE_READONLY|SEC_COMMIT, 0,0,0)))

    {

        printf("Mapping failed.\n");

        CloseHandle(hFile);

        return FALSE;

    }

   

    // 把文件头映象存入baseointer

    if (!(basepointer = MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0)))

    {

        printf("View failed.\n");

        CloseHandle(hMapping);

        CloseHandle(hFile);

        return FALSE;

    }

    IMAGE_DOS_HEADER * dos_head =(IMAGE_DOS_HEADER *)basepointer;

   

    // 得到PE文件头

    header = (PE_HEADER_MAP *)((char *)dos_head + dos_head->e_lfanew);

   

    // 得到OEP地址.

    DWORD dwOEP=header->opt_head.AddressOfEntryPoint;

   

    // 清除内存映射和关闭文件

    UnmapViewOfFile(basepointer);

    CloseHandle(hMapping);

    CloseHandle(hFile);

   

    // 显示OEP地址

    printf("OEP by memory:%d\n",dwOEP);

    return TRUE;

}

2.4.3  读取OEP值方法的测试

为了检验以上两种获取OEP值方法的正确性和一致性,可以用以下的方法来测试:

l           

// oep.cpp:读取OEP的实例

//

#include <windows.h>

#include <stdio.h>

BOOL ReadOEPbyMemory(LPCSTR szFileName);

BOOL ReadOEPbyFile(LPCSTR szFileName);

void main()

{

    ReadOEPbyFile("..\\calc.exe");

    ReadOEPbyMemory("..\\calc.exe");

}

l           

运行以上代码后,可以得到如图2.3所示的结果。从图中可以看出,以上两种获取OEP值方法所得到的结果是一致的。

获取OEP值方法的测试结果

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论