终止进程是通过以下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
