(转载)Gloomy对Windows内核的分析(研究CreateProcess)

我给出一个反汇编Win32 API函数CreateProcess的例子,来演示研究子系统的技术,同时演
示Win32是如何与Windows NT的执行系统协同工作的。

从MSDN中得到函数原型:

BOOL CreateProcess(
  LPCTSTR lpApplicationName,// pointer to name of executable module
  LPTSTR lpCommandLine, // pointer to command line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // process security attributes
  LPSECURITY_ATTRIBUTES lpThreadAttributes,   // thread security attributes
  BOOL bInheritHandles, // handle inheritance flag
  DWORD dwCreationFlags, // creation flags
  LPVOID lpEnvironment, // pointer to new environment block
  LPCTSTR lpCurrentDirectory,   // pointer to current directory name
  LPSTARTUPINFO lpStartupInfo, // pointer to STARTUPINFO
  LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION
);

函数中所有的参数都没有详尽的描述。很快,在开始的几行中,建立了异常处理__except_h
andler3(堆栈中的结构体对应于Visual C的结构体)。然后根据dwCreationFlags进行相应
有趣的处理。在任何情况下都会在dwCreationFlags里去掉标志CREATE_NO_WINDOW(对于我来
说这是个迷)。之后检查不允许的标志组合DETACH_PROCESS | CREATE_NEW_CONSOLE。如果这
些位被同时设置就会输出错误。从新进程优先级中选择一个优先级(除一个之外,清除所有
dwCreationFlags中的优先级位)。如果要求是REAL_TIME优先级,但不能分到处理器,则设
置为HIGH_PRIORITY。接下来是对参数lpApplicationName、lpCommandLine、lpEnvironment
的繁琐处理。分析结果标明,函数CreateProcessW实际上在文档中已经写明。因此,我们考
虑到命令行和应用程序名已不相同。DOS风格的形式为完整的路径。使用未公开的ntdll.dll
中的函数:

NTSYSAPI
BOOLEAN
NTAPI
RtlDosPathNameToNtPathName_U (char* lpPath,
                    RTL_STRING *NtPath,
                    BOOLEAN AllocFlag,
                    RTL_STRING *Reserved);

结果会得到/??/:/样的路径。然后,填充公开了的OBJECT_ATTRIBUTES结构体,在ObjectNam
e域中放入指向获得的路径的指针并调用未公开的函数:

NTSYSAPI
IOSTATUS
NTAPI
NtOpenFile (OUT DWORD* Handle, IN ACCESS_MASK DesiredAccess,
      OBJECT_ATTRIBUTES* ObjAttr, PIO_STATUS_BLOCK IoStatusBlock,
      DWORD ShareAccess, DWORD OpenOptions);

访问使用的是SYNCHRONYZE | FILE_EXECUTE。取得打开文件的句柄用于调用另外一个未公开
的函数:

NTSYSAPI
NTSTATUS
NTAPI
NtCreateSection(
  OUT PHANDLE SectionHandle,
  IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  IN PLARGE_INTEGER MaximumSize OPTIONAL,
  IN ULONG Protect,
  IN ULONG Attributes,
  IN HANDLE FileHandle OPTIONAL
  );

大多数未公开的系统函数都是由相应的公开的Win32 API调用的。API函数CreateFileMappin
g是对NtCreateSection的封装。实际上,即使系统直接调用这些函数,也没人会干扰(而且
还节省开销)。有趣的是NtCreateSection的一个主要的、由API函数生成的参数:

DesiredAccess=(flProtectLow==PAGE_READWRITE)?STANDARD_RIGHTS_REQUIRED|7 :
                STANDART_RIGHTS_REQUIRED | 5;

DesiredAccess只可以取两个值。从CreateProcessW中调用的形式如下:

NtCreateSection ( &SectionHandle, STANDARD_RIGHTS_REQUIRED| 0x1F,
            NULL, &qwMaximumSize,
            PAGE_EXECUTEREAD, SEC_IMAGE, NtFileHandle );

这样就得到了映象,并将文件——映象源——关闭。这是用公开的NtClose函数进行的。来分
析一下NtCreateSection返回后的代码。对错误处理这里就不进行讨论了,否则会十分繁琐,
要讨论大量的次要的函数。我们来研究没有发生错误而且映象是PE映象的情况。调用著名的
未公开函数:

