Windows進程實踐技巧:父子進程,錯誤模式,自動提升權限,作業(yè)管理

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

Windows系統(tǒng)下,什么是環(huán)境變量?進程的環(huán)境變量和系統(tǒng)的環(huán)境變量有什么關系?
在Windows系統(tǒng)下,環(huán)境變量是一種包含在操作系統(tǒng)環(huán)境中的特殊變量,它可以影響某些程序的運行。環(huán)境變量是存儲在系統(tǒng)的注冊表中的鍵值對,包含了操作系統(tǒng)和應用程序所需的各種信息。比如在配置一些開發(fā)環(huán)境(比如Python)時,需要設置path環(huán)境變量,才可以在命令行里直接啟動python開發(fā)環(huán)境。這本質(zhì)是告訴命令行程序python.exe的路徑,從而讓命令行自動檢索用戶輸入的應用程序的所在位置。

系統(tǒng)的環(huán)境變量存儲在Windows注冊表的HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment鍵下。系統(tǒng)管理員可以通過編輯這個鍵來添加、修改或刪除系統(tǒng)的環(huán)境變量。進程可以通過調(diào)用GetEnvironmentVariables函數(shù)讀取系統(tǒng)的環(huán)境變量,或通過調(diào)用SetEnvironmentVariable函數(shù)修改或添加環(huán)境變量。

進程的環(huán)境變量和系統(tǒng)的環(huán)境變量之間存在繼承和覆蓋的關系。每個進程都維護著自己的環(huán)境變量,稱為進程的環(huán)境變量。每個進程可以通過操作系統(tǒng)提供的接口來讀取和修改自己的環(huán)境變量。進程的環(huán)境變量一般從系統(tǒng)的環(huán)境變量中繼承而來,進程啟動時會拷貝一份系統(tǒng)的環(huán)境變量作為進程自身的環(huán)境變量。

如果進程要修改系統(tǒng)的環(huán)境變量,必須擁有管理員權限。修改系統(tǒng)的環(huán)境變量后,所有后續(xù)創(chuàng)建的新進程都會從新的系統(tǒng)環(huán)境變量中繼承相關的設置。如果進程具有父進程,它會從父進程繼承一份環(huán)境變量并在此基礎上修改。

總之,環(huán)境變量是一種在Windows系統(tǒng)中用于存儲操作系統(tǒng)和應用程序配置信息的技術。系統(tǒng)的環(huán)境變量是所有進程共享的設置,而進程的環(huán)境變量是獨立于其他進程的,由每個進程自行管理。環(huán)境變量在程序的運行中起到重要作用,開發(fā)者可以使用環(huán)境變量來配置程序的行為,便于程序的管理和維護。

如何提取環(huán)境塊并輸出到數(shù)組中,如何查詢/修改/添加環(huán)境變量?
可以使用GetEnvironmentStrings函數(shù)來獲取完整的環(huán)境塊,注意返回值是PTSTR ,一般需要逐個解析并存儲到鍵值對或者其他結(jié)構(gòu)的變量中再去使用。
GetEnvironmentVariable則可以檢查某個具體的變量是否存在,并獲取變量值。SetEnvironmentVariable則用于設置,若已有則會進行覆蓋。注意,這里提到的API操作的是進程的環(huán)境變量,如何修改系統(tǒng)環(huán)境變量則不在此贅述。

進程的錯誤模式
SetErrorMode函數(shù)可以為進程設置一組標志,這些標志的作用是讓系統(tǒng)知道進程如何響應嚴重錯誤,包括磁盤介質(zhì)錯誤、未處理的異常、文件查找錯誤以及數(shù)據(jù)對齊錯誤等。SetErrorMode函數(shù)來告訴系統(tǒng)如何處理這些錯誤。啟用SEM_FAILCRITICALERRORS標志位,可以使應用程序忽略所有的致命錯誤,并返回默認的錯誤值。這常常用于一些比較穩(wěn)定的服務程序,如服務器等,因為當這些程序遇到致命錯誤時,可以自動忽略錯誤并繼續(xù)工作,從而保證系統(tǒng)的穩(wěn)定性和可用性。

"致命錯誤"通常指的是可能會中斷進程執(zhí)行的錯誤,例如訪問非法內(nèi)存、不受支持的指令,或者硬件故障等。這些錯誤通常會導致進程崩潰或者異常終止,從而影響到應用程序的正常運行。

