Latest Blog Posts

Hello everybody. Today I am going to summarize my progress on Word 2010 installation so far, concerning the main two problems I did encounter: setting a correct environment block for services, and understanding why the SLInitialize function fails, leading to the failure of the Word 2010 installation. The third problem, namely correctly stopping services, will be addressed in a subsequent report. Reading the two previous weekly reports: "Part 1: Installation", and "Part 2: Installation (cont.)", is strongly advised ;-)

Services environment block

In my first report, section "OSPPSVC service investigation, 1/2", I showed that our services were not started with a correct environment block, and in particular some of them, for example the OSPPSVC service used by Word 2010 installation, needed particular environment variables: ALLUSERSPROFILE, USERPROFILE, CommonProgramFiles and ProgramFiles. These environment variables are not present by default in the system, for example, the environment block for programs loaded under the SYSTEM account, such as winlogon.exe or services.exe, only have a restricted number of variables, basically the following ones:

while regular applications, as well as services, also have the ones related to ProgramFiles and the per-user ones. The latter are initialized by the ReactOS component "User Environment DLL" (userenv.dll), responsible, amongst other things, for loading and unloading the so-called "user profiles", which consist in the "current user" registry settings, for each different users.

The JIRA report CORE-12414 tracks the commits related to fixing the environment block problem for services. In commit r73431 I added an APITest: advapi32:ServiceEnv, which looks for the existence of the user-related environment variables Inside services' environment blocks. The test of course succeeds on a clean Windows (2k3, Vista+) installation, while it currently fails in ReactOS as seen in this test result (revision 73432). The correct fix, committed by Eric Kohl in revision 73433 on my behalf, consists in using the CreateEnvironmentBlock API to initialize a suitable environment block for the services. You can see here the result of the advapi32:ServiceEnv test (left–r73432: before the fix; right–r73433: after the fix).

The reason of the SLInitialize function failure

In my second report, section "OSPPSVC service investigation 2/2, and MSI installation (cont.)", I showed that another main cause of the Word 2010 installation failure was due to the SLInitialize function. This is a function that is exported by a DLL named OSPPCEXT.DLL (a helper for the Office Software Protection service), and is called by the Word 2010 installation. As I explained it in the previous report, this function is partially crypted, and is decrypted at run-time.

The approach that I have chosen here is to trace all the API calls done by SLInitialize. Following a suggestion by Sylvain Petreolle I decided first to take a trace of the Word 2010 installation under Wine. The reason for this choice was partly driven by the fact that ReactOS uses most of the its user-mode dlls from Wine, in particular msi.dll and some others. The aim is then to compare the calls made during SLInitialize executions, with the ones made by this function under ReactOS. Taking the full list of API calls under Wine is done by running (in a terminal): WINEDEBUG=+relay wine setup.exe, possibly redirecting the output to a file. The command sets a shell environment variable WINEDEBUG to +relay, then starts the setup executable proper under Wine. The environment variable asks Wine to trace the API calls with a special built-in mechanism. This generates at the end a 5GB log file, of which only approximately 450kB of trace is useful and corresponds to the calls made during SLInitialize execution. Performing the trace under ReactOS is actually a bit easier: I place a breakpoint before the call to SLInitialize using WinDbg, as well as other breakpoints inside some critical called functions (using the Wine trace as a guidance).

For the curious reader, here are some excerpts, first under Wine, then under ReactOS. I display two interesting RegCreateKeyExW calls, the second one is the one that makes the installation to actually fail under ReactOS. The first parameter of this function is either an existing registry key handle or one of the special values, amongst which:

Now let's see the call trace in ReactOS, by stepping through with WinDbg: I am stepping inside RegCreateKeyExW and I display first the value of the subkey being created, then the value of a NTSTATUS Status variable from which the return value of RegCreateKeyExW will be derived.

Hence, while the call: RegCreateKeyExW(HKEY_USERS, L"S-1-5-20\\SOFTWARE\\Microsoft\\OfficeSoftwareProtectionPlatform\\Policies\\PayloadOverride\\", ...) succeeds under Wine, it plainly fails under ReactOS. Why?

To elucidate this problem, let us remind that RegCreateKeyExW(HKEY_USERS, ...) opens the registry key "HKEY_USERS" that contains the registry hives for each logged user on the system. On Windows (see first screen-capture), there is usually the default user registry settings inside the .DEFAULT hive, then the settings of the special accounts (see MS KB243330): S-1-5-18 for "Local System" (associated with SYSTEM rights), S-1-5-19 for "NT AUTHORITY\Local Service", and S-1-5-20 for "NT AUTHORITY\Network Service". Then comes the regular users' settings. On ReactOS we do not have the S-1-5-19 and S-1-5-20 accounts loaded yet, because they are unused.

Contents of HKEY_USERS under Windows 2003

Some Windows 2003 services with their default user account assignments

Why does this matter for the Word 2010 installation problem? As seen from the call traces above, the installation code happens to hardcode the registry key to open/create, and in particular hardcodes that it wants to create a subkey inside HKEY_USERS\S-1-5-20 , in other words expects the existence of the S-1-5-20 ("Network Service") account to be loaded. On Windows this expectation is correct because, first, the Office protection service OSPPSVC is loaded under this account, as well as other native Windows services, while on ReactOS, this is not the case: all of our services are loaded under the "Local System" account. No one loaded under "Network Service" means, no S-1-5-20 key loaded under HKEY_USERS, and therefore the installation is going to fail (as well as all other code trying to open/create subkeys under HKEY_USERS\S-1-5-20).

