 |
|
 |
Hi Andrei,
First, thank you so much for your work with this! 
I'm using these DLL's for my Setup program which is used by a number of people. However, I just discovered that specifying a working directory is not working. This is important for many programs run at the end of Setup/install because they are looking for files relative to the program, etc. I've looked at the source and see that the problem is that the vistalib dll's don't parse the command-line for working directory....they stop right after looking for arguments. But yet, it looks like the functionality is there in the underlying vistatools.cxx file.
Sadly, I am not really a C++ programmer (and even all I have is VC6, which doesn't seem to work), so I can't really change this. It looks like a really easy change though - just an extra parsing layer on the command-line if I'm not mistaken. Is this something you can quickly just change and either repost here or email to me?
It would be greatly appreciated (by me, and many I'm sure).
Chris
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hello
Thanks for posting this link. You are right: You can do it without DLLs - much easier.
But the code on the page you link to is very ugly and has SIX bugs!
I completely cleaned the code and fixed all the bugs. I tested it on Windows 2000, XP, Vista and Windows 7. It works perfectly.
If you compile and run this code "As Administrator" it will start the Calculator process under the account of the currently logged-in user. In Taskmanager you will see the new Calculator running under the same user account as Explorer. This is important in an installer because the new process must access the correct "Documents and Settings" folder.
Elmü
#include "stdafx.h"
#include "windows.h"
#ifndef SECURITY_MANDATORY_HIGH_RID #define SECURITY_MANDATORY_UNTRUSTED_RID (0x00000000L) #define SECURITY_MANDATORY_LOW_RID (0x00001000L) #define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) #define SECURITY_MANDATORY_HIGH_RID (0x00003000L) #define SECURITY_MANDATORY_SYSTEM_RID (0x00004000L) #define SECURITY_MANDATORY_PROTECTED_PROCESS_RID (0x00005000L) #endif
#ifndef TokenIntegrityLevel #define TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25) #endif
#ifndef TOKEN_MANDATORY_LABEL typedef struct { SID_AND_ATTRIBUTES Label; } TOKEN_MANDATORY_LABEL; #endif
typedef BOOL (WINAPI *defCreateProcessWithTokenW) (HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
DWORD GetProcessIL(DWORD u32_PID, DWORD* pu32_ProcessIL) { *pu32_ProcessIL = 0; HANDLE h_Process = 0; HANDLE h_Token = 0; DWORD u32_Size = 0; BYTE* pu8_Count = 0; DWORD* pu32_ProcIL = 0; TOKEN_MANDATORY_LABEL* pk_Label = 0;
h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_PID); if (!h_Process) goto _CleanUp;
if (!OpenProcessToken(h_Process, TOKEN_QUERY, &h_Token)) goto _CleanUp; if (!GetTokenInformation(h_Token, TokenIntegrityLevel, NULL, 0, &u32_Size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto _CleanUp; pk_Label = (TOKEN_MANDATORY_LABEL*) HeapAlloc(GetProcessHeap(), 0, u32_Size); if (!pk_Label) goto _CleanUp;
if (!GetTokenInformation(h_Token, TokenIntegrityLevel, pk_Label, u32_Size, &u32_Size)) goto _CleanUp;
pu8_Count = GetSidSubAuthorityCount(pk_Label->Label.Sid); if (!pu8_Count) goto _CleanUp; pu32_ProcIL = GetSidSubAuthority(pk_Label->Label.Sid, *pu8_Count-1); if (!pu32_ProcIL) goto _CleanUp;
*pu32_ProcessIL = *pu32_ProcIL; SetLastError(ERROR_SUCCESS);
_CleanUp: DWORD u32_Error = GetLastError(); if (pk_Label) HeapFree(GetProcessHeap(), 0, pk_Label); if (h_Token) CloseHandle(h_Token); if (h_Process) CloseHandle(h_Process); return u32_Error; }
DWORD CreateProcessMediumIL(WCHAR* u16_Path, WCHAR* u16_CmdLine) { HANDLE h_Process = 0; HANDLE h_Token = 0; HANDLE h_Token2 = 0; PROCESS_INFORMATION k_ProcInfo = {0}; STARTUPINFOW k_StartupInfo = {0};
BOOL b_UseToken = FALSE;
if (GetProcAddress(GetModuleHandleA("Kernel32"), "GetProductInfo")) { DWORD u32_CurIL; DWORD u32_Err = GetProcessIL(GetCurrentProcessId(), &u32_CurIL); if (u32_Err) return u32_Err;
if (u32_CurIL > SECURITY_MANDATORY_MEDIUM_RID) b_UseToken = TRUE; }
if (!b_UseToken) { if (!CreateProcessW(u16_Path, u16_CmdLine, 0, 0, FALSE, 0, 0, 0, &k_StartupInfo, &k_ProcInfo)) return GetLastError();
return ERROR_SUCCESS; }
defCreateProcessWithTokenW f_CreateProcessWithTokenW = (defCreateProcessWithTokenW) GetProcAddress(GetModuleHandleA("Advapi32"), "CreateProcessWithTokenW");
if (!f_CreateProcessWithTokenW) return ERROR_INVALID_FUNCTION; HWND h_Progman = ::FindWindow("Progman", NULL); if (!h_Progman) return ERROR_INVALID_WINDOW_HANDLE;
DWORD u32_ExplorerPID = 0; GetWindowThreadProcessId(h_Progman, &u32_ExplorerPID);
h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_ExplorerPID); if (!h_Process) goto _CleanUp;
if (!OpenProcessToken(h_Process, TOKEN_DUPLICATE, &h_Token)) goto _CleanUp;
if (!DuplicateTokenEx(h_Token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &h_Token2)) goto _CleanUp;
if (!f_CreateProcessWithTokenW(h_Token2, 0, u16_Path, u16_CmdLine, 0, 0, 0, &k_StartupInfo, &k_ProcInfo)) goto _CleanUp;
SetLastError(ERROR_SUCCESS);
_CleanUp: DWORD u32_Error = GetLastError(); if (h_Token) CloseHandle(h_Token); if (h_Token2) CloseHandle(h_Token2); if (h_Process) CloseHandle(h_Process); return u32_Error; }
int main(int argc, char* argv[]) { DWORD u32_Err = CreateProcessMediumIL(L"C:\\Windows\\System32\\Calc.exe", NULL);
printf("CreateProcessMediumIL() exited with error %d\r\n", u32_Err); Sleep(2000); return 0; }
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
Hi Andrei,
If we ship these 2 dlls in our installer, do we need your written permission ? Thank you.
Regards, Kenny
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
> If we ship these 2 dlls in our installer, do we need your written permission ? Thank you.
Kenny,
you (or anyone else) don't need a written permission, the libraries are free to use and redistribute, as long as you agree that they come with no warranties, yadda, yadda. (You can find the terms of use in the Readme.txt file included in the source code of the library).
Thanks for asking though.
Good luck!
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Andrei,
I am having problem with the RunNonElevated (VE32.dll) method in order to start a process on Vista.
I am having a NSIS installer which is installing an executable file and the calls RunNonElevated in order to run the installed program.
That works all fine on XP (administrator/standard user rights) and on Vista with administrator rights. But when I try to install it on Vista from a standard user account it does not work.
When I start the installer Vista pops up a window to put in credentials of an administrator with user name and password. After entering the information the NSIS installer runs, copies the executable file to the program files folder and then it calls RunNonElevated from the VE32.dll in order to start the program.
After I debugged RunNonElevated it looks like everything works as supposed to be but the hook "VistaElevator_HookProc_MsgRet" is never receiving the message "VistaElevatorMsg".
Would you have an idea what is going wrong or why the hook is never receiving the message.
It would be great if you could give me some help.
Thanks Mike
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Mike,
If I remember correctly, when I tested this code while writing the article, it used to work with the standard users.
Are you sure you are using the correct DLL? Because you mentioned VE32.DLL, which is form another article of mine. This article comes with a dll named VistaLib32.dll, you may want to try it instead of VE32.DLL.
Hope this helps.
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Why are people so offended by this idea? If you haven't installed Windows Vista yet, or you haven't attempted to solve the problem on your own, then you probably shouldn't criticize.
The solution is pretty good actually, because it is low overhead, and the complications with FindWindow (that everyone seems to focusing on) are not the main point of the article. It is fixed easily by (A) finding a different window via heuristic or (B) using another method to find a non-administrative process.
|
| Sign In·View Thread·PermaLink | 3.00/5 (2 votes) |
|
|
|
 |
|
 |
I have successfully integrated your pre-built VistaLib32 and VistaLib64 DLLs into our Inno Setup-based installer (completely painlessly thanks to your detailed instructions may I add), and it does work in that it launches the app non-elevated after installation is finished on both 32- and 64-bit Vistas. So far so good.
However, on 32-bit versions of Vista, the app is launched twice most of the time. It seems to happen in something like 80% of the installs I've tried so far. On 64-bit Vista, I have never seen it launch twice.
I'm using the following lines in the InnoSetup script:
[Run] Filename: "RunDll32.exe"; Parameters: "{code:AddQuotes|{app}\VistaLib32.dll},RunNonElevated {code:AddQuotes|{app}\LastFM.exe}"; Description: "{cm:LaunchProgram,Last.fm}"; Flags: nowait postinstall; MinVersion: 0,6
If I revert back to a normal launch, i.e.
Filename: "{app}\LastFM.exe"; Description: "{cm:LaunchProgram,Last.fm}"; Flags: nowait postinstall
it never launches twice so I can only deduce that it's the VistaLib32 DLL that does it. Any ideas why?
Thanks, Erik
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Just discovered that this only seems to happen when launching two executables after installation. The above was a simplification, the Run section looks like this:
Filename: "RunDll32.exe"; Parameters: "{code:AddQuotes|{app}\VistaLib32.dll},RunNonElevated {code:AddQuotes|{app}\LastFMHelper.exe}"; Flags: nowait runhidden; MinVersion: 0,6 Filename: "RunDll32.exe"; Parameters: "{code:AddQuotes|{app}\VistaLib32.dll},RunNonElevated {code:AddQuotes|{app}\LastFM.exe}"; Description: "{cm:LaunchProgram,Last.fm}"; Flags: nowait postinstall; MinVersion: 0,6
If I remove either line, the remaining exe launches fine, and only once. But if I leave both lines in, it seems either of the two (or both) get launched twice. Is this not a good technique when you need to launch more than one exe?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I know this article is about Vista security But I have a similar problem with a limited user account under XP or W2K running an installer with a temporary administrator account. (through the login prompt, using "runas" context menu or when the installer is named "Install.exe" or "Setup.exe") When the installer has finished its job, how to make it run a non-admin program, using the initial user credentials, not as the temporary administrator account I couldn't find a good answer on Internet...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Olivier,
I did not try it, but the same method of hooking into the shell process should work with XP just as well. Of course, the code of the DLL should be modified to make the DLL load under XP (that is, all static calls to the Vista-only APIs should be changed to the dynamic ones). If you do try it, please let us know the results here. Thanks!
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I am using InstallShield 12 to install our application and we would like to also have our apps triggered to run in non-elevated mode during the install so I would like to find out more about the command line to trigger it.
In InstallShield we can call an executable and set the parameters that we have to pass to it, but my question is how would you set this up if the app you are calling from the install also requires some parameters?
Can this be done or will the Rundll32.exe see the extra parameters as something passed to it and therefore not work at all, or do the parameter need to be wrapped within the exe command. ie: RunDll32.exe "app path\VistaLib32.dll,RunNonElevated" "app path\our application.exe,-NOSPU"
If this does not support this then what would the next best thing be?
Thanks,
Tim.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
> my question is how would you set this up if the app you are calling from the install also requires some parameters?
You should enter the parameters on the same command line that you pass to RunDLL32, after the application path. The sample setup script included with the article shows how to do that (look for /some_args - it illustrates how you can pass the command line paramenters to your application by calling RunDll32).
HTH
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks your work on this Andrei.
Yes, adding /some_args after the path to the application to execute works, but I can't seem to be able to specify the working directory for starting the application using this method (rundll32). Using:
ProcessStartInfo pi = new ProcessStartInfo("rundll32.exe"); pi.Arguments = "VistaLib32.dll,RunNonElevated \"" + fileName + "\" " + arguments + " " + workingDir; Process.Start(pi);
The process is launched, but Process explorer shows that the arguments passed to it are {arguments workingdir} and the actual working directory is set to some default (e.g. c:\windows\system32). Is there any way to specify the working directory for the process using a call to rundll32.exe? Perhaps this has to do with RunDLL32 expecting the entrypoint function it is calling to do the parsing (as stated here http://support.microsoft.com/kb/164787)...
As an alternative I tried to call RunNonElevated directly (from C#), however my DLLImport prototype must be wrong, because I just get a messagebox entitled RunNonElevatedA with some garbage characters.
[DllImport("VistaLib32.dll", EntryPoint = "RunNonElevated")] public static extern bool RunNonElevated( IntPtr hwnd, [MarshalAs(UnmanagedType.LPTStr)] string filePath, [MarshalAs(UnmanagedType.LPTStr)] string arguments, [MarshalAs(UnmanagedType.LPTStr)] string workingDir);
Can you offer any advice how to achieve this? Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Ok I figured this out - firstly I was looking at the source to VC32.dll which was confusing me. Once I found the correct source for VistaLib32.dll, I saw that only RunNonElevatedA and RunNonElevatedW are exported as C functions in the .def file. And these two functions do not support passing a working directory for the process to be created. Additionally, there is a bug in RunNonElevatedA caused by the following two lines:
TCHAR szCmdLineW[ MAX_PATH ]; MessageBox( hwnd, szCmdLineW, _T("RunNonElevatedA"), MB_OK );
which shows the message box I was seeing with garbage characters. This should be converting the ANSI string to wide like RunElevatedA.
So the solution for me was adding RunNonElevated to DLL.def. Then I can use the following DLLImport:
[DllImport("VistaLib32.dll", EntryPoint = "RunNonElevated")] public static extern int RunNonElevated( IntPtr hwnd, [MarshalAs(UnmanagedType.LPTStr)] string filePath, [MarshalAs(UnmanagedType.LPTStr)] string arguments, [MarshalAs(UnmanagedType.LPTStr)] string workingDir, IntPtr hProcess);
Once again thanks for writing an article about this and providing source code. I find it incredible that MS didn't include a API for doing this. But then that's consistent with the mess that is Vista
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I wonder if your library can be used to solve the problem of elevation privileges in Windows Vista when the setup is run by a standard (non admin) user. The desktop icons and all the Current User registry settings are saved to the admin account and not the standard user account. Any idea on how to fix that? Thanks. Andrea
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
> I wonder if your library can be used to solve the problem of elevation privileges in Windows Vista when the setup is run by a standard (non admin) user. The desktop icons and all the Current User registry settings are saved to the admin account and not the standard user account. Any idea on how to fix that?
Andrea,
I see that you have asked this question in the InnoSetup newsgroup as well and Jordan has already answered it.
No, this DLL will not solve such a problem directly. If you must create the user-specific items during the installation, then you can solve the problem by creating a separate executable that does only the user-specific customizations, and you can launch it non-elevated from the main installer using my DLL. However, an easier way would be to do what Jordan suggested: do not create any user-specifc items during the install. Instead, let your program detect when it's being run for the first time, and if so, create those items from within the program itself.
However, that would not remove such items during uninstall... It may be better not to create any user-specific items at all...
Good luck!
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thank you. I'm thinking about creating an /uninstall parameter for my application so that when the user runs the Uninstall, it can be called using your library (with non elevated privileges).
I'm wondering what happens in this scenario. User Standard actually uses the application but he's not an administrator.
When Standard uninstall the application, Vista requires admin privileges so user Admin is the one who actually runs the setup. If I launch an application using your library who's the active user? The admin or the standard one? If it's the admin, there's no way I can remove any setting from the registry who are specific to the user who's actually running the application.
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hey,
I am not able to build VistaLib solution. Following error is reported,
1>e:\study\vista\vistalib\source\vistatools.cxx(179) : error C2065: 'TOKEN_ELEVATION_TYPE' : undeclared identifier 1>e:\study\vista\vistalib\source\vistatools.cxx(179) : error C2065: 'ptet' : undeclared identifier 1>e:\study\vista\vistalib\source\vistatools.cxx(179) : fatal error C1903: unable to recover from previous error(s); stopping compilation
I know that these errors are due to not having proper header file (Platform SDK). So, downloaded the latest SDK from followint site:
http://www.microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en[^]
and downloaded PSDK-x86.exe
Do I have to download anything else or am I missing something here.
Please let me know on this.
Thanks, Venki
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello Venki,
I was able to resolve the issue you indicated by configuring Visual Studio 2005 SP1 in the following manner:
Tools > Options > Projects and Solutions > VC++ Directories > Platform: Win32
> Executable Files Add C:\Program Files\Microsoft SDKs\Windows\v6.0\bin
> Include Files Add C:\Program Files\Microsoft SDKs\Windows\v6.0\Include Add C:\Program Files\Microsoft SDKs\Windows\v6.0\Include\gl
> Library files Add C:\Program Files\Microsoft SDKs\Windows\v6.0\lib Add C:\Program Files\Microsoft SDKs\Windows\v6.0\Lib\x64
> Exclude Directories Add C:\Program Files\Microsoft SDKs\Windows\v6.0\Include Add C:\Program Files\Microsoft SDKs\Windows\v6.0\Include\gl
I had to place these directories first (at the very top) because they are searched in that order (top to bottom)
Hope that works out for you. -Rami
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I also find the idea of injecting code into another process, especially a process that you didn't write and have no control over, scary.
Here's an alternative idea:
The setup program's process runs unelevated but, through the UAC API, creates an elevated COM object to use for those parts of the install which require administrator rights. The COM object only has to be created once and can then be re-used, so there would still only be a single UAC prompt. Since the main process is still running unelevated it can launch other unelevated applications directly, and since it has its COM object it can perform whatever elevated actions it needs to through the course of the installation.
I believe this is a far better way to do the job although it does, of course, require modification of the installer itself. So it's something that should be built into InnoSetup (etc.) rather than something we can easily do as users of the installer creators.
BTW, creating a single elevated COM object and then keeping it around for several operations is a perfectly legal use of the UAC model. It's been tried and tested in Directory Opus 9 (see my guide here: http://nudel.dopus.com/opus9/page4.html#vistauac ). The only reason that Explorer and many installers pop up multiple UAC prompts for the same logical operation is lazy programming.
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
> I believe this is a far better way to do the job
I agree!
> although it does, of course, require modification of the installer itself.
And that's the drawback. If you are willing to rewrite the installer, then it's much better to redesign it and make it work like you described, or maybe split it in two parts, one to run elevated and perform the real administrative operations, and another one, to run non-elevated, to perform the user-specific configuration (sort of like the helper and the main modules I described in in the article).
However, if rewriting the installer is not an option, then the code injection method should get the job done.
Andrei Belogortseff http://www.winability.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|