NTSYSAPI NTSTATUS NTAPI
    NtQuerySection(
        IN HANDLE SectionHandle,
        IN SECTIONINFOCLASS SectionInformationClass,
        OUT PVOID SectionInformation,
        IN ULONG SectionInformationLength,
        OUT PULONG ReturnLength OPTIONAL
        );

系统中有一些类似于NtQueryInformationXxxxx这样的函数(未公开)。要说是未公开的,在
NTDDK.H中还是描述了一些函数的原型和调用这些函数用到的结构体信息。Matt Pietrek在其
在Microsoft Systems期刊(MSDN中有)的文章中详细描述了NTDDK.H中的NtQueryInformati
onProcess的主要功能。遗憾的是,关于NtQuerySection函数的信息是不存在的。所有这样的
函数都有实际上相同的原型并处理操作系统中的对象。NtQuerySection返回两类信息(Sect
ionInformationClass可以为0或1)。对应于取0还是取1,结构体的大小为16或是58个字节。
CreateProcessW调用的SectionInformation参数的信息类是1。

Struct SECTION_INFO_CLASS1 {
DWORD EntryPoint;
DWORD field_4;
DWORD StackReserved;
DWORD StackCommited;
DWORD SubSystem;
DWORD ImageVersionMinor;
DWORD ImageVersionMajor;
DWORD unknown1;
DWORD characteristics;
DWORD Machine;
DWORD Unknown[4];
};

我们看到,这个信息是从PE映象的首部中取得的。在主要的域characteristics中输出的是映
象的类型(是否是可执行的)。然后检查机器类型,解析SubSystem域,检查映象版本。并最
终调用未公开的函数:

NtCreateProcess(
  OUT PHANDLE ProcessHandle,
  IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,
  IN HANDLE ParentProcess, //-1
  IN BOOLEAN InheritHandles,
  IN HANDLE SectionHandle,
  IN HANDLE DebugPort OPTIONAL, // NULL
  IN HANDLE ExceptionPort OPTIONAL //NULL
  );

由此建立了Windows NT的进程对象。关闭映象,因为已经不再需要了。接着设置对象属性,
调用未公开函数NtSetInformationProcess

NTSYSAPI
NTSTATUS
NTAPI
NtSetInformationProcess(
  IN HANDLE ProcessHandle,
  PROCESSINFOCLASS ClassInfo,
  IN PVOID Information,
  IN ULONG Length,
);

在NTDDK.H中有枚举值_PROCESSINFOCLASS,这个值描述了信息类。调整信息类的值:Proces
sDefaultHardErrorMode,ProcessBasePriority。对于这些类,信息结构体本身就是一个32
位的DWORD。然后调用未公开的函数,Matt Pietrek在其文章中介绍过该函数:

NTSYSAPI
NTSTATUS
NTAPI
NtQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);

我们取得的信息是ProcessBasicInfo,NTDDK.H文件中有对其的描述。

