永利平台娱乐导入表的获取与动态填充&gt,反射

2020-01-05 19:23 来源:未知

<<导入表的获取与动态填充>>初步学习笔记

原文链接地址:

最近在黑防上看了一篇文章《简易杀毒软件编写》,讲到了通过扫描PE文件中危险API的方法判断PE文件是否为病毒的思路,很不错。但是在关键的地方,作者却用了一句“黑防以前的文章有关于查看PE导入表的,在这里不aoshu”的话,将如何扫描PE导入表搪塞过去了。狂郁闷呀。“以前的黑防”到底是哪一期呀??最后到黑防网站上下到了这篇文章的代码,代码中只有关于如何查找文件,如何获取进程列表以及如何判断一个文件是pe文件三个函数,根本没有关于扫描PE导入表的相关方法。

=

[1]导入表结构的获取
<1.>获取PE基址 (IMAGE_DOS_HEADER)

PE文件定义

PE 文件(”Portable executable”, 可移植的可执行文件)文件格式,是微软Windows NT, 中Win32、Win32s中的可执行的二进制的文件格式。 包括:.exe, .dll, .sys, .com, .ocs. PE文件最重要的两个因素:

1.磁盘上的可执行文件和它被映射到windows内存之后的格式非常相像。

2.对于Win32 来讲, 模块中多使用的所有代码、数据、资源、导入表、和其他需要的模块数据结构都在一个连续的内存块中。因此,只需要知道PE Loader把可执行文件映射到了内存的什么地方(基址),通过作为映像的一部分指针,就可以找到这个模块的所有不同的块。

PE文件总览:

永利平台娱乐 1

 

永利平台娱乐 2

<2>.获取PE头部(IMAGE_NT_HEADERS32)

放另一张图:

永利平台娱乐 3

再放一张:

永利平台娱乐 4

看来要靠自己动手了。以前就像好好学习一下PE结构,但是后几次都知难而退了。这次一定要拿下。

前言

上一篇帖子《常见进程注入的实现及内存dump分析——反射式DLL注入(上)》中,实现了反射式注射器的Dropper,这篇帖子中,将会实现Payload——DLL文件。个人认为,反射式DLL的精髓就在于DLL的反射加载功能。

<3>. 获取导入表的存储结构(IMAGE_NT_HEADERS32.OptionalHeader.DataDirectory[1])
导出 表:IMAGE_NT_HEADERS32.OptionalHeader.DataDirectory[0]
导入 表:IMAGE_NT_HEADERS32.OptionalHeader.DataDirectory[1]
结构其实就是一个 IMAGE_DATA_DIRECTORY,包含,导入表的相对偏移,和导入表的大小

1. DOS Header: (size:64byte)

_IMAGE_DOS_HEADER结构体:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

结构体中有两个重要的数据成员。第一个为e_magic,这个必须为MZ,即0x5A4D。另一个重要的数据成员是最后一个成员e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移。其中,*e_lfanew这个字段的值:   PE Header 在磁盘文件中相对于文件开始的偏移地址.

实例截图:

永利平台娱乐 5

2.     PE Header: (size: 248bytes)

IMAGE_NT_HEADERS 紧接在DOS Stub之后,位置有e_lfanew所指

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature; //4 bytes PE文件头标志:(e_lfanew)->‘PE’
    IMAGE_FILE_HEADER FileHeader;//20 bytes PE文件物理分布的信息
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件逻辑分布的信息
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

永利平台娱乐 6

PE Header 总览

永利平台娱乐 7

IMAGE_NT_HEADERS结构体成员解析:

到"看雪"找来了关于PE结构的资料,讲得很详细,也很好。唯一不满的地方是那些例子程序全是用Win32汇编写的,呵呵,勉强能认懂...花了整整一个下午总算从"dos_header"看到了"import table(导入表)"。没看一部分我都用VC++试验一下。前面都还顺利,到了导入表就挂了。这个结构太复杂了,加上rva和offset的装换,头越来越大...

环境

OS:Windows 10 PRO 1709

IDE:Visual Studio 2015 Community

语言:Visual C++

Payload:DLL的实现

<4>.导入表定位
IMAGE_DATA_DIRECTORY.VirtualAddress+PE 基址(第一步)
导入表第一个模块结构

2.1.Signature: (4 bytes)

永利平台娱乐 8

 

原理:

将已经注入到目标进程的DLL加载到内存,实现LoadLibrary的功能。

<5>.获取第一个导入模块
结 构:IMAGE_IMPORT_DESCRIPTOR
长度:20字节
是一个结构表-最后以一个空结构结束
意思就是:
IMAGE_IMPORT_DESCRIPTOR:-kernel32.dll
IMAGE_IMPORT_DESCRIPTOR:-user32.dll
IMAGE_IMPORT_DESCRIPTOR:-NULL

