Windows进程实践技巧:父子进程,错误模式,自动提升权限,作业管理

该文章大量文案使用ChatGPT生成

Windows系统下,什么是环境变量?进程的环境变量和系统的环境变量有什么关系?
在Windows系统下,环境变量是一种包含在操作系统环境中的特殊变量,它可以影响某些程序的运行。环境变量是存储在系统的注册表中的键值对,包含了操作系统和应用程序所需的各种信息。比如在配置一些开发环境(比如Python)时,需要设置path环境变量,才可以在命令行里直接启动python开发环境。这本质是告诉命令行程序python.exe的路径,从而让命令行自动检索用户输入的应用程序的所在位置。

系统的环境变量存储在Windows注册表的HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment键下。系统管理员可以通过编辑这个键来添加、修改或删除系统的环境变量。进程可以通过调用GetEnvironmentVariables函数读取系统的环境变量,或通过调用SetEnvironmentVariable函数修改或添加环境变量。

进程的环境变量和系统的环境变量之间存在继承和覆盖的关系。每个进程都维护着自己的环境变量,称为进程的环境变量。每个进程可以通过操作系统提供的接口来读取和修改自己的环境变量。进程的环境变量一般从系统的环境变量中继承而来,进程启动时会拷贝一份系统的环境变量作为进程自身的环境变量。

如果进程要修改系统的环境变量,必须拥有管理员权限。修改系统的环境变量后,所有后续创建的新进程都会从新的系统环境变量中继承相关的设置。如果进程具有父进程,它会从父进程继承一份环境变量并在此基础上修改。

总之,环境变量是一种在Windows系统中用于存储操作系统和应用程序配置信息的技术。系统的环境变量是所有进程共享的设置,而进程的环境变量是独立于其他进程的,由每个进程自行管理。环境变量在程序的运行中起到重要作用,开发者可以使用环境变量来配置程序的行为,便于程序的管理和维护。

如何提取环境块并输出到数组中,如何查询/修改/添加环境变量?
可以使用GetEnvironmentStrings函数来获取完整的环境块,注意返回值是PTSTR ,一般需要逐个解析并存储到键值对或者其他结构的变量中再去使用。
GetEnvironmentVariable则可以检查某个具体的变量是否存在,并获取变量值。SetEnvironmentVariable则用于设置,若已有则会进行覆盖。注意,这里提到的API操作的是进程的环境变量,如何修改系统环境变量则不在此赘述。

进程的错误模式
SetErrorMode函数可以为进程设置一组标志,这些标志的作用是让系统知道进程如何响应严重错误,包括磁盘介质错误、未处理的异常、文件查找错误以及数据对齐错误等。SetErrorMode函数来告诉系统如何处理这些错误。启用SEM_FAILCRITICALERRORS标志位,可以使应用程序忽略所有的致命错误,并返回默认的错误值。这常常用于一些比较稳定的服务程序,如服务器等,因为当这些程序遇到致命错误时,可以自动忽略错误并继续工作,从而保证系统的稳定性和可用性。

"致命错误"通常指的是可能会中断进程执行的错误,例如访问非法内存、不受支持的指令,或者硬件故障等。这些错误通常会导致进程崩溃或者异常终止,从而影响到应用程序的正常运行。

使用 SetErrorMode 函数可以将当前进程的错误模式设置为 SEM_FAILCRITICALERRORS 标志位,这样当进程遇到致命错误时,操作系统会将错误信息记录到事件日志中,并返回一个默认的错误值,而不是直接崩溃。这样的处理方式可以增强应用程序的稳定性和可靠性,避免可能的数据损坏和系统崩溃。

需要注意的是,启用 SEM_FAILCRITICALERRORS 标志位并不一定能够避免程序崩溃,因为存在一些致命错误确实无法通过忽略错误方式进行处理,可能最终仍然会导致程序崩溃。例如,如果程序持续非法访问内存,就有可能导致内存分配器无法继续为程序分配内存导致进程崩溃。

然而,在一些需要保证应用程序稳定性和可用性的场合下,设置 SEM_FAILCRITICALERRORS 标志位仍然有一定的意义,因为它可以尽可能地避免因为一些可控的致命错误而导致程序直接崩溃。在这种情况下,虽然程序仍然可能会存在一些问题,但是它们不会导致程序直接崩溃,从而减小了对用户的影响。

总之,启用 SEM_FAILCRITICALERRORS 标志位需要根据具体情况进行权衡和评估,如果能够避免程序直接崩溃,并且不会给用户带来过大的负面影响,那么启用该标志位是有意义的。但在一些特殊情况下,如在程序中采用了非法操作等情况下,启用该标志位可能反而会带来更加严重的后果。

CreateProcess函数解析
函数签名如下所示

BOOL CreateProcess(
  PCTSTR pszApplicationName,
  PTSTR pszCommandLine,
  PSECURITY_ATTRIBUTES psaProcess,
  PSECURITY_ATTRIBUTES psaThread,
  BOOL bInheritHandles,
  DWORD fdwCreate,
  PVOID pvEnvironment,
  PCTSTR pszCurDir,
  PSTARTUPINFO psiStartInfo,
  PPROCESS_INFORMATION ppiProcInfo);