typedef struct _PROCESS_BASIC_INFORMATION {
  NTSTATUS ExitStatus;
  PPEB PebBaseAddress;
  KAFFINITY AffinityMask;
  KPRIORITY BasePriority;
  ULONG UniqueProcessId;
  ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

对于CreateProcessW来说,必需的信息是PEB的地址。因为在获得这项信息之后很快就调用内
部函数_BasePushProcessParameters。从参数判断,其用途是调整仅由此进程产生的地址空
间。接下来调用两个内部的复杂函数。先调用_BaseCreateStack。_BaseCreateStack分配并
调整进程堆栈。第一,选出用于保留和提交(reservrd和commited)堆栈的值。而且,如果
SizeReserved和SizeCommited为0,则要从发出CreateProcess的进程的PE文件的首部中获取
这些值。接着对这些值进行修整并在进程产生的地址空间中保留内存,对此用到未公开的函
数NtAllocateVirtualMemory(对应于Win32 API函数VirtualAllocEx,VirtualAllocEx是对
其非常简单的封装,而且这两个函数的参数完全相同)。之后,进行两个调用,用下面的伪
码能更简洁的说明:

FreeReserved=SizeReserv-SizeCommited;
ReservedAddr+=FreeReserved;
if(SizeReserved<=SizeCommited) fl=0;
else {
  ReservedAddr-=Delta;
  SizeCommited+=Delta;
  fl=1;
    }
NtAllocateVirtualMemory(Han,&ReservedAddr,0,SizeCommited,1000,4);

//[skipped]

NtProtectVirtualMemory
  (ProcHan,&ReservedAddr,Delta,PAGE_READWRITE|PAGE_GUARD,&OldProt);
                /* 对VirtualProtectEx的封装 */

可见,这里在保留区域中分配内存(在其末尾)。并且分配的内存大于Delta。这一部分(大
小为Delta)的属性是PAGE_GUARD和PAGE_READWRITE。最后得到以下结构体:

***Stack***
---------------?-ReservedAddr
|         |
|         |
| RESERVED   |<- SizeReserved - (SizeCommited+Delta)
|         |
|--------------|-CommitedAddr
| GUARD PAGE |<- Delta
|--------------|
| READ_WRITE |<- SizeCommited
|         |
L----------------SS:ESP

这样,为堆栈分配了SizeCommited字节。保留了SizeReserved。之后在堆栈之下的保留区分
配的内存被转换为GUARD页(转换成这种页可以引发异常)。从源代码中可以看到,错误的D
elta的大小可能会产成悲惨的后果。因为这可是个关键的信息——我们来看从哪里找出Delt
a的值:

.text:77F04B99         mov   eax, large fs:18h
.text:77F04B9F         mov   ecx, [eax+30h] ; PEB
.text:77F04BA2         mov   eax, [ecx+54h] ; READ ONLY DATA
                ; ReadOnlyStaticServerData
.text:77F04BA5         mov   edx, [eax+4]
[skipped]
.text:77F04BB1         mov   esi, [edx+128h] ; Delta

在这一部分里,EAX寄存器指向用于所有进程的全局区域。这个区域只允许被读取。当然,已
给出的关于堆栈的更高层次的信息是众所周知的,而这些信息的真实性在源代码中得到了证
实。结果,执行BaseCreateStack函数填充StackInformation结构体。

Typedef struct _StackInformation
{
  DWORD Reserved0;
  DWORD Reserved1;
  DWORD AddressOfTop;
  DWORD CommitAddress;
  DWORD ReservedAddress;
} StackInformation;

从这个结构体中得到信息本质上是个参数,用来调用下面这个有趣的函数BaseInitializeCo
ntext:

BaseInitializeContext(PCONTEXT Context, // 0x200 bytes
PPEB Peb,
PVOID EntryPoint,
DWORD StackTop,
int Type // union (Process, Thread, Fiber)
);

这个函数的几个参数:PEB的地址,堆栈的入口点和参数定义了要创建的上下文(纤程,进程
、线程)。函数填充CONTEXT结构体(NTDDK.H中有)的几个域。其中一个域很有意思,在其
中放置了起始点(BaseFiberStart、BaseProcessStartThunk、BaseThreadStartThunk中的一
个)。这个点“分娩”出了线程,产生的线程就在新的上下文中执行。实际上,所有三个偏
移处的代码都很简短——就是填充相应的堆栈映象并转到两个函数中的某一个。这两个函数
分别是_BaseProcessStart和_BaseThreadStart。这两个函数很是相象,我们只看_BaseProc
essStart函数。

这个函数在链表中建立了第一个异常处理(见TEB)。当对内存进行了错误的访问时,正是这
个异常处理调用了那个有OK和CANCEL的对话框。这个处理程序会结束当前进程。但有时如果
异常由错误的服务线程产生,则只结束这个线程。

于是,在BaseInitializeContext返回后,就填充相应的结构体。并且这个结构体被用作未公
开的NtCreateThread函数的参数。NtCreateThread的原型如下:

NTSYSAPI
NTSTATUS
NTAPI
NtCreateThread(
    OUT PHANDLE ThreadHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    IN HANDLE ProcessHandle,
    OUT PCLIENT_ID ClientID,
    IN PCONTEXT Context, /* see _BaseInitializeContext */
    IN StackInformation* StackInfo, /* see _BaseCreateStack */
    IN BOOLEAN CreateSuspended /* ==1 */
);

终于,在对PE映象的SubSystem主要域的数据进行处理之后,通过LPC转到Win32服务。进程应
该只在Win32子系统下创建。关于此原因的一些高层次信息可以在Halen Kaster的书中读到。

对于CreateProcess函数来说,必须完成的任务就是启动线程(当然,如果没有在参数dwCre
ationFlags中设置CREATE_SUSPEND标志)。线程的启动进行对NtResumeThread(对Win32的R
esumeThread的封装)的调用。完成了!现在剩下的还有释放内存和正确的退出。

到此对Win32子系统的CreateProcess函数的主要分析可以得出结论:子系统通常与Windows
NT的执行体系统协同工作,子系统大多都使用未公开的函数,子系统通过LPC与自己的服务器
通讯,许多Win32 API函数都是对Nt函数的封装。所有这些都是我们熟知的,但我们需要用反
汇编来证实。

[转贴]Gloomy对Windows内核的分析(研究CreateProcess)相关推荐

  1. Gloomy对Windows内核的分析

    /Files/ddlzq/Gloomy对Windows内核的分析.pdf 转载于:https://www.cnblogs.com/ddlzq/archive/2010/09/03/1817244.ht ...

  2. Gloomy对Windows内核的分析(对象管理器)

    Gloomy对Windows内核的分析(对象管理器) 目录 Inside WINDOWS NT Object Manager 00."对象化的" Windows NT 01.Win ...

  3. Gloomy对Windows内核的分析(内核反汇编技术)

    内核反汇编技术 =============================== Windows  NT主要是由C写成的,所以总的来说进程本身的反汇编不是很复杂.通常对局部变 量和参数的使用是通过地址和 ...

  4. (转载)Gloomy对Windows内核的分析(介绍)

    INTRO (写给NT研究者) ============================= Мы всего лишь момен т во времени                       ...

  5. windows 内核情景分析

    原文很长:先转部分过来,有时间看一下: 一 windows 内核情景分析---说明 说明 本文结合<Windows内核情景分析>(毛德操著).<软件调试>(张银奎著).< ...

  6. Windows内核系统调用分析

    系统调用 进程 --> 调用OS API:OS进程管理 --> 调配进程. 仅从用户进程角度,OS就像是一个被动响应的运行时库.Windows提供了一个系统调用界面作为外层,即Win32A ...

  7. [14]Windows内核情景分析 --- 文件系统

    文件系统 一台机器上可以安装很多物理介质来存放资料(如磁盘.光盘.软盘.U盘等).各种物理介质千差万别,都配备有各自的驱动程序,为了统一地访问这些物理介质,windows设计了文件系统机制.应用程序要 ...

  8. windows内核情景分析 --- 文件系统

    文件系统 一台机器上可以安装很多物理介质来存放资料(如磁盘.光盘.软盘.U盘等).各种物理介质千差万别,都配备有各自的驱动程序,为了统一地访问这些物理介质,windows设计了文件系统机制.应用程序要 ...

  9. windows内核情景分析读书笔记-----HYPERSPACE

    主要介绍HYPERSPACE的创建映射函数 赏光看我这一系列文章的朋友最好结合毛德操老师的书来看,具体的细节我这里就不阐述了 简单说下这个函数功能 Windows内核有时候需要把某些物理页面临时映射到 ...

最新文章

  1. Deno 正式发布,彻底弄明白和 node 的区别
  2. OSChina 周三乱弹 —— 你是靠自己努力才失败的
  3. iOS开发 - 百度地图后台持续定位
  4. 佛教:关于时间(段)的几种描述
  5. VS2017断点调试
  6. Mysql编辑工具中使用(Navicat查询结果显示行号)
  7. ykcchf 2013 v2.1101 最新版下载
  8. Ubuntu安装nextcloud-17.0.1
  9. 2021年奎屯七中高考成绩查询,奎屯高考成绩查询
  10. mysql addslashes c_addslashes()用途与php怎样防止mysql注入?
  11. SourceTree下载与安装 ---记录一下,如果忘记了再拿来看看
  12. Java线程池 与Lambda
  13. 极路由1S HC5661A 刷入不死u-boot(breed)加刷潘多拉固件教程
  14. 音视频转码ffmpeg(十六)
  15. Python数据分析项目实例5: 分析某餐饮企业的订单详情表数据(基于matplotlib的python数据可视化分析)
  16. windows 11激活Office提示网络问题无法激活
  17. Firefox中fetch请求后直接调用location.reload(),返回NS_BINDING_ABORTED错误
  18. 微信中各种代码/符号合集
  19. java计算机毕业设计共享充电宝管理系统源码+mysql数据库+系统+lw文档+部署(2)
  20. 地球人都在玩跨境电商

热门文章

  1. 2.2.2 .6定点数的乘法运算-1原码一位乘法
  2. java 接口单元测试_单元测试
  3. Python prep 随想练习 Day7-红黑树
  4. WebRTC的视频解码原理简析
  5. Salt 翻译之Grains
  6. 网站外链应该都去哪里发?
  7. Spring Boot-延迟依赖注入
  8. 如何从字符串中提取相关内容
  9. 图片隐写 安恒ctf_CTF中图片隐写的一些整理总结
  10. 记一次p2v转换报错解决