首页 文章

IMAGE_SECTION_HEADER的VirtualAddress和PointerToRawData的区别

提问于
浏览
0

this文章中,定义是

DWORD VirtualAddress

在EXE中,此字段将RVA保存到加载程序应映射该部分的位置 . 要计算内存中给定部分的实际起始地址,请将图像的基址添加到此字段中存储的部分的VirtualAddress中 .

DWORD PointerToRawData

这是基于文件的偏移量,可以找到编译器或汇编器发出的原始数据 . 如果程序存储器本身映射PE或COFF文件(而不是让操作系统加载它),则此字段比VirtualAddress字段更重要 . 在这种情况下,您将拥有完全线性的文件映射,因此您将在此偏移处找到部分的数据,而不是在VirtualAddress字段中指定的RVA

RVA 也被定义为

PE文件中的许多字段都是根据RVAs指定的 . RVA只是某个项的偏移量,相对于文件的内存映射位置

要将RVA转换为可用指针,只需将RVA添加到模块的基址 . 基址是内存映射的EXE或DLL的起始地址

手头的问题是到达PE文件的 import section .

hFile = CreateFile(..);
hFileMapping = CreateFileMapping(..);
lpFileBase = MapViewOfFile(..);
ImageBase = (PIMAGE_DOS_HEADER)lpFileBase;
PEHeader = (ImageBase + ImageBase->e_lfanew);

现在 grab import table

PIMAGE_OPTIONAL_HEADER PEImageOptionalHeader = &(PEHeader->OptionalHeader);
IMAGE_DATA_DIRECTORY importTable = PEImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

由于 importTable.VirtualAddress 是一个RVA,为了得到一个可用的指针,我可以添加图像文件的基础 .

所以 ImageBase + importTable.virtualAddress 应该让我导入部分 . But it does not.Why?

然后,如果我到达正确的部分 Headers (通常是 .idata )并执行此操作 .

ImageBase + pointerToSection->PointerToRawData;

以上正确地将我带到 IMAGE_IMPORT_DESCRIPTORS 的数组 . 我明白使用 pointerToSection->virtualAddress 而不是上面的 PointerToRawData ,不会起作用,因为我自己映射PE文件 .

现在要获取项目的 name ,加载的模块依赖于,我可以使用指针 IMAGE_IMPORT_DESCRIPTORS 使用字段 name 这又是 RVA . 要转换 RVA ,我只需要添加 ImageBase ..

LPSTR libname = (PCHAR)((DWORD)ImageBase+ImageimportDescriptor->Name);

But it does not work.Why ?要转换 RVA ,我们只需添加图像的基址 . 下面有效

ImageBase+ImageimportDescriptor->Name + pointerToSection->PointerToRawData - pointerToSection->virtualAddress

每次我需要一个部分内的一些信息,我需要进行这种调整

pointerToSection->PointerToRawData - pointerToSection->virtualAddress

Why is this adjustment required?

2 回答

  • 1

    首先,这一行:

    PEHeader = (ImageBase + ImageBase->e_lfanew);
    

    是不正确的 . ImageBase 的类型为 PIMAGE_DOS_HEADERS 所以当你添加它 ImageBase->e_lfanew ,这是 DWORD 你正在做pointer arithmetic,也就是说,你正在向 ImageBase 添加多少字节 (ImageBase->e_lfanew)*sizeof(IMAGE_DOS_HEADERS) 这不是你想要的 . 你想要的是从 ImageBase 指向的地方推进 ImageBase->e_lfanew 个字节 . 您通过执行以下操作来实现此目的:

    PIMAGE_NT_HEADERS PEheader = (PIMAGE_NT_HEADERS) ((PBYTE)(ImageBase) + dosHeader->e_lfanew);

    注意转换为 PBYTE ,这使得操作逐字节前进 .

    这在处理PE文件时很常见,因为很多时候你想要从指针前进n个字节而不是指针所指向的n个数据结构 . 在article you refered to它甚至在评论中说:

    // Ignoring typecasts and pointer conversion issues for clarity...
    pNTHeader = dosHeader + dosHeader->e_lfanew;
    

    现在 your question 关于 VirtualAddressPointerToRawData

    由于以下原因, ImageBase + importTable.virtualAddress 行也不正确:

    PE Loader不会像在文件映射中那样在内存中连续映射所有可执行文件,它会将每个部分映射到相应的 VirtualAddress . 在后一种情况下,要从RVA获取VA,您只需将 ImageBase 添加到 RVA 并且它可以工作,因为磁盘上的文件旨在表示PE加载器在内存中映射的内容 . 但是,由于您没有映射PE加载程序将映射它们的每个部分,因此将 ImageBase 添加到 RVA 就像您所做的那样不起作用 .

    您需要一个给定RVA的函数,为您提供与磁盘上文件中的RVA相对应的文件偏移量 . 要获得VA,您只需将此文件偏移量添加到指向映射文件的字节指针即可 .

    每当您有RVA(如导入部分的RVA,或名称字符串或其他)时,为了访问它,您必须执行以下操作 .

    PBYTE actualAddress = (PBYTE) (lpFileBase + RVAtoFileOffset(pNTHeader, RVA))

    这是函数:我posted it on pastebin因为我无法在这里正确地格式化它 .

    以下是它的工作原理:当你有一个RVA时,那个RVA必须位于一个区域内,所以你遍历所有区段 Headers (它位于可选 Headers 之后),当你找到RVA所在的区域时,你计算出该部分中的RVA的偏移量 RVA - VirtualAddress 并将其添加到该部分的 PointerToRawData ,现在您具有RVA的文件偏移量 . 如果RVA无效(它不在任何部分中),则该函数返回0 .

    您需要执行此操作才能访问导入目录或每个导入描述符的名称,因为您拥有的是RVAs .

    我希望我有所帮助 .

  • 0

    一旦我们有了有效的PE / NT头地址,我们就可以直接跳转到第一部分:

    PIMAGE_SECTION_HEADER sectionHeader;
    sectionHeader = IMAGE_FIRST_SECTION(peHeader);
    

    我们也可以通过屏蔽找到部分的名称

    sectionHeader->Characteristics
    

相关问题