日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

《Windows核心编程》读书笔记四 进程

發布時間:2024/1/8 windows 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Windows核心编程》读书笔记四 进程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第四章 進程


本章內容


4.1 編寫第一個Windows應用程序

4.2 CreateProcess函數

4.3 終止進程

4.4 子進程

4.5 管理員以標準用戶權限運行時


進程定義為一個正在運行的程序的一個實例,它可以由一下兩部分構成。

a. 一個內核對象,操作系統用它來管理進程。內核對象也是系統保存進程統計信息的地方。

b.一個地址空間,其中包含所有可執行文件或DLL模塊的代碼和數據。還包含動態內存分配,比如線程棧和堆的分配。


進程是有“惰性”的,進程要做任何事情,都必須讓一個線程在他的上下文(環境 內存地址空間等)中運行。該線程執行進程地址空間包含的代碼。

進程可以包含多個線程,所有線程都在進程的地址空間中“同時”執行代碼。每個線程有自己的堆棧和自己的一組CPU寄存器

系統創建進程的時候會創建一個主線程。然后由主線程再創建更多的子線程。

如果沒有線程要繼續執行的代碼,進程就失去了存在的理由。系統回自動銷毀進程和其地址空間。


操作系統會輪流為每一個線程調度一些cpu時間。它采取循環(round-robin,輪詢或輪流)方式,為每個線程都分配時間片(稱為“量”或者“量程”)從而營造出并發的假象。

如果計算機裝載多cpu或者(多核cpu),操作系統會采用更復雜的算法為線程分配cpu時間。




4.1 編寫第一個Windows應用程序

Windows支持兩種類型的應用程序,GUI(Graphic User Interface)和CUI(Console User Interface)


事實上CUI程序也能顯示出圖形界面。也可以在一個GUI程序中像控制臺輸出文本。

GUI和CUI程序在VS中主要取決于連接器的設置。 CUI /SUBSYSTEM:CONSOLE

GUI :/SUBSYSTEM:WINDOWS


用戶運行應用程序時,操作系統加載程序(loader)會檢查可執行文件映像的文件頭,并獲取這個子系統值。然后進行相應的加載(開啟一個命令行窗口 或是創建一個主窗口)

等程序運行以后,操作系統就不再關心是CUI還是GUI了。

Windows應用程序的入口函數

INT WINAPI _tWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nShowCmd);int _tmain(_In_ int _Argc,_In_reads_(_Argc) _Pre_z_ wchar_t ** _Argv,_In_z_ wchar_t ** _Env);
操作系統本身并不會調用入口函數main和Winmain 而是調用C/C++運行實現并在連接時使用-entry:命令選項來設置的一個C/C++運行時的啟動函數。

該函數初始化C/C++運行庫,使我們可以調用malloc free之類的函數。 還確保在代碼執行前任何全局和靜態的C++對象都被正確構造。


連接器選擇正確的C/C++運行庫啟動函數。如果指定SUBSYSTEM:WINDOWS 連接器就會尋找WinMain

如果沒有找到WinMain則返回“unresolved external symbol”

如果選擇/SUBSYSTEM:CONSOLE 連接器默認會尋找main或者wmain 如果找不到則返回"unresolved external symbol"

可以自行關閉SUBSYSTEM連接器開關,讓連接器自動判斷(入口是main 還是winmain)。


可以從VC++自帶的運行庫的源代碼 crtexe.c文件中找到4個啟動函數的源代碼。所有啟動函數的用途簡單總結如下

1)獲取指向新進程的完整命令行的一個指針

2)獲取指向新進程的環境變量的一個指針

3)初始化C/C++運行庫的全局變量。如果保含了Stdlib.h 就可以訪問這些變量。

4)初始化C運行庫內存分配函數(malloc 和 calloc)和底層的I/O例程使用的堆

5)調用所有全局和靜態C++類對象的構造函數


完成以上所有初始化以后,C/C++啟動函數就會調用應用程序的入口函數。

如果我們定義了Unicode C/C++標準庫將執行以下代碼

STARTUPINFO StartupInfo;GetStartupInfo(&StartupInfo);int nMainRetVal = wWinMain((HINSTANCE)&__ImageBase, NULL, pszCommandLineUnicode,(STARTUPINFO.dwFlags & STARTF_USESHOWWINDOW)? STARTUPINFO.wShowWindow : SW_SHOWDEFAULT);
如果沒有定義Unicode則調用過程如下

STARTUPINFO StartupInfo;GetStartupInfo(&StartupInfo);int nMainRetVal = WinMain((HINSTANCE)&__ImageBase, NULL, pszCommandLineAnsi,(STARTUPINFO.dwFlags & STARTF_USESHOWWINDOW)? STARTUPINFO.wShowWindow : SW_SHOWDEFAULT);
_ImageBase是連接器定義的一個偽變量,表明可執行文件被映射到應用程序內存中的什么位置。


如果是CUI程序main函數的調用如下

int nMainRetVal = main(argc, argv, envp);

注意用Visual studio生存的默認main函數沒有第三個參數。可以自行增加表示環境變量

int main(int argc, char* argv[], char * env[]) {return 0; }
main函數返回以后,啟動函數調用C運行庫的exit,向其返回值nMainRetVal


exit函數執行以下任務

調用_onexit函數所注冊的一個回調函數。

調用所有全局和靜態C++類對象的析構函數

在DEBUG生成中,如果設置了_CRTDBG_LEAK_CHECK_DF標志, 會調用_CrtDumpMemoryLeaks函數來生成內存泄漏的報告。

調用操作系統的ExitProcess函數,向其傳入nMainRetVal,這會導致操作系統殺死進程,并設置他的退出代碼。


4.1.1 進程的實例句柄

加載到進程地址空間的每一個可執行文件或DLL文件都被賦予了一個獨一無二的實例句柄。可執行文件的實例句柄被當做WinMain函數的第一個參數hInstanceExe傳入。

在需要加載資源的函數中需要用到此句柄。例如

WINUSERAPI HICON WINAPI LoadIconW(_In_opt_ HINSTANCE hInstance,_In_ LPCWSTR lpIconName);
有的函數需要一個HMODULE類型的參數和HINSTANCE一致

WINBASEAPI _Success_(return != 0) _Ret_range_(1, nSize) DWORD WINAPI GetModuleFileNameW(_In_opt_ HMODULE hModule,_Out_writes_to_(nSize, ((return < nSize) ? (return + 1) : nSize)) LPWSTR lpFilename,_In_ DWORD nSize);
hInstanceExe參數實際是一個內存基地址,系統將可執行文件的映像加載到進程地址空間中的這個位置。

例如打開一個exe文件,并將他的內容加載到地址0x0040 0000 ?則WinMain的hInstanceExe參數值為 ?0x0040 0000.

基地址是由連接器決定的,使用/BASE:address 可以設置要將應用程序加載到哪個基地址。


為了知道一個可執行文件或DLL文件被加載到進程地址空間的什么位置,可以使用GetModuleHandle來返回一個句柄/基地址

WINBASEAPI _When_(lpModuleName == NULL, _Ret_notnull_) _When_(lpModuleName != NULL, _Ret_maybenull_) HMODULE WINAPI GetModuleHandleW(_In_opt_ LPCWSTR lpModuleName);
可以傳入NULL 就會獲得主調進程可執行文件的地址。


也可以通過連接器的偽變量__ImageBase查看



第二種方法是調用調用GetModuleHandleEx, 將GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS作為他的第一個參數,將當前函數的地址作為第二個參數,

最后一個參數是一個HMODULE的指針。GetModuleHandleEx會傳入函數所在DLL的基地址來填寫指針。

一個測試代碼

#include <tchar.h> #include <windows.h>extern "C" const IMAGE_DOS_HEADER __ImageBase;void DumpModule() {// Get the base address of the running application.// Can be different from the running module if this code is in a DLL.HMODULE hModule = GetModuleHandle(NULL);_tprintf(TEXT("with GetModuleHandle(NULL) = 0x%x\r\n"), hModule);// Use the pseudo-variable __ImageBase to get// the address of the current module hModule/hInstance._tprintf(TEXT("with __ImageBase = 0x%x\r\n"), (HINSTANCE)&__ImageBase);// Pass the address of the current method DumpModule// as parameter to GetModuleHandleEx to get the address// of the current module hModule/hInstance.hModule = NULL;GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(PCTSTR)DumpModule,&hModule);_tprintf(TEXT("with GetModuleHandleEx = 0x%x\r\n"), hModule); }int main(int argc, char* argv[], char * env[]) {DumpModule();return 0; }
執行結果



4.1.2 進程的前一個實例的句柄

hPrevInstance 參數用于16位windows系統,在32位系統中不要使用此參數。


可以不在參數列表中寫參數變量,也可以通過宏

UNREFERENCED_PARAMETER(hPrevInstance); ?來讓編譯器不發出警告



4.1.3 ?進程的命令行

命令行至少會有一個參數,也就是可執行文件的文件名。 C運行庫會調用GetCommandLine來獲取完整的命令行,忽略可執行文件的名稱,然后將剩余的部分指針傳遞給WinMain的pszCmdLine參數


PTSTR GetCommandLine(); 獲取完整命令行的指針

CUI的命令行參數傳入的為 argc 和 argv 可以利用 Shell32.dll的導出函數CommandLineToArgv 將完整的命令行參數轉換為argc和argv

SHSTDAPI_(LPWSTR *) CommandLineToArgvW(_In_ LPCWSTR lpCmdLine, _Out_ int* pNumArgs);
改函數會在內部分配內存,需要釋放。或者等進程退出時由操作系統釋放(leak)

一個例子

