VerQueryValueA 在资源块之外写入无效内存 [英] VerQueryValueA writes invalid memory outside of the resource block

查看:33
本文介绍了VerQueryValueA 在资源块之外写入无效内存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码检索当前可执行文件的版本字符串:

I am retrieving a version string for the current executable using the following code:

// http://stackoverflow.com/questions/13941837/how-to-get-version-info-from-resources
std::string get_version_string()
{
    auto hInst = GetModuleHandle(NULL);

// The following functions allocate persistent one-time space in the process's memory - they don't need to have results freed
    auto hResInfo = FindResourceA(hInst, MAKEINTRESOURCE(1), RT_VERSION);
    auto dwSize = SizeofResource(hInst, hResInfo);
    auto hResData = LoadResource(hInst, hResInfo);
    char *pRes = static_cast<char *>(LockResource(hResData));
    if ( !dwSize || !pRes ) return {};

// Copy is required because VerQueryValue modifies the object, but LoadResource's resource is non-modifiable.
// SizeofResource yielded the size in bytes, according to its documentation.
    std::vector<char> ResCopy(dwSize);
    std::copy(pRes, pRes + dwSize, ResCopy.begin());

// https://stackoverflow.com/a/1174697/1505939
    LPVOID pvFileVersion{};
    UINT iFileVersionLen{};

    if ( !VerQueryValueA(&ResCopy[0], "\\StringFileInfo\\040904E4\\FileVersion", &pvFileVersion, &iFileVersionLen) )
        return "(unknown)"s;

    char buf[200];
    sprintf(buf, "%p\n%p\n%p", &ResCopy[0], &ResCopy[0] + ResCopy.size(), pvFileVersion);
    debug_output ( buf );

    auto s = static_cast<char *>(pvFileVersion);
    return std::string( s, s + iFileVersionLen );
}

VerQueryValue 文档和关于该主题的其他 SO 问题表明 VerQueryValue 应该返回指向资源块的指针.但是,我得到的输出是:

The VerQueryValue documentation, and other SO questions on the topic, indicate that VerQueryValue is supposed to return a pointer into the resource block. However, the output I get is:

000000000594b460
000000000594b748
000000000594b802

此外,如果我将 ResCopy 的分配更改为 std::vector;ResCopy(dwSize + 0x200); ,然后我得到输出:

Furthermore, if I change the allocation of ResCopy to std::vector<char> ResCopy(dwSize + 0x200); , then I get the output:

000000000594b460
000000000594b948
000000000594b802

我唯一能得出的结论是 VerQueryValueA 函数在原始情况下执行越界写入;它的写入超过了由 SizeofResource 给出的资源大小的末尾;它写在我的向量之外.

and the only thing I can conclude from this is that the VerQueryValueA function is doing an out-of-bounds write in the original case; it's writing past the end of the resource's size as was given by SizeofResource; it's writing outside of my vector.

尽管该功能似乎可以正常工作,但我怀疑这实际上可能是一个错误.

Even though the function seems to work properly I suspect this might actually be a bug.

我的问题是:我做错了什么,还是 VerQueryValueA 中的错误?我应该如何解决这个问题?

My question is: am I doing something wrong, or is this a bug in VerQueryValueA ? And how should I fix the problem?

注意:如果我使用 VerQueryValueW 那么它首先会在 ResCopy 中返回一个指针.

Note: If I use VerQueryValueW then it does return a pointer inside ResCopy in the first place.

这个答案 似乎暗示了这个问题,但我没有使用 GetFileVersionInfo (这需要一个文件名,似乎没有任何等效的函数可以使用模块句柄).

This answer seems to allude to the issue however I'm not using GetFileVersionInfo (which requires a filename, there doesn't seem to be any equivalent function that takes the module handle).

这样做的更大目的是能够在日志文件中记录我的应用程序的版本字符串,当我们显然已经加载了可执行文件来运行它.

The greater purpose of this is to be able to log my application's version string in the log file, and trying to find and open a file based on filename seems like a bunch more possible points of failure when we have obviously already loaded the executable to be running it.

推荐答案

GetFileVersionInfo() 执行 VerQueryValue() 所依赖的修正和数据转换.Raymond Chen 甚至写了一篇关于它的博客文章:

GetFileVersionInfo() performs fixups and data conversions that VerQueryValue() relies on. Raymond Chen even wrote a blog article about it:

VerQueryValue 的第一个参数确实必须是您获得的缓冲区来自 GetFileVersionInfo

VerQueryValue 函数的文档指出,第一个参数是指向包含 GetFileVersionInfo 函数返回的版本信息资源的缓冲区的指针".然而,有些人决定绕过这一步并传递一个指向以其他方式获得的数据的指针,然后想知道为什么 VerQueryValue 不起作用.

