StartRestrictedProcess

//狼影 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);
  //关闭一个作业对象不会迫使作业中的所有进程都终止运行
  //作业对象实际只是加了一个删除标记,只有在作业中的所有进程都已经终止运行
  //之后,才会自动销毁,关闭作业的句柄后导致所有进程都不可访问此作业,即使这个作业
  //仍然存在
 }
 
}

posted on 2013-10-12 10:26  言止予思  阅读(303)  评论(0)    收藏  举报