int main(int argc, char* argv[], char * env[]) {int nNumArgs;PWSTR *ppArgv = CommandLineToArgvW(GetCommandLineW(), &nNumArgs);// Use the arguments...// Free the memory blockHeapFree(GetProcessHeap(), 0, ppArgv);return 0; }
在watch中查看



4.1.4 進程的環境變量

每個進程都有一個與他關聯的環境塊,這是在進程地址空間內分配的內存塊,其中包含字符串類似下面



前面是環境變量名,后面是環境變量值

使用GetEnvironmentStrings函數能獲得完整的環境塊,類似上面的字符串。

以下例子展示了如何在這樣的串中提取內容

void DumpEnvStrings() {PTSTR pEnvBlock = GetEnvironmentStrings();// Parse the block with the following format:// =::=::\// =...// var=value\0// ...// var=value\0\0// Note that some other strings might begin with '='.// Here is an example when the application is started from a network share.// [0] =::=::\// [1] =C:=C:\Windows\System32// [2] =ExitCode=00000000//TCHAR szName[MAX_PATH];TCHAR szValue[MAX_PATH];PTSTR pszCurrent = pEnvBlock;HRESULT hr = S_OK;PCTSTR pszPos = NULL;int current = 0;while (pszCurrent != NULL) {// Skip the meaningless strings like:// "=::=::\"if (*pszCurrent != TEXT('=')) {// Look for '=' separator.pszPos = _tcschr(pszCurrent, TEXT('='));// Point now to the first character of the value.pszPos++;// Copy the variable name.size_t cbNameLength = // Without the '='(size_t)pszPos - (size_t)pszCurrent - sizeof(TCHAR);hr = StringCbCopyN(szName, MAX_PATH, pszCurrent, cbNameLength);if (FAILED(hr)) {break;}// Copy the variable value with the last NULL character// and allow truncation because this is for UI only.hr = StringCchCopyN(szValue, MAX_PATH, pszPos, _tcslen(pszPos) + 1);if (SUCCEEDED(hr)) {_tprintf(TEXT("[%u] %s=%s\r\n"), current, szName, szValue);}else if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { // something wrong happened; check for truncation._tprintf(TEXT("[%u] %s=%s...\r\n"), current, szName, szValue);}else { // This should never occur._tprintf(TEXT("[%u] %s=???\r\n"), current, szName);break;} }else {_tprintf(TEXT("[%u] %s\r\n"), current, pszCurrent);}// Next variable please.current++;// Move to the end of the string.while (*pszCurrent != TEXT('\0'))pszCurrent++;pszCurrent++;// Check if it was not the last string.if (*pszCurrent == TEXT('\0'))break;}// Don't forget to free the memory.FreeEnvironmentStrings(pEnvBlock); }
運行結果



訪問環境變量的第二種方式是CUI程序專用。他通main函數入口的TCHAR *env[]參數來實現。env是一個字符串指針數組,每個指針都指向一個不同的環境變量。(名稱=值)的格式。

在指向自后一個環境變量字符串的指針后面會有一個NULL指針,表明這是數組末尾。

代碼如下

void DumpEnvVariables(PTSTR pEnvBlock[]) {int current = 0;PTSTR *pElement = (PTSTR*)pEnvBlock;PTSTR pCurrent = NULL;while (pElement != NULL) {pCurrent = (PTSTR)(*pElement);if (pCurrent == NULL) {pElement = NULL;}else {_tprintf(TEXT("[%u] %s\r\n"), current, pCurrent);current++;pElement++;} } }
運行結果



環境變量的值和名稱可以包含空格,只是靠等號分割。

例如 XYZ= Windows

ABC=Windows

兩個變量的值不同

XYZ =home

XYZ=Work

兩個變量的名稱不同

系統環境變量

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment

當前用戶環境變量

HKEY_CURRENT_USER\Environment


可以使用各種api來修改環境變量,但是用戶必須注銷。

也可以發送消息WM_SETTINGCHANGE

SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment"));


父進程可以通過CreateProcess來控制子進程繼承哪些環境變量。子進程繼承后環境塊屬于自己,可以自行增刪而不影響父進程。


使用環境變量GetEnvironmentVariable

WINBASEAPI _Success_(return != 0 && return < nSize) DWORD WINAPI GetEnvironmentVariableW(_In_opt_ LPCWSTR lpName,_Out_writes_to_opt_(nSize, return + 1) LPWSTR lpBuffer,_In_ DWORD nSize);
第三個參數傳入0的時候返回 保持環境變量值所需要字符個數,包含結尾的'\0'

一個測試代碼

void PrintEnvironmentVariable(PCTSTR pszVariableName) {PTSTR pszValue = NULL;// Get the size of the buffer that is required to store the valueDWORD dwResult = GetEnvironmentVariable(pszVariableName, NULL, 0);if (dwResult != 0) {// Allocate the buffer to store the environment variable valueDWORD size = dwResult * sizeof(TCHAR);pszValue = (PTSTR)malloc(size);GetEnvironmentVariable(pszVariableName, pszValue, size);_tprintf(TEXT("%s=%s\n"), pszVariableName, pszValue);free(pszValue);}else {_tprintf(TEXT("'%s'=<unknown value>\n"), pszVariableName);} }int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {PrintEnvironmentVariable(TEXT("PATH"));return 0; }
運行結果



可以將環境變量展開的函數 ExpandEnvironmentStrings

WINBASEAPI _Success_(return != 0 && return <= nSize) DWORD WINAPI ExpandEnvironmentStringsW(_In_ LPCWSTR lpSrc,_Out_writes_to_opt_(nSize, return) LPWSTR lpDst,_In_ DWORD nSize);
例子

int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {const TCHAR szSrc[] = TEXT("PATH='%PATH%'");DWORD chValue = ExpandEnvironmentStrings(szSrc, NULL, 0);if (chValue != 0){PTSTR pszBuffer = new TCHAR[chValue];chValue = ExpandEnvironmentStrings(szSrc, pszBuffer, chValue);_tprintf(TEXT("%s\r\n"), pszBuffer);delete[] pszBuffer;}return 0; }
SetEnvironmentVariable函數可以添加一個變量,刪除一個或者修改一個變量的值。

將pszName所表示的變量設置成pszValue的值。 設置為NULL則刪除


4.1.5 進程的關聯性

通常進程中的線程可以在主機的任何cpu上運行,然而,也可以強迫線程在可用cpu的一個子集上運行,這稱為處理器相關性(processor affinity)


4.1.6 進程的錯誤模式

每個進程都關聯了一組標志,這些標志的作用是讓進程知道進程如何響應嚴重錯誤,包括磁盤介質錯誤,未處理的異常,文件查找錯誤以及數據對齊錯誤。

進程可以調用SetErrorMode來通知系統如何處理這些錯誤

UINT SetErrorMode(UINT fuErrorMode);


錯誤模式會被子進程繼承,除非在CreatePcocess中設置CREATE_DEFAULT_ERROR_MODE


4.1.7 進程當前所在的驅動器和目錄

WINBASEAPI _Success_(return != 0 && return < nBufferLength) DWORD WINAPI GetCurrentDirectoryW(_In_ DWORD nBufferLength,_Out_writes_to_opt_(nBufferLength, return + 1) LPWSTR lpBuffer);WINBASEAPI BOOL WINAPI SetCurrentDirectoryW(_In_ LPCWSTR lpPathName);如果進程中的某一線程調用了SetCurrentDirectory修改了默認的進程目錄,則所有使用相對路徑處理的可能會導致意外的錯誤。

GetCurrentDirectory可以傳入 ?(0, NULL) 會返回需要字符的個數包括末尾的'\0'

或者直接傳入MAX_PATH 尺寸的buffer

4.1.8 進程的當前目錄

系統跟著記錄這進程的當前驅動器和目錄,但沒有記錄每個驅動器的當前目錄。可以通過環境變量來支持

=C:=C:\Utility\Bin

=D:=D:\Program Files

例如假定當前進程的目錄是C:\Utility\Bin ,而我們調用CreateFile來打開D:ReadMe.Txt 那么系統會查找環境變量=D:

由于=D:是存在的,系統將嘗試從D:\Program Files目錄打開ReadMe.txt文件,如果=D:不存在,系統會嘗試從D盤根目錄打開ReadMe.txt文件

Windows的文件函數從來不會添加或更改驅動器號。他們只是讀取這種變量。

_chdir也可以更改當前目錄其內部調用SetCurrentDirectory, _chdir還會調用SetEnvironmentVariable來添加或修改環境變量,從而使不同驅動器的當前目錄得以保留。


獲取每個驅動器的當前目錄

TCHAR szCurDir[MAX_PATH] = { 0 };DWORD cchLength = GetFullPathName(TEXT("D:"), MAX_PATH, szCurDir, NULL);_tprintf(TEXT("%s\n"), szCurDir);
驅動器號環境變量通常必須放在環境塊的開始處。

以下例子修改D盤的當前目錄并調用GetFullPathName獲取

//SetEnvironmentVariable(TEXT("=D:"), TEXT("D:\\Program Files (x86)"));//_chdir("D:\\Program Files (x86)");SetCurrentDirectory(TEXT("D:\\Program Files (x86)"));TCHAR szCurDir[MAX_PATH] = { 0 };DWORD cchLength = GetFullPathName(TEXT("d:"), MAX_PATH, szCurDir, NULL);_tprintf(TEXT("%s\n"), szCurDir);
!注意修改當前目錄是必須真實存在的目錄,否則會設置失敗。但SetEnvironmentVariable并不會檢查目錄是否存在。

而SetCurrentDirectory又會直接更改當前的驅動器和目錄。



如果一個父進程創建了一個希望傳遞給子進程的環境塊,子進程的環境塊不會自動繼承父進程的當前目錄。子進程的默認當前目錄為每個驅動器的根目錄。

如果希望子進程繼承父進程的當前目錄,父進程就必須在生成子進程之前,創建這些驅動器號的環境變量,并把它添加到環境塊中。


GetCurrentDirectory

SetCurrentDirectory

GetFullPathName 在多線程應用中應該特別小心,因為他們的值是進程相關的。在獲取過程中可能被其他線程所修改了。而且也應該避免使用相對路徑


4.1.9 系統版本

DWORD GetVersion(); //主版本號在低字節,次版本號在高字節


NOT_BUILD_WINDOWS_DEPRECATE WINBASEAPI __drv_preferredFunction("IsWindows*", "Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.") BOOL WINAPI GetVersionExW(_Inout_ LPOSVERSIONINFOW lpVersionInformation);此函數傳入一個OSVERSIONINFO的結構體并賦值給他。

typedef struct _OSVERSIONINFOW {DWORD dwOSVersionInfoSize;DWORD dwMajorVersion;DWORD dwMinorVersion;DWORD dwBuildNumber;DWORD dwPlatformId;WCHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usage } OSVERSIONINFOW, *POSVERSIONINFOW, *LPOSVERSIONINFOW, RTL_OSVERSIONINFOW, *PRTL_OSVERSIONINFOW;
OSVERSIONINFOEX結構

typedef struct _OSVERSIONINFOEXW {DWORD dwOSVersionInfoSize;DWORD dwMajorVersion;DWORD dwMinorVersion;DWORD dwBuildNumber;DWORD dwPlatformId;WCHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usageWORD wServicePackMajor;WORD wServicePackMinor;WORD wSuiteMask;BYTE wProductType;BYTE wReserved; } OSVERSIONINFOEXW, *POSVERSIONINFOEXW, *LPOSVERSIONINFOEXW, RTL_OSVERSIONINFOEXW, *PRTL_OSVERSIONINFOEXW;

參考MSDN 文檔

Getting the System Version

https://msdn.microsoft.com/en-gb/library/ms724429.aspx


VISTA以上的系統還提供了VerifyVersionInfo 能比較主機系統和應用程序需求的版本。


需要構建一個OSVERSIONINFOEX結構,并用宏(VER_SET_CONDITION)設置一個 ?dwConditionMask

就可以調用VerifyVersionInfo來判斷操作系統版本了。

以下例子展示了如何測試主機系統是不是Windows Vista


// Prepare the OSVERSIONINFOEX structure to indicate Windows Vista.OSVERSIONINFOEX osver = { 0 };osver.dwOSVersionInfoSize = sizeof(osver);osver.dwMajorVersion = 6;osver.dwMinorVersion = 0;osver.dwPlatformId = VER_PLATFORM_WIN32_NT;// Prepare the conditionmask.DWORDLONG dwlConditionMask = 0; // you must initialize this to 0.VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);// Perform the version test.if (VerifyVersionInfo(&osver, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID,dwlConditionMask)) {_tprintf(TEXT("The host system is Windows Vista exactly.\n"));}else {_tprintf(TEXT("The host system is NOT Windows Vista.\n"));}
這里進行的是VER_EQUAL對比,筆者的測試機器是Win7 x64 sp1這里返回非VISTA系統。

The host system is NOT Windows Vista.


4.2 CreateProcess函數

使用CreateProcess來創建進程原型如下

WINBASEAPI BOOL WINAPI CreateProcessW(_In_opt_ LPCWSTR lpApplicationName,_Inout_opt_ LPWSTR lpCommandLine,_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,_In_ BOOL bInheritHandles,_In_ DWORD dwCreationFlags,_In_opt_ LPVOID lpEnvironment,_In_opt_ LPCWSTR lpCurrentDirectory,_In_ LPSTARTUPINFOW lpStartupInfo,_Out_ LPPROCESS_INFORMATION lpProcessInformation);
1)系統將創建一個進程內核對象,其初始引用計數器為1.進程內核對象并不是進程本身,而是操作系統用來管理該進程用的一個小型數據結構。

2)然后系統為進程創建一個虛擬地址空間,并將可執行文件(和所有必要的DLL)的代碼以及數據加載到進程的地址空間。

3)接著系統為進程的主線程創建一個線程內核對象(引用計數器為1)。線程的內核對象也是一個小型數據結構用來管理線程。這個主線程也會調用C/C++運行庫的啟動例程(由連接器設定的應用程序入口)最終調用WinMain 或者main函數。如果成功創建了新進程和主線程,CreateProcess返回TRUE。


