[PATCH v2 1/3] kernelbase: Implement compatibility mode for GetVersionEx.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Mar 18 09:36:02 CDT 2020


On 18/03/2020 15:52, Nikolay Sivov wrote:
> 
> 
> On 3/18/20 4:09 PM, Gabriel Ivăncescu wrote:
>> On 17/03/2020 19:24, Nikolay Sivov wrote:
>>>
>>>
>>> On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
>>>> On 17/03/2020 18:28, Nikolay Sivov wrote:
>>>>> On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
>>>>>> +/*********************************************************************** 
>>>>>>
>>>>>> + * Win8 info, reported if app doesn't provide compat GUID in 
>>>>>> manifest.
>>>>>> + */
>>>>>> +static const RTL_OSVERSIONINFOEXW windows8_version_data =
>>>>>> +{
>>>>>> +    sizeof(RTL_OSVERSIONINFOEXW), 6, 2, 0x23f0, 
>>>>>> VER_PLATFORM_WIN32_NT,
>>>>>> +    {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0
>>>>>> +};
>>>>>> +
>>>>>> +
>>>>>> +/*********************************************************************** 
>>>>>>
>>>>>> + * Windows versions that need compatibility GUID specified in 
>>>>>> manifest
>>>>>> + * in order to be reported by the APIs.
>>>>>> + */
>>>>>> +static const struct
>>>>>> +{
>>>>>> +    RTL_OSVERSIONINFOEXW info;
>>>>>> +    GUID guid;
>>>>>> +} version_data[] =
>>>>>> +{
>>>>>> +    /* Windows 8.1 */
>>>>>> +    {
>>>>>> +        {
>>>>>> +            sizeof(RTL_OSVERSIONINFOEXW), 6, 3, 0x2580, 
>>>>>> VER_PLATFORM_WIN32_NT,
>>>>>> +            {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0
>>>>>> +        },
>>>>>> + 
>>>>>> {0x1f676c76,0x80e1,0x4239,{0x95,0xbb,0x83,0xd0,0xf6,0xd0,0xda,0x78}}
>>>>>> +    },
>>>>>> +    /* Windows 10 */
>>>>>> +    {
>>>>>> +        {
>>>>>> +            sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 0x42ee, 
>>>>>> VER_PLATFORM_WIN32_NT,
>>>>>> +            {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0
>>>>>> +        },
>>>>>> + 
>>>>>> {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}}
>>>>>> +    }
>>>>>> +};
>>>>> For that you only need to store 3 values - major/minor/build.
>>>>
>>>> Ah alright, I just copy-pasted the entires from ntdll.
>>>>
>>>>>> +/*********************************************************************** 
>>>>>>
>>>>>> + * Holds the current version (including compatibility mode).
>>>>>> + * Call init_current_version before using it.
>>>>>> + */
>>>>>> +static RTL_OSVERSIONINFOEXW current_version;
>>>>>> +
>>>>>> +
>>>>>> +/****************************************************************************** 
>>>>>>
>>>>>> + *  init_current_version
>>>>>> + *
>>>>>> + * Initialize the current_version variable.
>>>>>> + *
>>>>>> + * For compatibility, Windows 8.1 and later report Win8 version 
>>>>>> unless the app
>>>>>> + * has a manifest that confirms its compatibility with newer 
>>>>>> versions of Windows.
>>>>>> + *
>>>>>> + */
>>>>>> +static BOOL CALLBACK init_current_version_callback(PINIT_ONCE 
>>>>>> init_once, PVOID parameter, PVOID *context)
>>>>>> +{
>>>>> Should it actually be static and initialized once? What happens if 
>>>>> you activate another context dynamically?
>>>>>
>>>>
>>>> I actually have no idea, didn't know you can do that. I'm somewhat 
>>>> unfamiliar with activation context APIs. What API should I be using 
>>>> to test this?
>>>
>>> There are functions to create, activate and deactivate contexts. We 
>>> use that in tests for example.
>>>
>>
>> So, I tested with the ACTCTX_FLAG_SET_PROCESS_DEFAULT flag for 
>> CreateActCtx, followed by ActivateActCtx, at the beginning of the test.
>>
>> Just to make sure, I retrieved the GUIDs with 
>> CompatibilityInformationInActivationContext and they seemed in order, 
>> so the manifest was loaded correctly and the activation context 
>> pushed. i.e. it returned the same GUIDs as if the app itself had the 
>> manifest, without pushing it manually on the stack.
>>
>> However, GetVersionEx reported Windows 8, as if no manifest. So I 
>> suspect it gets cached at startup somewhere... and any further 
>> activation context changes won't matter.
>>
>> Any idea of a good place to do that? It must be a point where the 
>> default activation context become available but application code 
>> hasn't run yet. Would DllMain for kernelbase be a good place to cache it?
> 
> I'm not sure. It makes sense I guess that user context can't change 
> version set with application default context, so version is consistent.
> 
> I don't know if it's safe to assume that kernelbase is always loaded 
> early enough, if for example it's possible to start something that does 
> not load kernel32/kernelbase. But initializing on first GetVersion() 
> using current context is too late,
> according to your testing results. Maybe default context could (or 
> should) be stashed in PEB somewhere, being accessible at all times.
> 

I can't find a pointer-sized reserved/unknown field where to place the 
default context, they're all LONGs, and if I placed a pointer it would 
mess up the offsets for 64-bit. Do you have some suggestion?

Though, I could use an internal __wine_* export but I guess that's kind 
of a last resort right?



More information about the wine-devel mailing list