2.2.IMAGE_FILE_HEADER(20 bytes)

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                //运行平台
    WORD    NumberOfSections;        //文件区块数目
    DWORD   TimeDateStamp;            //文件创建日期和时间
    DWORD   PointerToSymbolTable;    //指向符号表(主要用于调试)
    DWORD   NumberOfSymbols;        //符号表中符号个数
    WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 结构大小
    WORD    Characteristics;            //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

IMAGE_FILE_HEADER结构体成员解析:

第二天又花了一个上午,硬着头皮看那些资料上的Win32汇编,功夫不负有心人。在我尝试N次后,终于在14:48分搞定了。

步骤:

这里我先描述下大体的流程,后面会展开。

获取目标进程PEB,从而获取一些需要用到的函数地址,如:VirtualAlloc。

复制PE头,由于PE头的形态并没有像节一样需要展开,所以为复制。

解析PE头,并加载节,与2不一样的是,这里用的是加载,到了节这里,已经在PE头中的信息指定了RVA,所以这里要进行“加载”。

处理导入表,获取导入函数的地址。

处理重定位表,由于基址和默认的加载地址不同,所以需要修改重定位表,否则,程序内的直接寻址会出问题。

调用镜像入口点,到这里,镜像已经加载完毕。

由于直接编写DLL,直接进行反射加载,无法用VS进行调试,所以我之前新建了一个可执行的项目,在该项目中,实现了加载的功能,后续只需将函数导出,和变换下加载的DLL即可。

详细步骤:

获取DLL起始位置。

//caller功能:获取当前指令的下一条指令的地址。

uiLibraryAddress = caller();

while (TRUE)

{

    if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)

    {

        //pe头偏移RVA

        uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;

        //判断PE头的正确性

        if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)

        {

            //pe头在内存中的位置

            uiHeaderValue += uiLibraryAddress;

            //如果找到文件头就退出循环

            if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)

                break;

        }

    }

    uiLibraryAddress--;

}

我在调试的可执行的Demo中,更改的。

//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程

uiLibraryAddress = callAddress + 0x10;

获取目标进程PEB,获取需要的函数地址,需要的函数有:VirtualAlloc(用来为镜像要加载的地址分配空间)、LoadLibraryA(处理导入表)、GetProcAddress(同上)、NtFlushInstructionCache(刷新数据,让CPU执行新指令)。

获取PEB的方法:FS:[0x30]和GS:[0x60],前者为32位系统,后者为64位系统。从下面的图中是微软公布的PEB的数据结构(在网络上可以找到更详细的结构),在_PEB_LDR_DATA这个数据结构中,存储着当前进程所加载的模块信息,就是我们想要的,我们需要遍历已经加载的模块,从中找到我们需要的模块,获得以上几个函数的地址。(会在附件中上传详细的PEB图)

提示:在解析PEB的结构的时候,要注意字节对齐的问题,以前没有注意到结构体的这个问题,算是填了个坑。

永利平台娱乐 9

PEB及_PEB_LDR_DATA的数据结构

永利平台娱乐 10

模块之间的关系(来自网络,侵删)

由以上两图,贴出代码如下:

uiBaseAddress = __readgsqword(0x60);//peb结构的地址

uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;

uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;

while (uiValueA)

{

    //当前模块名地址

    uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;

    usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;

    uiValueC = 0;

    //计算模块名的hash

    do

    {

        uiValueC = ror((DWORD)uiValueC);

        // normalize to uppercase if the madule name is in lowercase

        if (*((BYTE *)uiValueB) >= 'a')

            uiValueC += *((BYTE *)uiValueB) - 0x20;

        else

        uiValueC += *((BYTE *)uiValueB);

        uiValueB++;

    } while (--usCounter);

    //获取目标进程中的接下来需要的函数地址

    if ((DWORD)uiValueC == KERNEL32DLL_HASH)

    {

        uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;

        uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;

uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];

        uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);

        uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);

        uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);

        usCounter = 3;

        // 找函数

        while (usCounter > 0)

        {

            dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));

            if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)

            {

                uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);

                uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));

                if (dwHashValue == LOADLIBRARYA_HASH)

                    pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));

                else if (dwHashValue == GETPROCADDRESS_HASH)

                    pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));

                else if (dwHashValue == VIRTUALALLOC_HASH)

                    pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_3