CreateProcess在進程完全初始化好之前就返回TRUE。OS的進程Loader尚未嘗試定位所有DLL。如果一個DLL找不到或者無法正確初始化,進程就會終止。但因為CreateProcess返回TRUE,所以父進程并不會注意到任何初始化問題。


4.2.1 pszApplicationName和pszCommandLine參數

前者是新進程可執行文件的名字

后者是要傳遞給新進程的命令行參數。 CreateProcess實際會修改我們傳給他的命令行字符。但在CreateProcess返回前,他會將此字符還要成原來的形式。

這是很重要的,不能傳遞只讀內存區。否則可能會報錯



在調用之前將字符串常量復制到一個臨時變量則不會報錯。如下 。cc編譯器的/Gf開關可以消除重復的字符串,并判斷是否將那些字符串放在一個只讀的區域。注意/ZI開關,會關閉/Gf

STARTUPINFO si = { sizeof(si) };PROCESS_INFORMATION pi;TCHAR szCommandLine[] = TEXT("NOTEPAD");CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
另外在Vista系統上調用ANSI版本的CreateProcess不會發生違規,因為其內部創建了在堆上一個Unicode的副本(可讀可寫)

CreateProcess會解析pszCommandLine的第一個token 并假定標記是我們要運行的可執行文件名稱。并且默認擴展名是exe。CreateProcess按照以下順序搜索可執行文件

1)主調進程EXE文件所在的目錄

2)主調進程當前的目錄(GetCurrentDirectory)

3)Windows系統目錄,(GetSystemDirectory 通常是system32)

4)Windows目錄

5)PATH環境變量中列出的目錄

當然如果包含了絕對路徑,則會直接使用絕對路徑來查找可執行文件。


只要pszApplicationName設置為NULL,就會發生上述情況。

也可以在pszApplicationName只指定應用程序的名字,但是必須指定擴展名。CreateProcess假定當前目錄,除非文件名之前有指定目錄。并且CreateProcess不會在其他路徑查找文件。


4.2.2 psaProcess,psaThread和bInheritHandles參數

psaProcess和psaThread指定進程對象和線程對象的安全性。也可傳入NULL(默認的安全描述符。)

為psaProcess和psaThread參數使用SECURITY_ATTRIBUTES結構可以使其支持繼承(繼承父進程的句柄表)

一個繼承的例子

1.進程A創建了進程B 并且設置進程B的進程內核對象可繼承,主線程內核對象不可繼承。(兩個對象在進程B創建以后會更新A的句柄表,并且可以看到兩個內核對象的繼承標志)

2.由于創建進程B的時候CreateProcess的bInheritHandles設置為FALSE,所以進程B不會繼承進程A中的任何“可繼承”的內核對象。

3.接著進程A又創建了進程C,并設置CreateProcess的兩個SECURITY_ATTRIBUTES為NULL,表明進程C的進程對象和線程對象在進程A的句柄表中是不可被繼承的。

4.創建C的時候CreateProcess設置了bInheritHandles為TRUE,此時進程C將繼承進程A中的所有可繼承內核對象。這里有之前創建的進程B的進程內核對象,但不包含進程B的主線程內核對象。

代碼如下。

// Prepare a STARTUPINFO structure for spawning process.STARTUPINFO si = { sizeof(si) };SECURITY_ATTRIBUTES saProcess, saThread;PROCESS_INFORMATION piProcessB, piProcessC;TCHAR szPath[MAX_PATH];// Prepare to spawn Process B from Process A.// The handle identifying the new process// object should be inheritable.saProcess.nLength = sizeof(saProcess);saProcess.lpSecurityDescriptor = NULL;saProcess.bInheritHandle = TRUE;// The handle identifying the new thread// object should NOT be inheritable.saThread.nLength = sizeof(saThread);saThread.lpSecurityDescriptor = NULL;saThread.bInheritHandle = FALSE;// Spawn Process B._tcscpy_s(szPath, _countof(szPath), TEXT("ProcessB"));CreateProcess(NULL, szPath, &saProcess, &saThread,FALSE, 0, NULL, NULL, &si, &piProcessB);// The pi structure contains two handles// relative to Process A:// hProcess, which identifies Process B's process// object and is inheritable, and hTrhead, which identifies// Process B's primary thread object and is NOT inheritable.// Prepare to spawn Process C from Process A.// Since NULL is passed for the psaProcess and psaThread// parameters, the handle to Process's process and// primary thread objects default to "noninheritable."// If Process A were to spawn another process, this new// process would NOT inherit handles to Process's process// and thread object.// Because TRUE is passed for the bInheritHandles parameter,// Process C will inherit the handle that identifies Process// B's process object but will not inherit a handle to// Process B's primary thread object._tcscpy_s(szPath, _countof(szPath), TEXT("ProcessC"));CreateProcess(NULL, szPath, NULL, NULL,TRUE, 0, NULL, NULL, &si, &piProcessC);


4.2.3 fdwCreate參數

fdwCreate參數影響了創建新進程創建方式的flag


DEBUG_PROCESS 標志父進程希望調試子進程以及子進程將來創建的所有進程。(父進程現在的身份是調試器)


DEBUG_ONLY_THIS_PROCESS 最近創建的一個進程會通知父進程。而其創建的子進程將不會通知父進程。


這兩項通常用來寫調試器用。參考MSDN

http://www.cppblog.com/cxl82116/archive/2007/06/05/25535.html


CREATE_SUSPENDED 讓系統創建新進程的同時掛起子進程的主線程。這樣父進程就可以修改子進程地址空間中的內存,更改子進程主線程的優先級,或者在進程執行任何代碼以前將次進程添加到一個作業中。父進程修改好子進程,可以調用ResumeThread函數來允許子進程執行代碼。


DETACHED_PROCESS 標志阻止一個基于CUI的子進程訪問其父進程的控制臺窗口,并告訴系統它的輸出發送到一個新的控制臺窗口。

通常一個一個CUI進程創建了一個新的CUI子進程,那么默認情況下新進程也會使用父進程的控制臺。如果指定了此標志那么子進程必須調用AllocConsole來創建自己的控制臺。

一個例子

main.cpp

#include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {TCHAR szPath[MAX_PATH] = TEXT("SubProcess");PROCESS_INFORMATION piProcess;STARTUPINFO si = { sizeof(si) };_tprintf(TEXT("Start to Create Sub Process\n"));CreateProcess(NULL, szPath, NULL, NULL,TRUE, 0, NULL, NULL, &si, &piProcess);return 0; }
SubProcess.cpp

#include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {_tprintf(TEXT("Print from the Sub Process\n"));return 0; }
運行主進程main.exe 查看控制臺打印的字符



如果使用了DETACH_PROCESS子進程除了必須AllocConsole以后還需要重定向stdout

main.cpp

#include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {TCHAR szPath[MAX_PATH] = TEXT("SubProcess");PROCESS_INFORMATION piProcess;STARTUPINFO si = { sizeof(si) };_tprintf(TEXT("Start to Create Sub Process\n"));DWORD dwCreationFlag = DETACHED_PROCESS;CreateProcess(NULL, szPath, NULL, NULL,TRUE, dwCreationFlag, NULL, NULL, &si, &piProcess);return 0; }Subprocess.cpp

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {if (AllocConsole()){freopen("CONOUT$", "w", stdout);_tprintf(TEXT("Print from the Sub Process\n"));}else{}system("pause");return 0; }
運行結果


CREATE_NEW_CONSOLE,會自動為子進程創建自己的CONSOLE不能和DETACHED_PROCESS一起使用。

例子

main.cpp

#include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {TCHAR szPath[MAX_PATH] = TEXT("SubProcess");PROCESS_INFORMATION piProcess;STARTUPINFO si = { sizeof(si) };_tprintf(TEXT("Start to Create Sub Process\n"));DWORD dwCreationFlag = CREATE_NEW_CONSOLE;CreateProcess(NULL, szPath, NULL, NULL,TRUE, dwCreationFlag, NULL, NULL, &si, &piProcess);return 0; }

Subprocess.cpp

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <tchar.h> #include <windows.h>int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {if (AllocConsole()){freopen("CONOUT$", "w", stdout);_tprintf(TEXT("Print from the Sub Process\n"));}else{_tprintf(TEXT("Sub Process has owned a console!\n"));_tprintf(TEXT("Print from the Sub Process\n"));}system("pause");return 0; }
運行結果



CREATE_NO_WINDOW ?標志應用程序不要為子進程創建任何控制臺,用于執行沒有用戶界面的程序。


CREATE_NEW_PROCESS_GROUP 對CUI程序而言的。用于創建新進程組。在同一組中的所有進程,當按下Ctrl+C中斷當前操作時,系統會向這個組的進程發出通知。


CREATE_DEFAULT_ERROR_MODE 新進程不會繼承父進程的錯誤模式


CREATE_SEPARATE_WOW_VDM 表明創建一個16位windows程序(Virtual DOS Machine) 默認創建的16位進程會共享一個VDM(因為創建一個會消耗較多的資源)


CREATE_SHARED_WOW_VDM 在運行16位應用程序才有用, 可以修改注冊表 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\WOW\

DefaultSeparateVDM 設置為yes

可以調用IsWow64Process 來判斷檢測進程句柄是不是32位進程(在64bit系統下)


CREATE_UNICODE_ENVIRONMENT 告訴系統子進程的環境塊包含UNICODE字符,進程環境塊默認包含ANSI字符


CREATE_FORCEDOS 強制系統運行嵌入一個在16位OS中的MS-DOS程序


CREATE_BREAKAWAY_FROM_JOB?允許一個作業中的進程生成一個和作業無關的進程


EXTENDED_STARTUPINFO_PRESENT ?告知操作系統傳給psiStartInfo參數的是STARTUPINFOEX結構


fdwCreate還運行給進程分配優先級。但是大部分應用沒這個必要。

IDLE_PRIORITY_CLASS

