《解读window核心编程》 之 进程
1. 进程是执行文件的运行时形态。包括两部分:内核数据(对应内核对象)、地址空间(包括执行文件代码和栈堆等动态内存)。
2. 把VC的“系统-子系统”值删除掉,即不指定控制台或GUI,则编译器会根据代码中存在main或者WinMain来自动选择子系统(这里不谈Unicode了),很方便。
3. 启动程序:根据子系统执行mainCRTStartup/WinMainCRTStartup,在该函数中干几件事(1)准备命令行和环境变量(用于char *argv[]和char *env[])(2)初始化CRT的全局变量(包括_osver、_winmajor、_winver、__argc、_environ等)(3)初始化CRT运行库的内存分配(malloc、free)、IO函数等(4)初始化全局对象调用C++构造函数。
4. 退出程序:main返回后mainCRTStartup会调用exit,exit干以下几件事:(1)执行通过_onexit注册的函数(2)执行全局对象的C++析构函数(通过atexit注册的)(3)判断_CrtDumpMemoryLeaks设置的内存泄漏检测标志,尝试检测内存泄漏(4)调用ExitProcess。
5. HINSTANCE和HMOUDLE完全相同,都是表示映像文件加载到内存后的基址(链接器中可以配置)。GetModuleHandle传入文件名可以获得模块基址;传入NULL可以得到执行文件的HINSTANCE(即使调用者位于某个模块中同样返回应用程序基址);GetModuleHandleEx可以根据函数地址得到模块基址
6. 访问环境变量:char *env[]参数、GetEnvironmentStrings、GetEnvironmentVariable、ExpandEnvironmentStrings(将一个使用了类似”%USERPROFILE%”环境变量的字符串中的变量替换成值)。
7. 系统环境变量:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Enviroment。用户环境变量:HKEY_CURRENT_USER\Enviroment。
8. 修改环境变量后可以通知相关的系统窗口(如控制面板等):SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) “Enviroment”)。
9. 可以设置特定线程在一个CPU核心集合上执行。
10. SetErrorMode。设置该进程如何响应各种错误。
11. 关于相对路径:在通过GetEnvironmentStrings返回的环境变量中,有一部分不是真正的环境变量,比如“=C:=C:\Windows”“ =F:=F:\Projects\Test05”,他们表示一种进程相关配置“本进程在特定驱动器下对应的当前文件夹”。一个进程除了有以上配置外,还有一个当前驱动器,最终GetCurrentDirectory返回的当前路径就是当前驱动器+当前驱动器对应的当前文件夹。使用SetCurrentDirectory会改变该驱动器的当前文件夹,还会改变进程的当前驱动器(但这个API的改变并不会在GetEnvironmentStrings上体现出来,使用C函数_chdir可以同时改变两者,故C函数更优)。进程刚启动时,如果不考虑从父进程继承的环境,则只有进程当前驱动有当前文件夹,其他驱动都无配置。使用相对路径访问文件的时候,其绝对路径可以用GetFullPathName得到。”文件名”这样的相对路径的绝对路径是GetCurrentDirectory() + “文件名”;”驱动器盘符:文件名”(注意不是”驱动符:/文件名”)这样的相对路径的绝对路径就是”该驱动器的当前文件夹”(如果无配置,则是根目录) + “文件名”。
看如下代码:
_chdir("D:/Downloads"); // 修改D:的当前路径为Downloads,且进程当前驱动器为D:
_chdir("F:/Projects"); // 修改F:的当前路径为Projects,且进程当前驱动器为F:
std::ofstream("1.txt"); // 当前驱动器是F:,所以绝对路径是F:/Projects/1.txt
std::ofstream("d:1.txt"); // D:的当前路径是Downloads,所以绝对路径是D:/Downloads/1.txt
这种行为从cmd的cd命令也可以看得出点端倪。
归纳:相对路径访问文件的时候,首先将相对路径展开成绝对路径,使用GetFullPathName,后者分两步:首先判断是否包含驱动器(以X:开头),如果没有,则在开头添加进程当前驱动器;然后检查是否以”X:/”开头,如果没有,则将”X:”展开成”X:/” + “对应驱动的当前文件夹”。两步过后得到绝对路径。
12. GetVersionEx获取系统版本信息。VerifyVersionInfo检测当前系统是否满足版本需要。
13. CreateProcess的参数:关于lpApplicationName和lpCommandLine,有两种用法:(1)前者指定应用程序路径,后者指定参数(第一个参数前面要有一个空格,似乎底层会直接连接两个串)(2)前者为NULL,后者指定路径和参数,空格隔开。常用第二种方法。注意,lpCommandLine中由于是用空格分隔参数的,所以对其中含有空格的路径一定要用内层引号括起来。另外CreateProcessW有一个奇怪的行为,它会修改参数lpCommandLine(似乎只在lpApplicationName为空的时候会修改),所以使用Unicode版本的时候传入的该参数不能是常字符串(如L”Nodepad 1.txt”),而应该另外准备缓冲传给该API供其修改,因为ANSI版本是调用Unicode版本的且在编码转换的时候内置了缓冲,所以CreateProcessA的lpCommandLine参数可以是常串(最终API会修改转换编码的临时缓冲)。默认情况下,CUI的CUI型子进程会和父进程共享控制台,在参数dwCreationFlags中添加DETACHED_PROCESS或CREATE_NEW_CONSOLE标志可以阻止这种行为。在dwCreationFlags中添加CREATE_NEW_PROCESS_GROUP标志,可以控制进程组的组织,用户按下Ctrl+C的时候同一进程组的所有进程得到通知。lpEnvironment指定为NULL的时候,底层为用GetEnvironmentStrings来填充。lpCurrentDirectory为NULL的时候,子进程继承父进程的当前目录。lpStartupInfo不能为空,至少要初始化结构为0并将cb赋为sizeof。使用STARTUPINFOEX结构作为lpStartupInfo参数,还可以具体指定子进程要继承哪些父进程的可继承内核对象(即使bInheritHandles参数为FALSE)。
14. cmd进程输入命令行前显示的路径,就是其当前路径(GetCurrentDirectory)。在CreateProcess时,cmd没有设置子进程当前路径,而资源管理器将路径设置成子进程镜像目录。因为cmd的子进程会继承cmd的当前路径(lpCurrentDirectory为空的结果),因此最好在用cmd启动程序的时候先将cmd的当前路径设置为新进程的镜像路径。
15. 进程和线程结束后,句柄对象被标记为激活, WaitForSingleObject会返回。
16. CreateProcess后,可以使用WaitForInputIdle或类似函数来等待新进程初始化环境完毕开始运行。
17. WoW64:Windows 32 On Windows 64。所有64位windows运行着这个虚拟机,用来执行32位程序。判断一个32位程序是否是运行在64位系统的32位虚拟机中:IsWow64Process。
18. 父进程创建子进程时使用的lpStartupInfo,在子进程中可以使用GetStartupInfo来查询。
19. 创建一个子进程时,进程和主线程本身的存在就有了引用1,而调用CreateProcess的父进程又会有他们的引用所以计数到了2。要完全销毁进程和线程,需要计数为0,所以除了需要进程本身结束外,引用的该进程的其他线程也要释放引用。当然,CreateProcess过后父进程马上CloseHandle并不会结束子进程,只是释放自己的引用,使其计数为1,这是正常的行为。要确保某个进程或线程不被销毁,不调用CloseHandle即可。如果进程本身已经退出了,但还有其他进程引用它,则它的地址空间被回收,只有内核对象还存在(比如这时再对句柄使用API查看内存,则内存信息为空),这也是为什么可以查看已经退出的进程的退出码的原因(退出码保存在内核对象中)。
20. 进程和线程的ID位于同一个系统顶层名空间。即任意进程的任意线程ID绝不可能和任意进程ID相同。这个ID会被系统循环利用。
21. GetProcessIDOfThread。
22. 进程只有在它所有线程都结束后才会结束。ExitProcess会杀死所有线程,所以可以直接结束进程,在主线程中调用ExitThread只会结束主线程(即,主线程创建一个死循环线程后自己_exitthreadex,这个进程不会退出。)。main返回后CRT调用exit后者再调用ExitProcess,所以在main中return可以直接结束进程。
23. 通过ExitProcess或ExitThread(单线程时)结束进程,由于这些API比CRT更底层,他们只能保证正确的释放Windows资源(内存、内核对象引用),并不保证释放C++资源(CRT底层资源、全局对象的析构函数),故一定要从main中返回自然的结束进程(其他原因在后面章节说明)。TerminateProcess也出于相同的原因应该避免使用。
24. CreateProcess创建的子进程会继承父进程的Security Token权限,而ShellExecuteEx可以提高子进程的权限(令lpVerb参数为”runas”)。资源管理器使用前者创建子进程,所以通过它开打的程序都具有和资源管理器相同的权限。
25. 关于Vista及更高系统的UAC(User Account Control):Vista以前的系统如果以管理员账号登陆,资源管理器(Explorer)会获得一个管理员权限的Security Token,然后从资源管理器打开的子进程都会继承这个最高权限,这种行为非常危险。Vista以后,即使以管理员账号登陆,资源管理器仍然只持有一个一般权限的Token(Filtered Token),子进程如果想提升权限,有两种途径:(1)用户“以管理员身份运行”启动该进程(2)子进程自己提出请求要求用户提升权限(子进程是安装程序、或者子进程配置有.manifest文件说明权限需求)。另外,在很多软件中出现有小盾牌图标的按钮,也是要求提高权限,点击过后会结束当前进程,重启一个高权限进程(如资源管理器中“显示所有用户的进程”按钮)。其实这三种提高权限都是父进程调用了ShellExecuteEx。
26. IsUserAnAdmin判断当前用户是否是管理员。在Vista及以上的系统中,即使是管理员,进程也有可能因为筛选Token而不具备最高权限。
27. 枚举所有进程:Process32First、Process32Next、EnumProcesses。
28. 可以从HMOUDLE中读取IMAGE_DOS_HEADER和IMAGE_NT_HEADERS,进而从这些PE头中取得模块的推荐加载地址等信息。
29. PEB(Process Enviroment Block)包含了进程的启动命令行、当前路径等数据。该字段可以通过NtQueryInformationProcess的PROCESS_BASIC_INFORMATION参数取得。
30. 可以通过WinDbg的dt命令,查看一些结构的具体成员布局,如PEB等。
31. Windows完整性机制(Windows Integrity Mechanism):这是UAC之外的另一套安全机制,Windows通过在系统访问控制表(SACL, System Access Control List)中增加访问控制项(ACE, Access Control Entry)实现,每一种受保护的资源都有对应的完整性级别(Integrity Level),每个进程都有一个基于Token计算的完整性级别,如果进程的级别小于资源的级别,则不能访问资源。提升Token权限之前的进程级别为中,提升后为高,而像IE这样可以能执行网络代码的进程为低。可以通过GetTokenInfomation查看一些和完整性级别相关的策略。窗口系统也根据完整性级别,拒绝低级别者向高级别使用PostMessage、SendMessage等API。
32. Vista以上有一些进程是特殊的受保护进程,ToolHelp API对他们无效,因此无法查看进程信息。
33. GetProcessTime查看进程时间,GetProcessIoCounters查看IO次数。
34. GetProcessImageFileName返回内核格式的文件名
http://blog.csdn.net/biggbang/article/details/24390241
《解读window核心编程》 之 进程的更多相关文章
- 《解读window核心编程》 之 字符和字符串处理方式
推荐的字符和字符串处理方式 開始将文本字符串想象为字符的数组,而不是 char 或字节的数组. 用通用数据类型(如 TCHAR/PTSTR )来表示文本字符和字符串. 用明白的数据类型(如 BYTE ...
- 《解读window核心编程》 之 注冊表
1 注冊表的作用及组织形式 Windows系统使用注冊表来存储系统和应用程序配置数据.非常多系统和应用程序重要的配置的信息都存储在注冊表中. 注冊表是一种以树型结构组织的数据库.树的每个节点称 作键( ...
- windows核心编程之进程间共享数据
有时候我们会遇到window进程间共享数据的需求,例如说我想知道系统当前有多少某个进程的实例. 我们能够在程序中定义一个全局变量.初始化为0.每当程序启动后就加1.当然我们我们能够借助第三方介质来储存 ...
- windows核心编程-信号量(semaphore)
线程同步的方式主要有:临界区.互斥区.事件.信号量四种方式. 前边讲过了互斥器线程同步-----windows核心编程-互斥器(Mutexes),这章我来介绍一下信号量(semaphore)线程同步. ...
- Windows核心编程:第4章 进程
Github https://github.com/gongluck/Windows-Core-Program.git //第4章 进程.cpp: 定义应用程序的入口点. // #include &q ...
- 回忆读windows 核心编程
看<windows 核心编程> 第五版到纤程了,下一章节即将介绍内存体系编程.如果做window平台下的开发,我感觉此书一定要读.记得开始讲解了window的基础,然后讲解内核对象.内核对 ...
- 【windows核心编程】 第六章 线程基础
Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ① 一个是线程的内核 ...
- python核心编程第二版笔记
python核心编程第二版笔记由网友提供:open168 python核心编程--笔记(很详细,建议收藏) 解释器options:1.1 –d 提供调试输出1.2 –O 生成优化的字节码(生成 ...
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
随机推荐
- TensorFlow 下 mnist 数据集的操作及可视化
from tensorflow.examples.tutorials.mnist import input_data 首先需要连网下载数据集: mnsit = input_data.read_data ...
- C 删除字符串1字符串2
#include<stdio.h> #include<string.h> void main() { char s1[1000],s2[100],b[100]; int i,j ...
- 一个git pull无法使用的问题
之前通过git管理的一个项目,今天直接用eclipse通过ssh加入工程后,每次通过git命令行pull代码都报以下错误: Unable to negotiate with 21.12.1.167 p ...
- 【25.47%】【codeforces 733D】Kostya the Sculptor
time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- tomcat启动时自动加载一个类 MyServletContextListener
目的: 我们知道在tomcat启动后,需要页面请求进行驱动来执行操作接而响应.我们希望在tomcat启动的时候能够自动运行一个后台线程,以处理我们需要的一些操作.因此需要tomcat启动时就自动加载一 ...
- new与属性访问的顺序,从一道JS面试题说起
这段时间一直在研究设计模式,在看工厂模式的时候,看到一段代码 VehicleFactory.prototype.createVehicle = function ( options ) { if( o ...
- win10 uwp 使用 msbuild 命令行编译 UWP 程序
原文:win10 uwp 使用 msbuild 命令行编译 UWP 程序 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http:// ...
- 正确 C#
未来的期望
接触 C# 一年.整体上是一个很完好的语言,可是某些细节特征还是不够完美.这里记下我如今对它将来的一些期望. 更强大的泛型约束 与 C++ 的模板相似,C# 的泛型使得编写适用于多种类型的代码更加简洁 ...
- 使用WinPcap获取网卡MAC地址
关键字:WinPcap 网卡 MAC地址 作者:txw1958 在WpdPack_4_1_2\WpdPack\Examples-remote\PacketDriver\GetMacAddress\目录 ...
- sql server 2016 JSON 学习笔记
虽然现在win服务器已经几乎不用了,但是网上看到2016开始原生支持json 还是想试试 建立一个表 id int , json varchar(2000) json字段中输入数据 {"r ...