笔者注意到自己机器上的CreateProcess函数的参数名与《Windows核心编程》上的略有不同,但参数类型和顺序,数量是基本一致的。相信这只是Windows系统版本更新带来的差异,不影响理解和使用。这里按照书上的函数接口标准来讲解。

一般情况下,建议pszApplicationName传NULL,而在pszCommandLine中加入需要启动的可执行文件名。当CreateProcess解析pszCommandLine字符串时,它会检查字符串中的第一个标记(token),并假定此标记是我们想运行的可执行文件的名称。如果可执行文件的名称没扩展名,就会默认是.exe扩展名。

关于psaProcess,psaThread和bInheritHandles参数,一般主要关注的是内核句柄继承问题。psaProcess和psaThread代表进程和线程的安全描述符,一般传NULL即可[注1]。bInheritHandles传TRUE,则会让子进程继承父进程的句柄。在调用CreateProcess时,系统会为子进程分配一个进程内核对象和线程内核对象,如果需要指定子进程是否能够继承这两个内核对象,则可以在psaProcess和psaThread参数中指定。(psaProcess和psaThread参数的类型为SECURITY_ATTRIBUTES结构体,里面有一个bInheritHandle参数,通过这个参数去设置。注意不要和CreateProcess的bInheritHandles参数弄混)

关于fdwCreate参数,它标识了影响新进程创建方式的标志。多个标志可以使用按位或起来,以便同时指定多个标志组合。笔者觉得比较值得关注的标志位有这几个:CREATE_SUSPENDED标志让系统在创建新进程的同时挂起其主线程。这样一来,父进程就可以修改子进程地址空间中的内存,更改子进程的主线程的优先级,或者在进程执行任何代码之前,将此进程添加到一个作业(job)中;CREATE_BREAKAWAY_FROM_JOB标志允许一个作业中的进程生成一个和作业无关的进程;EXTENDED_STARTUPINFO_PRESENT标志向操作系统表明传给psiStartInfo参数的是一个STARTUPINFOEX结构。至于什么是作业,什么是STARTUPINFOEX结构,我们稍后会提及。

pvEnvironment参数指向一块内存,其中包含新进程要使用的环境字符串。大多数时候,为这个参数传入的值都是NULL,这将导致子进程继承其父进程使用的一组环境字符串。

psiStartInfo参数指向一个STARTUPINFO结构或STARTUPINFOEX结构。这个参数个人认为比较重要,值得关注.

typedef struct _STARTUPINFO(
  DWORD cb;
  PSTR lpReserved;
  PSTR lpDesktop;
  PSTR lpTitle;
  DWORD dwx;
  DWORD dwY;
  DWORD dwxSize;
  DWORD dwYSize;
  DWORD dwXCountChars;
  DWORD dwYCountChars;
  DWORD dwFillAttribute;
  DWORD dwFlags;
  WORD wShowwindow;
  WORD cbReserved2;
  PBYTE lpReserved2;
  HANDLE hStdInput;
  HANDLE hStdOutput;
  HANDLE hStdError;
}STARTUPINFO,*LPSTARTUPINFO;

typedef struct _STARTUPINFOEX {
  STARTUPINFO StartupInfo;
  struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;
} STARTUPINFOEX,*LPSTARTUPINFOEX;

数据结构中每一个参数的具体参数可以参考<u>https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa</u><u>https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-startupinfoexa</u>。这里不进行赘述。总的来说,这个参数控制了进程启动时的一些行为,如果传入的是STARTUPINFO结构,包括:在那个桌面上启动应用程序,控制台的标题,在屏幕上的位置(xy坐标),指定窗口宽高等。而对于STARTUPINFOEX结构,就比较有意思了。可以看到,STARTUPINFOEX结构一个成员是STARTUPINFO结构,另一个成员是一个List指针,指向的List则标识了进程的额外属性。目前官方文档中包含了两个属性键:PROC_THREAD_ATTRIBUTE_HANDLE_LIST用于标识子进程具体应该继承哪一些内核对象;PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 则用于标识子进程应该成为哪一个进程的子进程(而非实际调用CreateProcess的进程)。

ppiProcInfo参数指向一个PROCESS_INFORMATION结构,里面包含了进程句柄和线程句柄,以及它们的id。

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

进程与线程的退出
应该保证只有在主线程的入口点函数返回之后,这个应用程序的进 程才终止。只有这样,才能保证主线程的所有资源都被正确清理。通过进程中的一个线程调用ExitProcess和ExitThread函数,另一个进程中的线程调用TerminateProcess和TerminateThread函数,虽然也能让进程和线程退出,但可能会导致一些副作用。就操作系统而言,这样做是没有什么问题的,进程或线程的所有操作系统资源都会被正确清理。不过,C/C++应用程序应避免调用这些函数,因为C/C++运行库也许不能执行正确清理工作。比如,线程中正在执行的函数未从调用栈返回,一些类的析构函数没有被调用,而这些没有被执行的代码,可能导致一些业务逻辑没有被完整的执行,比如可能需要在析构函数中回写和刷新文件输出流,做信息收集等。