低于標準BELOW_NORMAL_PRIORITY_CLASS

標準NORMAL_PRIORITY_CLASS

高于標準ABOVE_NORMAL_PRIORITY_CLASS

HIGH_PRIORITY_CLASS

實時REALTIME_PRIORITY_CLASS


4.2.4 pvEnvironment參數

pvEnvironment參數指向一塊內存,其中包含新進程要使用的環境字符串。大多數時候這個參數傳入NULL,

子進程會繼承其父進程使用的一組環境字符串。

還可以通過GetEnvironmentString函數獲得父進程的環境塊的串地址,傳遞給CreateProcess用于創建子進程。 該功能和傳入NULL的行為一致。

不使用環境字符串的時候調用FreeEnvironmentStrings釋放其空間。


4.2.5 pszCurDir參數

允許父進程設置當前進程的當前驅動器和目錄。如果為NULL則默認和主進程一致。如果非NULL

pszCurDir必須執行一個以'\0'結尾的字符串,其中包含了工作驅動器和目錄。


4.2.6 psiStartInfo參數

指向一個STARTUPINFO 或者 STARTUPINFOEX的結構體

typedef struct _STARTUPINFOW {DWORD cb;LPWSTR lpReserved;LPWSTR lpDesktop;LPWSTR lpTitle;DWORD dwX;DWORD dwY;DWORD dwXSize;DWORD dwYSize;DWORD dwXCountChars;DWORD dwYCountChars;DWORD dwFillAttribute;DWORD dwFlags;WORD wShowWindow;WORD cbReserved2;LPBYTE lpReserved2;HANDLE hStdInput;HANDLE hStdOutput;HANDLE hStdError; } STARTUPINFOW, *LPSTARTUPINFOW; typedef struct _STARTUPINFOEXW {STARTUPINFOW StartupInfo;LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; } STARTUPINFOEXW, *LPSTARTUPINFOEXW;


默認值只要生成次結構

STARTUPINFO si = {sizeof(si)};

CreateProcess(..., &si, ...);

如果沒有把結構內容清0,則成員將包含線程棧上的垃圾數據。會造成CreateProcess 未知運行結果。






關于dwFlags成員,包含一組標志用于修改子進程的創建方式。



另外還有兩個標志

STARTF_FORCEONFEEDBACKCreateProcess會監控進程的初始化過程,并根據結果更改光標的形狀。一旦子進程調用了GetMessage(表明初始化完畢)CreateProcess則停止監控。

START_FORCEOFFFEEDBACK 改為等待圖標

在啟動進程時候控制鼠標指針。CreateProcess臨時將系統的護鏢指針改為 加載等待 圖片


wShowWindow會作為參數傳遞給子進程WinMain函數中的nCmdShow參數。 ?可能值是SW_SHOWNORMAL, SW_SHOWMINNOACTIVE 和 SW_SHOWDEFAULT

也可以在應用程序的快捷方式中修改此值。



Microsoft為了避免創建多個CreateProcess版本,僅僅是通過擴展STARTUPINFOEX結果來升級新的feature。

STARTUPIINFOEX保護一個字段 lpAttributeList用于傳遞額外屬性。

PROC_THREAD_ATTRIBUTE_HANDLE_LIST 該屬性告知CreateProcess進程究竟應該繼承哪一些內核對象的句柄。這些對象句柄必須在創建時指定可繼承(SECURIT_ATTRIBUTES結構中保護設置為TRUE的bInheritHandle字段)。

使用這個屬性,子進程只能繼承一組選定的句柄,而不是繼承所有可繼承的句柄。

Note??if you use this attribute, pass in a value of TRUE for the?bInheritHandles?parameter of the?CreateProcessfunction.


PROC_THREAD_ATTRIBUTE_PARENT_PROCESS? 自行指定進程成為當前要創建的進程的父進程。 但是不改變調試關系。調用CreateProcess的進程仍然能收到調試通知。

調用以下函數兩次,才能創建一個空白的屬性列表。

WINBASEAPI _Success_(return != FALSE) BOOL WINAPI InitializeProcThreadAttributeList(_Out_writes_bytes_to_opt_(*lpSize, *lpSize) LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,_In_ DWORD dwAttributeCount,_Reserved_ DWORD dwFlags,_When_(lpAttributeList == nullptr, _Out_) _When_(lpAttributeList != nullptr, _Inout_) PSIZE_T lpSize);dwFlags參數是保留的始終傳入0.第一次調用是獲得保存屬性的內存塊大小

SIZE_T cbAttributeListSize = 0;BOOL bReturn = InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize);//bReturn is FALSE but GetLastError() return ERROR_INSUFFICIENT_BUFFERPPROC_THREAD_ATTRIBUTE_LIST pAttributeList =(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);bReturn = InitializeProcThreadAttributeList(pAttributeList, 1, 0, &cbAttributeListSize);
接下來可以根據需要用下面函數添加鍵值對。

WINBASEAPI BOOL WINAPI UpdateProcThreadAttribute(_Inout_ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,_In_ DWORD dwFlags,_In_ DWORD_PTR Attribute,_In_reads_bytes_opt_(cbSize) PVOID lpValue,_In_ SIZE_T cbSize,_Out_writes_bytes_opt_(cbSize) PVOID lpPreviousValue,_In_opt_ PSIZE_T lpReturnSize);
lpAttributeList是之前初始化的attribute列表, 他接受 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS ?或者 ? PROC_THREAD_ATTRIBUTE_HANDLE_LIST的值。

如果是前者pValue 必須執行一個變量的地址,包含了新的父進程句柄,而cbSize應該使用sizeof(HANDLE)作為其值。

如果是后者pValue必須執行一個數組的起始地址,包含了運行進程訪問的,可繼承的內核對象句柄, cbSize = sizeof(HANDLE)*句柄數。

dwFlags, PreviousValue和pReturnSize是保留參數,必須設定為0,NULL和NULL

一個初始化Attributelist 并使用STARTUPINFOEX創建進程的例子

創建了一個mutex 并設定AttributeList讓子進程繼承此Mutex

// 1.Create a mutex.SECURITY_ATTRIBUTES sa;sa.nLength = sizeof(sa);sa.lpSecurityDescriptor = NULL;sa.bInheritHandle = TRUE; // Make the returned handle inheritable. HANDLE hMutex = CreateMutex(&sa, FALSE, NULL);// 2. Create the attributelist and fill the attributeSIZE_T cbAttributeListSize = 0;BOOL bReturn = InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize);//bReturn is FALSE but GetLastError() return ERROR_INSUFFICIENT_BUFFERPPROC_THREAD_ATTRIBUTE_LIST pAttributeList =(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);bReturn = InitializeProcThreadAttributeList(pAttributeList, 1, 0, &cbAttributeListSize);UpdateProcThreadAttribute(pAttributeList,0,PROC_THREAD_ATTRIBUTE_HANDLE_LIST,(PVOID)&(hMutex), // pointer to the inheritable mutex handle.sizeof(HANDLE),NULL, NULL);// 3. prepare the attribute for the process.PROCESS_INFORMATION processInfo = { 0 };STARTUPINFOEX esi = { sizeof(STARTUPINFOEX) };esi.lpAttributeList = pAttributeList;TCHAR szPath[] = TEXT("SubProcess");DWORD dwCreationFlag = EXTENDED_STARTUPINFO_PRESENT;// you must set the bInheritable = TRUE ,otherwise the createProcess will be failed.// if you just specified the parent handle. it would be OK.bReturn = CreateProcess(NULL, szPath,NULL, NULL, TRUE,dwCreationFlag, NULL, NULL,(LPSTARTUPINFO)&esi, &processInfo); //...do something.// free the attributelistDeleteProcThreadAttributeList(pAttributeList);
最后應用程序可以調用以下函數獲得 STARTUPINFO結構的一個副本。此結構是由父進程初始化的。子進程可以檢查這個結構并根據成員值來修改其行為。

VOID GetStartupInfo(LPSTARTUPINFO pStartupInfo);

StartupInfo中的一些句柄值在父進程中創建的,其地址是父進程的地址空間。


4.2.7 ppiProcInfo參數

指向一個PROCESS_INFORMATION的結構

typedef struct _PROCESS_INFORMATION {HANDLE hProcess;HANDLE hThread;DWORD dwProcessId;DWORD dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
CreateProcess返回的與當前進程相關的 進程內核對象句柄 和 線程內核對象句柄。引用計數器為1. 而CreateProcess內部打開這些對象引用計數器又會加1

因此若要讓子進程和子進程的主線程在父進程退出之前關閉,子進程必須先終止(引用計數器-1)必須在父進程中調用CloseHandle將其引用計數器再-1.?

釋放子進程的主線程內核對象同理。

注意:關閉句柄并不會真正關閉子進程和子線程(只改變其引用計數器)!


進程會被操作系統分配一個獨一無二的ProcessID, PID=0 是System Idle Process 其線程數等于CPU數量


PID主要供一些系統工具辨識進程使用。

PID由操作系統管理會被回收分配和重用。所以要特備注意。

GetCurrentProcessId獲得PID

GetCurrentThreadId 獲得當前正在運行線程的ID

根據指定句柄獲得PID ?GetProcessId

根據指定句柄獲得線程ID ?GetThreadId

獲得當前線程所在進程的PID GetProcessIdOfThread


ToolHelp函數允許進程通過PROCESSENTRY32 結構查詢其父進程的PID。

但是由于PID具有實效性,可能不準確。最好使用內核對象,窗口句柄等來定位一個進程的父進程。

如果一定要使用PID,唯一的辦法就是保證進程或線程的內核對象不被銷毀。(例如將父進程的內核對象繼承給子進程) 在不需要使用以后調用CloseHandle


4.3 終止進程

4種方式可以終止進程:

1)主線程的入口點函數返回(強烈推薦)

2)進程中有一個線程調用ExitProcess函數(要避免這種方式)

3)另一個進程中的線程調用TerminateProcess函數(要避免這種方式)

4)進程中所有線程都“自然死亡”(這種情況幾乎不會發生)


4.3.1 主進程的入口點函數返回

應該確保只有在主線程的入口點函數返回之后,這個應用程序才終止。這樣主線程的所以資源才能被正確清理。確保以下操作會被執行

1)該線程創建的任何C++對象都將由這些對象的析構函數正確銷毀。

2)操作系統將正確釋放線程棧使用的內存

3)系統將進程的退出代碼(在進程的內核對象中維護)設為入口點函數的返回值

4)系統遞減內核對象的使用計數器


4.3.2 ExitProcess函數

WINBASEAPI DECLSPEC_NORETURN VOID WINAPI ExitProcess(_In_ UINT uExitCode);
該函數將終止進程,并忽略其后的所有代碼。

C運行庫在Main函數返回后將清理所有C運行時資源,最后調用ExitProcess

crt0dat.c中最終調用ExitProcess退出

