Holy cow, I wrote a book!
Last time we looked at the format of 32-bit version resources, but I ended with the remark that what you saw purported to be the resources of shell32.dll but actually weren't. What's going on here?
shell32.dll
The resources I presented last time were what the resources of shell32.dll should have been, but in fact they aren't.
A common mistake in generating 32-bit resources is to mistreat the cbData field of the structure I called a VERSIONNODE as a count of characters rather than a count of bytes if the type is Unicode text. Even Microsoft's own Resource Compiler has fallen into this trap! For example, consider this VERSIONNODE I presented last time:
cbData
VERSIONNODE
0098 4C 00 // cbNode (node ends at 0x0088 + 0x004C = 0x00D40) 009A 2C 00 // cbData 009C 01 00 // wType = 1 (string data) 009E 43 00 6F 00 6D 00 70 00 61 00 6E 00 79 00 4E 00 61 00 6D 00 65 00 00 00 // L"CompanyName" + null terminator 00B6 00 00 // padding to restore alignment 00B8 4D 00 69 00 63 00 72 00 6F 00 73 00 6F 00 66 00 74 00 20 00 43 00 6F 00 72 00 70 00 6F 00 72 00 61 00 74 00 69 00 6F 00 6E 00 00 00 // L"Microsoft Corporation" + null terminator 00E4 // no padding needed
In real life, the data take the following form:
0098 4C 00 // cbNode (node ends at 0x0088 + 0x004C = 0x00D40) 009A 16 00 // cchData (!) 009C 01 00 // wType = 1 (string data) ...
These malformed version resources manage to get away without crashing too horribly because the standard format of version resources uses string data only in leaf nodes. Therefore, the incorrect cbData affects only the node itself and doesn't cause the child nodes to be parsed incorrectly (since there are no child nodes).
Until somebody tries to read, say, \StringFileInfo\040904B0\CompanyName\oops. After the VerQueryValue function locates the VERSIONNODE corresponding to CompanyName, it tries to locate the first child node and, due to the incorrect cbData, ends up misinterpreting the middle of the string as if it were the start of a child VERSIONNODE. Things only go downhill from there.
\StringFileInfo\040904B0\CompanyName\oops
VerQueryValue
CompanyName
They're just lucky that nobody actually asks for that.
But wait, there's more. Somebody who calls the VerQueryValueA function expects to have the version string returned as ANSI, so VerQueryValueA needs to know how many characters to convert from Unicode to ANSI. If VerQueryValue trusted the erroneous cbData value, then ANSI callers would get only half the data they were expecting.
VerQueryValueA
As a result of this mess, the VerQueryValue function keeps its eyes open and anticipates that the version resource it was given to parse may have been generated by one of these buggy version resource compilers and goes to some extra effort to accommodate those bugs.