使用 SetErrorMode 函數(shù)可以將當前進程的錯誤模式設置為 SEM_FAILCRITICALERRORS 標志位,這樣當進程遇到致命錯誤時,操作系統(tǒng)會將錯誤信息記錄到事件日志中,并返回一個默認的錯誤值,而不是直接崩潰。這樣的處理方式可以增強應用程序的穩(wěn)定性和可靠性,避免可能的數(shù)據(jù)損壞和系統(tǒng)崩潰。

需要注意的是,啟用 SEM_FAILCRITICALERRORS 標志位并不一定能夠避免程序崩潰,因為存在一些致命錯誤確實無法通過忽略錯誤方式進行處理,可能最終仍然會導致程序崩潰。例如,如果程序持續(xù)非法訪問內(nèi)存,就有可能導致內(nèi)存分配器無法繼續(xù)為程序分配內(nèi)存導致進程崩潰。

然而,在一些需要保證應用程序穩(wěn)定性和可用性的場合下,設置 SEM_FAILCRITICALERRORS 標志位仍然有一定的意義,因為它可以盡可能地避免因為一些可控的致命錯誤而導致程序直接崩潰。在這種情況下,雖然程序仍然可能會存在一些問題,但是它們不會導致程序直接崩潰,從而減小了對用戶的影響。

總之,啟用 SEM_FAILCRITICALERRORS 標志位需要根據(jù)具體情況進行權衡和評估,如果能夠避免程序直接崩潰,并且不會給用戶帶來過大的負面影響,那么啟用該標志位是有意義的。但在一些特殊情況下,如在程序中采用了非法操作等情況下,啟用該標志位可能反而會帶來更加嚴重的后果。

CreateProcess函數(shù)解析
函數(shù)簽名如下所示

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函數(shù)的參數(shù)名與《Windows核心編程》上的略有不同,但參數(shù)類型和順序,數(shù)量是基本一致的。相信這只是Windows系統(tǒng)版本更新帶來的差異,不影響理解和使用。這里按照書上的函數(shù)接口標準來講解。

一般情況下,建議pszApplicationName傳NULL,而在pszCommandLine中加入需要啟動的可執(zhí)行文件名。當CreateProcess解析pszCommandLine字符串時,它會檢查字符串中的第一個標記(token),并假定此標記是我們想運行的可執(zhí)行文件的名稱。如果可執(zhí)行文件的名稱沒擴展名,就會默認是.exe擴展名。

關于psaProcess,psaThread和bInheritHandles參數(shù),一般主要關注的是內(nèi)核句柄繼承問題。psaProcess和psaThread代表進程和線程的安全描述符,一般傳NULL即可[注1]。bInheritHandles傳TRUE,則會讓子進程繼承父進程的句柄。在調(diào)用CreateProcess時,系統(tǒng)會為子進程分配一個進程內(nèi)核對象和線程內(nèi)核對象,如果需要指定子進程是否能夠繼承這兩個內(nèi)核對象,則可以在psaProcess和psaThread參數(shù)中指定。(psaProcess和psaThread參數(shù)的類型為SECURITY_ATTRIBUTES結(jié)構(gòu)體,里面有一個bInheritHandle參數(shù),通過這個參數(shù)去設置。注意不要和CreateProcess的bInheritHandles參數(shù)弄混)

關于fdwCreate參數(shù),它標識了影響新進程創(chuàng)建方式的標志。多個標志可以使用按位或起來,以便同時指定多個標志組合。筆者覺得比較值得關注的標志位有這幾個:CREATE_SUSPENDED標志讓系統(tǒng)在創(chuàng)建新進程的同時掛起其主線程。這樣一來,父進程就可以修改子進程地址空間中的內(nèi)存,更改子進程的主線程的優(yōu)先級,或者在進程執(zhí)行任何代碼之前,將此進程添加到一個作業(yè)(job)中;CREATE_BREAKAWAY_FROM_JOB標志允許一個作業(yè)中的進程生成一個和作業(yè)無關的進程;EXTENDED_STARTUPINFO_PRESENT標志向操作系統(tǒng)表明傳給psiStartInfo參數(shù)的是一個STARTUPINFOEX結(jié)構(gòu)。至于什么是作業(yè),什么是STARTUPINFOEX結(jié)構(gòu),我們稍后會提及。

pvEnvironment參數(shù)指向一塊內(nèi)存,其中包含新進程要使用的環(huán)境字符串。大多數(shù)時候,為這個參數(shù)傳入的值都是NULL,這將導致子進程繼承其父進程使用的一組環(huán)境字符串。

