Archive: Vista/7 Registry VirtualStore


Vista/7 Registry VirtualStore
I'm dealing with a legacy application (1997), so all it knows about is Win95 and very lax rules about where it can write in the registry. It writes to HKEY_LOCAL_MACHINE\Software (of course), which really isn't a problem thanks to Vista/7 virtualizing the writes and reads to a user-based virtualized registry (specifically for HKLM\Software).

I need NSIS to be able to interact with this virtualized registry to manage settings maintenance, the problem being that Vista/7 seems to really not want to virtualize NSIS's use of the registry. Looking up Microsoft's documentation on this, right off the bat, it's disabling registry virtualization because my NSIS-based program has a ''requestedExecutionLevel'' entry in its manifest. That's easy enough to deal with, however, it seems that Vista/7 still won't let an NSIS-based program (with a manifest, but no requestedExecutionLevel entry) at the virtualized registry.

From what I read about installer auto-detection, as long as there's a manifest of any sort, it shouldn't be triggered, but just to be safe, I turned off NSIS's CRC check and went through the executable with a hex editor and replaced any strings that were installer or NSIS related, with the exception of the NullsoftInst string. This didn't help.

I REALLY would prefer not to directly access the virtualized entries by accessing them in VirtualStore, as I'd have to add a fun layer of Windows version checking to change behavior, and this would also leave forwards-compatibility up in the air (if a future version of Windows drops registry virtualization). I'd prefer it if my NSIS-based program can simply interact with the registry the exact same way the legacy application it's assisting does.

Quib


did you also remove the supportedOS tag in the manifest?


Yup, and tried various ways of stripping out other values, for example, leaving an empty security tag. I also tried leaving an empty manifest (instead of removing it entirely).

I spent probably a good hour messing with every conceivable permutation and mutation of the manifest, and am thinking it doesn't hold a solution, but there could certainly still be some odd entry that can be added that'd do what I want.

I'm nearly done with an NSIS reg file parser that handles them identically to the way regedit does (I haven't worked out all the nuances of regedit's handling of binary value imports yet) that would let me check for incoming HKLM\Software values and change them to their corresponding VirtualStore locations, but it's a really ugly workaround compared to just having NSIS's registry calls virtualized.

Quib


Virtualization really means that the registry read/writes are being redirected to a different part of the registry (which should be accessible to your installer). There is no need to impersonate a virtualized application. In the case of reads/writes to HKLM, substitute the following "HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE". To use an example of legacy program on my own computer:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Money

is redirected to

HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\SOFTWARE\Microsoft\Money

Your installer should be able to read and write to this branch. Bear in mind that you are in HKCU. Accordingly, any changes are on a per user base. In addition, if your user used the "runas" feature to run the installer as somebody else, then you will be in somebody else's HKCU. My guess is that this will not be a common case. Finally, remember that a standard account can read from HKLM, even though it cannot write there. You may wish to check MSDN on the rules of precedence when the same key exists both in HKLM and HKCU. This might happen if your installer writes to HKLM.

I hope this helps.


Well, I agree that there's no ''need'' for my registry calls to be virtualized since the redirected location IS accessible, however, it would certainly have made things simpler. Here's the solution I decided on (and implemented):
1) Check Windows' major number.
2) If 6 (Vista/7), check if admin. If not 6, assume admin (for virtualization purposes).
3) If not admin, operation will occur on the VirtualStore location instead of HKLM.
4) Parse a REG file containing the program's settings. Dynamically alter key location based on the preceding two checks. If the REG file's data is saved pointing at the VirtualStore, it needs to write to HKLM instead on non-Vista/7 system, or on Vista/7 if running elevated.
5) Run the legacy program and wait for its termination.
6) Copy settings from the correct registry location to a REG file for backup.

Now, imagine if the NSIS-based program's registry calls were simply virtualized? That whole ordeal would've been as simple as import REG file, run program and wait, export REG file.

Step 4 was a huge pain in the butt; I put together a REG file parser that mimics regedit.exe's handling of REG files as closely as possible. All the nuances about comments and whitespace, its incredibly forgiving handling of key names, but unforgiving handling of values, its 4 escape characters (but only in values, not in key names).

Quib


I suggest you don't check windows versions manually. Use winver.nsh instead.


But WinVer adds so much bloat; I don't use LogicLib either. I'm using the following to check Windows' major version number:
System::Alloc 148
Pop $0
System::Call '*$0(i 148)'
System::Call 'kernel32::GetVersionEx(i r0) !e'
System::Call '*$0(i .n,i .s)'
System::Free $0
Pop $0
Where the final result on $0 is the major version number. Isn't this just a very cut down and efficient version of what WinVer is doing? For my check, all I do is compare $0 to "6" and if it matches, that means it's Vista/7.

