Windows学习(007)--进程

来源:互联网 发布:ubuntu 14.04 安装gcc 编辑:程序博客网 时间:2024/06/05 22:42

进程

  1. 进程的组成

    • 进程是“惰性”的,进程要做任何事情,都必须让线程来运行,线程执行进程地址空间中包含的代码。
    • 一个进程可以拥有多个线程,所有的线程都能够在进程的地址空间 中“同时”运行代码,每个线程都有他自己的CPU寄存器以及自己的堆栈。
    • 每个进程必须有一个线程,当系统创建亿个进程时,会自动创建亿个线程,这个线程我们称为主线程,如歌没有线程要执行进程地址空间的代码,进程就会失去了存在的意义,此时系统会自动销毁进程以及他的地址空间。
    • 造作系统会以轮询的方式为每个线程分配CPU时间片,从而营造出“并行”假象。
    • 如果计算机拥有多个CPU,操作系统会更复杂的方式为线程分配CPU时间。
    • 问题:在单核的情况下,为什么操作系统不以多进程,而是以多线程的方式运行程序,多线
      程和多进程不是一样吗?
      答:1,空间不一样。2,切换成本不一样。
  2. 内核对象

    操作系统用来管理进程。

  3. 地址空间

    包涵了可执行文件DLL模块的代码和数据。

应用程序类型

  1. 控制台用户界面(CUI)

    控制台程序是基于文本的,他们一般不会创建窗口或进程消息,而且不需要GUI。

  2. 图形用户界面(GUI)

    GUI程序是一个图形化的前端,它可以创建窗口,可以拥有菜单,能通过对话框与用户交互,还能更新使用所有标准的“视窗化”的东西,Windows的机会所有附件应用程序都是GUI程序。

  3. 区别

    • 在VS中,CUI程序的链接器开关为/SUBSYSTEM:CONSOLE,GUI程序的链接器开关为/SUBSYSTEM:WINDOWS,在加载时,会获取此值,如果是一个文本控制台,操作系统会使用“命令提示符”程序来启动这个程序,否则他只会加载这个,由程序自己来管理自己的窗口。
    • 入口函数不同。

      int main(int argc, char *argv[], char *envp[]);int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_ HINSTANCE hPrevInstance,_In_ LPSTR     lpCmdLine,_In_ int       nCmdShow);
    • 启动函数不同,GUI的启动函数为WinMainCRTStartup或wWinMainCRTStartup,CUI的启动函数为mainCRTStartup或wmainCRTStartup。

进程实例句柄

  1. 加载到进程地址空间的每一个执行体或DLL文件都被赋予了亿个独一无二的实例句柄。
  2. HMODULE和HINSTANCE为同一类型,无区别。
  3. 获取实例句柄
HMODULE WINAPI GetModuleHandle( _In_opt_ LPCTSTR lpModuleName);

命令行参数

获取CommandLine

    LPTSTR WINAPI GetCommandLine(void);//返回的是一个不能改的指针    LPWSTR* CommandLineToArgvW( _In_  LPCWSTR lpCmdLine, _Out_ > > int*pNumArgs);//返回是一个指针的指针,使用要释放    HLOCAL WINAPI LocalFree(_In_ HLOCAL hMem);

环境变量

  1. 环境变量相当于全局的命令行参数
  2. 获取环境变量

     strEnvi_ = GetEnvironmentStrings();
  3. 打印环境变量

    LPTCH strEnvi = strEnvi_;if (strEnvi_){    while (*strEnvi_)    {        _tprintf(TEXT("%s\n"), strEnvi_);        strEnvi_ += lstrlen(strEnvi_) + 1;    }}
  4. 删除获取到环变量指针

    FreeEnvironmentStrings(strEnvi_);