在上面的代码中,获取函数地址的部分没有具体写,上一篇帖子中详细的说明了获取的过程,差别就是上一篇帖子中需要将RVA转化为文件偏移。代码中有一些Hash值,这种方法在shellcode中比较常见,shellcode中是为了减小空间,这里除了这个原因,我在用IDA查找信息的时候并不能从字符串中直接找到函数名,也许这也是一个原因。(如有错误,或者其他原因,欢迎指出)。

开辟缓冲区(DLL要加载到的空间),复制PE头和节表。

uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;

//分配空间,首地址即为DLL加载的基地址

uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;//所有头+节表的大小

uiValueB = uiLibraryAddress;//DLL的起始地址,即缓冲区的起始地址

uiValueC = uiBaseAddress;//dll将被加载的地址的起始地址

//复制头和节表的数据到新开辟的缓冲区

while (uiValueA--)

    *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;

PE头和节表可直接复制的原因:

永利平台娱乐 11

映射关系(来自网络,侵删)

根据节表加载节。

//节表的第一项

uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);

//pe中节的数量

uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;

while (uiValueE--)

{

    //节的虚拟地址

    uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);

    //节的文件偏移地址

    uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);

    //节的大小

    uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;

    //拷贝数据

    while (uiValueD--)

        *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;

    //下一个节

    uiValueA += sizeof(IMAGE_SECTION_HEADER);

}

处理导入表,导入表的结构图在上一篇帖子中没有详细画,附件中会更新。

永利平台娱乐 12

代码入下:

// uiValueB :导入表地址

uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

//基地址+RVA即导入表描述符的地址VA

uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);

//链接库名字

while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)

{

    //使用LoadLibraryA将需要的模块加载到内存

    uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));

    //指向INT的IMAGE_THUNK_DATA的VA

    uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);

    //要导入IAT的IMAGE_THUNK_DATA结构体

    uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);

    // 迭代函数,如果没有名,则获取序号

    while (DEREF(uiValueA))

    {

                //在调试过程中发现都是获取的函数序号

        // sanity check uiValueD as some compilers only import by FirstThunk

        if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)

        {

            uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;

uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];

            uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);

            uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);

uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));

            DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));

        }

        else

        {

            uiValueB = (uiBaseAddress + DEREF(uiValueA));

DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);

        }

        uiValueA += sizeof(ULONG_PTR);

        if (uiValueD)//INT

            uiValueD += sizeof(ULONG_PTR);

    }

    uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);

}

同样,关于导出表的部分没有详细注释,已经在上篇帖子中有详细的介绍。

处理重定位表,由于基址改变,所以程序中的一些直接寻址等会出问题,所以要更改重定向表。

永利平台娱乐 13

接下来需要用到的重定位表的关系

代码如下:

//实际装载和建议装载的偏移,原重定位表中的值是以程序建议的装载地址为基址

uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;//程序建议的装载地址 

uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];

if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)//重定位表大小

{

    //重定位表的地址

    uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);

    while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)//重定位块的大小

    {

        //重定位内存页的起始RVA

        uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);

      //重定位块中的项数(整个块的大小减去结构体的大小,得到重定位项的总大小,除以每个重定位项的大小)             

        uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);

        //重定位块的第一项

        uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);

        //遍历重定位项

        while (uiValueB--)

        {

            //重定位项的高四位代表此重定位项的类型

            if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)

                *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;

            else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)

                *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;

            else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)

                *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);

            else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)

                *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);

            //下一个重定位项

            uiValueD += sizeof(IMAGE_RELOC);

        }

        //下一个重定位块

        uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;

    }

}

调用程序入口点,使其执行DllMain,并传递消息为Dll的状态为DLL_PROCESS_ATTACH(这个消息在上篇文章中有讲到)

