Tinkering with Tinker: How Vista Ultimate is detected
There seems to be a misconception, in the normal user population, that Windows Vista, Ultimate Edition, provides some sort of extra kick that Ultimate Extras need to run properly. This isn’t the case. In fact, the requirement is purely arbitrary, from a technical standpoint.
While Microsoft partner Oberon Games took the more officially-undocumented SLGetWindowsInformationDWORD route to determine whether or not to run (Hold’Em), Fuel Games took the more traditional route.

Upon cursory glance of the Tinker executable, I noticed it imported the GetVersionEx and GetProductInfo APIs from kernel32.dll. For those playing at home, GetProductInfo wasn’t explicitly imported but rather done at runtime using LoadLibrary/GetProcAddress. Why? I’m not sure.
It’s easy to rule out GetVersionEx because, as documentation shows, it does not provide enough information to make a determination as to which SKU of Windows you’re running. GetProductInfo, on the other hand, does and was specifically engineered for this task. We’ll see its use in a second.

I fired up WinDbg and attached it to Tinker.exe (File—>Open Executable), set a breakpoint on GetProductInfo (bp GetProductInfo) and ran the game (g).

After a few seconds of runtime, my breakpoint was hit and the debugger shot us into the first instruction within the function.

I stepped out (gu) of the GetProductInfo function as its inner workings are of no interest to me today and got a higher level picture of what this program is doing. Let’s take this apart, piece by piece.
00000000`00f71787 push 11Ch 00000000`00f7178c lea eax,[ebp-744h] 00000000`00f71792 push ebx 00000000`00f71793 push eax 00000000`00f71794 call Tinker!memset (00f74cb2) 00000000`00f71799 add esp,0Ch 00000000`00f7179c lea eax,[ebp-744h] 00000000`00f717a2 push eax 00000000`00f717a3 mov dword ptr [ebp-744h],11Ch 00000000`00f717ad call dword ptr [Tinker!_imp__GetVersionExW (00f5107c)]
OSVERSIONINFOEX osvi; memset(&osvi,0,sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionExW(&osvi);
As the GetProductInfo function requires a bunch of version numbers to be passed, the game first calls GetVersionEx, passing a pointer to a chunk of zeroized memory to receive an OSVERSIONINFOEX block of information.
00000000`00f717b3 lea eax,[ebp-760h] 00000000`00f717b9 push eax 00000000`00f717ba movzx eax,word ptr [ebp-62Eh] 00000000`00f717c1 push eax 00000000`00f717c2 movzx eax,word ptr [ebp-630h] 00000000`00f717c9 push eax 00000000`00f717ca push dword ptr [ebp-73Ch] 00000000`00f717d0 push dword ptr [ebp-740h] 00000000`00f717d6 call dword ptr [ebp-754h]
DWORD dwProductType = 0; BOOL bRet = GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, 1, 0,&dwProductType);
Now having the platform’s major and minor build/service pack numbers, all this information is passed neatly to the GetProductInfo function. As MSDN states, a non-zero number is returned if everything checks out (true) otherwise a zero is returned (false). That function will also write which SKU we’re running into a DWORD variable passed in by reference.
00000000`00f717dc cmp dword ptr [ebp-740h],6 ss:002b:00000000`001cee84=00000006 00000000`00f717e3 jne Tinker!SparkEngine::Init+0x1b1 (00f717ed) 00000000`00f717e5 cmp dword ptr [ebp-73Ch],ebx 00000000`00f717eb je Tinker!SparkEngine::Init+0x1b7 (00f717f3) 00000000`00f717ed mov byte ptr [ebp-745h],bl 00000000`00f717f3 cmp dword ptr [ebp-760h],1 00000000`00f717fa je Tinker!SparkEngine::Init+0x1cd (00f71809) 00000000`00f717fc cmp dword ptr [ebp-760h],1Ch 00000000`00f71803 jne Tinker!SparkEngine::Init+0x415 (00f71a51) 00000000`00f71809 cmp byte ptr [ebp-745h],bl 00000000`00f7180f je Tinker!SparkEngine::Init+0x415 (00f71a51)
if(osvi.dwMajorVersion != 6 && osvi.dwMinorVersion != 0)
bSupported = bRet;
if(bSupported && (dwProductType == PRODUCT_ULTIMATE || dwProductType == PRODUCT_ULTIMATE_N)) {
/* Spin up game, supported operating system! :) */
} else {
/* Spin down game, unsupported operating system :( */
}
This confusing block of assembly is the meat of the check. It handles the checking of the SKU output from GetProductInfo. Note the smart check for Ultimate N SKUs — I would’ve totally forgotten about “them”. I wonder if this covers the Ultimate K SKU… (Korea)
That’s it.
Although this SKU check is far from complex, I’m curious as to why Tinker wasn’t tied to the Microsoft-Windows-Shell-PremiumInboxGames licensing ‘feature’ like Texas Hold’Em. A simple call to SLGetWindowsInformationDWORD would have sufficied…
Note to Fuel Games: Thanks to Known DLLs, you don’t need to ascertain the full path to kernel32.dll and do a bunch of string concatenation trickery. Simply use: LoadLibrary(_T(”kernel32″));

Some people like to do things the hard way I guess? The overly hard way…
Nobody would complain about Vista Ultimate if Microsoft had not promised Ultimate Extras. All MS has to do is not promise anything and they will be good for Windows 7 Ultimate.
Good stuff. How about “documenting” the strings for SLGetWindowsInformationDWORD next? ;)