进程路径

  1. 所在路径

    • 会变化了,在VS调试中,会定义到我们源代码的位子;在运行exe时,是程序的位置。
    • 获取所在路径

           BOOL GetCurrentDirectory( LPTSTR pstrDirName, LPDWORD lpdwLen ) const;
    • 设置所在路径

           BOOL SetCurrentDirectory( LPCTSTR pstrDirName );    
  2. Module路径

    • 是来定义我们的exe程序的位置。
    • 获取Module路径

          DWORD WINAPI GetModuleFileName(      _In_opt_ HMODULE hModule,      _Out_    LPTSTR  lpFilename,      _In_     DWORD   nSize    );
  3. 当前路径(默认路径)

    • 获取当前路径

           DWORD WINAPI GetFullPathName(            _In_  LPCTSTR lpFileName,            _In_  DWORD   nBufferLength,            _Out_ LPTSTR  lpBuffer,            _Out_ LPTSTR  *lpFilePart            );
    • 设置当前目录

      • “所在目录”被修改,“当前目录”也被修改(针对于盘)。比如,c盘的”所在目录”被更改,d盘的“所在目录“不被更改,则,c盘的”当前目录”就会被更改,d盘的”当前目录“”不被更改,反之亦然。
      • 在环境变量里面,设置了当前目录。所以我们可以通过更改环境变量来更改当前目录。
          BOOL WINAPI SetEnvironmentVariable(  _In_     LPCTSTR lpName,  _In_opt_ LPCTSTR lpValue    );

