예제 #1
0
        void _ThreadProc()
        {
            AHookWin hk = null, hm = null; AHookAcc hwe = null;

            try {
                try {
                    if (_block.Has(BIEvents.Keys))
                    {
                        hk = AHookWin.Keyboard(_keyHookProc ??= _KeyHookProc);
                    }
                    if (_block.HasAny(BIEvents.MouseClicks | BIEvents.MouseMoving))
                    {
                        hm = AHookWin.Mouse(_mouseHookProc ??= _MouseHookProc);
                    }
                }
                catch (AuException e1) { ADebug.Print(e1); _block = 0; return; }                //failed to hook

                //This prevents occassional inserting a foreign key after the first our-script-pressed key.
                //To reproduce, let our script send small series of chars in loop, and simultaneously a foreign script send other chars.
                ATime.DoEvents();

                //AOutput.Write("started");
                Api.SetEvent(_syncEvent);

                //the acc hook detects Ctrl+Alt+Del, Win+L, UAC consent, etc. SystemEvents.SessionSwitch only Win+L.
                try { hwe = new AHookAcc(AccEVENT.SYSTEM_DESKTOPSWITCH, 0, _winEventProc ??= _WinEventProc); }
                catch (AuException e1) { ADebug.Print(e1); }                //failed to hook

                AWaitFor.Wait_(-1, WHFlags.DoEvents, _stopEvent, _threadHandle);

                if (_blockedKeys != null)
                {
                    bool onlyUp = _discardBlockedKeys || ATime.WinMilliseconds - _startTime > c_maxResendTime;
                    _blockedKeys.SendBlocked_(onlyUp);
                }
                //AOutput.Write("ended");
            }
            finally {
                _blockedKeys = null;
                hk?.Dispose();
                hm?.Dispose();
                hwe?.Dispose();
                Api.SetEvent(_syncEvent);
            }
            GC.KeepAlive(this);
        }
예제 #2
0
            /// <summary>
            /// Waits until the target app gets (Paste) or sets (Copy) clipboard text.
            /// Throws AuException on timeout (3 s normally, 28 s if the target window is hung).
            /// </summary>
            /// <param name="ctrlKey">The variable that was used to send Ctrl+V or Ctrl+C. This function may call Release to avoid too long Ctrl down.</param>
            public void Wait(ref AKeys.Internal_.SendCopyPaste ctrlKey)
            {
                //AOutput.Write(Success); //on Paste often already true, because SendInput dispatches sent messages
                for (int n = 6; !Success;)                  //max 3 s (6*500 ms). If hung, max 28 s.
                {
                    AWaitFor.Wait_(500, WHFlags.DoEvents, null, this);

                    if (Success)
                    {
                        break;
                    }
                    //is hung?
                    if (--n == 0)
                    {
                        throw new AuException(_paste ? "*paste" : "*copy");
                    }
                    ctrlKey.Release();
                    _wFocus.SendTimeout(5000, 0, flags: 0);
                }
            }
예제 #3
0
        /// <summary>
        /// Starts UAC Medium integrity level (IL) process from this admin process.
        /// </summary>
        /// <param name="need">Which field to set in <b>Result</b>.</param>
        /// <exception cref="AuException">Failed.</exception>
        /// <remarks>
        /// Actually the process will have the same IL and user session as the shell process (normally explorer).
        /// Fails if there is no shell process (API GetShellWindow fails) for more than 2 s from calling this func.
        /// Asserts and fails if this is not admin/system process. Caller should at first call <see cref="AUac.IsAdmin"/> or <see cref="AUac.IntegrityLevel"/>.
        /// </remarks>
        internal Result StartUserIL(Result.Need need = 0)
        {
            if (s_userToken == null)
            {
                Debug.Assert(AUac.IsAdmin);                 //else cannot set privilege
                if (!ASecurity.SetPrivilege("SeIncreaseQuotaPrivilege", true))
                {
                    goto ge;
                }

                //APerf.First();
#if false //works, but slow, eg 60 ms, even if we don't create task everytime
                var s = $"\"{AFolders.ThisAppBS}{(AVersion.Is32BitProcess ? "32" : "64")}\\AuCpp.dll\",Cpp_RunDll";
                WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, AFolders.System + "rundll32.exe", s);
                //WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, AFolders.System + "notepad.exe"); //slow too
                //APerf.Next();
                int pid = WinTaskScheduler.RunTask("Au", "rundll32");
                //APerf.Next();
                //AOutput.Write(pid);
                var hUserProcess = Handle_.OpenProcess(pid);
                //AOutput.Write((IntPtr)hUserProcess);
                if (hUserProcess.Is0)
                {
                    goto ge;
                }
#else
                bool retry = false;
g1:
                var w = Api.GetShellWindow();
                if (w.Is0)                  //if Explorer process killed or crashed, wait until it restarts
                {
                    if (!AWaitFor.Condition(2, () => !Api.GetShellWindow().Is0))
                    {
                        throw new AuException($"*start process '{_exe}' as user. There is no shell process.");
                    }
                    500.ms();
                    w = Api.GetShellWindow();
                }

                var hUserProcess = Handle_.OpenProcess(w);
                if (hUserProcess.Is0)
                {
                    if (retry)
                    {
                        goto ge;
                    }
                    retry = true; 500.ms(); goto g1;
                }

                //two other ways:
                //1. Enum processes and find one that has Medium IL. Unreliable, eg its token may be modified.
                //2. Start a service process. Let it start a Medium IL process like in QM2. Because LocalSystem can get token with WTSQueryUserToken.
                //tested: does not work with GetTokenInformation(TokenLinkedToken). Even if would work, in non-admin session it is wrong token.
#endif
                //APerf.NW();

                using (hUserProcess) {
                    if (Api.OpenProcessToken(hUserProcess, Api.TOKEN_DUPLICATE, out Handle_ hShellToken))
                    {
                        using (hShellToken) {
                            const uint access = Api.TOKEN_QUERY | Api.TOKEN_ASSIGN_PRIMARY | Api.TOKEN_DUPLICATE | Api.TOKEN_ADJUST_DEFAULT | Api.TOKEN_ADJUST_SESSIONID;
                            if (Api.DuplicateTokenEx(hShellToken, access, null, Api.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, Api.TOKEN_TYPE.TokenPrimary, out var userToken))
                            {
                                s_userToken = new SafeAccessTokenHandle(userToken);
                            }
                        }
                    }
                }
                if (s_userToken == null)
                {
                    goto ge;
                }
            }

            bool suspended = need == Result.Need.NetProcess && !_NetProcessObject.IsFast, resetSuspendedFlag = false;
            if (suspended && 0 == (flags & Api.CREATE_SUSPENDED))
            {
                flags |= Api.CREATE_SUSPENDED; resetSuspendedFlag = true;
            }
            bool ok = Api.CreateProcessWithTokenW(s_userToken.DangerousGetHandle(), 0, null, cl, flags, envVar, curDir, si, out var pi);
            if (resetSuspendedFlag)
            {
                flags &= ~Api.CREATE_SUSPENDED;
            }
            if (!ok)
            {
                goto ge;
            }
            return(new Result(pi, need, suspended));

            ge : throw new AuException(0, $"*start process '{_exe}' as user");
        }