void __cdecl __crtExitProcess (int status) { #if defined (_CRT_APP) && !defined (_KERNELX)(status);__crtExitProcessWinRT(); #else /* defined (_CRT_APP) && !defined (_KERNELX) */#if !defined (_KERNELX)__crtCorExitProcess(status); #endif /* !defined (_KERNELX) *//** Either mscoree.dll isn't loaded,* or CorExitProcess isn't exported from mscoree.dll,* or CorExitProcess returned (should never happen).* Just call ExitProcess.*/ExitProcess(status); #endif /* defined (_CRT_APP) && !defined (_KERNELX) */ }
SDK中指出,一個進程在其所有線程都終止以后才會終止。 C/C++運行庫采用了一個不同的策略:

不管進程中是否還有其他線程在運行,只要英語程序的主線程從他的入口函數返回,C/C++就會調用ExitProcess來終止進程。

如果在入口函數中調用的是ExitThread那么只會終止主線程,其他線程繼續運行,進程就不會終止。


注意:ExirProcess或ExitThread會導致進程或線程直接終止運行再也不會返回當前函數的調用。對操作系統而言這樣沒什么問題。(進程或線程的資源會被清理)

但C/C++應用程序應該避免這樣調用,因為C/C++運行庫也許不能執行正確的清理

看一下例子

#include <tchar.h> #include <windows.h> #include <stdio.h>class CSomeObj{ public:CSomeObj() { printf("Constructor \n"); }~CSomeObj() { printf("Destructor \n"); } };CSomeObj g_GlobalObj;int _tmain(int argc, TCHAR* argv[], TCHAR * env[]) {CSomeObj LocalObj;ExitProcess(0);return 0; }

全局對象和局部對象都沒有調用析構函數,C++對象沒有被正確析構,因為ExitProcess造成進程當場終止運行。C/C++運行庫沒有機會執行清理工作。

只需要主線程的入口函數返回,C/C++運行庫就能執行其清理工作。

所以不要顯示調用ExitProcess和ExitThread


4.3.3 TerminateProcess函數

調用TerminateProcess也可以終止一個進程

WINBASEAPI BOOL WINAPI TerminateProcess(_In_ HANDLE hProcess,_In_ UINT uExitCode);
任何線程都可以調用TerminateProcess來終止另一個進程或者自己的進程。 hProcess指定了要終止的進程的句柄。其退出代碼的值就是傳給uExitCode的值。

被終止的進程得不到自己要被終止的通知--應用程序不能正確清理,也不能阻止它自己被強行終止。例如這種情況下進程不能將它在內存中的信息flush到磁盤上。

操作系統在進程終止以后會進行徹底清理,保證不會泄露任何操作系統資源。在進程終止后絕對不會泄漏任何東西

TerminateProcess函數是異步的,并不等到進程完全終止了才返回。為了確定進程是否終止需要使用WaitForSingleObject


4.3.4 當進程中的所有線程終止時

當進程的所有線程終止時,操作系統認為沒有任何理由再保持進程的地址空間。并會終止這個進程。進程的退出代碼會被設置為最后一個終止的哪個線程的退出代碼。


4.3.5 當進程終止運行時

一個進程終止時,系統回依次執行以下操作。

1) 終止進程中駐留的任何線程

2)釋放進程分配的所有用戶對象和GDI對象,關閉所有內核對象(如果沒有其他進程打開這些內核對象的句柄,那么他們會被銷毀。否則引用計數器-1)

3)進程的退出代碼從STILL_ACTIVE變為傳給ExitProcess或TerminateProcess函數的代碼

4)進程內核對象的狀態變為已觸發狀態。

5)進程內核對象引用計數器-1


當父進程忘記關閉子進程句柄時候,子進程即使結束。其進程內核對象句柄仍然不會被銷毀。此時可以獲得一些統計信息,例如GetExitCodeProcess來獲得一個已經終止的進程的退出代碼。

WINBASEAPI BOOL WINAPI GetExitCodeProcess(_In_ HANDLE hProcess,_Out_ LPDWORD lpExitCode);
如果被調用的進程未終止,lpExitCode會返回 STILL_ACTIVE(0x103)


重點,如果對進程的統計數據不再感興趣應該調用CloseHandle來遞減內核對象的使用計數器,并釋放它。


4.4 子進程

為了執行復雜的任務而不讓當前線程一直等待,可以創建一個新的進程來完成工作。父進程和子進程之間可以進行一些數據共享。

(DDE dynamic Data Exchange) OLE, 管道, 郵件槽等。共享數據最方便的方式就是使用內存映像文件(CreateFileMapping)


以阻塞方式運行子進程

以下代碼創建了一個子進程,并等待子進程完成相應的工作正常結束以后,再繼續當前線程的執行。

PROCESS_INFORMATION pi;DWORD dwExitCode;//Spawn the child process.BOOL fSuccess = CreateProcess(..., &pi);if (fSuccess) {// Close the thread handle as soon as it is no longer needed!CloseHandle(pi.hThread);// Suspend our execution until the child has terminated.WaitForSingleObject(pi.hProcess, INFINITE);// The child process terminated; get its exit code.GetExitCodeProcess(pi.hProcess, &dwExitCode);// Close the process handle as soon as it is no longer needed.CloseHandle(pi.hProcess);}
上面的例子使用了WaitForSingleObject函數,該函數會一直等待直到hObject參數所表示的對象變為已觸發。 進程對象在終止的時候會變為已觸發。

WaitForSingleObject將會掛起當前線程,直到子進程終止。


一個良好的編程習慣:在不需要使用子進程的相關內核對象應該立即CloseHandle。 否則假定子進程生成了另外一個新的子進程,而自己的主線程已經退出。由于調用CreateProcess的當前進程未釋放子進程的進程對象,因此自進程的內核對象不會被操作系統釋放。


運行獨立的子進程

大多數時候應用程序將另一個進程作為獨立的進程(detached process)來啟動。這就意味著一旦子進程創建,父進程就不再與其通信,或者不必等他它完成工作之后再繼續自己的工作(當前進程不必掛起等待)。這時候只需要調用CloseHandle關閉子進程的進程句柄和主線程句柄。

一個例子

PROCESS_INFORMATION pi;//Spawn the child process.BOOL fSuccess = CreateProcess(..., &pi);if (fSuccess) {// Allow the system to destroy the process & thread kernel// objects as soon as the child process terminates.// Close the thread handle as soon as it is no longer needed!CloseHandle(pi.hThread);CloseHandle(pi.hProcess);}

4.5 管理員以標準用戶權限運行時。

每個用戶登錄windows以后會有一個安全令牌(Security token)其后該用戶啟動的進程都擁有此令牌的權限。

由于許多windows用戶使用Administrator登錄,此用戶的權限太高(可以修改系統文件)可能導致操作系統以高權限執行而已軟件而破壞操作系統。


在Vista以上用戶以Administrator登錄出來具有高特權的安全令牌,還會具有一個篩選過的令牌(filtered token) 只有普通的標準用戶權限(standard user)。

之后從第一個進程開始所有啟動的進程都和篩選過的令牌相關聯。因此默認運行的應用程序將無法訪問受限資源。


可以在進程啟動之前(進程邊界, 進程已經啟動以后會與篩選過的令牌相關聯并且運行時不可修改)讓操作系統提示用戶取得提升權限的同意。也可以在快捷菜單中選擇以管理員身份運行。


可以在自己的應用程序上顯示一個盾牌圖,會彈出一個權限提升對話框。


Windows只允許進程邊界上進行權限提升(未啟動以前)。 可以用一個未提升權限的進程來生成另一個提升了權限的進程,后者將包含一個com服務器,這個新進程將保持活動狀態。這樣未提升權限的進程就可以向已經提升權限的進程發出IPC調用,而不必啟動一個新的實例再終止它自身。


4.5.1 自動提升進程的權限

windows每次啟動應用程序都將自動彈框詢問并提升應用程序的權限(比如安裝程序)

在可執行文件中嵌入資源(RT_MANIFEST) 系統會檢查<trustInfo>段

一個例子 參考blog :?http://blog.csdn.net/sesiria/article/details/51939231

<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator"/> </requestedPrivileges> </security> </trustInfo>
level 屬性可能有3個不同的值



也可以保存成一個 ?應用程序.exe.manifest的文件的外部清單

如果exe本身內嵌了一個清單,則外部清單會被忽略。

也可以自行設定應用程序默認以管理員權限運行



4.5.2 手動提升進程的權限

CreateProcess函數沒有提供提升應用程序權限的功能。 可以調用ShellExecuteEx函數

SHSTDAPI_(BOOL) ShellExecuteExW(_Inout_ SHELLEXECUTEINFOW *pExecInfo);typedef struct _SHELLEXECUTEINFOW {DWORD cbSize; // in, required, sizeof of this structureULONG fMask; // in, SEE_MASK_XXX valuesHWND hwnd; // in, optionalLPCWSTR lpVerb; // in, optional when unspecified the default verb is choosenLPCWSTR lpFile; // in, either this value or lpIDList must be specifiedLPCWSTR lpParameters; // in, optionalLPCWSTR lpDirectory; // in, optionalint nShow; // in, requiredHINSTANCE hInstApp; // out when SEE_MASK_NOCLOSEPROCESS is specifiedvoid *lpIDList; // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLISTLPCWSTR lpClass; // in, valid when SEE_MASK_CLASSNAME is specifiedHKEY hkeyClass; // in, valid when SEE_MASK_CLASSKEY is specifiedDWORD dwHotKey; // in, valid when SEE_MASK_HOTKEY is specifiedunion { HANDLE hIcon; // not used #if (NTDDI_VERSION >= NTDDI_WIN2K)HANDLE hMonitor; // in, valid when SEE_MASK_HMONITOR specified #endif // (NTDDI_VERSION >= NTDDI_WIN2K)} DUMMYUNIONNAME; HANDLE hProcess; // out, valid when SEE_MASK_NOCLOSEPROCESS specified } SHELLEXECUTEINFOW, *LPSHELLEXECUTEINFOW;
SHELLEXECUTEINFO結構中 lpVerb 和lpFile 為主要關注的參數。前者設定為 “runas” 后者包含可執行文件的路徑。

代碼如下所示

// Initialize the structure.SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };// Ask for privileges elevation.sei.lpVerb = TEXT("runas");// Create a Command Prompt from which you will be able to start// other elevated applications.sei.lpFile = TEXT("cmd.exe");// Don't forget this parameter; otherwise, the window will e hidden.sei.nShow = SW_SHOWNORMAL;if (!ShellExecuteEx(&sei)) {DWORD dwStatus = GetLastError();if (dwStatus == ERROR_CANCELLED) {// the user refused to allow privileges elevation.}else if (dwStatus == ERROR_FILE_NOT_FOUND) {// The file defined by lpFile was not found and// an error message popped up.}}


但一個進程提升權限以后,每次他再調用 CreateProcess來生成另一個子進程都會獲得和它的父進程一樣的提升后的權限。

某些任務需要高權限的時候應該在啟動該任務的界面元素盤顯示一個盾牌圖標。

由于任務管理器由另一個進程或者一個進程中的COM服務來執行,所以應該需要將需要管理員權限的所有任務集中到另一個應用程序中,并通過ShellExecuteEx(lpVerb 傳送runas)來提升他的權限。 具體要執行的特權操作采用命令行參數傳遞。(SHELLEXECUTEINFO的lpParameters字段)


4.5.3 何為權限上下文

如何判斷應用程序是以管理與身份運行還是以篩選令牌權限運行的

以下代碼的函數GetProcessElevation返回一個提升類型并指出進程是否以管理員身份運行的BOOL值。

BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE * pElevationType, BOOL * pIsAdmin) {HANDLE hToken = NULL;DWORD dwSize;// Get current process tokenif (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))return FALSE;BOOL bResult = FALSE;// Retrive elevation type informationif (GetTokenInformation(hToken, TokenElevationType,pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {// Create the SID corresponding to the Administrators groupBYTE adminSID[SECURITY_MAX_SID_SIZE];dwSize = sizeof(adminSID);CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL,&adminSID, &dwSize);if (*pElevationType == TokenElevationTypeLimited) {// Get handle to linked token (will have one if we are lua)HANDLE hUnfilteredToken = NULL;GetTokenInformation(hToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);// Check if this original token contains admin SIDif (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin)){bResult = TRUE;}// Don't forget to close the unfiltered tokenCloseHandle(hUnfilteredToken);}else {*pIsAdmin = IsUserAnAdmin();bResult = TRUE;}}// Don't forget to close the process tokenCloseHandle(hToken);return bResult; }
TOKEN_ELEVATION_TYPE枚舉類型定義


首先獲取這些值并判斷使用的令牌是否被篩選過。

接下來判斷用戶是否是管理員。

如果沒有被篩選過,直接調用IsUserAnAdmin()返回

如果被篩選過,首先獲取未篩選的令牌把TokenLinkedToken傳給GetTokenInformation)然后判斷其是否包含管理員的Sid。(借助于CreateWellKnownSid和CheckTokenMembership)