So, the "fix" would be to have some of our services to load under the "Network Service", and the job is done, you would say? Sadly the code needed for services to be started under a different user account than the default "Local System" is not completely implemented yet (as of revision 73438), so more work has to be done to be able to do that properly. Eric Kohl, one of the ReactOS developers, is currently working on this topic. In the meantime, one plausible hack I see would be to unconditionally load the "Network Service" account, to be able to have its registry key available.

Conclusion/criticism: Why does it work on Wine?!

But Why does it (already) work on Wine?! As it turns out, this is a side-effect of their own hacks. The code of their registry implementation (in the Wine server) allows anybody to create direct subkeys under HKEY_USERS (this is forbidden on a real NT implementation, or in ReactOS, since HKEY_USERS is a virtual registry key, under which real registry keys are loaded), subkeys that are only volatile (are not stored anywhere in a file). As a consequence, when the Word 2010 installation attempts to create/open a subkey under HKEY_USERS\S-1-5-20, even when S-1-5-20 does not exist (and it does not in Wine, since they do not have the special accounts implemented), this key is created on the fly, allowing for the installer to continue its normal route and ultimately succeed. However, as soon as the Word 2010 installer quits (and the Wine server exits), these registry keys just plainly vanish. This fact was confirmed by Sylvain Petreolle. As it may seem acceptable for Wine, this hack is not acceptable at all for ReactOS; we need instead a proper implementation.

In this second report I continue my investigations on the Word 2010 installation. Last time we saw that our services (and in particular the "Office Software Protection Platform" OSPPSVC service) were started without a complete environment block, and as a result some needed environment variables were missing, causing e.g. OSPPSVC to fail opening some of its files. We now analyse what happens after my local fix (NOTE: I have not committed the fix yet, as I validate it in my local installation first).

Retesting the installation (hacked way)

After having fixed the environment variables problem, I decided to retry the Word 2010 installation. Sadly it again failed at the same place (MSI action "CAInitSPPTokenStore.x86" failure with error 1603), so I decided to attempt to see what happened if I worked around the failure. I placed breakpoints in our MSI code where the installation error is caught, in order to overwrite the failure error code with a success code (0: ERROR_SUCCESS) to make the installation continue, and thanks to that I was able to finish the installation. This allowed me to confirm that only the MSI action "CAInitSPPTokenStore.x86" actually failed.

Breakpoint in MSI code

Running the installation and stepping in the code

Hitting the error

Forcing the error to be 0 and continuing execution

Installation finished after working around the errors

Here are some additional pictures of Word 2010 installed in ReactOS:

Word 2010 files

Word 2010 shortcuts

However, trying to run Word in ReactOS triggers again the installer, which attempts to perform extra configuration steps. This does not happen on a successful installation in Win2k3. This is certainly due to the fact that the OSPPSVC was not able to initialize correctly itself and I was forced to manually skip its configuration step at installation time.

Configuration while starting Word 2010

Word installation asks for a restart

Word cannot verify its own license

Translation: "Microsoft Office cannot verify the license for this product. You should repair the Office installation from within the Configuration Panel"

So far, the installation succeeded just because I overwrote the error codes due to the MSI action "CAInitSPPTokenStore.x86" that was failing, with a success error code. While this procedure allows me to confirm that only this step in the installation process fails and everything else seems to work so far, the procedure does not shed light on why the MSI action fails in the first place.

OSPPSVC service investigation 2/2, and MSI installation (cont.)

With advice from Sylvain Petreolle, I searched through the temporary generated MSI files which one(s) referred to the "CAInitSPPTokenStore" action. (Note: during installation the MSI framework creates temporary PE files corresponding to the different MSI actions performed during the installation, that can be rolled back in case the user cancelled the installation, or an error occurred during the process. The MSI action itself shows as an exported function, that is subsequently called by the MSI framework.) I then analysed what was done inside this action, and noticed that it first just stopped the OSPPSVC service, with some code following a pattern similar to that described in the JIRA report CORE-12413, and then performed an external DLL API call, then restarts OSPPSVC. I therefore made a first hypothesis that something should went wrong when we try stopping / querying a service status. While this problem actually really happens when the service is started, then stopped manually, this generally does not appear to be the case during a clean Office installation because the service is initially stopped. So the problem should arise after. The observed problem looks similar as (but the causes certainly are different than) what is described at this link (MS Office forum).

Performing a step-by-step debugging inside this MSI action let me allowed to find the second and crucial problem. After the OSPPSVC service is stopped the MSI action calls the SLInitialize function that is exported by a DLL named OSPPCEXT.DLL (a helper for the Office Software Protection service). This function, which sadly is partially crypted (and is decrypted at run-time: this is recognizable by the fact that its first bytes are constituted of valid code that ends with a call to a subfunction, and the following bytes are seemingless data; the subfunction performs some operations using the CPU instruction pointer, and returns as a regular function, to its caller). The SLInitialize function returns with an error code that makes the MSI action fail.

Now my job for the next days will consist in understanding enough of SLInitialize and what it really does and why it fails. I then hope that I will then be able to fix what is wrong and have a working Word 2010 installation! But that's for next week! (8^D