Valhalla Legends Forums Archive | General Programming | GetFileVersionInfo

AuthorMessageTime
iago
Does anybody have any details on how GetFileVersionInfo works?  I'd like to have an implementation in Java, it would save me some work in other areas, but I can't seem to find any details on how it's implemented. 

I don't care what language, as long as it's not making the Win32 call GetFileVersionInfo :)
January 23, 2006, 5:57 PM
K
Take a look at the WINE implementation.

You can find the meat in GetFileVersioInfoW here
January 23, 2006, 6:55 PM
Skywing
[quote author=iago link=topic=13993.msg142866#msg142866 date=1138039031]
Does anybody have any details on how GetFileVersionInfo works?  I'd like to have an implementation in Java, it would save me some work in other areas, but I can't seem to find any details on how it's implemented. 

I don't care what language, as long as it's not making the Win32 call GetFileVersionInfo :)
[/quote]
Yes.  You need to implement an NE/PE/PE+ parser (depending on which image formats that you care about).  PE/PE+ share a common resource format that is fairly well documented; I don't know much for sure about NE though, except that the file format is a mess.  Fortunately, being for Win16, nothing much uses it anymore so you may not care about it.

Anyways, the basic idea is to navigate to the resource directory in the PE image and pull out the resource structure you care about.

This may be useful to you: http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx

As far as I know this only covers PE and not PE+, but PE+ is fairly similar.  You can probably figure out the relevant differences from the PE documentation linked above and the differing structures in winnt.h from the Platform SDK for PE and PE+.
January 23, 2006, 8:15 PM
iago
Excellent, thanks to you both! 

Just to be clear, I need this for Battle.net logins.  It'll be nice to be able to query the file instead of manually updating the file version. 
January 24, 2006, 12:30 AM
Skywing
You might consider just using the Mac version check files.  It is much easier to extract the version information from those than the PC ones if you aren't on Windows.
January 24, 2006, 4:57 PM
iago
Hmm, good point.  Unfortunately, it's harder to get updated Mac files when the version changes. 

