StartRestrictedProcess
标签:windows核心编程作业 |
//狼影 2013.10.5 vs2012 《windows核心编程》
//windows提供一个作业内核对象,它允许我们将进程组合在一起并创建一个“沙箱”
//(这里的沙箱实际就是对作业中
//的的进程做一些限制的意思),来限制进程能做什么,可以将作业想像成一个进程容器,创建只包含一个进程
//的作业,可以对进程施加平时不能施加的限制
//StartRestrictedProcess函数将一个进程放入了一个作业中,用来限制进程具体能够做哪些事情
#include "stdafx.h"
#include "windows.h"
#include "strSafe.h"
void StartRestrictedProcess(void);
int _tmain(int argc, _TCHAR* argv[])
{
StartRestrictedProcess();
return 0;
}
//将进程放入作业中的实现
void StartRestrictedProcess(void)
{
//课本原话是:如果进程已经于一个作业关联,就无法将当前进程或者它的任何子进程
//从作业中去除,这个安全特性可以确保进程无法摆脱对他施加的限制。
//所以先判断当前进程也就是父进程是不是已经放入了一个作业,如果是的话,
//创建的进程将无法放入别的进程中
BOOL bInJob=FALSE;
::IsProcessInJob(::GetCurrentProcess(),NULL,&bInJob);
//先来看看这个函数
//验证一个进程是否在一个指定的作业中(第二个参数为NULL表明在现有的作业中)
//第一个参数:要判断的进程的句柄;
//第二个参数:一个作业的句柄
//第三个参数:用来接收结果,如果在作业中为TRUE,否则为FALSE
if(bInJob)
{
//如果进程在一个作业的话就直接返回
MessageBox(NULL,TEXT("Process Already in a Job"),NULL,MB_OK|MB_ICONINFORMATION);
return;
}
//创意一个作业对象
HANDLE hJob = ::CreateJobObject(NULL,TEXT("LANGYING"));
//现在来看下这个函数
//第一个参数:一个指向SECURITY_ATTRIBUTES结构的指针
//第二个参数:作业对象的名称
//返回值:如果成功,则返回一个句柄,如果对象在调用 这个函数前已经存在,
//函数返回这个存在对象的句柄 GetLastError返回ERROR_ALREADY_EXISTS
//函数调用失败,返回NULL
if(hJob)
{
//现在给作业中的进程做一些限制
//施加限制的函数是:SetInformationJobObject()
//BOOL WINAPI SetInformationJobObject(
// HANDLE hJob,
// JOBOBJECTINFOCLASS JobObjectInfoClass,
// LPVOID lpJobObjectInfo,
// DWORD cbJobObjectInfoLength)
//为作业设置限制
//hJob:要设置限制的作业句柄
//JobObjectInfoClass:这是个枚举类型可以取值如下:
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms686216(v=vs.85).aspx
//它的取值表明第三个参数lpJobObjectInfo指向不同的结构
//lpJobObjectInfo:要为作业设置的限制或状态(具体取值依赖于第二个参数的取值)
//指向一个不同的数据结构
//cbJobObjectInfoLength:第三个参数的长度
//限制的分类:
//1.基本限制和扩展基本限制,用于防止作业中的进程独占系统资源
//第二个参数值:JobObjectBasicLimitInformation(枚举数据)
//第三个参数指向的结构:JOBOBJECT_BASIC_LIMIT_INFORMATION
//第二个参数值:JobObjectExtendedLimitInformation(枚举数据)
//第三个参数指向的结构:JOBOBJECT_EXTENDED_LIMIT_INFORMATION
//2.基本的UI限制,用于防止作业内的进程更改用户界面
//第二个参数值:JobObjectBasicUIRestrictions(枚举数据)
//第三个参数指向的结构:JOBOBJECT_BASIC_UI_RESTRICTIONS
//3.安全限制,用于防止作业内的进程访问安全资源(文件,注册表项)
//JOBOBJECT_SECURITY_LIMIT_INFORMATION
//MSDN:Support for this structure was removed starting with Windows Vista
//支持这个结构从Vista开始被移除
//从Windows Vista开始你必须为关联到作业的进程独立的设置安全限制。而不再是
//对作业对象做安全限制
//好啦。说了这么多现在开始下面的代码吧,做限制
//在这作者只做了两种限制 基本限制和基本的UI限制
//第一个限制: 基本限制
JOBOBJECT_BASIC_LIMIT_INFORMATION jobli={0};
//作业占用CPU时间不得查过1秒
jobli.PerJobUserTimeLimit.QuadPart = 1000;
//设置作业中进程的优先级类
jobli.PriorityClass = IDLE_PRIORITY_CLASS;
//设置结构中哪些成员被应用
jobli.LimitFlags = JOB_OBJECT_LIMIT_JOB_TIME|JOB_OBJECT_LIMIT_PRIORITY_CLASS;
//详细的信息请看:
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms684147(v=vs.85).aspx
//在这个结构中几个在这用的值解释如下:
//LimitFlags:这个成员决定这个结构的其他成员是否被用
//PerProcessUserTimeLimit:指定分配给进程最大的用户模式时间(时间间隔为100ns)
//要用这个成员变量要将成员LimitFlags包含JOB_OBJECT_LIMIT_PROCESS_TIME
//PerJobUserTimeLimit:每个作业在用户模式执行的时间限制
//要用这个成员变量要将成员LimitFlags包含JOB_OBJECT_LIMIT_JOB_TIME
//设置限制
::SetInformationJobObject(hJob,JobObjectBasicLimitInformation,&jobli,sizeof(jobli));
//第二个限制:设置基本的UI设置
JOBOBJECT_BASIC_UI_RESTRICTIONS jobui={0};
//该结构只含有一个成员UIRestrictionsClass:容纳一些标志位的集合(具体请看)
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms684152(v=vs.85).aspx
jobui.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; //值为0
//阻止进程通过调用ExWindows()或ExWindowsEx()来注销,关闭,重启,或断开电源的操作
jobui.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
//限制作业中的进程访问该作业以外的进程创建的对象
jobui.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
//设置限制:
::SetInformationJobObject(hJob,JobObjectBasicUIRestrictions,&jobui,sizeof(jobui));
//好啦。限制做完啦,现在开始创建一个进程,将它放到作业中
STARTUPINFO si={sizeof(si)};
PROCESS_INFORMATION pi;
TCHAR szCmdLine[256] = TEXT("CMD");
::CreateProcess(NULL,szCmdLine,NULL,NULL,FALSE,CREATE_SUSPENDED,
NULL,NULL,&si,&pi);
//CREATE_SUSPENDEN 标志说明在创建进程后,将主线程挂起,不要执行,
//直到调用ResumeThread()唤醒线程
//为什么要用CREATE_SUSPEND呢?
//由于StartRestrictedProcess()函数是由不属于该作业的一部分进程中执行的,
//所以子进程(在这就是CreateProcess()产生的进程)也不是作业的一部分,
//如果允许子进程立即执行代码,他会"逃离"沙箱成功的做一些我想禁止做的事情
//所以在刚创建时不让他执行,将主线程挂起,直到将进程显示的放入作业中
//然后调用ResumeThread()将其唤醒,让他执行
//创建了进程,接下来的意图很明显啦,就是进程加入到作业中
::AssignProcessToJobObject(hJob,pi.hProcess);
//现在让我们来讨论下这个函数
// BOOL WINAPI AssignProcessToJobObject(
// HANDLE hJob,
// HANDLE hProcess)
//添加进程到一个已经存在的作业中
//hJob:作业的句柄
//hProcess:进程的句柄
//现在,可以让主线程工作了
::ResumeThread(pi.hThread);
//现在,线程的句柄已经用不到了,关闭它
::CloseHandle(pi.hThread);
//等待进程终止或者所有分配给作业的CPU时间用完
HANDLE h[2];
h[0] = pi.hProcess;
h[1] = hJob;
DWORD dw = WaitForMultipleObjects(2,h,FALSE,INFINITE);
//让我们看下这个函数
// DWORD WINAPI WaitForMultipleObjects(
// DWORD nCount,
// const HANDLE * lpHandles,
// BOOL bWaitAll,
// DOWRD dwMilliseconds
// )
//等待一个或多个指定的对象在有信号状态(即终止状态) 或者
//指定的时间用完
//nCount:数组中句柄的个数
//lpHandles:盛放句柄的数组名
//bWaitAll:是否等待所有的对象为有信号状态
//dwMilliseconds:等待的时间(如果为 INFINITE表示等到指定的对象
//到有信号状态函数才返回)
//返回值:
//如果函数成功返回,这返回的值标志了引起函数返回的事件
//具体返回值请看:
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
//现在来判断是哪种原因引起了上面函数的返回,而做出不同的操作
switch(dw-WAIT_OBJECT_0)
{
case 0:
{
//进程终止
break;
}
case 1:
{
//给作业分配的CPU时间用完
break;
}
}
//现在已经将进程加到作业中,并能保证他终止或
//分配给作业的CPU时间用完l啦。接下来做什么呢?
//获得进程的信息(进程的CPU占用时间(用户模式的CPU时间和内核模式的CPU时间))
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
//FILETIME结构:
//表示了一个64位无符号的文件的日期和时间值
//此值表示自1601年1月1日开始的100纳秒为单位的时间
//typedef struct _FILETIME {
// DWORD dwLowDateTime; //低32位的文件的时间值
// DWORD dwHighDateTime;//高32位的文件的时间值
// } FILETIME, *PFILETIME;
::GetProcessTimes(pi.hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime);
//BOOL WINAPI GetProcessTimes(
// HANDLE hProcess,
// LPFILETIME lpCreationTime,
// LPFILETIME lpExitTime,
// LPFILETIME lpKernelTime,
// LPFILETIME lpUserTime
// )
//获得一个执行进程的时间信息
//hProcess:进程的句柄
//lpCreationTime:获得创建时间
//lpExitTime:进程的结束时间
//lpKernelTime:进程在内核模式执行的总时间
//lpUserTime:进程在用户模式执行的总时间
TCHAR szBuffer[MAX_PATH];
StringCchPrintf(szBuffer,_countof(szBuffer),TEXT("KernelTime=%u")
TEXT("UserTime=%u"),
KernelTime.dwLowDateTime/1000,
UserTime.dwLowDateTime/1000);
MessageBox(::GetActiveWindow(),szBuffer,NULL,MB_OK|MB_ICONINFORMATION);
//将句柄关闭
::CloseHandle(pi.hProcess);
::CloseHandle(hJob);
//关闭一个作业对象不会迫使作业中的所有进程都终止运行
//作业对象实际只是加了一个删除标记,只有在作业中的所有进程都已经终止运行
//之后,才会自动销毁,关闭作业的句柄后导致所有进程都不可访问此作业,即使这个作业
//仍然存在
}
}
浙公网安备 33010602011771号