該函數可以用來獲取當前進程的令牌和權限用以控制是否顯示盾牌圖標。


4.5.4 枚舉系統正在運行的進程

Windows在注冊表中維護一個性能數據庫,包含海量信息。例如RegQueryValueEx把注冊表的根目錄設為KEY_PERFORMANCE_DATA

但是該數據庫在Win95和98上不可用,?

沒有自己的函數,使用注冊表函數

數據庫信息布局非常復雜


為了更方便訪問此數據庫借助Performance Data Helper(PDH.dll)

在(Win9X中 使用ToolHelp API的Process32First 和Process32Next函數)

在WinNT中使用EnumProcess函數。

在Win2000開始Tool Help函數支持2K以上的NT內核系統。


4.5.5 Process Information示例程序 (該示例涉及較多的后續章節的知識 還用到了WinDbg)

運行結果


除了安全描述符(SID)和訪問控制列表(access control list, ACL). 系統還通過在系統訪問控制列表(SACL)中新增一個名為強制標簽的訪問控制項(Access control entry)來為受保護的資源分配一個所謂的完整性級別(integrity level)

信任級別


利用Process Explorer工具可以查看應用程序的完整性級別(integrity level)。請選擇Select Columns -> Process Image->Integrity Level



windows還利用完整性級別來拒絕低完整性級別的進程訪問高完整性級別的用戶界面。這個機制成為用戶界面特權隔離(User Interface Privilege Isolation,UIPI)

為了防止完整性低的進程獲取另一個完整性高的進程的信息或進行虛假輸入,Windows阻止完整性低的進程通過PostMessage/SendMessage向完整性高的進程發送Windows消息. 也阻止通過掛鉤子來攔截完整性較高的進程的windows消息。

可以利用WindowDump來做實驗或者spy++


總結

以上是生活随笔為你收集整理的《Windows核心编程》读书笔记四 进程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

