峥嵘岁月

joshua_yu的网络空间

joshua
joshua的公告
联系方式: QQ:404271575 MSN:joshua_yu@263.net
最近评论
wBlf_www:请问我在过滤函数中截获所有收发数据包,会否有漏包的现象,我发现通过web发邮件,截获的数据不能恢复出邮件发出的状态(邮件包括内容和多个附件)。
wBlf_www:请问我在过滤函数中截获所有收发数据包,会否有漏包的现象,我发现通过web发邮件,截获的数据不能恢复出邮件发出的状态(邮件包括内容和多个附件)。
ohyeath:在你的工程文件里面设置一下就可以了,project->setting->c/c++->preprosessor->addtional include headers 添加你的sdk路径,
ohyeath:在你的工程文件里面设置一下就可以了,project->setting->c/c++->preprosessor->addtional include headers 添加你的sdk路径,
ohyeath:在你的工程文件里面设置一下就可以了,project->setting->c/c++->preprosessor->addtional include headers 添加你的sdk路径,
文章分类
收藏
    相册
    08年第一期儿子照片
    过年
    交大新面貌
    我的可爱儿子
    周末烧烤之众生相
    关注的Blog
    EVA的回收站
    joyfire的space
    Kendiv的专栏
    PJF的Blog
    WebCrazy的Blog
    孟言的blog
    野路子(http://wulujia.com)
    铁卷大成天下
    网络收藏夹
    China CISSP论坛(文档保护)
    China Uniix
    developerWorks Linux 专栏
    docshow
    linux伊甸园
    OSR在线论坛
    PKI论坛
    reactos
    rootkit论坛
    Sysinternals论坛
    中国Linux公社
    中国Linux论坛
    协议分析网论坛
    安全焦点
    看雪技术学院论坛
    驱动开发网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 (转载)Gloomy对Windows内核的分析(研究CreateProcess)收藏

    新一篇: Gloomy对Windows内核的分析(内核反汇编技术) | 旧一篇: (转载)Gloomy对Windows内核的分析(介绍)

    研究CreateProcess
    ==========================
                                      Может быть я всегда знал
                                      Мои хрупкие мечты будут р
    азбиты ради тебя...
                                                      (c) by Anathema

    我给出一个反汇编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函数的封装。所有这些都是我们熟知的,但我们需要用反
    汇编来证实。

    ---------------------------------------------------------------------------
                              (c)Gloomy aka Peter Kosyh, Melancholy Coding'2001

                                                           http://gloomy.cjb.net
                                                           mailto:gl00my@mail.ru

                                                                         董岩 译
                                                    http://greatdong.blog.edu.cn

    发表于 @ 2006年01月31日 13:28:00|评论(loading...)|编辑|收藏

    新一篇: Gloomy对Windows内核的分析(内核反汇编技术) | 旧一篇: (转载)Gloomy对Windows内核的分析(介绍)

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © joshua