psiStartInfo參數(shù)指向一個STARTUPINFO結(jié)構(gòu)或STARTUPINFOEX結(jié)構(gòu)。這個參數(shù)個人認為比較重要,值得關注.

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;

數(shù)據(jù)結(jié)構(gòu)中每一個參數(shù)的具體參數(shù)可以參考<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>。這里不進行贅述??偟膩碚f,這個參數(shù)控制了進程啟動時的一些行為,如果傳入的是STARTUPINFO結(jié)構(gòu),包括:在那個桌面上啟動應用程序,控制臺的標題,在屏幕上的位置(xy坐標),指定窗口寬高等。而對于STARTUPINFOEX結(jié)構(gòu),就比較有意思了??梢钥吹?,STARTUPINFOEX結(jié)構(gòu)一個成員是STARTUPINFO結(jié)構(gòu),另一個成員是一個List指針,指向的List則標識了進程的額外屬性。目前官方文檔中包含了兩個屬性鍵:PROC_THREAD_ATTRIBUTE_HANDLE_LIST用于標識子進程具體應該繼承哪一些內(nèi)核對象;PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 則用于標識子進程應該成為哪一個進程的子進程(而非實際調(diào)用CreateProcess的進程)。

ppiProcInfo參數(shù)指向一個PROCESS_INFORMATION結(jié)構(gòu),里面包含了進程句柄和線程句柄,以及它們的id。

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

進程與線程的退出
應該保證只有在主線程的入口點函數(shù)返回之后,這個應用程序的進 程才終止。只有這樣,才能保證主線程的所有資源都被正確清理。通過進程中的一個線程調(diào)用ExitProcess和ExitThread函數(shù),另一個進程中的線程調(diào)用TerminateProcess和TerminateThread函數(shù),雖然也能讓進程和線程退出,但可能會導致一些副作用。就操作系統(tǒng)而言,這樣做是沒有什么問題的,進程或線程的所有操作系統(tǒng)資源都會被正確清理。不過,C/C++應用程序應避免調(diào)用這些函數(shù),因為C/C++運行庫也許不能執(zhí)行正確清理工作。比如,線程中正在執(zhí)行的函數(shù)未從調(diào)用棧返回,一些類的析構(gòu)函數(shù)沒有被調(diào)用,而這些沒有被執(zhí)行的代碼,可能導致一些業(yè)務邏輯沒有被完整的執(zhí)行,比如可能需要在析構(gòu)函數(shù)中回寫和刷新文件輸出流,做信息收集等。

進程的權限
Windows進程有四種權限,一般我們只用關心兩種:普通權限和管理員權限[注2]。如果需要在進程啟動時默認通過UAC(User Access Control)獲取管理員權限,一般有兩種方式。一種是使用manifest文件,嵌入到可執(zhí)行文件中,作為一種特殊的資源;另一種方式則是使用ShellExcecuteEx函數(shù),在程序中顯式地使用管理員權限啟動進程。
有意思的是,在實際開發(fā)中會發(fā)現(xiàn),使用管理員權限啟動的進程,似乎會接收不到外部進程的消息,表現(xiàn)可以為文件拖拽到窗口中沒反應等。這是因為,在Windows系統(tǒng)中,管理員權限啟動的進程默認擁有更高的權限,而低權限的進程無法向其發(fā)送消息。這種行為可以防止低權限的進程向管理員權限啟動的進程發(fā)送惡意消息或指令,從而保護系統(tǒng)的安全性。
如果確實需要低權限的進程與管理員權限啟動的進程進行消息通信,可以通過一些方式來解決,如使用管道、共享內(nèi)存等機制進行進程間通信,或者通過改變權限來實現(xiàn)。不過需要注意的是,這樣做可能會對系統(tǒng)的安全性產(chǎn)生一定的影響,因此需要謹慎使用。

