IMAGE_IMPORT_DESCRIPTOR 中的 RVA 值错误

逆向工程 视窗 二元分析 聚乙烯 二进制 二进制格式
2021-06-17 16:56:54

我将整个 PE 加载到std::vector<Byte> fileContentusing 中std::fstream

然后我获得了可执行文件的 dos 头:

IMAGE_DOS_HEADER* imageDosHeader = (IMAGE_DOS_HEADER*)fileContent.data();

之后,我检查 PE 是否有效(MZPE00签名)。

如果是,我会得到它的导入描述符:

IMAGE_IMPORT_DESCRIPTOR* imageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)imageDosHeader + imageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

//Note: the VirtualAddress field equals 0x4FFC, so I assume it's valid

现在,我遍历 dll 并尝试显示它们的名称如下:

for(DWORD i = 0; ; i++)
{
    bool isCurrentDllValid = true;

    //if all fields of the current dll are zeros, then this dll is the last one, so break the outer loop
    for(DWORD j = 0; j < sizeof(IMAGE_IMPORT_DESCRIPTOR); j++)
    {
        if((*(DWORD*)((DWORD)&imageImportDescriptor[i] + j)))
            break;
        else if(j == sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1)
            isCurrentDllValid = false;
    }

    if(!isCurrentDllValid)
        break;

    char* dllName = (char*)((DWORD)imageDosHeader + imageImportDescriptor[i].Name);

问题是:试图显示dllName崩溃原因。

此外,该Name领域是一个RVA,但其价值是0x6C61766E(同样的事情,与其余字段,最小的一个是TimeDateStamp0x637465,还是一个异常),而PE的大小小于0x7000

在十六进制编辑器中,例如的 RVA"KERNEL32.dll"0x46F0.

你知道为什么会这样吗?我错过了一些非常简单的东西吗?

2个回答

除非您在矢量中加载映射的 PE 文件,否则您必须将所有 RVA 转换为文件偏移量。

伪代码如下所示:

func rva2offset(pe, rva):
    for section in pe.sections:
        if rva >= section.rva and rva < section.rva + section.size:
            return section.fileoffset + (rva - section.rva)
    return nil

该代码未考虑内核在内存中映射 PE 文件的实际方式(对齐和其他边缘情况),因此请谨慎使用。

不幸的是,现实生活中的样本并不总是遵循漂亮和整洁的规范;他们只需要处理特定的实现,而不是文档中描述的理想实现。

Corkami收集有关实用PE 格式功能的详细信息,例如:

如果存在导入查找表,则它还确定导入地址表的长度,因为它们是并行解析的。在这种情况下,导入地址表不必以空值结尾。

或者也许这个:

如果 IAT 在描述符中链接但为空,则不会加载 DLL,并且即使使用无效的 dll 名称也会加载文件 - 跳过描述符。

因此,请检查您是否有任何一种情况。