进程的结束和UAC的权限提升

    技术2025-10-23  12

    终止进程是通过以下4种方式来实现的:

         *主线程入口点函数返回.(推荐)     *进程中线程调用ExitProcess函数(避免)     *另一个进程中线程调用TerminateProcess函数(避免)*     进程,自然死亡(几乎不可能)      一、主线程的入口点函数返回 主程序入口点函数返回之后,这个应用程序的进程才终止.          主线程的入口点函数返回,保证以下操作会被执行.        1.线程创建任何C++对象都将由这些对象的析构函数正确销毁.        2.正确释放线程栈使用内存.        3.将进程的退出代码,设为入口点函数的返回值.        4.递减进程内核对象使用计数.              二.ExitProcess函数:        进程中的线程调用ExitProcess函数时终止:      VOID ExitProcess(UINT fuExitCode);               终止进程,并将进程的退出代码设为fuExitCode。ExitProcess不会返回值当主线程入口点函数返回时,      会返回C/C++运行库启动代码,后者将正确清理进程使用的全部C运行时资源.释放了C运行时资源之后,      C运行时启动代码将显式调用ExitProcess,将入口点函数返回的值传给它.为什么只需要从主线程入口点函数返回,      就会终止整个进程.不管进程中是否还有其他线程在运行,只要应用程序的主线程从它的入口点函数返回,      C/C++运行库就会调用ExitProcess来终止进程.调用是ExitThread,或者入口点函数直接返回,应用程序的主线程将停止执行,      但只要进程中还有其他线程正在运行,进程就不会终止.      调用ExitProcess或ExitThread会导致进程或线程直接运行-再也不会返回当前函数调用.               C/C++应用程序应避免用这些函数,因为C/C++运行库也许不能执行正确清理工作.

          #include <windows.h>      #include <stdio.h>

          class CSomeObj {       public:           CSomeObj() { printf("Constructor/r/n"); }           ~CSomeObj() { printf("Destructor/r/n"); }       };

           CSomeObj g_GlobalObj;

         void main () {     CSomeObj LocalObj;     ExitProcess(0);    // This shouldn't be here

               // At the end of this function, the compiler automatically added           // the code necessary to call LocalObj's destructor.           // ExitProcess prevents it from executing.     }

        执行上述代码,会显示以下结果:

        Constructor    Constructor

        ExitProcess造成进程"当场终止运行";C/C++运行时没有机会执行清理工作.    任何时候都不要显式地调用ExitProcess.再运行这个程序就会得到以下结果...

        Constructor    Constructor    Destructor    Destructor

    三、 TerminateProcess函数          TerminateProcess函数可终止一个进程       Bool TerminateProcess(               HANDLE Process, // 制定终止进程句柄               UINT fuExitCode // 退出代码值,全传给fuExitCode参数       );

       任何线程可通过TerminateProcess结束另一个或自己的进程。   只有无法通过其它方法来强制进程退出时,才使用TerminateProcess   另外TerminateProcess函数是异步的,它告诉系统我们希望进程终止,   但函数返回时候,系统并不能保证进程已经被强行终止?   为了确定进程是否已终止应调用WaitForSingleObject或者一个类似的函数   并将进程句柄传给它。四、进程终止运行时       用过调用GetExitCodeProcess获得已经终止的一个进程退出代码

       BOOL GetExitCodeProcess(   HANDLE hProcess,   PDWORD pdwExitCode);

       函数会查找进程内核对象,并从内核对象的数据   结构中提取用于标识进程退出代码的成员。   任何时候都可调用此函数,调用GetExitCodeProcess进程还没终止   将用STILL_ACTIVE 标识符,来填充DWORD如果进程已经终止,就返回实际退出代码值。

    五、子进程

       Windows 提供几种不同进程间传递数据方式包括动态数据交换(DDE)、OLE、管道、邮件槽等。共享数据最好方式之一使用内存映射文件。   PROCESS_INFORMATION pi;   DWORD dwExitCode; // 退出代码

    // Spawn the child process.BOOL fSuccess = CreateProcess(..., &pi);if (fSuccess) {

       // Close the thread handle as soon as it is no longer needed!   CloseHandle(pi.hThread); // 关闭主线程内核对象句柄

       // Suspend our execution until the child has terminated.   WaitForSingleObject(pi.hProcess, INFINITE);   // 等待hObject参数被触发

       // The child process terminated; get its exit code.   GetExitCodeProcess(pi.hProcess, &dwExitCode);   // 获取子进程的退出代码

       // Close the process handle as soon as it is no longer needed.   CloseHandle(pi.hProcess);   // 关闭主进程内核对象句柄}

    WaitForSingleObject 函数声明:DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeout);

    运行独立子进程Windows 进程间的关系:如当Windows资源管理器为用户创建一个新的进程,之后就不再关系这个进程是否继续存在,也不关心用户是否要终止它。   为断绝与子进程所有联系,Windows资源管理器必须调用 CloseHandle来关闭新进程及其主线程句柄。

    五、管理员以标准用户权限运行时(UAC)

       为什么UAC,不一定询问,然后把安全数据保存到系统中,并让用户下次可不经过提示直接运行呢?   如果提供这样一个安全数据库。那么数据被入侵了怎么办呢?      任务管理器上的用户提示权限     实际上你注意这任务管理器,在点击后的PID是不一样的,这意味着任务管理器,生成它的另一个实例。六、自动提升进程权限   如果在应用程序中可执行文件中嵌入一种特殊资源(RT_MANIFEST)其中系统会检查<trustInfo>段,下面是示例清单文件的<trustInfo>段。我们可以将清单保存到可知性文件所在目录中,名称和可执行文件相同且扩展名使用 *.manifest那么效果也是一样的。不过这个清单须要在注销系统后生效,可知性文件嵌入清单的优先权会比外部清单文件大。

    ...<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">    <security>      <requestedPrivileges>         <requestedExecutionLevel            level="requireAdministrator"         />       </requestedPrivileges>    </security></trustInfo>...

    可能有3个不同的值                         level 属性的值    值                                 描述requireAdministrator     程序必须以管理员权限启动;否则不会运行highestAvailable         使用管理员帐户登入,就会出现一个要求批准提升权限的对话框

                             使用管理员帐户登入,会出现一个要求批准提升权限的对话框                         普通用户帐户登录,应用程序就会用这些标准权限来启动(不会提示用户提升权限)asInvoker                应用程序使用与主调进程应用程序一样的权限来启动。七、手动提升进程权限      shellExecutEx函数

    BOOL ShellExecuteEx(LPSHELLEXECUTEINFO pExecInfo);

    typedef struct _SHELLEXECUTEINFO {    DWORD cbSize;    ULONG fMask;    HWND hwnd;    PCTSTR lpVerb;      // 必须设为runas    PCTSTR lpFile;      // 提升后的权限启动一个可执行文件路径    PCTSTR lpParameters;    PCTSTR lpDirectory;    int nShow;    HINSTANCE hInstApp;    PVOID lpIDList;    PCTSTR lpClass;    HKEY hkeyClass;    DWORD dwHotKey;    union {        HANDLE hIcon;        HANDLE hMonitor;    } DUMMYUNIONNAME;    HANDLE hProcess;} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;

    ========================================================================   实用例子:

    // Initialize the structure.SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };

    // Ask for privileges elevation.sei.lpVerb = TEXT("runas");

    // Create a Command Prompt from which you will be able to start// other elevated applications.sei.lpFile = TEXT("cmd.exe");

    // Don't forget this parameter; otherwise, the window will be hidden.sei.nShow = SW_SHOWNORMAL;

    if (!ShellExecuteEx(&sei)) {   DWORD dwStatus = GetLastError();

       if (dwStatus == ERROR_CANCELLED) {      // The user refused to allow privileges elevation.   }   else   if (dwStatus == ERROR_FILE_NOT_FOUND) {     // The file defined by lpFile was not found and     // an error message popped up.   }}

    用户拒绝提升权限,shellExecutEx返回Flase,GetLastError通过使用一个ERROR_CANCELLED值来指出情况   当父进程以已经用shellExecutEx函数取得相关权限时,有继承权的子进程就不要在用CreateProcess函数去申请同样释放权限,否则调用会失败的,GetLastError会返回 ERROR_ELEVATION_REQUIRED

    八、 何为当前权限上下文

       我们可以通过GetProcessElevation函数 来返回提升类型和一个指出进程是否正以管理员身份运行的布尔值。

       BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE* pElevationType, BOOL* pIsAdmin) {   HANDLE hToken = NULL;   DWORD dwSize;

       // Get current process token   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))      return(FALSE);

       BOOL bResult = FALSE;

       // Retrieve elevation type information   if (GetTokenInformation(hToken, TokenElevationType,      pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {      // Create the SID corresponding to the Administrators group      BYTE adminSID[SECURITY_MAX_SID_SIZE];      dwSize = sizeof(adminSID);      CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID,         &dwSize);

          if (*pElevationType == TokenElevationTypeLimited) {         // Get handle to linked token (will have one if we are lua)         HANDLE hUnfilteredToken = NULL;         GetTokenInformation(hToken, TokenLinkedToken, (VOID*)            &hUnfilteredToken, sizeof(HANDLE), &dwSize);

             // Check if this original token contains admin SID         if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin)) {            bResult = TRUE;         }

             // Don't forget to close the unfiltered token         CloseHandle(hUnfilteredToken);      } else {         *pIsAdmin = IsUserAnAdmin();         bResult = TRUE;      }   }

       // Don't forget to close the process token   CloseHandle(hToken);

       return(bResult);}

    其中GetTokenInformation使用的与进程关联的安全令牌和 TokenElevationType 参数获得提升类型,提升类型的值由TOKEN_ELEVATION_TYPE 枚举类型来定义。

    TOKEN_ELEVATION_TYPE 的值          值                             说明TokenElevationTypeDefault         默认运行用户运行,UAC被禁用TokenElevationTypeFull            权限被成功提升,而且令牌没有被筛选过TokenElevationTypeLimited         进程以受限的权限运行,它对应于一个筛选过的令牌

     

     

     

    http://hi.baidu.com/csw8923/blog/item/c1f329a98587edf61f17a206.html

    最新回复(0)