使用作業(yè)機制對多個進程進行管理
Windows系統(tǒng)的作業(yè)機制(Job Objects)提供了以下能力:

  • 進程管理??梢詣?chuàng)建和管理多個進程,控制進程的運行權限和資源消耗,避免進程耗盡系統(tǒng)資源,導致系統(tǒng)崩潰。
  • 進程監(jiān)管??梢员O(jiān)控進程的行為,如CPU、內(nèi)存、磁盤和網(wǎng)絡等資源的使用情況,并對異常情況做出響應。例如,當某個進程占用了大量CPU資源時,可以讓系統(tǒng)暫?;蚪K止該進程,避免對其他進程或系統(tǒng)造成影響。 - 安全保障??梢詫崿F(xiàn)對進程訪問權限和權限分離管理,避免惡意程序?qū)ο到y(tǒng)造成攻擊和損壞。
  • 資源分配??梢钥刂葡到y(tǒng)資源的分配、定時任務的執(zhí)行和內(nèi)存的分配等,從而提高系統(tǒng)的資源利用率和效率。
    通過Job Objects的作業(yè)機制,可以有效提高系統(tǒng)的安全性、穩(wěn)定性和性能等。它可以解決多進程協(xié)作、資源分配和作業(yè)監(jiān)管等問題,使系統(tǒng)更加可靠和高效。

尾注:
[注1]:在 Windows 操作系統(tǒng)中,SECURITY_ATTRIBUTES 結(jié)構(gòu)和安全描述符(Security Descriptor)用于控制安全性和權限訪問。它們是用于實現(xiàn) Windows 安全模型的核心組成部分。SECURITY_ATTRIBUTES 結(jié)構(gòu)是一個用于在創(chuàng)建進程、文件、共享內(nèi)存等對象時指定訪問控制列表(ACL)的結(jié)構(gòu)。該結(jié)構(gòu)包括三個字段,分別是 nLength、lpSecurityDescriptor 和 bInheritHandle 。其中,nLength 字段表示該結(jié)構(gòu)的大??;lpSecurityDescriptor 字段表示指向 SECURITY_DESCRIPTOR 結(jié)構(gòu)的指針;bInheritHandle 字段指示該對象是否可以被繼承。該結(jié)構(gòu)可以用于指定應用程序?qū)ο蟮奶囟ò踩院驮L問權限。安全描述符是一個包含有關對象安全性信息的結(jié)構(gòu)。它描述了一個命名資源的訪問控制策略(Access Control Policy,簡稱:ACP),包括一個安全標識符(Security Identifier,SID)、一個自主擁有的 SID、系統(tǒng)資源的訪問控制列表(ACL)和一個安全描述符控制權(Security Descriptor Control,SDC)。該結(jié)構(gòu)定義了文件、目錄、共享、服務、進程等資源的安全訪問規(guī)則。在 Windows 操作系統(tǒng)中,每個對象都有一個安全描述符。安全描述符內(nèi)部存儲了對象的 SACL、DACL、owner SID 以及 group SID。DACL是控制訪問對象的重要部分,它定義了哪些主體(如用戶或組)可以訪問對象、以及哪些權限他們可以獲得。SACL是用于審計的可選部分,它定義了哪些操作對對象的訪問需要進行審核。通過設置權限、使用組策略等安全管理工具,管理員可以使用安全描述符和安全屬性來管理 Windows 系統(tǒng)中的安全性。這是實現(xiàn)系統(tǒng)安全性和權限的必要環(huán)節(jié)。

[注2]:Windows進程的權限主要有以下幾種: System權限,擁有系統(tǒng)級別的完全權限,可以訪問和修改系統(tǒng)資源、系統(tǒng)數(shù)據(jù)、硬件信息等一切資源; Administrator權限,擁有大部分管理權限,可以訪問和修改該計算機上的所有數(shù)據(jù)和應用程序; User權限,普通用戶權限,只能訪問和修改自己的個人文件和軟件,不能修改系統(tǒng)核心文件和所有用戶的數(shù)據(jù)、應用程序; Guest權限,訪客權限,允許訪問和修改的內(nèi)容更少。對進程權限進行管理的意義在于:高系統(tǒng)安全性,通過限制進程權限,可以避免惡意軟件修改系統(tǒng)核心文件、竊取用戶數(shù)據(jù)等行為,提高系統(tǒng)安全性;管理軟件行為,通過管理進程權限,可以限制某些軟件的操作,例如禁止某些程序在系統(tǒng)啟動時自動運行,避免系統(tǒng)資源被占用;
保護用戶隱私,通過限制進程權限,可以避免開發(fā)人員在未經(jīng)用戶允許的情況下訪問用戶數(shù)據(jù)和隱私??傊?,進程權限管理是系統(tǒng)安全和用戶隱私保護的必要措施。

參考:
《Windows核心編程》
https://openai.com/blog/chatgpt

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容