The documentation for the VerQueryValue function states that the first parameter is a "pointer to the buffer containing the version-information resource returned by the GetFileVersionInfo function." Some people, however, decide to bypass this step and pass a pointer to data that was obtained some other way, and then wonder why VerQueryValue doesn't work.

文档说 VerQueryValue 的第一个参数必须是 GetFileVersionInfo 函数返回的缓冲区,这是有原因的.GetFileVersionInfo 返回的缓冲区是一个专门格式化的不透明数据块,以便 VerQueryValue 可以工作.您不应该查看该缓冲区的内部,当然也不能尝试以其他方式获取数据".因为如果您这样做,VerQueryValue 将在缓冲区中查找未按函数预期方式格式化的内容.

The documentation says that the first parameter to VerQueryValue must be a buffer returned by the GetFileVersionInfo function for a reason. The buffer returned by GetFileVersionInfo is an opaque data block specifically formatted so that VerQueryValue will work. You're not supposed to look inside that buffer, and you certainly can't try to "obtain the data some other way". Because if you do, VerQueryValue will look for something in a buffer that is not formatted in the manner the function expects.

除了在资源数据的最开始查询VS_FIXEDFILEINFO之外,使用VerQueryValue()从原始资源中查询其他版本数据确实不安全数据.该数据尚未准备好供 VerQueryValue() 使用.您链接的问题的答案甚至说明了这一点,就像上面的文章一样:

Other than querying the VS_FIXEDFILEINFO at the very beginning of the resource data, it is really not safe to use VerQueryValue() to query other version data from the raw resource data. That data hasn't been prepared for VerQueryValue() to use. The answers to the question you linked to even state this, as does the above article:

如果文档中不够明显,您不能只传递指向以其他方式"获得的版本资源的指针,那么一旦您看到 32位版本资源的格式.请注意,所有字符串都以 Unicode 存储.但是,如果您调用 ANSI 版本 VerQueryValueA 来请求字符串,则该函数必须为您提供一个指向 ANSI 字符串的指针.原始版本资源中没有字符串的 ANSI 版本,那么它可能返回什么?你不能返回一个指向不存在的东西的指针.VerQueryValueA 需要生成一个 ANSI 字符串,它从内存中生成,这是在提取资源时 GetFileVersionInfo 准备的.

If it wasn't obvious enough from the documentation that you can't just pass a pointer to a version resource obtained "some other way", it's even more obvious once you see the format of 32-bit version resources. Notice that all strings are stored in Unicode. But if you call the ANSI version VerQueryValueA to request a string, the function has to give you a pointer to an ANSI string. There is no ANSI version of the string in the raw version resource, so what can it possibly return? You can't return a pointer to something that doesn't exist. VerQueryValueA needs to produce an ANSI string, and it does so from memory that GetFileVersionInfo prepared when the resources were extracted.

对于您尝试执行的操作,您只需要从复制的资源中查询 VS_FIXEDFILEINFO 即可.它包含您要查找的版本号,与语言无关,并且不依赖于 GetFileVersionInfo():

For what you are attempting to do, querying the VS_FIXEDFILEINFO from the copied resource is all you need. It contains the version number you are looking for, is language agnostic, and is not dependent on GetFileVersionInfo():

std::string get_version_string()
{
    auto hInst = GetModuleHandle(NULL);
    auto hResInfo = FindResourceA(hInst, MAKEINTRESOURCE(1), RT_VERSION);
    if ( !hResInfo ) return {};
    auto dwSize = SizeofResource(hInst, hResInfo);
    if ( !dwSize ) return {};
    auto hResData = LoadResource(hInst, hResInfo);
    char *pRes = static_cast<char *>(LockResource(hResData));
    if ( !pRes ) return {};

    std::vector<char> ResCopy(pRes, pRes + dwSize);

    VS_FIXEDFILEINFO *pvFileInfo;
    UINT uiFileInfoLen;

    if ( !VerQueryValueA(ResCopy.data(), "\\", reinterpret_cast<void**>(&pvFileInfo), &uiFileInfoLen) )
        return "(unknown)"s;

    char buf[25];
    int len = sprintf(buf, "%hu.%hu.%hu.%hu", 
        HIWORD(pvFileInfo->dwFileVersionMS),
        LOWORD(pvFileInfo->dwFileVersionMS),
        HIWORD(pvFileInfo->dwFileVersionLS),
        LOWORD(pvFileInfo->dwFileVersionLS)
    );

    return std::string(buf, len);
}

这篇关于VerQueryValueA 在资源块之外写入无效内存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