创建进程(CreateProcess)

  • 函数原型

    BOOL WINAPI CreateProcess(  _In_opt_    LPCTSTR               lpApplicationName,  _Inout_opt_ LPTSTR                lpCommandLine,  _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,  _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,  _In_        BOOL                  bInheritHandles,  _In_        DWORD                 dwCreationFlags,  _In_opt_    LPVOID                lpEnvironment,  _In_opt_    LPCTSTR               lpCurrentDirectory,  _In_        LPSTARTUPINFO         lpStartupInfo,  _Out_       LPPROCESS_INFORMATION lpProcessInformation);
  • 函数概述

    • 创建一个内核对象,进程内核对象并不是代表进程本身,它是操作系统用来管理这个进程的数据结构,该结构当中有一个使用计数,会在进程被创建时候设置为,然后系统会为新进程穿件一个虚拟地址空间,将所需的代码遗迹数据加载到进程的地址空间当众后,系统会为新进程的主线程创建一个线程内核对象,主线程最终会调用新进程的main函数.

    • 如果系统成功创建了新的进程和主线程,函数会返回true.

  • 参数详解

    • pszApplicationName

      指定信进程要使用的执行体文件的名称

      1. 当在pszApplicationName中传一个文件路径时,如果给的是一个相对路径,那么他只会在当前目录(指的是exe所在的目录)下来进行查找
      2. 当这个值为NULL时,pszCommandLine会被作为命令行参数传
    • pszCommandLine

      传给新进程的命令行字符串

      • 如果pszApplicationName为NULL,我们可以在pszCommandLine中传递一个文件的路径,如果我们传的的是一个相对路径,它会按照一下顺序来搜索:

        1. 所在目录
        2. 当前目录
        3. Windows系统目录(System32目录)
        4. Windows目录
        5. Path环境变量中列出的目录
      • pszCommandLine参数还有一个要注意的地方,CreateProcess内部会修改这个参数,所以我们不应该传入一个常量指针

    • psaProcess

      设置进程内核对象的安全属性

    • psaThread

      设置主线程内核对象的安全属性

    • bInheritHandles

      是否被继承

    • fdwCreate

      进程创建方式标志

      CREATER_NO_WINDOW

      标志指示系统不要为应用程序创建任何控制台窗口.可以使用这个标志来知行没有用户界面的控制台程序.

    • pvEnvironment

      pvEnvironment参数指向一个内存块,其中包含了信进程要使用的环境字符串.大多是时候,这个参数传入的值都是NULL,这导致子进程继承其父进程使用的一组环境字符串.另外,还可以使用GetEnvironmentStrings函数.

    • pszCurDir

      pszCurDir参数允许父进程设置子进程的当前驱动器和目录,如果这个参数为NULL,则新进程的工作目录与新进程的应用程序一样.

    • psiStartInfo

          typedef struct _STARTUPINFO     {     DWORD cb;             //初始化为sizeof ( STARTUPINFO )     PSTR lpReserved;    //保留。必须初始化为N U L L     PSTR lpDesktop;     //启动程序的桌面名称    PSTR lpTitle;            //用于设定控制台窗口的名称。    DWORD dwX;         //屏幕位子    DWORD dwY;         //屏幕位子    DWORD dwXSize;  //屏幕宽度    DWORD dwYSize;  // 屏幕高度    DWORD dwXCountChars;  //控制台可用,控制台宽度    DWORD dwYCountChars;  //控制台可用,控制台高度    DWORD dwFillAttribute;   //用于设定子应用程序的控制台窗口使用的文本和背景颜色     DWORD dwFlags;               //组合值    WORD wShowWindow;     //窗口可用,窗口如何显示    WORD cbReserved2;         //保留。必须被初始化为0     PBYTE lpReserved2;           //保留。必须被初始化为N U L L     HANDLE hStdInput;          //设置标准输入    HANDLE hStdOutput;      //设置标准输出    HANDLE hStdError;          //设置标准错误输出    } STARTUPINFO, *LPSTARTUPINFO;    //dwFlags    STARTF_USESIZE                 // 使用dwXSize 和dwYSize 成员     STARTF_USESHOWWINDOW           //使用wShowWindow 成员     STARTF_USEPOSITION             //使用dwX 和dwY 成员     STARTF_USECOUNTCHARS           //使用dwXCountChars 和dwYCount Chars 成员     STARTF_USEFILLATTRIBUTE        //使用dwFillAttribute 成员     STARTF_USESTDHANDLES           //使用hStdInput 、hStdOutput 和hStdError 成员     STARTF_RUNFULLSCREEN          //强制在x86 计算机上运行的控制台应用程序以全屏幕方式启动运行    STARTF_FORCEOFFFEEDBACK        //改变鼠标为等待    STARTF_FORCEONFEEDBACK         //不改变鼠标为等待    STARTF_PREVENTPINNING          //窗口无法被固定在任务栏上    STARTF_TITLEISAPPID            //设置任务栏和开始菜单的状态
    • ppiProcInfo

      typedef struct _PROCESS_INFORMATION {HANDLE hProcess;HANDLE hThread;DWORD  dwProcessId;DWORD  dwThreadId;} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

关闭进程

  1. 入口函数返回

    最正确的方式,只有这样才能保证主线程的所有资源都已经被正确的释放

    入口函数在返回时,为确保以下几件事情已经完成:

    • 主线程所创建的任何对象都已经被正确的销毁
    • 操作系统会正确的释放线程的堆栈
    • 将进程的退出代码设置成入口函数的返回值
    • 递减内核对象的使用计数
  2. 进程中的一个线程调用ExitProcess

    不应调用此函数来结束进程,当函数被调用的时候会被强制结束进程,并将退出代码设置为uExitCode.但此时的线程并未正确结束,会导致线程无法被正确清理

    VOID ExitProcess(UINTuExitCode);
  3. 另一个进程中的线程调用TerminateProcess

    不应该用此函数来结束进程,此函数能够结束其它进程

    BOOL TerminateProcess(    HANDLE hProcess,    DWORD uExitCode)
  4. 进程中的所有线程都自然死亡

    很少碰到这种情况,理论纯在

引用计数

  • 内核对象的清理,取决内核对象里面的引用计数,内核对象里面的引用计数不为0,内核对象就不会被清理.如果的使内核对象堆积,不被清理,会导致内核所使用的内存不足,从而导致系统崩溃.
  • 比如上述ExitProcess函数,使进程退出了,但不会改变进程的内核对象里面的引用计数,从而导致这个进程被强制退出了,但是这个进程的内核对象没有被清理.
  • 我们来想想这个情景,如果我们在进程A中,用CreateProcess创建了一个进程B,那么当进程B退出时,B进程的退出代码会被得到吗?既然这个进程消亡了,那么这个进程的所有东西都没意义了,内核对象没了,退出代码获取不到.其实不然.
  • CreateProcess函数里面存在的坑.当该函数创建了一个进程后,这个进程的引用计数被设置成了2.因为在创建了一个进程,这个内核对象的引用计数被加1,然后在给我们的PROCESS_INFOMATION赋值时也会给引用计数加1.
  • 这样当上述B进程结束时,B进程的内核对象的引用计数就会被减1,被设置为1,没有被释放,B进程退出后,我们仍可以使用B的内核对象获取B进程的退出代码了.
  • 然后再将B进程的内核对象引用计数减1,使用CloseHandle.这样B的引用计数就成了0,从而导致内核对象被释放了.

进程权限

  1. DOS

    • 在DOS下,直接操作物理内存,操作系统也好,进程也好,处于同一等级.
    • 会造成系统不稳定
    • 进程毫无保密性
  2. 虚拟内存

    • 杜绝了操作系统物理内存或是操作系统代码的可能,保证了系统的稳定性
    • 进程是完全封闭的,A进程,B进程毫无关联.但是我们软件需求,导致进程开放了一些接口
    • 在开放了这些接口后,你必须要拥有一定的权限(用户赋予),才能进行高危操作
  3. XP

    • 在XP下,权限被分为管理员权限和用户权限.
    • 分了之后发现这种划分并不明显,有些软件在进行操作时,会非常麻烦
    • 这就会导致了,电脑上的大多数软件都拥有管理员权限,这就造成权限划分无意义.
    • 在进程中有这样一个特性,一个拥有管理员权限的进程,创建了一个子进程,那么这个子进程就继承父进程的权限,拥有了管理员权限.以前就有网页木马利用这个机制,比如,IE(管理员权限)支持一个插件功能,它会有自动下载和安装功能,因为IE会为此创建一个进程,那么这个插件也会继承管理员权限,也不会有提示
  4. UAC

    • 带过滤表的权限
    • UAC不会拥有管理员的所有操作
    • 如果要求进程提升权限,那么必须在进程的边界上进行提升.进程边界是指的是在进程启动之时,来提升权限对进程进行提权,操作都是单次的
  5. 通过RT_MANIFEST提升权限

    值 效果 requireAdministrator 程序必须以管理员权限启动;否则不会运行 highestAvailable 程序以当前可用的高权限运行.如果用户使用一个管理管理账户登录,会出现要求批准提升权限的一个对话框.如果用户使用亿个普通用户账户登录,应用程序就用这些标准权限来启动(不会提示用户提升权限) asinvoker 程序使用与主调程序一样的权限来启动
  6. 提权操作

    BOOL ShellExecuteEx(_Inout_ SHELLEXECUTEINFO *pExecInfo);typedef struct _SHELLEXECUTEINFO {DWORD     cbSize;ULONG     fMask;HWND      hwnd;LPCTSTR   lpVerb;LPCTSTR   lpFile;LPCTSTR   lpParameters;LPCTSTR   lpDirectory;int       nShow;HINSTANCE hInstApp;LPVOID    lpIDList;LPCTSTR   lpClass;HKEY      hkeyClass;DWORD     dwHotKey;union {HANDLE hIcon;HANDLE hMonitor;} DUMMYUNIONNAME;HANDLE    hProcess;} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
  7. 权限继承

    管理员程序->CreateProcess->子程序正常运行(继承提升后的权限)
    UAC程序->CreatrProcee->子程序执行失败(ERROR_ELEVATION_REQUIRED)

原创粉丝点击