进程的权限
Windows进程有四种权限,一般我们只用关心两种:普通权限和管理员权限[注2]。如果需要在进程启动时默认通过UAC(User Access Control)获取管理员权限,一般有两种方式。一种是使用manifest文件,嵌入到可执行文件中,作为一种特殊的资源;另一种方式则是使用ShellExcecuteEx函数,在程序中显式地使用管理员权限启动进程。
有意思的是,在实际开发中会发现,使用管理员权限启动的进程,似乎会接收不到外部进程的消息,表现可以为文件拖拽到窗口中没反应等。这是因为,在Windows系统中,管理员权限启动的进程默认拥有更高的权限,而低权限的进程无法向其发送消息。这种行为可以防止低权限的进程向管理员权限启动的进程发送恶意消息或指令,从而保护系统的安全性。
如果确实需要低权限的进程与管理员权限启动的进程进行消息通信,可以通过一些方式来解决,如使用管道、共享内存等机制进行进程间通信,或者通过改变权限来实现。不过需要注意的是,这样做可能会对系统的安全性产生一定的影响,因此需要谨慎使用。

使用作业机制对多个进程进行管理
Windows系统的作业机制(Job Objects)提供了以下能力:

  • 进程管理。可以创建和管理多个进程,控制进程的运行权限和资源消耗,避免进程耗尽系统资源,导致系统崩溃。
  • 进程监管。可以监控进程的行为,如CPU、内存、磁盘和网络等资源的使用情况,并对异常情况做出响应。例如,当某个进程占用了大量CPU资源时,可以让系统暂停或终止该进程,避免对其他进程或系统造成影响。 - 安全保障。可以实现对进程访问权限和权限分离管理,避免恶意程序对系统造成攻击和损坏。
  • 资源分配。可以控制系统资源的分配、定时任务的执行和内存的分配等,从而提高系统的资源利用率和效率。
    通过Job Objects的作业机制,可以有效提高系统的安全性、稳定性和性能等。它可以解决多进程协作、资源分配和作业监管等问题,使系统更加可靠和高效。

尾注:
[注1]:在 Windows 操作系统中,SECURITY_ATTRIBUTES 结构和安全描述符(Security Descriptor)用于控制安全性和权限访问。它们是用于实现 Windows 安全模型的核心组成部分。SECURITY_ATTRIBUTES 结构是一个用于在创建进程、文件、共享内存等对象时指定访问控制列表(ACL)的结构。该结构包括三个字段,分别是 nLength、lpSecurityDescriptor 和 bInheritHandle 。其中,nLength 字段表示该结构的大小;lpSecurityDescriptor 字段表示指向 SECURITY_DESCRIPTOR 结构的指针;bInheritHandle 字段指示该对象是否可以被继承。该结构可以用于指定应用程序对象的特定安全性和访问权限。安全描述符是一个包含有关对象安全性信息的结构。它描述了一个命名资源的访问控制策略(Access Control Policy,简称:ACP),包括一个安全标识符(Security Identifier,SID)、一个自主拥有的 SID、系统资源的访问控制列表(ACL)和一个安全描述符控制权(Security Descriptor Control,SDC)。该结构定义了文件、目录、共享、服务、进程等资源的安全访问规则。在 Windows 操作系统中,每个对象都有一个安全描述符。安全描述符内部存储了对象的 SACL、DACL、owner SID 以及 group SID。DACL是控制访问对象的重要部分,它定义了哪些主体(如用户或组)可以访问对象、以及哪些权限他们可以获得。SACL是用于审计的可选部分,它定义了哪些操作对对象的访问需要进行审核。通过设置权限、使用组策略等安全管理工具,管理员可以使用安全描述符和安全属性来管理 Windows 系统中的安全性。这是实现系统安全性和权限的必要环节。

[注2]:Windows进程的权限主要有以下几种: System权限,拥有系统级别的完全权限,可以访问和修改系统资源、系统数据、硬件信息等一切资源; Administrator权限,拥有大部分管理权限,可以访问和修改该计算机上的所有数据和应用程序; User权限,普通用户权限,只能访问和修改自己的个人文件和软件,不能修改系统核心文件和所有用户的数据、应用程序; Guest权限,访客权限,允许访问和修改的内容更少。对进程权限进行管理的意义在于:高系统安全性,通过限制进程权限,可以避免恶意软件修改系统核心文件、窃取用户数据等行为,提高系统安全性;管理软件行为,通过管理进程权限,可以限制某些软件的操作,例如禁止某些程序在系统启动时自动运行,避免系统资源被占用;
保护用户隐私,通过限制进程权限,可以避免开发人员在未经用户允许的情况下访问用户数据和隐私。总之,进程权限管理是系统安全和用户隐私保护的必要措施。

参考:
《Windows核心编程》
https://openai.com/blog/chatgpt

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容