久久在线精品视频 | 97超碰国产精品女人人人爽 | 久久一区国产 | 98福利在线 | 18岁免费看片 | 久久综合桃花 | 麻花豆传媒一二三产区 | 成人97视频 | 二区三区中文字幕 | 久久综合狠狠 | 天天射天天爽 | 国产精品美女久久久久久久 | 综合中文字幕 | av在观看| 国产精品igao视频网网址 | 国产伦理一区二区 | 日本在线观看一区二区三区 | 成人中文字幕av | 精品国产一区二区三区久久久蜜月 | 婷婷亚洲五月色综合 | 国产午夜精品久久久久久久久久 | 亚洲涩涩网站 | 国产在线黄色 | 亚洲欧美成人在线 | 中文字幕a∨在线乱码免费看 | 国产福利在线免费观看 | 日韩免 | 亚洲 欧洲 国产 日本 综合 | 最新日韩精品 | 最新中文在线视频 | 四虎影视国产精品免费久久 | 国产一区国产二区在线观看 | 在线国产视频 | 91精品中文字幕 | 日韩电影在线观看一区二区 | 国产精品一区一区三区 | 久一在线| 亚洲精品美女久久17c | 中文字幕在线观看视频一区 | 最新国产中文字幕 | 精品国产激情 | www黄在线| 在线午夜av | 在线观看精品黄av片免费 | 超碰97中文 | 国产1区2区3区精品美女 | av日韩国产| 激情网站五月天 | 四虎成人精品永久免费av | 欧美日韩久久不卡 | 天天拍天天色 | 丁香伊人网 | 婷婷中文在线 | 欧美美女视频在线观看 | 久久精品系列 | 欧美性极品xxxx做受 | 色在线免费视频 | 91精品啪在线观看国产81旧版 | av一级一片 | 四虎成人精品 | 99 视频 高清 | 亚洲福利精品 | 热久久免费视频精品 | 国产免费国产 | 91精品夜夜 | 免费看av片网站 | 亚洲第一av在线 | 91在线看网站 | 色综合天天综合网国产成人网 | 色在线视频网 | 色www.| 麻豆视频网址 | 欧美精品亚州精品 | 免费在线日韩 | 在线观看视频福利 | 国产小视频免费在线观看 | 最近最新中文字幕视频 | 欧美精品在线观看 | 麻豆观看 | 99热这里只有精品1 av中文字幕日韩 | 久久精品3 | 九九色视频| 国产精品美女久久久久久久网站 | 五月婷婷视频在线 | 97超碰在线免费 | 国产九九在线 | 久操视频在线免费看 | 亚洲一级黄色片 | 婷婷久久一区二区三区 | 欧美一级免费 | 97色国产| 91黄视频在线 | 九九视频免费在线观看 | 中文字幕色在线视频 | 99热最新在线| 看av在线| 久草精品电影 | 国产美女视频 | 中文字幕av在线电影 | 欧美人zozo| 97人人澡人人爽人人模亚洲 | 91女人18片女毛片60分钟 | 国产69精品久久久久9999apgf | 久久视频在线观看免费 | 97超碰免费在线观看 | 伊人伊成久久人综合网站 | 日韩最新中文字幕 | 免费的国产精品 | 91亚色视频 | www.久久免费 | 欧美精品中文字幕亚洲专区 | 九九免费在线观看视频 | 女人18毛片a级毛片一区二区 | 蜜臀精品久久久久久蜜臀 | 色婷婷久久 | 99久久久久免费精品国产 | 欧美视频www | 9i看片成人免费看片 | 久久国产二区 | 国产裸体视频网站 | 色哟哟国产精品 | 超碰电影在线观看 | 狠狠网 | 国产午夜三级一区二区三桃花影视 | 亚洲国产精品电影 | 99久久9 | 在线观看亚洲电影 | 久久国产欧美日韩精品 | 最新av中文字幕 | 狠狠地日| 韩国三级在线一区 | 天天亚洲 | 色精品视频 | 黄色三级在线看 | 亚洲欧洲精品一区二区精品久久久 | 精品国产美女在线 | 成年人天堂com | av在线8 | av一区二区三区在线 | 久草视频首页 | 波多野结依在线观看 | 亚洲理论片在线观看 | 欧美aa在线 | 国产高清第一页 | 91精品在线播放 | 亚洲精品免费在线视频 | 欧美日韩大片在线观看 | 成人黄色资源 | 日韩精品中文字幕有码 | 综合色亚洲 | 中文在线免费看视频 | 久久神马影院 | 亚洲免费视频在线观看 | 亚洲jizzjizz日本少妇 | 亚洲国产日韩欧美在线 | 亚洲成a人片综合在线 | 高清av影院 | 欧美aaa级片| 伊人va | 97超碰中文 | 毛片永久新网址首页 | 国产精品 9999 | 五月天激情在线 | 999国内精品永久免费视频 | 欧美精品资源 | 国产亚洲精品av | 国产亚洲午夜高清国产拍精品 | 毛片a级片| 午夜av在线免费 | 久久久久久久久艹 | 亚洲国产片 | 精品国产欧美 | 韩国三级av在线 | 久久影院中文字幕 | 色停停五月天 | 国产精品一区二区在线观看 | 偷拍精偷拍精品欧洲亚洲网站 | 91欧美精品 | 亚洲精品日韩在线观看 | 国产精品99久久久久久久久久久久 | 91在线视频观看 | 精久久久久 | 久久观看最新视频 | 激情黄色av | 狠狠干狠狠艹 | 99久久精品免费看国产一区二区三区 | av综合网址 | 精品国产_亚洲人成在线 | 在线亚洲天堂网 | 国产在线91在线电影 | 欧美日韩性视频在线 | 成人国产精品 | 久久国产电影院 | 一区二区三区韩国免费中文网站 | 草莓视频在线观看免费观看 | 国产免费小视频 | 国产精品午夜久久 | 久久在线观看视频 | 伊在线视频 | 久热免费| 91精品啪在线观看国产 | 免费国产在线精品 | 免费精品在线观看 | 高潮久久久久久久久 | 亚洲伊人婷婷 | 欧美日韩中文在线 | 狠狠色狠狠色 | 亚洲成人av一区二区 | 免费亚洲黄色 | 在线观看成人毛片 | 中文字幕成人网 | 在线一区电影 | 91免费观看国产 | 日韩精品欧美专区 | 精品视频免费看 | 四川妇女搡bbbb搡bbbb搡 | 日韩电影在线观看一区二区三区 | 国产裸体视频网站 | 亚洲国产精品电影在线观看 | 日韩精品一区二 | 日韩av影视在线观看 | 日日夜夜综合 | 国产成人av免费在线观看 | 92中文资源在线 | av线上免费观看 | 91看片黄色 | 成人av片在线观看 | 亚洲精品天天 | 中中文字幕av在线 | 国产日产精品一区二区三区四区 | 激情 亚洲| 久久私人影院 | 久久精品久久久久 | 97超级碰 | 午夜精品久久久 | 91精品国产综合久久福利 | 欧美国产日韩一区二区三区 | 亚洲综合欧美激情 | 在线观看视频亚洲 | 国产日韩中文字幕 | 91av视屏| 久久婷婷丁香 | 国产精品国产亚洲精品看不卡15 | 国产精品成人免费精品自在线观看 | 99电影 | 成人a免费 | 中文字幕视频播放 | 亚洲九九爱| 天天艹天天干天天 | 一区在线观看视频 | 天天综合视频在线观看 | 亚洲黄色av网址 | www日韩欧美 | 精品99在线 | 国产精品99在线观看 | 精品国产乱码久久久久久1区二区 | 日韩高清国产精品 | 麻豆视频在线播放 | 欧美俄罗斯性视频 | 在线国产99 | 日夜夜精品视频 | 国产手机在线观看视频 | 在线观看视频福利 | 欧美精品在线免费 | 婷婷在线综合 | 国产精品亚州 | 国产成人免费精品 | 国产麻豆果冻传媒在线观看 | 玖草在线观看 | 天堂av影院| 91麻豆精品国产91久久久更新时间 | 久久精品999| 亚洲免费精彩视频 | 国产精品成人一区二区三区吃奶 | 精品国产视频在线观看 | 欧美成人黄色片 | 久久久久www| 久久手机在线视频 | 日本高清中文字幕有码在线 | a天堂一码二码专区 | 欧美国产日韩一区二区三区 | 成年人免费av | 成在线播放 | 欧美精彩视频在线观看 | 中文字幕在线观看视频一区 | 欧美日韩视频在线一区 | 欧美精品久久久久久久久免 | 在线观看国产区 | 久久久国产毛片 | 日韩一级理论片 | 精品国内 | 999ZYZ玖玖资源站永久 | 五月婷婷在线视频观看 | 国精产品永久999 | a在线观看国产 | 国内外成人在线 | 国产精品久久久久久久妇 | 丁香5月婷婷久久 | 在线免费观看一区二区三区 | 日韩一区二区三区高清在线观看 | 日本中文字幕在线免费观看 | 2020天天干夜夜爽 | 六月丁香伊人 | 97免费在线观看视频 | 国产精品视频最多的网站 | 看片的网址 | 中文字幕在线观看第一页 | 国产精品久久久久毛片大屁完整版 | 久久久久福利视频 | 操操爽 | 日韩激情综合 | 久久久久久久久久久久av | 久久精品国产第一区二区三区 | 久久国产福利 | 久久有精品 | 偷拍视频一区 | 高潮久久久久久 | 国产精品欧美激情在线观看 | 国产成人av电影 | 韩国av免费在线观看 | 免费网站在线观看人 | 亚洲资源网 | 亚洲成人av片在线观看 | 伊人久久国产精品 | 午夜精品久久久久久久99婷婷 | 日本久久精品视频 | 亚洲成人黄色 | 免费观看成人 | 亚洲第一区在线观看 | 国产一卡二卡在线 | 超碰国产97 | 国产精品你懂的在线观看 | 国产不卡一 | 伊人久久精品久久亚洲一区 | av电影在线观看 | 97电影手机| 在线观看国产www | 欧美日韩观看 | 色婷婷激婷婷情综天天 | 在线午夜 | 成 人 黄 色 视频播放1 | 欧美a级一区二区 | www.午夜视频 | 久久艹国产视频 | 日韩免费一区二区三区 | 国产精品中文字幕在线 | 中文字幕一区二区三区久久蜜桃 | 国产精品美女毛片真酒店 | 久久久久免费精品视频 | 俺要去色综合狠狠 | 成年人免费电影在线观看 | 欧美日韩成人 | 久久观看 | 亚洲精品国产拍在线 | 美女黄濒 | 99精品久久99久久久久 | 久久这里只有精品久久 | 亚洲爽爽网 | 欧美成人一二区 | 国产成人性色生活片 | 黄色特级片| 国产日韩精品一区二区三区 | 国产亚洲精品久久网站 | 91精品久久久久 | 狠狠躁夜夜av | 永久免费av在线播放 | 又黄又爽又色无遮挡免费 | 色偷偷88888欧美精品久久久 | 91精品国产欧美一区二区 | 91av99| 免费视频三区 | 女人18毛片a级毛片一区二区 | av888.com| 国产精品日韩欧美一区二区 | 91伊人久久大香线蕉蜜芽人口 | 国产成人精品亚洲 | 999国产精品视频 | 国产精品久久久免费看 | 日韩天堂在线观看 | 97人人网| 国产精品欧美久久 | 欧洲亚洲国产视频 | 天天射天天干天天 | 欧美日性视频 | 奇米网在线观看 | 99国产一区二区三精品乱码 | 精品久久久久久亚洲综合网站 | 国产黄色片久久久 | 久久这里只有精品久久 | 婷婷综合影院 | 人人射av | 日韩精品视频免费看 | 在线观看免费av网站 | 久草在线免费电影 | 欧美极品少妇xbxb性爽爽视频 | 国产色资源 | 天天综合91 | 久久99精品波多结衣一区 | 久久国产一区 | 日韩理论片在线 | 日韩免费电影 | 欧美资源在线观看 | 久久伊人国产精品 | 91福利试看 | 在线观看91久久久久久 | 丁香六月婷婷开心婷婷网 | 国产一区二区三区四区在线 | 精品久久久久久久久久久久 | 四虎影视成人永久免费观看视频 | 久久精品最新 | 国产一级淫片在线观看 | 一区二区三区久久精品 | 成人小视频在线 | 伊人视频| 最新av免费在线观看 | 日韩免费在线观看 | 黄网站色视频免费观看 | 视频在线观看入口黄最新永久免费国产 | 欧美激情第八页 | 97高清视频 | 欧美怡红院视频 | 亚洲国产精品成人av | 中文字幕中文字幕中文字幕 | 97精产国品一二三产区在线 | 久久久久免费网站 | 亚洲国产mv| 久久老司机精品视频 | 国产高清在线 | 91网免费观看 | 99亚洲视频 | 永久免费在线 | 最近中文字幕免费av | 操操综合网| 特级黄录像视频 | 成人免费看黄 | 天天爽夜夜爽人人爽一区二区 | 国产精品久久99综合免费观看尤物 | 伊人天堂av | 91在线国内视频 | 日韩高清一区 | 亚洲成人国产精品 | 黄色a在线观看 | 99视频免费看 | 五月天免费网站 | 日韩电影精品 | 成人av一区二区兰花在线播放 | 国产精品欧美久久久久无广告 | 国产一区二区久久精品 | 亚洲国产精品日韩 | 91网站免费观看 | 国产视频999 | 欧美怡红院视频 | 免费看片网站91 | 91色国产在线 | 国产资源精品在线观看 | 91精品国产麻豆国产自产影视 | av免费看网站| 超碰日韩在线 | 色在线国产 | 在线不卡中文字幕播放 | 成人性生交大片免费看中文网站 | 欧美一级视频在线观看 | 91视频免费观看 | 在线成人中文字幕 | 中文国产字幕在线观看 | 中文在线字幕免费观 | 91精品国产福利在线观看 | 黄色软件网站在线观看 | 日日操天天操夜夜操 | 日本69hd| 欧美性色xo影院 | 欧美精彩视频在线观看 | 毛片随便看 | 日韩高清av在线 | 日本中文字幕在线免费观看 | 国产99久久 | 国产一区二区三区在线 | 欧美国产日韩激情 | 婷婷在线看 | 国产破处在线播放 | 国产精品免费久久久久久久久久中文 | 9在线观看免费高清完整 | 97超碰人人澡人人爱 | 欧美91在线| www色综合| 91免费国产在线观看 | 久久香蕉电影网 | 日本中文在线播放 | 久久精品之 | 国产精品一区在线播放 | 在线午夜电影神马影院 | 久久草网站 | 久久精品日产第一区二区三区乱码 | 国产成人在线一区 | 一级免费黄色 | 国产精品自产拍在线观看桃花 | 日韩在线网 | 日韩城人在线 | 成人av网站在线观看 | 国产在线观看免费观看 | 麻豆免费精品视频 | 91网址在线看 | 久久久影视| 国产精品男女 | 国产日韩精品在线 | 亚洲精品色 | 一区二区三区免费在线观看视频 | 丁香五香天综合情 | 六月激情婷婷 | 男女免费av | 国产免费小视频 | 91精品国产高清自在线观看 | 国内久久视频 | 美女网色 | 免费福利视频网 | 婷婷激情网站 | 成人av电影免费在线观看 | 就要色综合 | 日韩欧美在线第一页 | 在线观看av片 | 综合色在线观看 | 国产精品久久久久婷婷二区次 | 麻豆国产露脸在线观看 | 国产福利不卡视频 | 一本到视频在线观看 | 免费福利片2019潦草影视午夜 | 亚洲国产日韩欧美 | 99精品欧美一区二区蜜桃免费 | 精品国产乱码久久久久久浪潮 | 中文字幕在线观看免费高清电影 | 久久99亚洲精品久久 | 免费看一级黄色 | 国产精品video | 亚洲国产日韩一区 | 69夜色精品国产69乱 | 国产精品久久久久久久久大全 | 久久成人综合 | 亚洲精品在线二区 | 精品999在线观看 | 国产精品久久久久久久久久久久午夜 | 五月天综合网 | 天天操天天射天天操 | 天天爱天天舔 | 奇米网网址 | 欧美性高跟鞋xxxxhd | 99视频久久| 色天天综合久久久久综合片 | 久久理论视频 | 色亚洲网| 三级动态视频在线观看 | 天堂av网址| 91精品在线免费观看视频 | 色婷婷欧美 | 免费一级片在线观看 | 国产高清久久久久 | 欧美a级免费视频 | 最近日韩免费视频 | 色综合久久久久综合99 | 四虎国产精品成人免费影视 | 99热99re6国产在线播放 | 久草五月 | 久久久国产精品网站 | 中文字幕在线观看完整 | 国产又黄又爽又猛视频日本 | 操操色 | 丰满少妇在线观看 | 久久一区二区三区四区 | 黄色a级片在线观看 | 欧美一二三区在线观看 | 99在线精品免费视频九九视 | 国产一级a毛片视频爆浆 | 91精品国产乱码在线观看 | 一本色道久久精品 | 在线观看日韩 | 国产福利网站 | 日本精a在线观看 | 精品国产乱码久久久久久三级人 | 日日干视频 | 久久激情片 | 人人干人人干人人干 | 97久久久免费福利网址 | 免费在线色视频 | 毛片网站在线 | 久久99热这里只有精品 | 久久久精品 一区二区三区 国产99视频在线观看 | 免费精品在线视频 | 久久久免费播放 | 久久久久久免费 | 亚洲伊人天堂 | 国产精品av在线 | 欧美韩日精品 | 欧美成人在线免费 | 亚洲欧洲国产视频 | 久久久久99精品国产片 | 欧美天天射 | 又黄又爽又刺激的视频 | 国产精品一区二区中文字幕 | 亚洲精品小视频 | 成人性生交视频 | 人人爽人人爽av | 久久国产乱 | 日韩高清dvd | 97超碰在线资源 | 欧美综合色 | 免费视频99 | 久久99视频 | 97综合在线 | 亚洲一区 影院 | 精品视频区 | 探花视频免费在线观看 | 黄色一集片 | 五月婷婷久久丁香 | 又黄又爽又无遮挡免费的网站 | 日韩av在线网站 | 区一区二区三在线观看 | 久久久黄色av | 操操操综合 | 精品福利视频在线 | 色瓜| 人人超碰97 | 狠狠色丁香久久综合网 | 天天爽天天搞 | 久久精品免费播放 | 97超碰精品| 黄污网| 亚洲少妇天堂 | 亚洲精品久久久蜜桃 | 97超碰伊人| 麻豆视频一区二区 | 在线中文字幕播放 | av大全在线 | 特级毛片在线观看 | 免费看国产一级片 | 国产精品久久久久久麻豆一区 | 91高清免费看 | 伊人久久av | 精品国产区 | 久久精品91久久久久久再现 | av在线播放快速免费阴 | 91福利影院在线观看 | 狠狠色免费 | 久久国产福利 | 免费国产在线精品 | 天天爽综合网 | 国产日产在线观看 | 国产99一区二区 | 久久久一本精品99久久精品 | 最新国产中文字幕 | 国产精品 中文在线 | 日韩精品一区二区三区三炮视频 | 色综合久久久久久久 | 天天插综合| 狠狠干五月天 | 欧美精品一区二区蜜臀亚洲 | 五月婷婷六月丁香 | 狠狠干狠狠色 | 日韩极品视频在线观看 | 日韩中文字幕在线观看 | 精品亚洲在线 | 久久91久久久久麻豆精品 | 狠狠操狠狠干2017 | 精品成人在线 | 久久超级碰视频 | 人人揉人人揉人人揉人人揉97 | 在线视频日韩一区 | 日日夜夜亚洲 | 久久久久久久久久久久久影院 | 日韩午夜精品福利 | 久久xxxx | 超碰97在线资源站 | 国产传媒一区在线 | 久久综合导航 | 久久香蕉一区 | 国产视频在线观看一区二区 | 精品久久久99 | 91精品免费看 | 99精品视频在线播放免费 | 欧美日韩裸体免费视频 | 久草视频在线免费 | 国产色a在线观看 | 久久久久影视 | 成人午夜电影网站 | 久久成人高清 | 亚洲在线资源 | 欧美日本高清视频 | 国产二区视频在线 | 国产女人18毛片水真多18精品 | 最近日本mv字幕免费观看 | 精品国产乱码久久久久久1区2匹 | 欧洲性视频 | 黄色一级片视频 | 国产精品久久99综合免费观看尤物 | 日韩在线欧美在线 | 国产精品99久久99久久久二8 | 成人一级黄色片 | 国产精品一区二区在线播放 | 精品久久久久久久久久久久久久久久 | 999久久久国产精品 高清av免费观看 | 99精品国产兔费观看久久99 | 日产乱码一二三区别免费 | 欧美一级在线看 | 国产精品ssss在线亚洲 | 亚洲九九精品 | 高清免费在线视频 | 五月婷婷另类国产 | 中文字幕在线观看不卡 | 国产精品av一区二区 | 日韩免费看 | 亚洲成人xxx | 国产69久久 | 国产日本高清 | www.亚洲激情.com | 国产91粉嫩白浆在线观看 | 中文字幕日韩免费视频 | 福利视频午夜 | 在线免费观看黄色av | 亚洲激情综合网 | 91色亚洲| 99免费在线视频观看 | 九九九视频在线 | 久久人人爽人人爽人人片av软件 | 国产专区精品 | 午夜av网站 | 免费色av| 国产伦精品一区二区三区高清 | 一级黄色免费 | 欧美亚洲一区二区在线 | 美女网站视频久久 | 中文字幕乱码日本亚洲一区二区 | 天天爱天天爽 | 久久久国产网站 | 激情丁香 | 久久激情视频 久久 | 欧美激情综合色 | 一本色道久久精品 | 精品久久久久久久久久久久久久久久 | 国产亚洲精品久久久久久无几年桃 | 久久夜色网 | 欧美成人理伦片 | 在线亚洲播放 | 国产亚洲精品中文字幕 | 国内精品久久久久久久影视简单 | 亚洲色影爱久久精品 | 国产 色 | 99热国产在线| 美国av片在线观看 | 久久99热久久99精品 | 99视频偷窥在线精品国自产拍 | 免费能看的黄色片 | 欧美午夜精品久久久久久浪潮 | 色香com.| 96精品高清视频在线观看软件特色 | 免费看黄20分钟 | 高清免费av在线 | 日本黄色免费在线观看 | 在线视频1卡二卡三卡 | 日韩av午夜| 久久综合九色综合97婷婷女人 | 亚洲精品久久久久999中文字幕 | 国产成人中文字幕 | 亚洲精品视频免费观看 | 亚洲女欲精品久久久久久久18 | 久久免费视频7 | 久久99久久久久久 | 中文字幕在线字幕中文 | 国产精品欧美精品 | 日韩精品视频免费在线观看 | 亚洲精品视频在线观看免费 | 日操操| 亚洲国产网站 | 成x99人av在线www | 天天综合区 | 九九久久婷婷 | 91人人揉日日捏人人看 | 国产 在线观看 | 欧美a免费 | 午夜国产福利在线 | 国产精品一区二区久久久久 | 久久久久久久久福利 | 欧美精品视 | www狠狠| 国产一区二区成人 | 久久综合狠狠综合久久狠狠色综合 | 欧美午夜精品久久久久久孕妇 | 欧美日韩综合在线观看 | 一区中文字幕在线观看 | www91在线观看 | 中文在线免费看视频 | 欧美激情视频在线免费观看 | 亚洲第二色 | 亚洲狠狠婷婷 | 在线观看av大片 | 97精品国产97久久久久久免费 | 一区三区视频在线观看 | 最新极品jizzhd欧美 | 国产精品久久99精品毛片三a | 亚洲成a人片在线观看中文 中文字幕在线视频第一页 狠狠色丁香婷婷综合 | 狠色狠色综合久久 | 欧美日韩高清一区二区 | 97精品久久人人爽人人爽 | 国产精品毛片一区二区三区 | 在线超碰av| 国产精品黄| 日韩av不卡在线播放 | 亚洲欧洲成人 | 五月天激情综合 | www国产一区 | 一区二区三区在线看 | 五月天综合网 | 精品国产伦一区二区三区观看体验 | 99久久精品免费看国产一区二区三区 | aaa毛片视频 | 99色在线 | 国产精品久久久久久久久久久久午夜 | 久久福利影视 | 中文字幕91 | 国产视频黄 | 国产精品一区在线播放 | 人人玩人人添人人澡97 | 色偷偷中文字幕 | 精品在线视频观看 | 精品久久免费看 | 天天操操操操操操 | 97在线观看免费观看高清 | 亚洲人成人在线 | 欧美天堂视频在线 | av线上免费看 | 99国产一区 | 久久久伦理 | 欧美日韩在线电影 | 国产精品一区二区在线 | 免费亚洲一区二区 | 国产香蕉在线 | 成人国产精品一区二区 | 亚洲视频综合 | 揉bbb玩bbb少妇bbb | 国产在线超碰 | 亚洲精品在线免费看 | 有码一区二区三区 | 日色在线视频 | 91免费高清在线观看 | 久久久午夜精品理论片中文字幕 | 日日躁夜夜躁aaaaxxxx | 国产精品国产毛片 | a级国产乱理论片在线观看 伊人宗合网 | 中文字幕在线日亚洲9 | 美女视频黄免费的 | 国产日产欧美在线观看 | 在线黄色免费av | 五月天色综合 | 久久免费美女视频 | 国产成人精品一区二区三区 | 国产大片免费久久 | 精品久久久久久久久亚洲 | 91av视频在线观看 | 欧美亚洲一区二区在线 | 日韩黄色免费电影 | 永久免费观看视频 | 久久免费视频在线 | 高清中文字幕 | 91黄色免费看 | 亚洲成人av电影 | 97精品欧美91久久久久久 | 在线a人片免费观看视频 | 亚洲一区久久久 | 亚洲精品女人久久久 | 色婷婷综合久色 | 91人人澡人人爽人人精品 | 久草免费色站 | 国产黄色在线看 | 欧美日本不卡 | 亚洲激情在线观看 | 国内久久精品 | 福利久久久| 国产手机在线视频 | 在线亚洲人成电影网站色www | 在线观看www视频 | 国产精品久久一区二区三区, | www.夜夜操.com| 特级西西www44高清大胆图片 | a久久久久久 | 综合色亚洲 | 亚洲经典视频在线观看 | 久久电影国产免费久久电影 | av中文在线| 亚洲精品www. | 天天操天天干天天爱 | 国产亚洲一级高清 | 久久久国产精品一区二区三区 | 91资源在线视频 | 国产成人久久77777精品 | 国产日韩在线播放 | 丝袜美女视频网站 | 五月婷在线 | 91大神精品视频在线观看 | 九九热在线观看视频 | 国产69精品久久99的直播节目 | 亚洲黄色成人网 | 国产又黄又爽无遮挡 | 国产精品视频区 | 在线av资源 | 久久在线精品视频 | 在线观看色网站 | 国产精品av在线免费观看 | 日韩精品中文字幕在线播放 | 在线国产高清 | 最近中文字幕国语免费av | 婷婷av色综合 | 91亚洲精品久久久蜜桃借种 | 国产一区二区成人 | 精品999在线 | 国产成人av免费在线观看 | 99自拍视频在线观看 | 日韩影片在线观看 | 久久久国产一区 | 狠狠的日日| 国产一卡在线 | 国产免费激情久久 | 成年人在线观看免费视频 | 可以免费观看的av片 | 在线 视频 亚洲 | 精品国产乱码一区二区三区在线 | 黄视频网站大全 | 国产精品一区二区你懂的 | 黄色大片免费播放 | 欧美91精品国产自产 | 毛片网在线观看 | 97人人模人人爽人人少妇 | 日韩精品免费 | 高清av免费一区中文字幕 | 伊人春色电影网 | 国产美女视频网站 | 久久精品中文视频 | 成年人黄色免费网站 | 日韩美在线观看 | 天天色天天射天天综合网 | 日韩www在线 | 久久久96| 午夜精品三区 | 波多野结衣视频一区 | 黄色av成人在线观看 | 国产精品乱码久久久久 | 国产一级片不卡 | 午夜精品一区二区三区免费 | 亚洲精品美女 | 国产精品日韩久久久久 | 黄p网站在线观看 | 中文字幕免 | 欧美极品一区二区三区 | 黄色特级一级片 | 欧美日韩三区二区 | 久久久久久久免费观看 | 91精品综合在线观看 | 黄色软件网站在线观看 | 亚洲激情国产精品 | 欧美在线观看视频免费 | 日韩高清精品一区二区 | 国产精品久久久久久久久久免费看 | 男女精品久久 | 久久久久久久久久电影 | 国产xxxx | 精品久久久久国产免费第一页 | 婷婷丁香六月 | 久久96国产精品久久99软件 | 在线欧美日韩 | 色婷婷中文 | 91精品中文字幕 | 精品亚洲视频在线 | 久久国产免费看 | 精品久久久久_ | 国产一级在线 | 欧美福利片在线观看 | 99精品免费久久久久久久久日本 | 成人毛片一区二区三区 | 天天狠狠干 | 成年人免费观看国产 | 欧美日韩高清在线一区 | 久草www | 国产亚洲欧洲 | 免费观看一级特黄欧美大片 | 国产自产在线视频 | 国产一级视频免费看 | 久久人人爽爽 | 青青草国产在线 | 精品亚洲成人 | 久久久久久久久久久久久9999 | 99热精品国产一区二区在线观看 | 91精品国产91 | 国产在线精品一区二区 | 在线观看mv的中文字幕网站 | www.久热 | 香蕉视频免费在线播放 | 久久精品国产成人 | 色国产视频 | 国产成人一区二区三区免费看 | 欧美精品中文在线免费观看 | 色吊丝在线永久观看最新版本 | 中文字幕久久精品亚洲乱码 |