uiValueA = (uiBaseAddress + ((PIMAGE_永利平台娱乐,NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);

// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.

pNtFlushInstructionCache((HANDLE)-1, NULL, 0);

((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);//调用入口点

分析:

在这种的注入是实现中,很少从外部导入函数,且使用了目标进程的部分导入库和函数,所以在IDA的导入中没有什么有价值的信息。不过,回忆整个流程,我们会发现这种注入有特别的地方,如获取PEB,如图,双重循环获取系统函数等,而且,这种注入由于需要修复的重定位表,也会使用双重循环。

永利平台娱乐 14

在导出函数中,由于在注射器中会通过导出表来获取反射函数的地址,所以导出表中会有一个反射函数,且加载功能都是在反射函数中进行的。

从内存分布看,由于都是新开辟的空间,且需要执行代码,所以权限都为RWX,如果查看内存,小的那段的开头,一定是MZ。

永利平台娱乐 15

将小的那段内存dump出来,虽然大小和原DLL有稍微的不同,但直接拖到IDA是可以进行分析的,因为那段内存就是dll本身。

<6>. 获取导入模块下的函数结构
结构:IMAGE_IMPORT_DESCRIPTOR
长度:20字节
结构获 取:IMAGE_IMPORT_DESCRIPTOR结构下有两个成员数指向函数结构体:
OriginalFirstThunk:序号获取 IMAGE_THUNK_DATA函数结构
FirstThunk:直接获取函数序号
两个都指向IMAGE_THUNK_DATA结构
(注 意:如果两个都为空,表明这个模块已经被"榨干"了~^_^)
IMAGE_THUNK_DATA结构和 IMAGE_IMPORT_DESCRIPTOR结构排序方式差不多
也是以

1). Machine 代表了CPU的类型  //运行平台
#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
………………….
#define IMAGE_FILE_MACHINE_CEE               0xC0EE

 

最后

全部源码地址:

<7>.函数结构体: (IMAGE_THUNK_DATA)
结构体长度20字节(5 * sizeof(DWORD))
其中 PIMAGE_IMPORT_BY_NAME指向函数名称结构:
IMAGE_IMPORT_BY_NAME-结构分为:
WORD Hint; ---函数序号-一般为空(除非~有的时候一些函数是没有名称的-例如...^_^)
BYTE Name[n]; ---这个是一个数组-不是字符指针了-就是函数名称

2)       NumberOfSections: 代表区块的数目,区块表紧跟在IMAGE_NT_HEADERS后面, 区块表大概是一个链表结构,链表长度由NumberOfSection的数值决定.

啥都先别说,先发个截图,自我安慰一下 呵呵

参考

代码:

《Windows PE权威指南》

《深入解析Windows操作系统》

《加密与解密》

本文由看雪论坛 sudozhange 原创  转载请注明来自看雪社区

[2]导入表填充
<8>.导入表填充: (IMAGE_THUNK_DATA)
主要结构体:IMAGE_THUNK_DATA
IMAGE_THUNK_DATA下只有一个指针指 向IMAGE_IMPORT_BY_NAME.
IMAGE_IMPORT_BY_NAME其实就是│函数导出序号│函数名│
PE Loader在加载的时候会根据IMAGE_IMPORT_BY_NAME结构里面的序号或者函数名称
所引函数.获取地址.然后填充 IMAGE_THUNK_DATA!没错把IMAGE_THUNK_DATA给填充了
由于是把IMAGE_THUNK_DATA结构填充了意思也 就是说指向IMAGE_IMPORT_BY_NAME的指针被填充了
那也就是说我们只要填充完毕地址以后 IMAGE_IMPORT_BY_NAME结构有没有都无所谓了
这也就解释了为什么加壳软件导入表动态填充完毕以后PE也不会出错的原因。。

3)       TimeDataStamp: 表明文件的创建时间

 

另 外为什么不需要修改原有导入表虚拟指向.
一般函数调用都是直接call [offset MessageBoxA]
类似这样的结构,翻译 成显示地址就是call [0x40FFFF]这样的字样
他不是从导入表从导入表中定位的,所以你即使把整个PE Header破坏了也没什么事情
OhMy Goddess~

4)       SizeOfOptionalHeader: 是IMAGE_NT_HEADERS的另一个子结构IMAGE_OPTIONAL_HEADER的大小

 

<9>.普通动态填充与高级动态填充:
普 通保护:
没有对导入表进行处理,的填充方式
1.解压释放所有资源和数据
2.获取原导入表虚拟偏移
3.获取导入表模块指 针,地址
4.进行填充

5)       Characteristics: 代表文件的属性EXE文件一般是0100h DLL文件一般是210Eh,多种属性可以用或运算同时拥有
#define IMAGE_FILE_RELOCS_STRIPPED   0x0001 // 重定位信息被移除 
#define IMAGE_FILE_EXECUTABLE_IMAGE   0x0002 // 文件可执行 
#define IMAGE_FILE_LINE_NUMS_STRIPPED  0x0004 // 行号被移除 
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除 
……..
#define IMAGE_FILE_32BIT_MACHINE  0x0100 // 32位机器 
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除 
………………….
#define IMAGE_FILE_SYSTEM       0x1000 // 系统文件 
#define IMAGE_FILE_DLL         0x2000 // 文件是一个dll 
#define IMAGE_FILE_UP_SYSTEM_ONLY    0x4000 // 文件只能运行在单处理器上

实例截图:

永利平台娱乐 16

永利平台娱乐 17

TAG标签:
版权声明:本文由永利平台娱乐发布于新闻动态,转载请注明出处:永利平台娱乐导入表的获取与动态填充&gt,反射