Quib


I am not sure I understand what you are trying to do. Nonetheless, the following observations may be helpful.

1. In my experience, Windows 7 will not automatically elevate if you use:

RequestExecutionLevel user

This is explained in the NSIS on-line help and works for me in both Vista and Windows 7

2. An application can pretend it is virtualized by reading and writing directly to HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\SOFTWARE. To use the same code for both instances (elevated and non-elevated) you would use SetShellVarContext and a variable for beginning of the subkey. The variable would be "" for addressing HKLM and would be "Software\Classes\VirtualStore\MACHINE\" for addressing the VirtualStore. For example, the following will address HKLM:

SetShellVarContext all
Strcpy $R0 ""
ReadRegStr $0 shctx "$R0SOFTWARE\MyProgram" "Path"

and the following will address the VirtualStore

SetShellVarContext current
Strcpy $R0 "Software\Classes\VirtualStore\MACHINE\"
ReadRegStr $0 shctx "$R0SOFTWARE\MyProgram" "Path"

Notice that in both cases you are using the same code for ReadRegStr.

3. Your follow-up message mentioned that you are running the legacy app from the installer. Bear in mind that if the installer is elevated the legacy app will also be running with elevated privileges and will be writing directly to HKLM.


LogicLib and WinVer have a minimal overhead of code size. Most of its logic is done with macros at compile time. LogicLib's overhead is almost non-existent compared regular labels and comparison yet it allows you to code freely without worrying of relative jumps and labels.

I want to insist on making this point so new users and hopefully everybody will use both as much as possible to make scripts simpler and more coherent and improve overall NSIS experience. So I ask you to back up future claims of bloat with hard data.

As for your question, Vista and probably 7 actually do look for that one string you didn't replace - "NullsoftInst". They also look for the name in the manifest. I would write a little legacy program that does the registry work, or rebuild NSIS with changes in Source\exehead\fileform.h.


I need to apologize and clear something up: I didn't mean to imply that LogicLib was bloated. I was stating that I don't use LogicLib and so that impedes using WinVer.

My choice to not use LogicLib is simply personal preference.

When I said ''WinVer adds so much bloat'' I really just meant in comparison to the solution I was using; all I need is the Windows' major version number, so WinVer's double call to GetVersionEx on 9x, its parsing of the versioninfo structure, and its logical tests (etc.) are all extra features I don't need (and so are bloat from the perspective of this program).

podnuh, something to consider is that I'm not actually using NSIS as an installer; I'm using it as a scripting language to automate settings backup for a legacy program. I do appreciate the feedback though, and I hadn't considered your approach (using SetShellVarContext) to handle which part of the registry I work with. I am fully aware of the user privileges situation I face; the problem I encountered seems to have more to do with Vista/7 installer detection than anything else.

kichik, I had considered writing this utility in a standard programming language (or even another scripting language), but really appreciate NSIS's string handling capabilities (and that it doesn't get flagged as malware as frequently as say AutoIt3) and the short development time provided by such a scripting language. I mean, this utility's been up and running for a couple days now since developing a work-around was a breeze (thanks mostly to NSIS's string handling). I would like to continue this discussion for academic reasons (such as nailing down a way for NSIS to go through Vista/7's virtualization). I do figure Vista/7 is checking for ''NullsoftInst'' but changing that with a hex editor breaks the NSIS-based program and I've yet to set up a NSIS build environment.

Quib


NSIS looks for that string so just changing it won't be enough. You need to rebuild or change the function that looks for it too. Search for:

81 7D E8 49 6E 73 74
75 60
81 7D E4 73 6F 66 74
75 57
81 7D E0 4E 75 6C 6C
75 4E
Notice the last four bytes are "Null", "soft" and "Inst".

As for the bloatage, it's your call as to what fits in your program. But I think making the code more complex that it can be and not delegating the maintenance work just to save the extra 50 or so opcodes which get compressed to way less than a KB, doesn't serve you. Not that I have not done it myself many times in the past...

Thanks for pointing me in the right direction kichik.

So here are the three things needed to get an NSIS-based program to have its HKLM\SOFTWARE registry calls virtualized:
1) The string ''NullsoftInst'' needs to be changed (and its corresponding validation check; search for ''Null'', ''soft'', and ''Inst'').
2) In the manifest, the string ''Nullsoft.NSIS.exehead'' needs to be changed.
3) The manifest must NOT contain a requestedExecutionLevel entry (but can contain any other trustInfo entries).

I only have tested this on Vista so far (not Windows 7).

Quib