// Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license, see License.txt for details. #include "uac.h" #include // CoInitialize using namespace NSIS; CHECKTOKENMEMBERSHIP _CheckTokenMembership=0; #ifdef FEAT_CUSTOMRUNASDLG GETUSERNAMEEX _GetUserNameEx=0; CREATEPROCESSWITHLOGONW _CreateProcessWithLogonW=0; SHGETVALUEA _SHGetValueA=0; #endif UAC_GLOBALS globals; HINSTANCE g_hInst; extra_parameters* g_pXP; FORCEINLINE UAC_GLOBALS& G() {return globals;} FORCEINLINE UAC_GLOBALS_OUTER& OG() {return G().outer;} FORCEINLINE bool IsInnerInstance() {return G().RunMode==RUNMODE_INNER;} FORCEINLINE bool IsOuterInstance() {return G().RunMode==RUNMODE_OUTER;} FORCEINLINE HWND xxxGetOuterIPCWND() {return G().hwndOuter;} FORCEINLINE HWND xxxGetOuterNSISMainWnd() {return(HWND)SendMessage(xxxGetOuterIPCWND(),OWM_GETOUTERSTATE,GOSI_HWNDMAINWND,0);} #ifdef FEAT_AUTOPAGEJUMP # define Outer_GetOuterNSISMainWnd() xxxGetOuterNSISMainWnd() # define Inner_GetOuterNSISMainWnd() xxxGetOuterNSISMainWnd() #endif #ifdef FEAT_AUTOPAGEJUMP # define Inner_GetInnerNSISMainWnd() G().hwndNSIS #endif #define Inner_GetOuterIPCWND xxxGetOuterIPCWND bool SysQuery_IsServiceRunning(LPCTSTR servicename) { bool retval=false; SC_HANDLE scdbh=NULL,hsvc; scdbh=OpenSCManager(NULL,NULL,GENERIC_READ); if (scdbh) { if (hsvc=OpenService(scdbh,servicename,SERVICE_QUERY_STATUS)) { SERVICE_STATUS ss; if (QueryServiceStatus(hsvc,&ss))retval=(ss.dwCurrentState==SERVICE_RUNNING); CloseServiceHandle(hsvc); } CloseServiceHandle(scdbh); } else { //Guest account can't open OpenSCManager, let's just lie and pretend everything is ok! retval = ERROR_ACCESS_DENIED==GetLastError(); } return retval; } inline bool NT5_IsSecondaryLogonSvcRunning() { return SysQuery_IsServiceRunning(_T("seclogon")); } BOOL QueryIsAdminOrGetIL(UINT32*pIL) { BOOL isAdmin=false; OSVERSIONINFO ovi={sizeof(ovi)}; SetLastError(NO_ERROR); GetVersionEx(&ovi); const bool OSAtleastVista = ovi.dwMajorVersion >= 6; HANDLE hToken = NULL; if (VER_PLATFORM_WIN32_NT != ovi.dwPlatformId) { isAdmin = true; } else { if (OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken)) { BYTE sidbuf[8+(sizeof(DWORD)*2)]; PSID psid = (PSID) sidbuf; ((UINT32*)psid)[0] = 1 | 2<<8; ((UINT32*)psid)[1] = 5<<24; ((UINT32*)psid)[2] = SECURITY_BUILTIN_DOMAIN_RID; ((UINT32*)psid)[3] = DOMAIN_ALIAS_RID_ADMINS; if (_CheckTokenMembership) { if (!_CheckTokenMembership(0,psid,&isAdmin)) isAdmin=false; } else { DWORD cbTokenGrps; if (!GetTokenInformation(hToken,TokenGroups,0,0,&cbTokenGrps)&&GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_GROUPS*ptg=0; if (ptg=(TOKEN_GROUPS*)NSIS::MemAlloc(cbTokenGrps)) { if (GetTokenInformation(hToken,TokenGroups,ptg,cbTokenGrps,&cbTokenGrps)) { for (UINT i=0; iGroupCount; ++i) { if (EqualSid(ptg->Groups[i].Sid,psid)) isAdmin=true; } } NSIS::MemFree(ptg); } } } } } UINT32 il = isAdmin ? 0x3000 : 0x2000; if (OSAtleastVista) { BYTE bufTokMandLbl[30]; PTOKEN_MANDATORY_LABEL pTML=(PTOKEN_MANDATORY_LABEL)bufTokMandLbl; DWORD RetLen; il = 0; if (GetTokenInformation(hToken,TokenIntegrityLevel,pTML,sizeof(bufTokMandLbl),&RetLen)) { PSID psid = pTML->Label.Sid; if ((1|1<<8) == ((UINT32*)psid)[0] && (16<<24) == ((UINT32*)psid)[1]) { il = ((UINT32*)psid)[2]; } } isAdmin = il >= 0x3000; } if (pIL) *pIL = il; CloseHandle(hToken); return isAdmin; } inline UINT32 GetIntegrityLevel() { UINT32 il = 0; QueryIsAdminOrGetIL(&il); return il; } bool IsCurrentProcessAdmin() { return !!QueryIsAdminOrGetIL(NULL); } bool SysQuery_UAC_IsActive() { //TODO: check AppInfo service? FARPROC CEE = GetProcAddress(LoadLibraryA("KERNEL32"),"CheckElevationEnabled"); BOOL bEE; if (CEE && 0==((DWORD(WINAPI*)(BOOL*))CEE)(&bEE)) return !!bEE; ASSERT(_SHGetValueA); DWORD type,cbio=sizeof(DWORD),data; return !_SHGetValueA(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System","EnableLUA",&type,&data,&cbio) && data!=0; } LPTSTR FindExePathEnd(LPTSTR p) { if ( *p=='"' && *(++p) ) { while( *p && *p!='"' )p=StrNextChar(p); if (*p) p=StrNextChar(p); else return 0; } else if ( *p!='/' )while( *p && *p!=' ' )p=StrNextChar(p); return p; } #ifdef FEAT_MSRUNASDLGMODHACK HHOOK g_MSRunAsHook; FORCEINLINE void MSRunAsDlgMod_Unload() { UnhookWindowsHookEx((HHOOK)g_MSRunAsHook); } LRESULT CALLBACK MSRunAsDlgMod_ShellProc(int nCode,WPARAM wp,LPARAM lp) { CWPRETSTRUCT*pCWPS; if (nCode >= 0 && (pCWPS=(CWPRETSTRUCT*)lp) && WM_INITDIALOG==pCWPS->message) { TCHAR buf[30]; GetClassName(pCWPS->hwnd,buf,ARRAYSIZE(buf)); if (!lstrcmpi(buf,_T("#32770"))) { #ifdef FEAT_AUTOPAGEJUMP if (!MyIsWindowVisible(G().hwndNSIS)) { WndModifyLong(pCWPS->hwnd,GWL_EXSTYLE,WS_EX_APPWINDOW,WS_EX_APPWINDOW); SetForegroundWindow(pCWPS->hwnd); } #endif const UINT IDC_USRSAFER=0x106,IDC_OTHERUSER=0x104,IDC_SYSCRED=0x105; GetClassName(GetDlgItem(pCWPS->hwnd,IDC_SYSCRED),buf,ARRAYSIZE(buf)); if (!lstrcmpi(buf,_T("SysCredential"))) //make sure this is the run as dialog { SndDlgItemMsg(pCWPS->hwnd,IDC_USRSAFER,BM_SETCHECK,BST_UNCHECKED); SndDlgItemMsg(pCWPS->hwnd,IDC_OTHERUSER,BM_CLICK); DBGONLY(UAC_DbgHlpr_LoadPasswordInRunAs(pCWPS->hwnd)); } } } return CallNextHookEx(g_MSRunAsHook,nCode,wp,lp); } void MSRunAsDlgMod_Init() { g_MSRunAsHook=NULL; if(GetOSVerMaj()==5 && GetOSVerMin()>=1) //only XP/2003 g_MSRunAsHook=SetWindowsHookEx(WH_CALLWNDPROCRET,MSRunAsDlgMod_ShellProc,0,GetCurrentThreadId()); } #endif FORCEINLINE void* DelayLoadFunction(HMODULE hLib,LPCSTR Export) { return GetProcAddress(hLib,Export); } DWORD DelayLoadFunctions() { #ifdef UNICODE # define __DLD_FUNCSUFFIX "W" #else # define __DLD_FUNCSUFFIX "A" #endif HMODULE hModAdvAPI=LoadLibraryA("ADVAPI32") ,hModShlWAPI=LoadLibraryA("ShlWAPI") ; // Optional: _CheckTokenMembership=(CHECKTOKENMEMBERSHIP)DelayLoadFunction(hModAdvAPI,"CheckTokenMembership"); #ifdef FEAT_CUSTOMRUNASDLG _GetUserNameEx=(GETUSERNAMEEX)DelayLoadFunction(LoadLibraryA("SECUR32"),"GetUserNameEx" __DLD_FUNCSUFFIX);//We never free this library _CreateProcessWithLogonW=(CREATEPROCESSWITHLOGONW)DelayLoadFunction(hModAdvAPI,"CreateProcessWithLogonW"); _SHGetValueA=(SHGETVALUEA)DelayLoadFunction(hModShlWAPI,"SHGetValueA"); #endif return NO_ERROR; } bool GetOuterHwndFromCommandline(HWND&hwndOut) { LPTSTR p=FindExePathEnd(GetCommandLine()); while(p && *p==' ')p=CharNext(p); TRACEF("GetOuterHwndFromCommandline:%s|\n",p); if (p && *p++=='/'&&*p++=='U'&&*p++=='A'&&*p++=='C'&&*p++==':') { hwndOut=(HWND)StrToUInt(p,true); return (/*IsWindow*/(hwndOut))!=0; } return false; } UINT_PTR __cdecl NSISPluginCallback(enum NSPIM Event) { TRACEF("NSISPluginCallback:%d %d:%d\n",Event,GetCurrentProcessId(),GetCurrentThreadId()); switch(Event) { case NSPIM_UNLOAD: if (IsOuterInstance()) { SendMessage(G().hwndOuter,WM_CLOSE,0,0); WaitForSingleObject(OG().hOuterThread,INFINITE); BUILDRLSCANLEAK(CloseHandle(OG().hOuterThread)); } BUILDRLSCANLEAK(CloseHandle(G().PerformOp)); BUILDRLSCANLEAK(CloseHandle(G().CompletedOp)); UnmapViewOfFile(G().pShared); BUILDRLSCANLEAK(CloseHandle(G().hSharedMap)); TRACEF("NSISPluginCallback:NSPIM_UNLOAD completed cleanup, ready to be unloaded...\n"); break; } return NULL; } BOOL CALLBACK ProcessThreadMessages(HWND hwndDlg=NULL,DWORD*pExitCode=NULL) { MSG msg; while( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ) { if (msg.message==WM_QUIT) { TRACEF("ProcessThreadMessages got WM_QUIT %d:%d, code=%u hDlg=%X\n",GetCurrentProcessId(),GetCurrentThreadId(),msg.wParam,hwndDlg); if (pExitCode)*pExitCode=msg.wParam; return true; // ASSERT(!"TODO: should we handle WM_QUIT?"); // ec=ERROR_CANCELLED; // break; } if (!hwndDlg || !IsDialogMessage(hwndDlg,&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return false; } bool Inner_DoOpInOuter(UINT opid,UINT Direction) { OP_HDR*pOP=(OP_HDR*)G().pShared; pOP->Op=opid; pOP->Direction=Direction; // TRACEF("DoOp %d %d PID=%u\n",opid,Direction,GetCurrentProcessId()); if (SetEvent(G().PerformOp)) { for(DWORD w;;) { w=MsgWaitForMultipleObjects(1,&G().CompletedOp,false,INFINITE,QS_ALLINPUT); switch(w) { case WAIT_OBJECT_0: return true; case WAIT_OBJECT_0+1: if (ProcessThreadMessages())break; continue; default: ASSERT(!"DoOp>MsgWaitForMultipleObjects failed!"); break; } } } ASSERT(!"DoOp failed!"); return false; } void OP_SYNCREG_CopyRegisters(bool VarToShared,UINT offset,const UINT count) { const UINT cbOneVar=G().NSIScchStr*sizeof(NSISCH); offset*=cbOneVar; BYTE*pD=G().pShared+sizeof(OP_SYNCREG),*pS=((BYTE*)G().NSISVars)+offset; if (!VarToShared){BYTE*temp=pD;pD=pS;pS=temp;} MemCopy(pD,pS,count*cbOneVar); } bool Inner_DoSyncRegisters(UINT Dir,UINT regoffset=INST_0,UINT count=10) { OP_SYNCREG*pOP=(OP_SYNCREG*)G().pShared; pOP->RegOffset=regoffset; pOP->Count=count; // TRACEF("*** Inner_DoSyncRegisters BEGIN PID=%u\n",GetCurrentProcessId()); for (int i=0;i<2;++i) { if (Dir==OPDIR_I2O)OP_SYNCREG_CopyRegisters(true,pOP->RegOffset,pOP->Count);//CopyRegisters(pSharedBaseOffset,G().NSISVars,pOP->RegOffset,pOP->Count); if (!Inner_DoOpInOuter(OPID_SYNCREG,Dir))return false; if (Dir==OPDIR_O2I)OP_SYNCREG_CopyRegisters(false,pOP->RegOffset,pOP->Count);//CopyRegisters(G().NSISVars,pSharedBaseOffset,pOP->RegOffset,pOP->Count); pOP->RegOffset=INST_R0; } // TRACEF("*** Inner_DoSyncRegisters END\n"); return true; } inline void Inner_SyncNSISVar(UINT Dir,bool instdir,bool outdir) { const UINT first=instdir?INST_INSTDIR:INST_OUTDIR; Inner_DoSyncRegisters(Dir,first,(INST_OUTDIR+1)-first); } LRESULT CALLBACK OuterWndProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp) { switch(msg) { case WM_DESTROY: #ifdef UAC_HACK_FGNDWND2011 SendMessage(hwnd,OWM_HIDESPLASH,0,0); #endif PostMessage(NULL,WM_QUIT,0,NULL); break; case WM_CLOSE: return DestroyWindow(hwnd); case OWM_INITINNER: TRACEF("OuterWndProc:OWM_INITINNER %X\n",OG().hInnerProcess); if (OG().hInnerProcess) { const HANDLE hThisProcess=GetCurrentProcess(),hTargetProcess=OG().hInnerProcess; HANDLE destMap; if (DuplicateHandle(hThisProcess,G().hSharedMap,hTargetProcess,&destMap,FILE_MAP_WRITE|FILE_MAP_READ,false,0)) { BYTE*p=(BYTE*)G().pShared; HANDLE dest,src[]={G().PerformOp,G().CompletedOp}; for (int i=0;iOp,pHdr->Direction,GetCurrentProcessId(),GetCurrentThreadId()); switch(pHdr->Op) { case OPID_SYNCREG: { OP_SYNCREG*pSR=(OP_SYNCREG*)G().pShared; OP_SYNCREG_CopyRegisters(pSR->OpHdr.Direction==OPDIR_O2I,pSR->RegOffset,pSR->Count); } break; case OPID_CALLCODESEGMENT: TRACEF("OPID_CALLCODESEGMENT: %u\n",((OP_CALLCODESEGMENT*)pHdr)->Pos); if (((OP_CALLCODESEGMENT*)pHdr)->Flags&OPCCSF_SETOUTPATH)SetCurrentDirectory(NSIS_UNSAFE_GETVAR(INST_OUTDIR,G().NSISVars,G().NSIScchStr)); g_pXP->ExecuteCodeSegment(((OP_CALLCODESEGMENT*)pHdr)->Pos,NULL); break; /* case OPID_GETOUTERSTATE: MemCopy(G().pShared,&G(),sizeof(UAC_GLOBALS)); break;*/ default: TRACEF("OPID_???\n"); } } SetEvent(G().CompletedOp); break; case WAIT_OBJECT_0+1: if (ProcessThreadMessages(NULL))ec=ERROR_CANCELLED; break; default: ec=GetLastError(); ASSERT(ec); TRACEF("OuterWndThread MsgWait failed with %u\n",ec); } } while( G().hwndOuter && NO_ERROR==ec ); } else ec=GetLastError(); // CoUninitialize(); TRACEF("OuterWndThread %d:%d ending with %d\n",GetCurrentProcessId(),GetCurrentThreadId(),ec); return ec; } FORCEINLINE DWORD Outer_InitShared(UINT NSIScchStr) { UINT ec,cbMap=sizeof(OP_SYNCREG) + ((sizeof(NSISCH)*NSIScchStr)*10); if (true && (G().PerformOp=CreateEvent(/*&sa*/NULL,false,false,NULL)) && (G().CompletedOp=CreateEvent(/*&sa*/NULL,false,false,NULL)) && (G().hSharedMap=CreateFileMapping(INVALID_HANDLE_VALUE,/*&sa*/NULL,PAGE_READWRITE|SEC_COMMIT,0,cbMap,NULL)) && (G().pShared=(BYTE*)MapViewOfFile(G().hSharedMap,FILE_MAP_ALL_ACCESS,0,0,0)) ) ec=NO_ERROR; else ec=GetLastError(); return ec; } FORCEINLINE DWORD Outer_CreateOuterWindow() { DWORD ec=NO_ERROR; if ( !(OG().hOuterThread=CreateThread(0,1,OuterWndThread,NULL,0,NULL)) )return GetLastError(); if ( WAIT_OBJECT_0!=WaitForSingleObject(G().CompletedOp,INFINITE) )GetExitCodeThread(OG().hOuterThread,&ec); return ec; } DWORD Inner_InitSharedData() { ASSERT(G().hwndOuter); TRACEF("Inner_InitSharedData() has=%X\n",G().hSharedMap); if (!G().hSharedMap) { HANDLE hOuterProcess,hThisProcess=GetCurrentProcess(); DWORD pid; ASSERT(G().hwndOuter); GetWindowThreadProcessId(G().hwndOuter,&pid); ASSERT(pid); BOOL HadDbgPriv; EnablePrivilege(SE_DEBUG_NAME,true,&HadDbgPriv); TRACEF("try to open process outer pid=%u\n",pid); if (!(hOuterProcess=OpenProcess(PROCESS_DUP_HANDLE,false,pid)))goto dieGLE; TRACEF("hOuterProcess with dup right =%X\n",hOuterProcess); DuplicateHandle(hThisProcess,hThisProcess,hOuterProcess,&hOuterProcess,PROCESS_DUP_HANDLE,false,DUPLICATE_CLOSE_SOURCE); EnablePrivilege(SE_DEBUG_NAME,HadDbgPriv,NULL); //The handle we get from ShExecEx can only be syncronized with (NT6+), so we give the outer process a handle with DUP_HANDLE rights so it can give us access to its shared memory if (0x666!=SendMessage(G().hwndOuter,OWM_SETOUTERSTATE,SOSI_PROCESSDUPHANDLE,(LPARAM)hOuterProcess))return ERROR_INVALID_WINDOW_HANDLE; //Wait for a very unlikely racecond. while(SendMessage(G().hwndOuter,OWM_ISREADYFORINIT,0,0)!=0x666)Sleep(50); TRACEF("Inner_InitSharedData: Sending OWM_INITINNER to get hSharedMap...\n"); G().hSharedMap=(HANDLE)SendMessage(G().hwndOuter,OWM_INITINNER,0,0); ASSERT(G().hSharedMap); if (G().pShared=(BYTE*)MapViewOfFile(G().hSharedMap,FILE_MAP_ALL_ACCESS,0,0,0)) { HANDLE*p=(HANDLE*)G().pShared; G().PerformOp=*p; G().CompletedOp=*(++p); } if (!G().pShared) { TRACEF("Inner_InitSharedData failed!\n"); ASSERT(G().pShared); return ERROR_INVALID_PARAMETER; } } return NO_ERROR; dieGLE: return GetLastError(); } #ifdef FEAT_AUTOPAGEJUMP LRESULT CALLBACK OuterMainWndSubProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp) { switch(msg) { case WM_NOTIFY_OUTER_NEXT: G().JumpPageOffset+=wp; TRACEF("WM_NOTIFY_OUTER_NEXT: now=%d change=%d\n",G().JumpPageOffset,wp); break; } return CallWindowProc(G().OrgMainWndProc,hwnd,msg,wp,lp); } #endif #ifdef FEAT_AUTOPAGEJUMP LRESULT CALLBACK InnerMainWndSubProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp) { switch(msg) { case WM_WINDOWPOSCHANGED: { WINDOWPOS&wp=*(WINDOWPOS*)lp; if (SWP_SHOWWINDOW&wp.flags && G().JumpPageOffset!=-1) { TRACEF("InnerMainWndSubProc:WM_WINDOWPOSCHANGED: Performing delayed onGuiInit pagejump and OWM_PERFORMWINDOWSWITCH"); HWND hOuterMainWnd=Inner_GetOuterNSISMainWnd(); G().JumpPageOffset=(WORD)SendMessage(G().hwndOuter,OWM_GETOUTERSTATE,GOSI_PAGEJUMP,0); TRACEF("\tposting WM_NOTIFY_OUTER_NEXT %d to autojump\n",G().JumpPageOffset); PostMessage(hwnd,WM_NOTIFY_OUTER_NEXT,G().JumpPageOffset,0); RECT rect; GetWindowRect(hOuterMainWnd,&rect); SetWindowPos(hwnd,hOuterMainWnd,rect.left,rect.top,0,0,SWP_NOSIZE); /*#ifdef UAC_DELAYLOAD_CHANGEWINDOWMESSAGEFILTER if (_ChangeWindowMessageFilter) { VERIFY(_ChangeWindowMessageFilter(IWM_HELPWITHWINDOWSWITCH,MSGFLT_ADD)); } #endif */ TRACEF("\tPosting OWM_PERFORMWINDOWSWITCH %X,%X\n",hOuterMainWnd,hwnd); PostMessage(G().hwndOuter,OWM_PERFORMWINDOWSWITCH,(WPARAM)hOuterMainWnd,(LPARAM)hwnd); G().JumpPageOffset=-1; } } break; /* case IWM_HELPWITHWINDOWSWITCH: TRACEF("InnerMainWndSubProc:IWM_HELPWITHWINDOWSWITCH\n"); SetParent(Inner_GetOuterNSISMainWnd(),hwnd); return TRUE; */ } return CallWindowProc(G().OrgMainWndProc,hwnd,msg,wp,lp); } #endif #define GetVarStr(varid) ((LPTSTR)(Vars+((varid)*NSIScchStr))) #define SetVarUINT(varid,data) wsprintf((Vars+((varid)*NSIScchStr)),_T("%u"),(data)) #define NSISHlpr_SetErrorFlag() pXP->exec_flags->exec_error=1 #define NSISHlpr_ClearErrorFlag() pXP->exec_flags->exec_error=0 INT_PTR CALLBACK SplashDialogProc(HWND hwnd,UINT uMsg,WPARAM wp,LPARAM lp) { return false; } extern "C" void __declspec(dllexport) __cdecl _(HWND hwndNSIS,UINT NSIScchStr,NSISCH*Vars,NSIS::stack_t**StackTop,NSIS::extra_parameters* pXP) { stack_t*sp1=(*StackTop); bool freeSP1=true; NSISCH*p=sp1->text; DWORD ec=NO_ERROR; if (G().RunMode==RUNMODE_INIT) { G().NSIScchStr=NSIScchStr; G().NSISVars=Vars; g_pXP=pXP; pXP->RegisterPluginCallback(g_hInst,NSISPluginCallback); if ( GetOSVerMaj() < 5 ) { G().RunMode=RUNMODE_LEGACY; } else { G().RunMode = GetOuterHwndFromCommandline(G().hwndOuter) ? RUNMODE_INNER : RUNMODE_OUTER ; if ( !(ec=DelayLoadFunctions()) ) { if ( G().RunMode==RUNMODE_OUTER && !(ec=Outer_InitShared(NSIScchStr)) ) ec=Outer_CreateOuterWindow(); } } } TRACEF("EXPORT cmd=%c outer=%d %d:%d ec=%d\n",*(p),IsOuterInstance(),GetCurrentProcessId(),GetCurrentThreadId(),ec); NSISHlpr_ClearErrorFlag(); if (!ec) switch(*(p)) { case '0':/*** RunElevated ***/ { bool IsAdmin=IsCurrentProcessAdmin(); BYTE UACMode=0; const UINT OSVMaj=GetOSVerMaj(); DWORD ForkExitCode=ERROR_CANCELLED;//is this the best value to use? is it required? TRACEF("UAC:RunElevated: admin=%d os=%d.%d runmode=%u\n",IsAdmin,OSVMaj,GetOSVerMin(),G().RunMode); #ifdef FEAT_AUTOPAGEJUMP G().hwndNSIS=hwndNSIS; #endif if (IsAdmin) { UACMode=2; if (IsInnerInstance()) { ec=Inner_InitSharedData(); ASSERT(!ec); #ifdef UAC_HACK_FGNDWND2011 PostMessage(Inner_GetOuterIPCWND(),OWM_HIDESPLASH,0,0); #endif } } else { if (IsInnerInstance()) //non-admin used in runas dialog { if (!(ec=Inner_InitSharedData())) { UACMode=1;//fake value so the script calls quit without further processing pXP->exec_flags->errlvl=0; ((OP_HDR*)G().pShared)->Op=OPID_OUTERMUSTRETRY; //DoOp(OPID_OUTERMUSTRETRY,0);//tell the outer process about this problem } else { ASSERT(!ec); } } else { SHELLEXECUTEINFO sei; MemZero(&sei,sizeof(sei)); LPTSTR pszExePathBuf=0; LPTSTR pszParamBuf=0; LPTSTR p,pCL=GetCommandLine(); UINT len; LPCTSTR verb_runas = _T("runas"); sei.cbSize=sizeof(sei); sei.hwnd=hwndNSIS?hwndNSIS:G().hwndOuter; sei.nShow=SW_SHOWNORMAL; sei.fMask=SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_DDEWAIT; //TODO: |= SEE_MASK_UNICODE ?? sei.lpVerb=verb_runas; #ifdef UAC_HACK_FGNDWND2011 if (!MyIsWindowVisible(hwndNSIS)) { HMODULE hExeMod=GetModuleHandle(0); if (!OG().hwndSplash) OG().hwndSplash=CreateDialog(hExeMod,MAKEINTRESOURCE(111),0,SplashDialogProc); if (OG().hwndSplash) { sei.hwnd=OG().hwndSplash; //MySetDlgItemText(sei.hwnd,1030,NULL); LONG xs = WS_EX_TOOLWINDOW; // Hacky invisible splash screen, // you can hex edit "runas" > "Runas" to remove this hack and display it. if (*verb_runas=='r') xs |= WS_EX_TRANSPARENT|WS_EX_LAYERED; WndModifyLong(sei.hwnd,GWL_EXSTYLE,xs,xs); SetWindowPos(sei.hwnd,0,0,0,0,0,SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER); HICON hIcon=LoadIcon(hExeMod,MAKEINTRESOURCE(103)); HWND hCtl = NULL; for(;hCtl = FindWindowEx(sei.hwnd,hCtl,NULL,NULL);) ShowWindow(hCtl,SW_HIDE); HWND hIcoCtl=GetDlgItem(sei.hwnd,1030); ShowWindow(hIcoCtl,SW_SHOW); RECT r; GetClientRect(sei.hwnd,&r); SetWindowPos(hIcoCtl,0,0,0,r.right,r.bottom,SWP_NOZORDER|SWP_NOACTIVATE); WndModifyLong(hIcoCtl,GWL_STYLE,0,SS_ICON|SS_CENTERIMAGE); SNDMSG(hIcoCtl,STM_SETICON,(WPARAM)hIcon,0); SNDMSG(sei.hwnd,WM_SETICON,ICON_BIG,(LPARAM)hIcon); ShowWindow(sei.hwnd,SW_SHOW); } } #endif p=FindExePathEnd(pCL); len=(p-pCL)/sizeof(TCHAR); ec=ERROR_FILE_NOT_FOUND; if (p && len) { for (;ec!=ERROR_OUTOFMEMORY;) { NSIS::MemFree(pszExePathBuf); if (!(pszExePathBuf=(LPTSTR)NSIS::MemAlloc((++len)*sizeof(TCHAR)))) ec=ERROR_OUTOFMEMORY; else { if ( GetModuleFileName(NULL,pszExePathBuf,len) < len ) //FUCKO: what if GetModuleFileName returns 0? { TRACEF("module=%s|\n",pszExePathBuf); ec=0; break; } } len+=MAX_PATH; } if (!ec) { sei.lpFile=pszExePathBuf; len=lstrlen(p); len+=20;//space for "/UAC:xxxxxx /NCRC\0" if (!(pszParamBuf=(LPTSTR)NSIS::MemAlloc(len*sizeof(TCHAR)))) ec=ERROR_OUTOFMEMORY; else { wsprintf(pszParamBuf,_T("/UAC:%X /NCRC%s"),G().hwndOuter,p);//NOTE: The argument parser depends on our special flag appearing first sei.lpParameters=pszParamBuf; if (OSVMaj==5) { bool hasseclogon=NT5_IsSecondaryLogonSvcRunning(); TRACEF("SysNT5IsSecondaryLogonSvcRunning=%d\n",hasseclogon); if (!hasseclogon)ec=ERROR_SERVICE_NOT_ACTIVE; } if (!ec) { SetForegroundWindow(sei.hwnd); TRACEF("UAC:Elevate: Execute: %s|%s|\n",sei.lpFile,sei.lpParameters); #ifdef FEAT_CUSTOMRUNASDLG if (OSVMaj>=6 && !SysQuery_UAC_IsActive()) //NOTE: v6 not hardcoded, will MS ever fix this? { ec=MyRunAs(g_hInst,sei); TRACEF("MyRunAs ret, ec=%u\n",ec); } else #endif { #ifdef FEAT_MSRUNASDLGMODHACK MSRunAsDlgMod_Init(); #endif ec=(ShellExecuteEx(&sei)?NO_ERROR:GetLastError()); TRACEF("ShellExecuteEx ret, gle=%u\n",ec); #ifdef FEAT_MSRUNASDLGMODHACK MSRunAsDlgMod_Unload(); #endif } if (!ec) { bool endWait=false; TRACEF("RunElevated: %d:%d waiting for fork hP=%X\n",GetCurrentProcessId(),GetCurrentThreadId(),sei.hProcess); do { DWORD w=MsgWaitForMultipleObjects(1,&sei.hProcess,false,INFINITE,QS_ALLINPUT); switch(w) { case WAIT_OBJECT_0: VERIFY(GetExitCodeProcess(sei.hProcess,&ForkExitCode)); endWait=true; UACMode= ((OP_HDR*)G().pShared)->Op==OPID_OUTERMUSTRETRY ? 3 : 1; pXP->exec_flags->errlvl=ForkExitCode; TRACEF("UAC:RunElevated: Inner process exit with %d, UACMode=%d\n",ForkExitCode,UACMode); break; case WAIT_OBJECT_0+1: ProcessThreadMessages(hwndNSIS); break; default: endWait=true; ec=GetLastError(); TRACEF("UAC:RunElevated:MsgWaitForMultipleObjects failed, ec=%u w=%u\n",ec,w); } } while(!endWait && NO_ERROR==ec); CloseHandle(sei.hProcess); if (OG().ecInner_InitSharedData) { TRACEF("inner process completed, but never got access to shared mem? ec=%u forkexit=%u ecInner_InitSharedData=%u\n",ec,ForkExitCode,OG().ecInner_InitSharedData); ec=OG().ecInner_InitSharedData; } } } } } } } } if (IsOuterInstance()) { if(G().JumpPageOffset)--G().JumpPageOffset; CloseHandle(OG().hInnerProcess); OG().hInnerProcess=NULL; } pXP->exec_flags->errlvl=ec?ec:ForkExitCode;//We now set the errlvl no matter what, is this a good idea? SetVarUINT(INST_0,ec); SetVarUINT(INST_1,UACMode); SetVarUINT(INST_2,ForkExitCode); SetVarUINT(INST_3,IsAdmin); TRACEF("UAC:RunElevated end: ec=%u mode=%d forkexit=%u admin=%d isouter=%d\n",ec,UACMode,ForkExitCode,IsAdmin,IsOuterInstance()); } break; case '1':/*** ExecCodeSegment(flags) ***/ { int pos=StrToUInt(&p[1]); if (pos--) { UINT ecsflags=0; TCHAR*pFlags=&p[1]; while(*pFlags && *pFlags!=':')++pFlags; if (*pFlags) { ++pFlags; TRACEF("ExecCodeSegment>Flags=%s|\n",pFlags); ecsflags=StrToUInt(pFlags); } if (IsInnerInstance()) { if (!(ec=Inner_InitSharedData())) { const bool SyncRegisters=(ecsflags&UAC_SYNCREGISTERS)!=0; const bool SyncInstDir=(ecsflags&UAC_SYNCINSTDIR)!=0; const bool SyncOutDir=(ecsflags&UAC_SYNCOUTDIR)!=0; if (SyncRegisters)Inner_DoSyncRegisters(OPDIR_I2O); Inner_SyncNSISVar(OPDIR_I2O,SyncInstDir,SyncOutDir); OP_CALLCODESEGMENT*pOP=(OP_CALLCODESEGMENT*)G().pShared; pOP->Pos=pos; pOP->Flags=SyncOutDir?OPCCSF_SETOUTPATH:NULL; Inner_DoOpInOuter(OPID_CALLCODESEGMENT,OPDIR_I2O); if (SyncRegisters)Inner_DoSyncRegisters(OPDIR_O2I); Inner_SyncNSISVar(OPDIR_O2I,SyncInstDir,SyncOutDir); if (SyncOutDir)SetCurrentDirectory(NSIS_UNSAFE_GETVAR(INST_OUTDIR,G().NSISVars,G().NSIScchStr)); } else { ASSERT(!ec); } } else { pXP->ExecuteCodeSegment(pos,NULL); } } } break; case '2':/*** IsAdmin ***/ TRACEF("UAC_IsAdmin: %d:%d outer=%d admin=%d\n",GetCurrentProcessId(),GetCurrentThreadId(),IsOuterInstance(),IsCurrentProcessAdmin()); if (p[1]=='s')freeSP1=false; wsprintf(freeSP1 ? (Vars+(INST_0*NSIScchStr)) : p ,_T("%u"),IsCurrentProcessAdmin()); ec=GetLastError(); TRACEF("UAC:IsAdmin PID=%X ret=%d useReg=%d ec=%X\n",GetCurrentProcessId(),IsCurrentProcessAdmin(),freeSP1,ec); break; case '3':/*** IsInnerInstance ***/ TRACEF("UAC_IsInnerInstance: %d:%d outer=%d admin=%d\n",GetCurrentProcessId(),GetCurrentThreadId(),IsOuterInstance(),IsCurrentProcessAdmin()); if (p[1]=='s')freeSP1=false; wsprintf(freeSP1 ? (Vars+(INST_0*NSIScchStr)) : p ,_T("%u"),IsInnerInstance()); TRACEF("UAC:IsInnerInstance PID=%d ret=%d useReg=%d\n",GetCurrentProcessId(),IsInnerInstance(),freeSP1); break; #ifdef FEAT_AUTOPAGEJUMP case '4':/*** Notify_OnGuiInit / PageElevation_OnGuiInit ***/ ASSERT(hwndNSIS); TRACEF("UAC_Notify_OnGuiInit: %d:%d outer=%d admin=%d\n",GetCurrentProcessId(),GetCurrentThreadId(),IsOuterInstance(),IsCurrentProcessAdmin()); if (!(G().OrgMainWndProc=(WNDPROC)SetWindowLongPtr(hwndNSIS,GWLP_WNDPROC,(LONG_PTR)(IsInnerInstance()?InnerMainWndSubProc:OuterMainWndSubProc)))) { ec=ERROR_INVALID_WINDOW_HANDLE; } if (IsInnerInstance()) { if (!ec) ec=Inner_InitSharedData(); ASSERT(!ec); TRACEF("Inner_InitSharedData ret %u\n",ec); } else { DBGONLY(SetForegroundWindow(FindWindowA("dbgviewClass",0))); } //SetVarUINT(INST_0,ec); break; case '5':/*** PageElevation_OnInit ***/ ASSERT(!ec); if (IsInnerInstance()) { ec=Inner_InitSharedData(); SendMessage(Inner_GetOuterIPCWND(),OWM_SETOUTERSTATE,SOSI_ELEVATIONPAGE_GUIINITERRORCODE,ec); } break; #endif case '6':/*** GetTokenIntegrityLevel ***/ { const UINT32 il = GetIntegrityLevel(); wsprintf(p,_T("%u"),il); freeSP1 = false; } break; /* case '?':/*** FindCmdLineParams *** / { freeSP1=false; NSISCH*pStart=&p[1]; NSISCH*pParams=FindExePathEnd( *pStart == ':' ? ++pStart : pStart=GetCommandLine() ); pParams=StrSkipWhitespace(pParams); MemCopy(p,pParams,(lstrlen(pParams)+1)*sizeof(NSISCH)); } break;*/ } if (freeSP1) { *StackTop=(*StackTop)->next; NSIS::MemFree(sp1); } if (ec) NSISHlpr_SetErrorFlag(); } #ifdef _DEBUG EXTERN_C BOOL WINAPI DllMain(HINSTANCE hInst,DWORD Event,LPVOID lpReserved) #else EXTERN_C BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst,ULONG Event,LPVOID lpReserved) #endif { if(Event==DLL_PROCESS_ATTACH) { g_hInst=hInst; MemZero(&globals,sizeof(UAC_GLOBALS)); ASSERT(G().RunMode==RUNMODE_INIT); DBGONLY(HWND dummy;if(!GetOuterHwndFromCommandline(dummy))DBG_RESETDBGVIEW();); TRACEF("*** UAC DllMain PID=%u TID=%d\n",GetCurrentProcessId(),GetCurrentThreadId()); } TRACEF("*** UAC DllMain Event=%d\n",Event); return true; }