Didn't I read somewhere that Mac just sends 0x0000000 for its version hash?
January 24, 2006, 11:37 PM
UserLoser
[quote author=iago link=topic=13993.msg142997#msg142997 date=1138145863]
Hmm, good point.  Unfortunately, it's harder to get updated Mac files when the version changes. 

Didn't I read somewhere that Mac just sends 0x0000000 for its version hash?
[/quote]

I think you did, and I believe that is true.
January 24, 2006, 11:46 PM
Skywing
[quote author=iago link=topic=13993.msg142997#msg142997 date=1138145863]
Hmm, good point.  Unfortunately, it's harder to get updated Mac files when the version changes. 

Didn't I read somewhere that Mac just sends 0x0000000 for its version hash?
[/quote]
1. The Mac patching system is for the most part compatible with the Win32 one.  There are some slight differences in the way they are packaged (as an mpq embedded in a self-executing patch just like the standalone Win32 patches) but it is otherwise compatible.  Assuming you have written a compatible patcher it should be fairly easy to modify it to work with the Mac patches.  Alternatively, you could probably rewrite patch.lst slightly and tell bnupdate to work with the Mac patch MPQ for you (perhaps under WINE).

2. No, Mac versions of the client do not send a zero ulong for that field.  At one time you could send any value for the version field to Blizzard Battle.net and it would accept it, but as far as I know, this is no longer the case.  The Mac clients use something compatible with CheckRevision, except that it uses a much simpler structure at the end of the file to retrieve the version number and datestamp.
January 25, 2006, 3:28 AM
iago
I was playing around with 2 different PE Parsers.  On both of them, I could get Diablo2's version information, but I couldn't get Starcraft, War3, or War2 (they are 0x00).  Does anybody know what's different about Diablo 2's format that could make it work?  My first thought was that Diablo 2 either is or isn't PE+?

The fields I'm looking at are MajorImageVersion and MinorImageVersion in the "NT Additional Fields" section.  Are those the proper fields? 

I might just have to implement it from the specifications, we'll see :)

Thanks

<edit> I notice that the "Resources" section has some version information in it.  I'm guessing that that's what I should look for, because even commercial PE browsers say that Major and MinorImageVersion are 0x00. 

<edit2> Nope, all the files are PE, not PE+.  Guess they just compiled Diablo2's files differently. 
January 28, 2006, 6:32 PM
iago
Ok, this is driving me crazy.  I wrote a parser for PE headers, and I'm almost done.  At the very last step, while reading the resources table, you end up with a "Resource Data Entry" structure (section 6.8.4 in the guide Skywing posted). 

Here is what I get (the "Leaf" is the type, name, and language):

Leaf: [1, 3, 1033]
  address = 002d3d18, size = 02ec
Leaf: [3, 1, 1033]
  address = 002d32b0, size = 0128
Leaf: [3, 2, 1033]
  address = 002d33d8, size = 02e8
Leaf: [5, 106, 1033]
  address = 002d3a28, size = 02ec
Leaf: [9, 113, 1033]
  address = 002d4430, size = 0050
Leaf: [14, 102, 1033]
  address = 002d36c0, size = 0022
Leaf: [16, 1, 1033]
  address = 002d36e8, size = 033c

The last one (16) is the version.  However, the address that it gives in the entries is a RVA, it's not a pointer to within a file.  Is there any way to convert the RVA to an address in the file?
January 29, 2006, 4:03 AM
Skywing
Look at the IMAGE_SECTION_HEADER for the section the resource directory data is contained in - between VirtualAddress and PointerToRawData I think you should be able to calculate it.
January 29, 2006, 6:04 AM
iago
Yep, you're absolutely right, I just figured it out.  Now I just have to parse the resource, but that looks easy enough. 

Here's the snippit of code I used to get the virtual address:

[code] int virtualLength = file.getInt(ptrSectionTable + (i * 40) + 8);
int virtualStart = file.getInt(ptrSectionTable + (i * 40) + 12);
int sectionLength = file.getInt(ptrSectionTable + (i * 40) + 16);
int sectionStart = file.getInt(ptrSectionTable + (i * 40) + 20);

System.out.println(String.format(
"Section %s [%d]; virtual is from %08x to %08x [%08x]; raw is from %08x to %08x [%08x]",
name.toString(), i, virtualStart, virtualStart + virtualLength, virtualLength, sectionStart, sectionStart + sectionLength, sectionLength));

if(file.getLong(ptrSectionTable + (i * 40)) == rsrc)
{
int rsrcVirtualToRaw = sectionStart - virtualStart;
ptrRsrcInfo = ptrSectionTable + (i * 40);
parseRSRC(file, file.getInt(ptrRsrcInfo + 20), file.getInt(ptrRsrcInfo + 16),
rsrcVirtualToRaw);
}
[/code]

It feels messy, but it seems to work. 

Thanks for the help!  I've learned more about PE files today than I ever thought I'd know.  I'm glad I decided to do this.

[Skywing: Edit: Changed line spacing a bit on the big System.out.println line so it wouldn't break the forum tables quite so badly.]
January 29, 2006, 6:08 AM
Skywing
Sure.

BTW, make sure that you grab the version info from the VS_FIXEDFILEINFO instead of grabbing the Translation\language\ProductVersion, which does not always match.  YobGuls's C CheckRevision implementation incorrectly did the latter originally.
January 29, 2006, 6:13 AM
iago
Hmm, I'm grabbing the RT_VERSION section, then I use the one named "1" and the language "1033".  I actually take the first name and the first language, but all the files I've checked only have one section, so I think that'll work.  Is that the right place to get the information from?

I noticed that I'm getting different results than BNLS for War2 -- BNLS is sending me 01 01 00 01 and in the version info (in every place I could find the version using PE Explorer), it's 02 00 02 00.  Any idea why that's happening?  I haven't had a chance to try it on Battle.net (I haven't integrated this into any bots), but I'm curious if it's going to work.
<edit> I just packetlogged War2, and the real client is sending 00 02 00 02.  So I guess we are working with different files? 

The other 3 products work fine, though.  But I noticed that Warcraft 3 has a weird version, 1.20.2.6065.. although the 6065 is truncated to 177 when it's sent to Battle.net.  Any idea what Blizzard did?  I assume they mistyped something. 

<edit> Here's my output:
[quote]Loading /home/ron/.hashes/STAR/starcraft.exe
01 01 03 0b

Loading /home/ron/.hashes/W2BN/Warcraft II BNE.exe
02 00 02 00

Loading /home/ron/.hashes/WAR3/war3.exe
01 14 02 b1

Loading /home/ron/.hashes/D2DV/Game.exe
01 00 0b 00
[/quote]
January 29, 2006, 6:53 AM
Skywing
Looks like BNLS has actually been using an older version of the War2BNE check files for a long time, so either nobody has ever tried War2BNE with BNLS or Battle.net doesn't bother to check the version/checksum for that product anymore if the version code is correct.

If you get a chance, let me know if the values BNLS sends are actually good on Battle.net - if not, I'll fix it.

As for War3, if you look at how CheckRevision is implemented...:

[code].text:10002A72                 call    VerQueryValueA
.text:10002A77                 test    eax, eax
.text:10002A79                 jz      short loc_10002AAF
.text:10002A7B                 mov     eax, [ebp+lpBuffer]
.text:10002A7E                 cmp     eax, ebx
.text:10002A80                 jz      short loc_10002AAF
.text:10002A82                 cmp     [ebp+puLen], 34h
.text:10002A86                 jb      short loc_10002AAF
.text:10002A88                 mov     ecx, [eax+VS_FIXEDFILEINFO.dwProductVersionMS]
.text:10002A8B                 xor     edx, edx
.text:10002A8D                 shr     ecx, 10h
.text:10002A90                 mov     dh, cl
.text:10002A92                 mov     ecx, [eax+VS_FIXEDFILEINFO.dwProductVersionLS]
.text:10002A95                 mov     dl, byte ptr [eax+VS_FIXEDFILEINFO.dwProductVersionMS]
.text:10002A98                 movzx   eax, byte ptr [eax+VS_FIXEDFILEINFO.dwProductVersionLS]
.text:10002A9C                 shr     ecx, 10h
.text:10002A9F                 movzx   ecx, cl
.text:10002AA2                 shl     edx, 8
.text:10002AA5                 or      edx, ecx
.text:10002AA7                 shl     edx, 8
.text:10002AAA                 or      edx, eax
.text:10002AAC                 mov     [ebp+A], edx
[/code]

It does something like this:

[code]
unsigned char* buf; // allocated above
VS_FIXEDFILEINFO* info;
unsigned long size;
DWORD A;

if(VerQueryValueA(buf, "\\", (void**)&info, &size) && info && size >= sizeof(VS_FIXEDFILEINFO))
{
  A = ((info->dwProductVersionMS >> 16) << 8) & 0x000000FF;
  A |= (info->dwProductVersionMS) & 0x000000FF;
  A <<= 8;
  A |= (info->dwProductVersionLS >> 16) & 0x000000FF;
  A <<= 8;
  A |= (info->dwProductVersionLS) & 0x000000FF;
}
[/code]

or perhaps cleaned up, the middle bit might look more like this:

[code]
DWORD A;

A = (info->dwProductVersionMS <<  8) & 0xFF000000 |
    (info->dwProductVersionMS << 16) & 0x00FF0000 |
    (info->dwProductVersionLS >>  8) & 0x0000FF00 |
    (info->dwProductVersionLS      ) & 00000000FF
    ;
[/code]
January 29, 2006, 3:56 PM
iago
[quote author=Skywing link=topic=13993.msg143660#msg143660 date=1138550207]
Looks like BNLS has actually been using an older version of the War2BNE check files for a long time, so either nobody has ever tried War2BNE with BNLS or Battle.net doesn't bother to check the version/checksum for that product anymore if the version code is correct.

If you get a chance, let me know if the values BNLS sends are actually good on Battle.net - if not, I'll fix it.

As for War3, if you look at how CheckRevision is implemented...:

[code].text:10002A72                call    VerQueryValueA
.text:10002A77                test    eax, eax
.text:10002A79                jz      short loc_10002AAF
.text:10002A7B                mov    eax, [ebp+lpBuffer]
.text:10002A7E                cmp    eax, ebx
.text:10002A80                jz      short loc_10002AAF
.text:10002A82                cmp    [ebp+puLen], 34h
.text:10002A86                jb      short loc_10002AAF
.text:10002A88                mov    ecx, [eax+VS_FIXEDFILEINFO.dwProductVersionMS]
.text:10002A8B                xor    edx, edx
.text:10002A8D                shr    ecx, 10h
.text:10002A90                mov    dh, cl
.text:10002A92                mov    ecx, [eax+VS_FIXEDFILEINFO.dwProductVersionLS]
.text:10002A95                mov    dl, byte ptr [eax+VS_FIXEDFILEINFO.dwProductVersionMS]
.text:10002A98                movzx  eax, byte ptr [eax+VS_FIXEDFILEINFO.dwProductVersionLS]
.text:10002A9C                shr    ecx, 10h
.text:10002A9F                movzx  ecx, cl
.text:10002AA2                shl    edx, 8
.text:10002AA5                or      edx, ecx
.text:10002AA7                shl    edx, 8
.text:10002AAA                or      edx, eax
.text:10002AAC                mov    [ebp+A], edx
[/code]

It does something like this:

[code]
unsigned char* buf; // allocated above
VS_FIXEDFILEINFO* info;
unsigned long size;
DWORD A;

if(VerQueryValueA(buf, "\\", (void**)&info, &size) && info && size >= sizeof(VS_FIXEDFILEINFO))
{
  A = ((info->dwProductVersionMS >> 16) << 8) & 0x000000FF;
  A |= (info->dwProductVersionMS) & 0x000000FF;
  A <<= 8;
  A |= (info->dwProductVersionLS >> 16) & 0x000000FF;
  A <<= 8;
  A |= (info->dwProductVersionLS) & 0x000000FF;
}
[/code]

or perhaps cleaned up, the middle bit might look more like this:

[code]
DWORD A;

A = (info->dwProductVersionMS <<  8) & 0xFF000000 |
    (info->dwProductVersionMS << 16) & 0x00FF0000 |
    (info->dwProductVersionLS >>  8) & 0x0000FF00 |
    (info->dwProductVersionLS      ) & 00000000FF
    ;
[/code]
[/quote]

Sure, I can test out BNLS.  I haven't hooked up versioning to the rest of my bot, though, so it'll take some time. 
January 29, 2006, 4:22 PM
iago
Whatever you're currently using on BNLS is working, but I suspect that you updated because I'm getting different information back today:

ix86 filename: IX86ver0.mpq
Value string: A=578106926 B=903338692 C=439132539 4 A=A+S B=B+C C=C^A A=A-B
** -> bnls: Warcraft II BNE.exe 11/10/01 17:10:00 712704, 0x02000200, 0xd4b47564

January 29, 2006, 9:58 PM
Skywing
[quote author=iago link=topic=13993.msg143710#msg143710 date=1138571895]
Whatever you're currently using on BNLS is working, but I suspect that you updated because I'm getting different information back today:

ix86 filename: IX86ver0.mpq
Value string: A=578106926 B=903338692 C=439132539 4 A=A+S B=B+C C=C^A A=A-B
** -> bnls: Warcraft II BNE.exe 11/10/01 17:10:00 712704, 0x02000200, 0xd4b47564


[/quote]
No, I haven't altered the version check files since you tested.  W2BN has never had a 1.xx version (for the Battle.net edition anyway) so I assume the first time you must have requested the wrong product or something.  2.02 is not the latest though, the W2BN install I have on this computer has 2.021 (and the exe is not the same as the one on the computer that runs BNLS).  I suppose perhaps 2.021 is an optional upgrade or something (used the standalone patch and not the Battle.net patcher, because W2BN has a bug that makes it incompatible with having NX enabled by default and I was hoping the patch would fix it, which it didn't).
January 29, 2006, 10:17 PM
iago
Hmm, that's weird.  I'm sure I was getting 01 versions from BNLS, which was confusing me.  Maybe I was doing it locally and not realizing it. 

In any case, BNLS is working for War2.
January 29, 2006, 10:25 PM

Search