首页
社区
课程
招聘
[分享]Windows NT内核分析(PDF)(重新整理,已更新19楼)
发表于: 2007-8-27 22:09 85502

[分享]Windows NT内核分析(PDF)(重新整理,已更新19楼)

2007-8-27 22:09
85502
由于我在看到这篇文章时已经没有了作者与译者的信息,抱歉一下,这的确是一篇好文,值得转:
     00.系统组件
     01.Windows NT操作系统的内存格局
     02.Windows NT与FLAT模型
     03.线程信息块(THREAD INFORMATION BLOCK)
     04.进程控制域(PROCESSOR CONTROL REGION)
--------------------------------------------------------------------------

重新整理更新了一个。又添加了4篇重要的内核分析。
附件19楼。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (66)
雪    币: 224
活跃值: (147)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
2
很好的文章 可否有word版?我想整理下再打印出来 thanks
2007-8-27 22:24
0
雪    币: 66
活跃值: (16)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
3
不错的书   翻了下 不过讲的比较少噢
2007-8-27 23:07
0
雪    币: 8599
活跃值: (5065)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
有word版的,加我QQ:25656016发给你。
2007-8-27 23:31
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
5
呵呵,不错.
2007-8-28 09:07
0
雪    币: 248
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
已经有人发过了,去年的精华上有
2007-8-28 17:49
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
7
PDF不能打?
2007-8-28 21:13
0
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
----------------------- Page 1-----------------------

Windows NT  内核分析

(文章整理 by sunsjw 2007 年8 月27  日21 时58 分QQ:509207758)

     由于我在看到这篇文章时已经没有了作者与译者的信息,抱歉一下,这的确是一篇好文,值

得转:

     00.系统组件

     01.Windows NT 操作系统的内存格局

     02.Windows NT 与FLAT模型

     03.线程信息块(THREAD INFORMATION BLOCK)

     04.进程控制域(PROCESSOR CONTROL REGION)

00.系统组件

=====================

    实际上,所有的Windows NT 组件其本身都是DLL、PE格式的.EXE文件、导入导出函

数。Windows NT 的主要组件有:

*Ntoskrnl.exe

    系统核心。系统执行函数都集中于此,这些函数又会调用其它组件。核心组件有:对象管理

器、内存管理器、进线程创建、进线程控制、LPC、安全管理、异常处理、文件系统、输入输出、

VDM、和т .д .一般都位于大于80100000h 的地址上。

*Hal.dll

    硬件抽象层(Hardware      Abstraction     Layer)-硬件相关的模块。该隔离层将操作

系统中硬件相关的部分隔离出来以增强系统的可移植性。主要的模块实现了非常底层的函数:程

序控制中断、硬件输入输出等等。一般位于大于80100000h 的地址上。

*Ntdll.dll

    实现某些Win32 API 函数。提供了核心模式与用户模式之间的接口。位于用户空间。换句

话说,系统函数调用主要由这里导出。

*Kernel32.dll

    实现了一些函数,类似于Win9x 的核心。其中有很多的函数封装在ntdll.dll 中。

*Csrss.exe

    进程服务器子系统。其是一个单独的进程,因此受到保护以免受其它进程(位于其它进程的

地址空间中)影响。对服务的请求要借助于LPC产生。

*Win32k.sys

    驱动程序。用以减少调用Csrss 服务开销损失。在此程序中实现了GDI和USER 函数。不

用LPC而用系统调用-这很明显提高了Windows NT4.0 和2K 的图形处理性能。

01.Windows NT Windows NT 操作系统的内存格局

=============================

----------------------- Page 2-----------------------

    在Windows NT 中高2G 的32 位线性地址空间保存了供系统使用的程序。这种格局,地

址空间80000000      -  ffffffff 为系统组件:驱动程序、系统表、系统数据结构等等。系统内

存的精确格局是不可能得到的,但是通过其功能用途和大致位置可以区分出各个区域。

*80000000-9FFFFFFF

    系统代码。在这里驻留的是Hal和ntoskrnl 的代码和数据,还有驱动程序(boot驱动和

加载ntosldr 的驱动)。GDT、IDT 和TSS 结构体同样驻留在这些区域中。

*C0000000-C0FFFFFF

    系统表的区域。这个线性地址空间区域保存着进程的页表,页目录和其它与进程结构体有关

的东西。这个区域不是全局性的,不像其它的系统空间区域,而且对于于每一个进程来说会映射

到不同的物理空间,其保存着当前进程的数据结构。

*E1000000-E57FFFFF

    分页池。这个区域可以换出到磁盘上。操作系统中的大多数对象都在这个区域中产生。实际

上一些内存池位于这个区域中。

*FB000000-FFDFEFFF

    不可换出页的区域,即非分页区(Non Paged Poll)。这个区域中的数据永远不能换出到

磁盘上。这个区域中的数据总是系统必需的数据。例如,这里有进程与线程的信息块(Thread

environment block, Process Environment block)。

*FFDFF000-FFFFFFFF

    PCR   - Processor Control Region (进程控制域) 用于每一个进程。这个区域中保存

着PCR 结构体。在此结构体中保存着系统状态的信息。例如,关于IRQL、当前线程、IDT 等

的信息。

    低 2G 线性地址空间(00000000-0FFFFFFFF)为进程用户模式的地址空间(每个进程

自己的空间)。Win32 地址空间看上去一般是下面这个样子:

*00000000-0000FFFF

    保护区域。访问此区域会引发异常。被用于检测NULL指针。

*00xx0000

    通常,应用程序加载在这样的地址上。

*70000000-78000000

    Win32 子系统的库通常映射到这里。

*7FFB0000-7FFD3FFF

    代码页。

*7FFDE000-7FFDEFFF

    用户模式的Thread Environment Block。

----------------------- Page 3-----------------------

*7FFDF000-7FFDFFFF

    用户模式的Process Environment Block。

*7FFE0000-7FFE0FFF

    共享数据区。

*7FFFF000-7FFFFFFF

    保护区域。

02.Windows NT 与FLAT模型

===========================

    自i286 开始,在Intel 的处理器里实现了四级保护机制,相应的就有四个特权级。代码与

数据能够拥有某级别的特权。这样,应用程序、系统程序、内核等等都运行在自己的特权级上,

而且不能随意访问比自己特权级高的代码和数据。实际上没有一个基于Intel 处理器的操作系统

能用到所有的四个特权级(不为人知的那些不算在内)。Windows NT 操作系统也不例外,只

用到了两个特权级(ring)。0 级(最高特权级)下运行内核,3 级(最低特权级)为用户级。

Intel 处理器提供了强大的内存分段机制,其与特权级一起实现了直到段级的保护(例如,程序

的每一个逻辑段都可以由一个描述符表来描述)。但是Windows NT 实现的是FLAT模型。这

就将选择子的使用降到了最低限度。处理器的全局描述符表GDT(Global Descriptor Table),

由Windows NT 操作系统管理,其包含以下描述符(由SoftIce'a 得到):

Sel.  Type       Base      Limit      DPL   Attributes

GDTbase=80036000        Limit=03FF

  0008 Code32      00000000     FFFFFFFF    0    P   RE

  0010 Data32         00000000  FFFFFFFF   0    P   RW

001B   Code32      00000000     FFFFFFFF    3    P   RE

  0023 Data32         00000000  FFFFFFFF   3    P   RW

  0028 TSS32       8024D000     000020AB     0    P   B

  0030 Data32      FFDFF000    00001FFF     0    P   RW

003B   Data32      7FFD9000     00000FFF    3    P   RW

  0043 Data16         00000400  0000FFFF    3    P   RW

  0048 LDT        E1190000     000001FF    0    P

  0050 TSS32       80149F60    00000068     0    P

  0058 TSS32       80149FC8    00000068     0    P

  0060 Data16         00022940  0000FFFF    0    P   RW

  0068 Data16      000B8000     00003FFF    0    P   RW

  0070 Data16      FFFF7000    000003FF     0    P   RW

  0078 Code16      80400000     0000FFFF    0    P   RE

  0080 Data16         80400000  0000FFFF    0    P   RW

  0088 Data16         00000000  00000000    0    P   RW

  0090 Reserved    00000000     00000000    0     NP

...

00E0   Reserved    00008003     00006100    0     NP

----------------------- Page 4-----------------------

00E8    Data16       00000000      0000FFFF     0     P   RW

00F0    Code16       80117DB0      0000028D      0     P    EO

00F8    Data16       00000000      0000FFFF     0    P    RW

  0100  Reserved     00008003      00006108      0    NP

...

03F8    Reserved     00000000      00000000      0    NP

    前四个选择子全都位于线性地址空间。而且前两个选择子的描述符特权级DPL(Descriptor

Privilege Level)等于0,而后面两个的都是3。选择子8 和10 由用户应用程序使用。在FLAT

模型下,应用程序本身并不关心段寄存器的内容。在ring3 工作时,CS、DS、SS 寄存器总是

分别为值8、10、10。这样,系统代码就可以监视段寄存器的值。选择子1b和23用于内核(驱

动程序、系统代码)工作时的寻址。选择子30 和3b 分别指向Kernel Process Region 和

Thread Information Block。当代码运行在ring0 时,FS 寄存器的值为30,如过运行在

ring3,则FS 的值为3b。选择子30 总是指向基址为FFDFF000 的描述符。选择子3b指示

的基址则依赖于用户线程。选择子48 定义了局部描述符表LDT (Local Descriptor Table)。

LDT只在Virtual DOS       machine  (VDM)应用程序下使用。当运行该进程时,在处理器的

LDTR 寄存器中加载着相应的指针,否则,LDTR 的值为0。LDT主要用在Windows 3.x 应

用程序下。Windows 3.x 应用程序运行在WOW (Windows On Windows)下,而WOW

则实现在VDM 进程里。VDM 进程的LDT使用上和Win3.x 里的一样。在GDT表里总会有两

个TSS 类型的选择子。这是因为运行在Intel处理器上的Windows NT 操作系统没有使用基

于任务门的任务切换机制。IDT包含以下描述符(由SoftIce'a 得到):

Int    Type       Sel:Offset      Attributes Symbol/Owner

IDTbase=F8500FC8         Limit=07FF

  0000  IntG32     0008:8013EC54        DPL=0     P    _KiTrap00

...

  0007  IntG32     0008:8013F968        DPL=0     P    _KiTrap07

  0008  TaskG      0050:00001338        DPL=0     P

  0009  IntG32     0008:8013FCA8        DPL=0     P    _KiTrap09

...

  0012  IntG32     0008:80141148        DPL=0     P    _KiTrap0F

...

001F    IntG32     0008:80141148        DPL=0     P    _KiTrap0F

  0020  Reserved 0008:00000000          DPL=0     NP

...

  0029  Reserved 0008:00000000          DPL=0     NP

002A    IntG32      0008:8013E1A6       DPL=3     P    _KiGetTickCount

002B    IntG32      0008:8013E290       DPL=3     P    _KiCallbackReturn

002C    IntG32     0008:8013E3A0        DPL=3     P   _KiSetLowWaitHighThread

002D    IntG32      0008:8013EF5C       DPL=3     P    _KiDebugService

002E    IntG32     0008:8013DD20         DPL=3     P   _KiSystemService

002F    IntG32     0008:80141148        DPL=0     P    _KiTrap0F

  0030  IntG32     0008:80014FFC        DPL=0     P   _HalpClockInterrupt

  0031  IntG32     0008:807E4224        DPL=0     P

  0032  IntG32     0008:8013D464        DPL=0     P    _KiUnexpectedInterrupt2

----------------------- Page 5-----------------------

0033     IntG32      0008:80708864         DPL=0      P

0034     IntG32      0008:807CEDC4         DPL=0      P

0035     IntG32      0008:807E3464         DPL=0     P

0036     IntG32      0008:8013D48C         DPL=0      P    _KiUnexpectedInterrupt6

0037     IntG32      0008:8013D496         DPL=0      P  _KiUnexpectedInterrupt7

0038     IntG32      0008:80010A58         DPL=0      P    _HalpProfileInterrupt

0039     IntG32      0008:8013D4AA         DPL=0      P    _KiUnexpectedInterrupt9

...

00FF    IntG32       0008:8013DC66         DPL=0      P   _KiUnexpectedInterrupt207

    表中主要的门类型为中断门。中断任务只是用在关键的时刻,保证特殊情况下能正确完成应

急处理,比如说,双重异常(8号中断)。中断2e是系统调用。中断30 到3f对应于irq0                  -

irq15。于是,总是有两个TSS 选择子。其中一个(50)用于双重异常。TSS 中除了其自己必

需部分所占空间外,还在104个字节中保存了一个输入输出位图。在执行Win32 应用程序的时

候,TSS 中的指针保存着不正确的值,这样任何对端口的操作都会引发异常。在VDM 工作的时

候,位图用来选择是否禁止对端口的访问。我们来看一下在FLAT 模型下是如何进行保护的。要

研究这个问题需要将注意力转向下面这个条件——保护的原则应该是:保护内核不受用户进程的

干扰、保护一个进程不受另一个进程的干扰、保护子系统的代码和数据不受用户进程的干扰。

Windows NT 的线性地址空间可以分成用户空间(通常为0-7fffffff)和系统与内核空间(通

常为 80000000-ffffffff)。切换上下文时,内核空间对于所有进程几乎都是一样的。在

Windows NT 中使用了分页保护的内存。这样,实现内核同用户进程隔离方法就是将核心空间

地址页的页表项指针的U/S位置零。这样就不能在ring3 下随意访问ring0 下的代码和数据了。

同样页的寻址也使得进程彼此间的地址空间得到隔离。Windows   NT 保留了4KB 的页作为区

域c0300000 中的页目录,该页目录映射了所有的4GB 物理地址空间。在页目录中有1024

个页目录项。每个页目录项都应是4B大小(4KB/1024)。1024个页目录项指向1024个

页表,每个页表指向一个4KB 的页。页目录的基地址在上下文切换时会相应发生变化并指向将被

使用的页目录,当执行新线程时这个页目录被用于线性地址向物理地址的转换(在Intel 处理器

中这个物理基地址位于CR3 寄存器中)。

    结果,不同的上下文中里的地址空间(00000000-7fffffff)中一样的线性地址能够映射到

不同物理地址上,实现了进程地址空间的彼此隔离。内核空间实际上对所有的上下文都是一样的,

其被保护起来不能由用户代码访问,内核代码的页表项的U/S位为零。下面我们将不讨论内核模

式而是将注意力转到用户模式上来。的确,ring0和ring3 寻址使用的描述符有相同的基址和界

限,这可以作为用户在内核或内核驱动中出错的理由。发生错误时可能会破坏当前进程地址空间

中的代码和数据。例如,用户进程指针校验错误时调用内核服务可能破坏内核的代码和数据。

    操作系统的子系统通过导出调用的DLL模块来提供自己的服务。在用户进程向子系统转换时,

比如调用WIn32 API 函数,开始总是运行DLL模块的代码。随后,DLL模块的代码可能通过

LPC进行系统调用。所以,DLL模块被映射到用户空间中,共享进程上下文使用库函数必须保护

DLL模块中的代码和数据不会受到可能发生的直接访问。事实上,这种并不能保护数据。所有的

保护都是通过页表项的R/W和U/S位来实现的。DLL模块的代码和数据通常只允许被读取。

用户进程能够不经允许而向数据中写入,当然,如果告知它们大致的线性地址也可以允许写入。

但是,如果需要,每一个进程能产生自己的数据副本(copy-on-write 机制)。例如,会有以

下的情况出现:分析PE文件kernel32.dll 的文件头可以确定data section 的虚拟地址,随

后写程序向这个区域写入错误数据,之后校对脏数据。在新进程启动时,数据将是“恢复了的”。

----------------------- Page 6-----------------------

因此,如果有进程和子系统破坏系统的正常运行,则其只会在自己的上下文中发挥作用。除此之

外,所有关键的数据(直接访问可能会损害子系统的整体性的数据或是重要的数据)都位于单独

的进程地址空间中——子系统服务器。使用这些数据的操作都是通过从子系统DLL模块转向服务

来进行的(通过LPC)。我们注意到,这个操作开销较大,所以在Windows NT 4.0 中开始

进行一系列的尝试来使之减少。特别重要的是,现在GDI 和USER 函数都实现在内核里(更准

确的说是在驱动程序 Win32k.sys 里)。事实上,产生了一个结论,在安全性上子系统大量依

赖于对其的周密考虑。页保护机制本身提供的保护是不够的。

03.线程信息块(THREAD INFORMATION BLOCK)

=================================================

=====

    在线程执行的时候,在用户模式下,FS 寄存器的值为3b。这个选择子用于寻址一个结构体,

这个结构体位于NTDDK.H文件中,叫做THREAD ENVIRONMENT BLOCK。每一个线程

都有其自己的结构体,并在上下文切换时,选择子中的基址会改变,以指向当前线程线程的这个

结构体。在 NTDDK.H  中对这个结构体只描述了很少的一部分,如_NT_TIB  (Thread

Information Block)。其实_NT_TIB 是结构体NT_TEB和NT_TIB 的结合。NT_TEB

是用户模式线程访问的结构体。NT_TIB通过选择子30 从内核模式下访问。TEB 结构体的几个

域在NTDDK.H (Windows NT 4.0)中没有描述:

typedef struct _EXCEPTION_REGISTRATION_RECORD

{

    struct _EXCEPTION_REGISTRATION_RECORD * pNext;

    FARPROC                                    pfnHandler;

}EXCEPTION_REGISTRATION_RECORD,

*PEXCEPTION_REGISTRATION_RECORD;

typedef struct _NT_TIB

{

    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;

        // 00h Head of exception

        // record list

    PVOID StackBase;                             // 04h

    PVOID StackLimit;                            // 08h

    PVOID SubSystemTib;                          // 0Ch

    union

    {                                    // 10h

        PVOID FiberData;                         // for TIB

        ULONG Version;                           // for TEB

    };

    PVOID ArbitraryUserPointer;                  // 14h Available

        // for application use

    struct _NT_TIB *Self;                        // 18h Linear address

----------------------- Page 7-----------------------

        // of TEB structure

} NT_TIB;

typedef NT_TIB *PNT_TIB;

typedef struct _TEB

{                // Size: 0xF88

/*000*/      NT_TIB NtTib;

/*01C*/      VOID *EnvironmentPointer;

/*020*/      CLIENT_ID ClientId;             // PROCESS id, THREAD id

/*028*/      HANDLE ActiveRpcHandle;

/*02C*/      VOID *ThreadLocalStoragePointer;

/*030*/      PEB *ProcessEnvironmentBlock;             // PEB

/*034*/      ULONG LastErrorValue;

/*038*/      ULONG CountOfOwnedCriticalSections;

/*03C*/      ULONG CsrClientThread;

/*040*/      ULONG Win32ThreadInfo;

/*044*/      UCHAR Win32ClientInfo[0x7C];

/*0C0*/      ULONG WOW32Reserved;

/*0C4*/      ULONG CurrentLocale;

/*0C8*/      ULONG FpSoftwareStatusRegister;

/*0CC*/      UCHAR SystemReserved1[0xD8];                // ExitStack ???

/*1A4*/      ULONG Spare1;

/*1A8*/      ULONG ExceptionCode;

/*1AC*/      UCHAR SpareBytes1[0x28];

/*1D4*/      UCHAR SystemReserved2[0x28];

/*1FC*/      UCHAR GdiTebBatch[0x4E0];

/*6DC*/      ULONG gdiRgn;

/*6E0*/      ULONG gdiPen;

/*6E4*/      ULONG gdiBrush;

/*6E8*/      CLIENT_ID RealClientId;

/*6F0*/      ULONG GdiCachedProcessHandle;

/*6F4*/      ULONG GdiClientPID;

/*6F8*/      ULONG GdiClientTID;

/*6FC*/      ULONG GdiThreadLocalInfo;

/*700*/      UCHAR UserReserved[0x14];

/*714*/      UCHAR glDispatchTable[0x460];

/*B74*/      UCHAR glReserved1[0x68];

/*BDC*/      ULONG glReserved2;

/*BE0*/      ULONG glSectionInfo;

/*BE4*/      ULONG glSection;

/*BE8*/      ULONG glTable;

/*BEC*/      ULONG glCurrentRC;

/*BF0*/      ULONG glContext;

----------------------- Page 8-----------------------

/*BF4*/    ULONG LastStatusValue;

/*BF8*/    LARGE_INTEGER StaticUnicodeString;

/*C00*/    UCHAR StaticUnicodeBuffer[0x20C];

/*E0C*/    ULONG DeallocationStack;

/*E10*/    UCHAR TlsSlots[0x100];

/*F10*/    LARGE_INTEGER TlsLinks;

/*F18*/    ULONG Vdm;

/*F1C*/    ULONG ReservedForNtRpc;

/*F20*/    LARGE_INTEGER DbgSsReserved;

/*F28*/    ULONG HardErrorsAreDisabled;

/*F2C*/    UCHAR Instrumentation[0x40];

/*F6C*/    ULONG WinSockData;

/*F70*/    ULONG GdiBatchCount;

/*F74*/    ULONG Spare2;

/*F78*/    ULONG Spare3;

/*F7C*/    ULONG Spare4;

/*F80*/    ULONG ReservedForOle;

/*F84*/    ULONG WaitingOnLoaderLock;

} TEB, *PTEB;

   在Windows 95 下,位于TIB 中的偏移0x30 的是指向拥有该线程的进程的基址数据指

针。在 Windows   NT   4.0 中,这个偏移保存的是指向结构体的指针,该结构体实现于

kernel32.dll。遗憾的是,到现在为止,除了几个域之外我还不清楚这个结构体的格式。除此之

外,类似的,在Win 2K 中PEB结构体也发生了变化。

typedef struct _PROCESS_PARAMETERS {

/*000*/     ULONG AllocationSize;

/*004*/     ULONG ActualSize;

/*008*/     ULONG Flags;//PPFLAG_xxx

/*00c*/    ULONG Unknown1;

/*010*/     ULONG Unknown2;

/*014*/     ULONG Unknown3;

/*018*/     HANDLE InputHandle;

/*01c*/    HANDLE OutputHandle;

/*020*/     HANDLE ErrorHandle;

/*024*/     UNICODE_STRING CurrentDirectory;

/*028*/     HANDLE CurrentDir;

/*02c*/    UNICODE_STRING SearchPaths;

/*030*/     UNICODE_STRING ApplicationName;

/*034*/     UNICODE_STRING CommandLine;

/*038*/     PVOID EnvironmentBlock;

/*03c*/    ULONG Unknown[9];

     UNICODE_STRING Unknown4;

     UNICODE_STRING Unknown5;

     UNICODE_STRING Unknown6;

----------------------- Page 9-----------------------

     UNICODE_STRING Unknown7;

} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;

typedef struct _PEB {                   // Size: 0x1D8

/*000*/ UCHAR InheritedAddressSpace;

/*001*/ UCHAR ReadImageFileExecOptions;

/*002*/ UCHAR BeingDebugged;

/*003*/ UCHAR SpareBool;                        // Allocation size

/*004*/ HANDLE Mutant;

/*008*/ HINSTANCE ImageBaseAddress;                  // Instance

/*00C*/ VOID *DllList;

/*010*/ PPROCESS_PARAMETERS *ProcessParameters;

/*014*/ ULONG SubSystemData;

/*018*/ HANDLE DefaultHeap;

/*01C*/ KSPIN_LOCK FastPebLock;

/*020*/ ULONG FastPebLockRoutine;

/*024*/ ULONG FastPebUnlockRoutine;

/*028*/ ULONG EnvironmentUpdateCount;

/*02C*/ ULONG KernelCallbackTable;

/*030*/ LARGE_INTEGER SystemReserved;

/*038*/ ULONG FreeList;

/*03C*/ ULONG TlsExpansionCounter;

/*040*/ ULONG TlsBitmap;

/*044*/ LARGE_INTEGER TlsBitmapBits;

/*04C*/ ULONG ReadOnlySharedMemoryBase;

/*050*/ ULONG ReadOnlySharedMemoryHeap;

/*054*/ ULONG ReadOnlyStaticServerData;

/*058*/ ULONG AnsiCodePageData;

/*05C*/ ULONG OemCodePageData;

/*060*/ ULONG UnicodeCaseTableData;

/*064*/ ULONG NumberOfProcessors;

/*068*/ LARGE_INTEGER NtGlobalFlag;                // Address of a local copy

/*070*/ LARGE_INTEGER CriticalSectionTimeout;

/*078*/ ULONG HeapSegmentReserve;

/*07C*/ ULONG HeapSegmentCommit;

/*080*/ ULONG HeapDeCommitTotalFreeThreshold;

/*084*/ ULONG HeapDeCommitFreeBlockThreshold;

/*088*/ ULONG NumberOfHeaps;

/*08C*/ ULONG MaximumNumberOfHeaps;

/*090*/ ULONG ProcessHeaps;

/*094*/ ULONG GdiSharedHandleTable;

/*098*/ ULONG ProcessStarterHelper;

/*09C*/ ULONG GdiDCAttributeList;

/*0A0*/ KSPIN_LOCK LoaderLock;

----------------------- Page 10-----------------------

/*0A4*/ ULONG OSMajorVersion;

/*0A8*/ ULONG OSMinorVersion;

/*0AC*/ USHORT OSBuildNumber;

/*0AE*/ USHORT OSCSDVersion;

/*0B0*/ ULONG OSPlatformId;

/*0B4*/ ULONG ImageSubsystem;

/*0B8*/ ULONG ImageSubsystemMajorVersion;

/*0BC*/ ULONG ImageSubsystemMinorVersion;

/*0C0*/ ULONG ImageProcessAffinityMask;

/*0C4*/ ULONG GdiHandleBuffer[0x22];

/*14C*/ ULONG PostProcessInitRoutine;

/*150*/ ULONG TlsExpansionBitmap;

/*154*/ UCHAR TlsExpansionBitmapBits[0x80];

/*1D4*/ ULONG SessionId;

} PEB, *PPEB;

    在TEB 的开头是NT_TIB结构体(TEB 和TIB 的结合)。这个结构体中的大部分名字都

很易懂,最有意思的是指向异常处理链表的指针peExcept (Fs:[0])。这个域经常被引用。如

果在随便某个Win32 应用程序下看一下实际的情况,可以看到类似下面这样的代码:

.01B45480: 64A100000000              mov        eax,fs:[000000000]

.01B45486: 55                        push       ebp

.01B45487: 8BEC                      mov        ebp,esp

.01B45489: 6AFF                     push        0FF

.01B4548B: 68F868B401               push       001B468F8

.01B45490: 687256B401               push       001B45672

.01B45495: 50                        push       eax

.01B45496: 64892500000000            mov        fs:[000000000],esp

.01B4549D: 83EC78                   sub        esp,078

    这 段 有 代 表 性 的 代 码 是 由 编 译 器 生 成 的 , 用 于 在 堆 栈 中 生 成

_EXCEPTION_REGISTRATION_RECORD 。这个堆栈中的结构体用于实现称作

“structured  exception   handling”的机制,这就是结构化异常处理。接着,我们来看

Windows NT 下的结构化异常处理。这个机制可真是十分著名,而且实现在编译器的细节之中。

在MSDN中可以找到Matt Petriek写得非常详细的文章,题为“A Crash Course       on  the

Depths    of Win32 Structured Exception Handling”,此文介绍的就是这项机制。

    FS:[0]中的指针是指向_EXCEPTION_REGISTRATION_RECORD 首部的指针。对应

地,每个结构体在pNext域中包含着指向下一个结构体的指针和指向回调函数pfnHandler 的

指针。不难猜到,这就是异常处理的处理程序。函数的原型如下:

EXCEPTION_DISPOSITION            __cdecl _except_handler(

struct _EXCEPTION_RECORD *ExceptionRecord,

void * EstablisherFrame,

struct _CONTEXT *ContextRecord,

void * DispatcherContext

);

----------------------- Page 11-----------------------

    我们来分析函数的参数。第一个参数是指向下面结构体的指针。

typedef struct _EXCEPTION_RECORD {

DWORD ExceptionCode;

DWORD ExceptionFlags;

struct _EXCEPTION_RECORD *ExceptionRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

ExceptionCode 是 Windows   NT  的异常代号。异常在 NTSTATUS.H 文件中被描述为

STATUX_xxxxxx:      ExceptionAddres - 发生异常的地址。

    第三个参数是指向CONTEXT 结构体的指针。

typedef struct _CONTEXT

{

DWORD ContextFlags;

DWORD Dr0;

DWORD Dr1;

DWORD Dr2;

:.

DWORD Esp;

DWORD SegSs;

} CONTEXT;

    这个结构体定义于WINNT.H 文件。其意义是不言而喻的,这里就不全写了。函数返回下面

枚举类型值中的一个:

typedef enum _EXCEPTION_DISPOSITION {

    ExceptionContinueExecution,

    ExceptionContinueSearch,

    ExceptionNestedException,

    ExceptionCollidedUnwind

} EXCEPTION_DISPOSITION;

ExceptionFlags 定义了下面的位标志:

#define EH_NONCONTINUABLE           1

#define EH_UNWINDING              2

#define EH_EXIT_UNWIND             4

#define EH_STACK_INVALID           8

#define EH_NESTED_CALL         0x10

    在发生异常时,控制传递到ntoskrnl.exe 中相应的处理程序。例如,如果试图下面这段代

码:

----------------------- Page 12-----------------------

mov eax,80100000h

mov dword ptr [eax],0

    这会引发异常0e,控制传递向处理程序KiTrap0E,堆栈中错误号为07 (试图在用户模式

下向内核写入。该页位于内存中,因为线性地址80100000h是内核加载的起始地址)。之后,

控制传递到ntdll.dll,在这里解析线程的TEB 并顺次执行链表中所有的处理程序,直到某个函

数的返回代号不是ExceptionContinueSearch。在此之后,再次调用链表中的所有处理函数,

直到找到某个函数,但这次用的是另外一个代号ExceptionCode (STATUS_UNWIND)并

在ExceptionFlags 设置位EH_UNWINDINGS。这个标志用于异常处理,进行堆栈的清除

和其它必要的工作。如果没有一个处理程序能处理,则就来到链表中最后一个处理程序,由Win32

子系统建立的处理程序。这个处理程序是如何被建立的以及建立在哪里在以后研究

CreateProcessW 函数时会讲到。关于异常处理的完整介绍可以从MSDN 获得,因为在反汇

编源代码中所得到的是扩大化了的异常处理机制(更高层次的机制)。

04. 进程控制域(PROCESSOR CONTROL REGION)

===========================================

    当线程在内核模式下执行时,在FS 寄存器中加载的是选择子30,用于寻址PCR 结构体(基

址0xFFDFF000,界限0x00001FFF)。在NTDDK.H 中远没有描述结构体所有的成员,

只是其中不多的部分。为了说明PCR 中的成分信息,这里列出用于i386 的结构体(Windows

NT 4.0)

typedef struct _KPCR {                // Size: 0xB10

/*000*/ NT_TIB NtTib;

/*01C*/ struct _KPCR* SelfPcr;

/*020*/ struct _KPRCB* Prcb;             // Current PCB

/*024*/ KIRQL Irql;               // Current IRQL

/*028*/ ULONG IRR;

/*02C*/ ULONG IrrActive;

/*030*/ ULONG IDR;

/*034*/ ULONG Reserved2;

/*038*/ struct _KIDTENTRY* ULONG IDT;

/*03C*/ struct _KGDTENTRY* GDT;

/*040*/ struct _KTSS* TSS;

/*044*/ USHORT MajorVersion;               // 1

/*046*/ USHORT MinorVersion;               // 1

/*048*/ KAFFINITY SetMember;

/*04C*/ ULONG StallScaleFactor;

/*050*/ UCHAR DebugActive;

/*051*/ UCHAR Number;

// End of official portion of KPCR

/*052*/ BOOLEAN VdmAlert;

/*053*/ UCHAR Reserved;

/*054*/ UCHAR KernelReserved[0x90-0x54];

----------------------- Page 13-----------------------

/*090*/ ULONG SecondLevelCacheSize;

/*094*/ UCHAR HalReserved[0xD4-0x94];

/*0D4*/ ULONG InterruptMode;

/*0D8*/ ULONG Spare1;

/*0DC*/ UCHAR KernelReserved2[0x120-0xDC];

// Note that current thread is at offset 0x124 (pointer to KTHREAD)

// This essentially means that the 1st PRCB must match the active CPU

/*120*/     UCHAR    PrcbData[0xB10-0x120];          //   PCBs   for  all  CPUs

supported

} KPCR, *PKPCR;

    PCR包含着指向PCRB (Processor Control Region)的指针,但实际上,PCRB位

于 PCR 的偏移0x120 处,并且所有的向 PCRB 域的转换都是相对于 PCR 的起点的。在

WINNT.H 中描述了PCRB结构体。这里给出整个结构体(Windows NT 4.0):

// 0x120 from KPCR

typedef struct _KPRCB {

/*000*/      USHORT MinorVersion;

/*002*/      USHORT MajorVersion;

/*004*/      struct _KTHREAD *CurrentThread;

/*008*/      struct _KTHREAD *NextThread;

/*00c*/     struct _KTHREAD *IdleThread;

/*010*/      CCHAR Number;

/*011*/      CCHAR Reserved;

/*012*/      USHORT BuildType;

/*014*/      KAFFINITY SetMember;

/*015*/      struct _RESTART_BLOCK *RestartBlock;

/*018*/      CCHAR CpuType;

/*019*/      CCHAR CpuID;

/*01A*/      CCHAR CpuStep;

/*01C*/      KPROCESSOR_STATE ProcessorState;

/*13C*/      CCHAR KernelReserved[0x40];

/*17C*/      CCHAR HalReserved[0x40];

/*1BC*/      ULONG NpxThread;

/*1C0*/      ULONG InterruptCount;

/*1C4*/      ULONG KernelTime;

/*1C8*/      ULONG UserTime;

/*1CC*/      ULONG DpcTime;

/*1D0*/      ULONG InterruptTime;

/*1D4*/      ULONG ApcBypassCount;

/*1D8*/      ULONG DpcBypassCount;

/*1DC*/      ULONG AdjustDpcThreshold;

/*1E0*/      UCHAR Spare2[0x14];

----------------------- Page 14-----------------------

/*1F4*/        ULONG64 ThreadStartCount;

/*1FC*/        SINGLE_LIST_ENTRY FsRtlFreeSharedLockList;

/*200*/        SINGLE_LIST_ENTRY FsRtlFreeExclusiveLockList;

/*204*/        ULONG CcFastReadNoWait;

/*208*/         ULONG CcFastReadWait;

/*20C*/        ULONG CcFastReadNotPossible;

/*210*/        ULONG CcCopyReadNoWait;

/*214*/        ULONG CcCopyReadWait;

/*218*/        ULONG CcCopyReadNoWaitMiss;

/*21C*/        ULONG KeAlignmentFixupCount;

/*220*/        ULONG KeContextSwitches;

/*224*/         ULONG KeDcacheFlushCount;

/*228*/        ULONG KeExceptionDispatchCount;

/*22C*/        ULONG KeFirstLevelTbFills;

/*230*/        ULONG KeFloatingEmulationCount;

/*234*/        ULONG KeIcacheFlushCount;

/*238*/        ULONG KeSecondLevelTbFills;

/*23C*/        ULONG KeSystemCalls;

/*240*/        SINGLE_LIST_ENTRY FsRtlFreeWaitingLockList;

/*244*/        SINGLE_LIST_ENTRY FsRtlFreeLockTreeNodeList

/*248*/        CCHAR ReservedCounter[0x18];

/*260*/        PVOID SmallIrpFreeEntry;

/*264*/        PVOID LargeIrpFreeEntry;

/*268*/        PVOID MdlFreeEntry

/*26C*/        PVOID      CreateInfoFreeEntry;

/*270*/        PVOID NameBufferFreeEntry

/*274*/        PVOID SharedCacheMapEntry

/*278*/        CCHAR CachePad0[8];

/*280*/        CCHAR ReservedPad[0x200];

/*480*/        CCHAR      CurrentPacket[0xc];

/*48C*/        ULONG TargetSet;

/*490*/         PVOID WorkerRoutine;

/*494*/        ULONG IpiFrozen;

/*498*/        CCHAR      CachePad1[0x8];

/*4A0*/         ULONG RequestSummary;

/*4A4*/         ULONG SignalDone;

/*4A8*/         ULONG ReverseStall;

/*4AC*/         ULONG IpiFrame;

/*4B0*/         CCHAR CachePad2[0x10];

/*4C0*/        ULONG DpcInterruptRequested;

/*4C4*/        CCHAR CachePad3 [0xc];

/*4D0*/         ULONG MaximumDpcQueueDepth;

/*4D4*/         ULONG MinimumDpcRate;

/*4D8*/         CCHAR CachePad4[0x8];

----------------------- Page 15-----------------------

/*4E0*/      LIST_ENTRY DpcListHead;

/*4E8*/      ULONG DpcQueueDepth;

/*4EC*/      ULONG DpcRoutineActive;

/*4F0*/      ULONG DpcCount;

/*4F4*/      ULONG DpcLastCount;

/*4F8*/      ULONG DpcRequestRate;

/*4FC*/      CCHAR KernelReserved2[0x2c];

/*528*/      ULONG DpcLock;

/*52C*/      CCHAR SkipTick;

/*52D*/       CCHAR VendorString[0xf];

/*53C*/      ULONG MHz;

/*540*/      ULONG64 FeatureBits;

/*548*/      ULONG64 UpdateSignature;

/*550*/      ULONG QuantumEnd;

} KPRCB, *PKPRCB, *RESTRICTED_POINTER PRKPRCB;

    PCRB 中最有用的就是指向当前线程的指针(KTHREAD结构体)。通常内核代码以以下代

码取得此指针:

     《加密解密》中没有讲得很清楚,初学者可能有点疑问。这里写得教清楚:FS:[0]中的指针

是指向_EXCEPTION_REGISTRATION_RECORD  首部的指针。对应地,每个结构体在

pNext 域中包含着指向下一个结构体的指针和指向回调函数pfnHandler 的指针。不难猜到,

这就是异常处理的处理程序。函数的原型如下:

EXCEPTION_DISPOSITION            __cdecl _except_handler(

struct _EXCEPTION_RECORD *ExceptionRecord,

void * EstablisherFrame,

struct _CONTEXT *ContextRecord,

void * DispatcherContext

);

    我们来分析函数的参数。第一个参数是指向下面结构体的指针。

typedef struct _EXCEPTION_RECORD {

DWORD ExceptionCode;

DWORD ExceptionFlags;

struct _EXCEPTION_RECORD *ExceptionRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

    ExceptionCode是Windows NT 的异常代号。异常在NTSTATUS.H文件中被描述为

STATUX_xxxxxx:      ExceptionAddres - 发生异常的地址。第三个参数是指向CONTEXT

结构体的指针。

typedef struct _CONTEXT

{

----------------------- Page 16-----------------------

DWORD ContextFlags;

DWORD Dr0;

DWORD Dr1;

DWORD Dr2;

:.

DWORD Esp;

DWORD SegSs;

} CONTEXT;

    这个结构体定义于WINNT.H 文件。其意义是不言而喻的,这里就不全写了。函数返回下面

枚举类型值中的一个:

typedef enum _EXCEPTION_DISPOSITION {

    ExceptionContinueExecution,

    ExceptionContinueSearch,

    ExceptionNestedException,

    ExceptionCollidedUnwind

} EXCEPTION_DISPOSITION;

ExceptionFlags 定义了下面的位标志:

#define EH_NONCONTINUABLE              1

#define EH_UNWINDING                 2

#define EH_EXIT_UNWIND               4

#define EH_STACK_INVALID             8

#define EH_NESTED_CALL           0x10

    在发生异常时,控制传递到ntoskrnl.exe 中相应的处理程序。例如,如果试图下面这段代

码:

mov eax,80100000h

mov dword ptr [eax],0

    这会引发异常0e,控制传递向处理程序KiTrap0E,堆栈中错误号为07 (试图在用户模式

下向内核写入。该页位于内存中,因为线性地址80100000h是内核加载的起始地址)。之后,

控制传递到ntdll.dll,在这里解析线程的TEB 并顺次执行链表中所有的处理程序,直到某个函

数的返回代号不是ExceptionContinueSearch。在此之后,再次调用链表中的所有处理函数,

直到找到某个函数,但这次用的是另外一个代号ExceptionCode (STATUS_UNWIND)并

在ExceptionFlags 设置位EH_UNWINDINGS。这个标志用于异常处理,进行堆栈的清除

和其它必要的工作。如果没有一个处理程序能处理,则就来到链表中最后一个处理程序,由Win32

子系统建立的处理程序。这个处理程序是如何被建立的以及建立在哪里在以后研究

CreateProcessW 函数时会讲到。关于异常处理的完整介绍可以从MSDN 获得,因为在反汇

编源代码中所得到的是扩大化了的异常处理机制(更高层次的机制)。

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

研究CreateProcess

==========================

                                   Может быть я всегда знал

                                   Мои хрупкие мечты будут р

----------------------- Page 17-----------------------

аз биты ради тебя...

                                                  (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_handler3      (堆栈中的结构体对应于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 结构体,在

ObjectName 域中放入指向获得的路径的指针并调用未公开的函数:

NTSYSAPI

  IOSTATUS

  NTAPI

  NtOpenFile (OUT DWORD* Handle, IN ACCESS_MASK DesiredAccess,

          OBJECT_ATTRIBUTES*                 ObjAttr,          PIO_STATUS_BLOCK

IoStatusBlock,

----------------------- Page 18-----------------------

          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  函数

CreateFileMapping 是对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

----------------------- Page 19-----------------------

中的NtQueryInformationProcess 的主要功能。遗憾的是,关于NtQuerySection 函数

的信息是不存在的。所有这样的函数都有实际上相同的原型并处理操作系统中的对象。

NtQuerySection 返回两类信息(SectionInformationClass 可以为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,这个值描述了信息类。调整信息类的

值:ProcessDefaultHardErrorMode,ProcessBasePriority。对于这些类,信息结构

----------------------- Page 20-----------------------

体本身就是一个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。这一部分(大

----------------------- Page 21-----------------------

小为Delta)的属性是PAGE_GUARD和PAGE_READWRITE。最后得到以下结构体:

***Stack***

---------------?-ReservedAddr

|                   |

|                   |

|  RESERVED         |<- SizeReserved - (SizeCommited+Delta)

|                   |

|--------------     |-CommitedAddr

|  GUARD PAGE  |<- Delta

|--------------     |

|  READ_WRITE |<- SizeCommited

|                   |

L----------------SS:ESP

    这样,为堆栈分配了SizeCommited 字节。保留了SizeReserved。之后在堆栈之下的

保留区分配的内存被转换为GUARD页(转换成这种页可以引发异常)。从源代码中可以看到,

错误的Delta 的大小可能会产成悲惨的后果。因为这可是个关键的信息——我们来看从哪里找出

Delta 的值:

.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;

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

BaseInitializeContext:

BaseInitializeContext(PCONTEXT Context, // 0x200 bytes

PPEB Peb,

PVOID EntryPoint,

DWORD StackTop,

int Type // union (Process, Thread, Fiber)

);

    这个函数的几个参数:PEB 的地址,堆栈的入口点和参数定义了要创建的上下文(纤程,进

----------------------- Page 22-----------------------

程、线程)。函数填充CONTEXT 结构体(NTDDK.H 中有)的几个域。其中一个域很有意思,

在 其 中 放 置 了 起 始 点 ( BaseFiberStart  、 BaseProcessStartThunk                 、

BaseThreadStartThunk  中的一个)。这个点“分娩”出了线程,产生的线程就在新的上下

文中执行。实际上,所有三个偏移处的代码都很简短——就是填充相应的堆栈映象并转到两个函

数中的某一个。这两个函数分别是_BaseProcessStart 和_BaseThreadStart。这两个函数

很是相象,我们只看_BaseProcessStart 函数。

    这个函数在链表中建立了第一个异常处理(见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  函数来说,必须完成的任务就是启动线程(当然,如果没有在参数

dwCreationFlags       中设置 Create_SUSPEND           标志)。线程的启动进行对

NtResumeThread (对Win32 的ResumeThread 的封装)的调用。完成了!现在剩下的

还有释放内存和正确的退出。

    到此对 Win32 子系统的CreateProcess 函数的主要分析可以得出结论:子系统通常与

Windows NT 的执行体系统协同工作,子系统大多都使用未公开的函数,子系统通过LPC与自

己的服务器通讯,许多Win32 API 函数都是对Nt 函数的封装。所有这些都是我们熟知的,但

我们需要用反汇编来证实。

Edit 控件密码窗口的秘密--一种全新的通用密码记录技术

创建时间:2007-08-09

文章属性:转载

文章提交:l0pht (vbs_at_21cn.com)

Author: czy

Date: 2007-08-09 (pub)

----------------------- Page 23-----------------------

http://www.ph4nt0m.org

*目前的各类密码记录技术*

    目前各类密码记录程序多如牛毛,但实现原理无非有以下六个:

    一:调用CreateRemoteThread 函数远程DLL 或代码注入.

    二:调用SetWindowsHookEx 安装键盘钩子记录按键,或是键盘驱动记录按键.(注五)

    三:伪造登陆界面.

    四:登录信息在网络传输过程中被嗅探.

    五:分析目标程序流层,文件补丁技术(注一).

    六:分析目标程序流层,搜索并读取存放在内存中(比如:全局变量)的密码.

    由于后四个技术都要对目标程序进行专门的分析所以更多的用在专用游戏盗号程序中.这样目

前通用的获取目标主机各类程序登录密码的技术还是紧紧局限于前两个.

*两大主流密码记录技术的局限性*

    对于键盘记录技术由于用户可能会不按顺序输入密码所以正确率有限,要是安装键盘驱动还要

Admin权限同时更难以区分用户是输密码还是其它输入(在驱动下可没有GetActiveWindow

函数呵呵).

    对于第一种技术前面所说的问题都不存在,并且用各种语言编写的源代码广为流传,所以水平

高一点点的黑客都会使用,但也正因为这个远线程注入技术实在太流行,所以很多杀毒软件一但发

现有程序调用了CreateRemoteThread 这个API 就会提示并拦截(比如江民公司的"木马一

扫光").同样安装键盘钩子比如调用SetWindowsHookEx 有些杀毒软件也会提示.难道就没有

通用性相对较好,记录正确率高,不容易被杀毒软件查杀的技术了吗?请看下文.

*目前的思路*

    对于WINDOWS 程序中的密码窗口通常是具有ES_PASSWORD风格的EDIT控件(通

常输入内容显示为*号),在 WINDOWS   98  下要记录密码,只用给这种窗体发送一个

WM_GETTEXT 消息就可以了没有任何限制,在WIN2000 以后的操作系统中,微软也意识到这

样太不安全,所以限制为进程只可以给自已的具有ES_PASSWORD风格的EDIT控件窗口发送

WM_GETTEXT消息并正确得到窗口内容(注二).这样也就很好理解为什么目前的两大主流技术

要么是建一个远程线程,要么HOOK 键盘了.现在的程序和WIN98 时代很明显的区别就是都要

多一个DLL.(直接代码注入的可以不要DLL但还是会调用可能引起杀毒软件提示的API 函数)

*新的思路*

    在EDIT控件输入字符以后,这些字符当然是被记录在EDIT控件所在的进程可以仿问的内存

中的.可不可以直接从内存中读取内容呢?也就是写了一个自已的不受微软限制的

GetWindowText 函数,或是叫GetWindowPass 函数.读内存可以调用OpenProcess 和

ReadProcessMemory 或是集成这两个函数的Toolhelp32ReadProcessMemory.怎么

读的问题解决了,现在就是读哪个位置的问题.另外OpenProcess不代写内存的参数一般杀毒软

件不会提示(注三).

*读哪儿?*

----------------------- Page 24-----------------------

    解决这个问题首先我们还是看看微软是怎么读的吧.大家都知道要取得EDIT控件的内容可以

发 WM_GETTEXT  消息或是调用 USER32.DLL  中的 GetWindowTextA  函数.打开

WIN32DASM 和 SOFTICE.一路跟踪后总算基本明白了其中的原理,重要代码反汇编如下:共

有三部分(USER32.DLL 5.1.2600.2180,XPSP2 PRO CN)

第一部分:

GetWindowText 函数执行后很快就会调用如下代码:重要的地方会有注解:)

:77D184D0 8BFF                    mov edi, edi

:77D184D2 55                      push ebp

:77D184D3 8BEC                    mov ebp, esp

:77D184D5 51                      push ecx

:77D184D6 53                      push ebx

:77D184D7 56                      push esi

:77D184D8 57                      push edi

:77D184D9 8855FC                  mov byte ptr [ebp-04], dl

:77D184DC 8BF9                    mov edi, ecx

;edi 中为密码窗口句柄

:77D184DE 33F6                    xor esi, esi

:77D184E0 64A118000000            mov eax, dword ptr fs:[00000018]

;得到当前线程的TEB

:77D184E6 8B0D6000D777            mov ecx, dword ptr [77D70060]

:77D184EC 8D98CC060000            lea ebx, dword ptr [eax+000006CC]

;当前线程TEB 的基地址+6CCH放入EBX 中

:77D184F2 8BC7                    mov eax, edi

:77D184F4 25FFFF0000              and eax, 0000FFFF

;eax 中为密码窗口句柄的低16位

:77D184F9 3B4108                  cmp eax, dword ptr [ecx+08]

:77D184FC 734D                    jnb 77D1854B

:77D184FE 8B0D8400D777            mov ecx, dword ptr[77D70084]

;77D70084 是USER32.DLL 中的一个全局变量的地址,重要!

:77D18504 8D0440                  lea eax, dword ptr [eax+2*eax]

:77D18507 8D0C81                  lea ecx, dword ptr [ecx+4*eax]

;ecx 为(密码窗口句柄低16位x12)+一个未知全局变量

--------------------------无关代码省略之-------------

:77D1852F 8B31                    mov esi, dword ptr [ecx]

;ecx 的值没变,取里面的值给esi

:77D18531 0F8471A40100           je 77D329A8

:77D18537 3B30                   cmp esi, dword ptr [eax]

:77D18539 0F8269A40100           jb 77D329A8

:77D1853F 3B7004                  cmp esi, dword ptr [eax+04]

:77D18542 0F8360A40100           jnb 77D329A8

:77D18548 2B731C                 sub esi, dword ptr [ebx+1C];

    刚才的值-RealClientID,EBX+1C 接合上面的代码看就是当前线程 TEB  的基地址

+6CCH+1CH,取得的值也就是当前线程的RealClientID

----------------------- Page 25-----------------------

第二部分

    经过一些跳转后会调用EditWndProc,其中的关键代码如下:

Exported fn(): EditWndProc - Ord:00C1h ;函数入口

:77D2C538 8BFF                       mov edi, edi

:77D2C53A 55                         push ebp

:77D2C53B 8BEC                       mov ebp, esp

:77D2C53D 83EC1C                     sub esp, 0000001C

:77D2C540 8B550C                     mov edx, dword ptr [ebp+0C]

;如果EDX为0Dh说明是取得窗口的内容

:77D2C543 53                         push ebx

:77D2C544 56                         push esi

:77D2C545 57                         push edi

:77D2C546 8B7D08                     mov edi, dword ptr [ebp+08]

:77D2C549 8B07                       mov eax, dword ptr [edi]

:77D2C54B 8BB7A4000000               mov esi, dword ptr [edi+000000A4]

;这儿的EDI和前面代码最后的ESI是同一个值,重要!

:77D2C551 33C9                       xor ecx, ecx

;计算后ESI就是一个指向窗口内容结构的指针

:77D2C553 8945F4                     mov dword ptr [ebp-0C], eax

:77D2C556 41                         inc ecx

---------------------无关代码省略之---------------

:77D2C5B9 51                         push ecx

:77D2C5BA FF7514                     push [ebp+14]

:77D2C5BD FF7510                     push [ebp+10]

:77D2C5C0 56                         push esi

:77D2C5C1 E88E040000                 call 77D2CA54      ;得到窗口内容

第三部分:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:

|:77D2C665(C)

|

:77D41496 837E0C00                       cmp dword ptr [esi+0C], 00000000

:77D4149A 7427                           je 77D414C3

:77D4149C 668B466A                       mov ax, word ptr [esi+6A]

:77D414A0 660FAF460C                     imul ax, word ptr [esi+0C]

;esi 和上面的一样指向窗口结构,ESI+0C 是取得密码长度

:77D414A5 668945FA                       mov word ptr [ebp-06], ax

:77D414A9 668945F8                       mov word ptr [ebp-08], ax

:77D414AD 8D45F8                         lea eax, dword ptr [ebp-08]

:77D414B0 50                             push eax

:77D414B1 33C0                           xor eax, eax

:77D414B3 8A86EC000000                   mov al, byte ptr [esi+000000EC]

;ESI+EC 解码密码的变量,总是一个字节

:77D414B9 897DFC                         mov dword ptr [ebp-04], edi

----------------------- Page 26-----------------------

:77D414BC 50                              push eax

* Reference To: ntdll.RtlRunDecodeUnicodeString, Ord:0304h

                                  |

:77D414BD FF154410D177                    Call dword ptr [77D11044]

;对该函数分析可知,esi+00 存放编码后的密码的地址

*分析GetWindowTextA 后的总结*

    分析流层可知道GetWindowTextA 函数要取得一个EDIT控件的内容要得到如下参数:

    1.窗口句柄,线程,进程ID,

    2.窗口所在的线程的TEB(线程环境块),

    3.窗口所在的进程加载的USER32.DLL 中的一个未知的全局变量.

我们的进程可不可以获得这三个参数呢?

    对于句柄可以使用的函数有GetWindow,WindowFromPoint,EnumWindows等,由

句柄得到进程,线程ID调用GetWindowThreadProcessId 对于窗口所在的线程的TEB,我

查阅 NATIVE      API  手册后找到了 ZwQueryInformationThread, 当然先要调用

OpenThread 得到线程句柄对于第三个参数,它的值一般总是为00XX0000,它其实就是进程

的GUI TABLE在R3层的映射的基地址.GUI TABLE也就是用户对象句柄表,它里面的值简单

的说就是一些指向窗体信息结构的指针.

*获得GUI TABLE在R3成层的映射基地址*

    我的系统中,记录这个地址的变量的地址是77D70084,在SOFTICE 中对这个地址下BPM

写断点, 发现每个进程加载 USER32.DLL           的时候一般是要调用这个 DLL        中的

UserClientDllInitialize,在这个函数的如下代码处

:77D21020 8DB5A0FAFFFF                    lea esi, dword ptr [ebp+FFFFFAA0]

:77D21026 BF8000D777                     mov edi, 77D70080 ;注意不是77D70084

:77D2102B F3                              repz

:77D2102C A5                              movsd

    会对这个变量赋初值.然后打开W32DASM,查找77d70084 和77d70080,结果发现了

一个UNDOCUMENT API:UserRegisterWowHandlers!分析这个函数的最后面的代码可

以看出这个函数的返回值就是记录GUI TABLE在R3成层的映射基地址的变量的地址-4.代码

如下:

:77D535F5 B88000D777                     mov eax, 77D70080

:77D535FA 5D                          pop ebp

:77D535FB C20800                        ret 0008

    到此理论上要实现直接内存读取密码应该没有问题了,下面看看具体的算法是什么:)

*把密码算出来*

第一步:

    取窗口句柄的低16位然后乘以12,我们设结果为HwndIndex

第二步:

----------------------- Page 27-----------------------

    得到GUI TABLE在R3成层的映射基地址,我们设这个地址为GuiTableBase

第三步:GuiTableBase+HwndIndex,然后取里面的值得到PHwndStruct1

第四步:

    TEB 基地址+6cch+1ch,取里面的值,得到RealClientID

第五步:

    PHwndStruct1-RealClientID 得到PHwndStruct2

第六步:

    PHwndStruct2+A4H,取里面的值得到真正的记录窗体信息的结构的地址设结果为

PRealWinStruct

第七步:

    PRealWinStruct+00h里面的值是编码后的密码的地址

    PRealWinStruct+0ch 里面值是密码长度我们叫PASSLEN

    PRealWinStruct+ech 里面值是解码要用到的一个变量我们叫ENCODE.

第八步:

    解码算法,通过对RtlRunDecodeUnicodeString 分析后解码算法如下:

    MOV EDX,ENCODE

        mov cl,dl

        mov edi,PASSLEN

@@nextpass:

        CMP EDI,1

        JBE @@firstpass

        mov eax,esi          ;esi指向编码后的密码的第一个字节.

        add eax,edi

        mov dl,[eax-2]

        xor dl,[eax-1]

        xor dl,cl ;重要

        mov [eax-1],dl

        dec edi

        jmp @@nextpass

@@firstpass:

        or   cl,43h

        mov edx,offset buffer1

        xor [edx],cl

    注意通过对多个 2K,XP,2003  系统的分析前面五步以及八步始终没有变化,第六步

WIN2000是+98h2003是+a0h,第七步,2000和2003都是+0CH,XP是+14H或+0ch

*具体编码*

    为了证明思路的正确性,专门写了一个WINDOWS2K/XP/2003 下看星号密码的小程序,

当然完全不用远程注入线程了.

下面把关键实现代码分析一下:

第一步:得到密码密窗口句柄:

invoke     GetCursorPos,addr @stPoint                    ;得到当前光标位置

----------------------- Page 28-----------------------

invoke      WindowFromPoint,@stPoint.x,@stPoint.y          ;得到光标下窗口的句柄

mov      @hWindow,eax

.if   eax !=      NULL

        invoke GetWindowLong,@hWindow,GWL_STYLE                   ;得到窗口风格

    .if (eax & ES_PASSWORD)                           ;是密码框吗?

        invoke GetClassName,@hWindow,offset   classname,64              ;如果是得到控

件类名

    invoke lstrcmpi,offset classname,offset editname

       .if eax == 0                                         ;如果类名是Edit,那么调用

ViewPass 函数读密码

       mov eax,@hWindow

       mov WINHAND,eax

       invoke ViewPass

                    .endif

                .endif

            .endif

第二步:判断系统:

LOCAL verinfo:OSVERSIONINFO

    mov       verinfo.dwOSVersionInfoSize,sizeof OSVERSIONINFO

    invoke   GetVersionEx,addr verinfo

    .if    (verinfo.dwPlatformId        ==     VER_PLATFORM_WIN32_NT              &&

verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 1)

        mov eax,1 ;xp

        mov passoffset,0A4H

        mov lenoffset ,14H

    程序只取WIN2000/XP/2003 系统的密码,同时根据不同的系统设置偏移.经过测试同一

种系统偏移没有变化,所以通用性应该很好.

第三步:得到密码窗口的线程和进程ID

invoke GetWindowThreadProcessId,eBx,addr parid

MOV WINTHREADID,EAX ;返回值为线程ID

第一个参数为窗口句柄,第二个参数为得到进程ID

第四步:根据窗口所在的进程的进程号得到这个进程加载的USER32.DLL 的基地址

invoke GetUser32Base,parid

返回值就是基地址:)

GetUser32Base      proc uses ebx esi edi remoteproid

            LOCAL hSnapshot:dword

----------------------- Page 29-----------------------

            LOCAL modinfo:MODULEENTRY32

            LOCAL modname[256]:byte

        mov         modinfo.dwSize,sizeof MODULEENTRY32

        invoke

CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid ;第一个参数表

示例举模块

        mov      hSnapshot,eax

        invoke   Module32First,hSnapshot,addr modinfo ;结果放在modinfo结构

中,modBaseAddr 成员记录

        .while eax                                   ;相应模块加载的基地址

        lea     ecx,modinfo.szModule

        invoke   lstrcmpi,offset user32dll,ecx    ;比较模块名是否为user32.dll

        .if    eax == 0

                mov eax,modinfo.modBaseAddr

                ret

        .endif

        invoke   Module32Next,hSnapshot,addr modinfo

        .endw

        invoke     CloseHandle,hSnapshot

                ret

GetUser32Base      endp

第五步:

根据窗口所在的线程得到该线程的TEB 地址

        invoke

OpenThread,THREAD_QUERY_INFORMATION,FALSE,WINTHREADID ;线程ID

        ..if  eax != NULL

                mov      THREADHAND,EAX

                invoke    LoadLibrary,offset Ntdll

                invoke                               GetProcAddress,eax,offset

_ZwQueryInformationThread ;调用NAVITE API

                mov      apiquerthread,eax

                push     0

                push     sizeof THREAD_BASIC_INFORMATION

                lea    ecx,threadinfo

                push     ecx

                push    ThreadBasicInformation

                push     THREADHAND

               call    apiquerthread

                .IF EAX == STATUS_SUCCESS

                   lea ecx,threadinfo

                   mov esi,[ecx+4] ;得到TEB 了,通常为7FFDX000

----------------------- Page 30-----------------------

                .ELSE

                    invoke MessageBox,0,offset errgetteb,offset vp,1

                    ret

                .ENDIF

        .else

            invoke MessageBox,0,offset erropenthread,offset vp,1

            ret

        .endif

第六步:得到TEB 中的RealClientID,注意这儿是读目标程序的内存,不是自已的了..

        add esi,6cch

            ;看第五步,ESI 中为目标线程的TEB 基地址,如果是程序自已获得自已的TEB

        add esi,1ch

            ;只用MOV EAX,FS:[18]就行了,也就是文章中间反汇编看到的那样.

        Invoke                  Toolhelp32ReadProcessMemory,parid,esi,offset

buffer1,4,NULL

    ;第一个参数为密码所在窗口进程 PID,第二个是读的起始地址,第三个是放在哪儿,第四是读

长度,第五实际读取

        .if eax == TRUE ;为真说明读成功

               mov eax,offset buffer1

               mov eax,[eax]

               mov edi,eax

               .if eax ==NULL

                invoke MessageBox,0,offset errnorealcid,offset vp,1

               ret

               .endif

        .endif

第七步:得到目标进程R3层的GUI TABLE基地址

    这一步应该是这个程序最关键的部分,希望大家认真阅读.先介绍一下我的思路:

我们已经知道这个基地址存放在目标程序加载的USER32.DLL 的全局变量中.并且这个DLL 中

的 UserRegisterWowHandlers 函数的返回值就是这个全局变量的地址.首先想到的办法是

直接调用这个函数,但是通过对这个函数的反汇编分析后发现该函数的参数难以正确构造特别是在

WIN2003 系统下该函数会比较严格的检查参数,所以就放弃了直接调用该函数得到基地址的办

法.

    通过对不同系统的这个函数反汇编我们可以很容易的找到共同点:

2K 系统:(5.0.2195.7032)

:77E3565D B880D2E477                      mov eax, 77E4D280

:77E35662 C20800                        ret 0008

XP 系统:(5.1.2600.2180)

:77D535F5 B88000D777                      mov eax, 77D70080

:77D535FA 5D                           pop ebp

----------------------- Page 31-----------------------

:77D535FB C20800                        ret 0008

2003 系统:(5.2.3790.1830)

:77E514D9 B8C024E777                      mov eax, 77E724C0

:77E514DE C9                          leave

:77E514DF C2080000                       ret 0008

    分析共同点以后,我们就可以写出相应的算法.我的算法是:

    1.得到我的进程自身的 USER32.DLL  的基地址,我们设为 user32base(其实也就是

LoadLibrary加载这个DLL 的返回值)

    2.调用GetProcAddress 得到UserRegisterWowHandlers 的入口地址.

    3.从入口地址处读1000个字节(这个函数功能其实很简单1000个字节足够了)

    4.在这1000个字节中,我使用了LDE32库的汇编指令长度判断函数(注四).给出指令的首

地址可以准确的计算出指令的长度.

    这样我先找长度为3 的指令,同时指令内容要为C20800(UserRegisterWowHandlers

只有两个参数所以用这种方法找这个指令正确率应该很高)

    在查找的过程中我用一个局部变量记录每一个指令的长度.在找到C20800 后我再倒过去找

指令长度为5,同时指令的第一个字节为B8(也就是mov eax,xxxxxxxx指令)

    5.在找到 mov eax,xxxxxxxx 指令后,取这个地址往后4 个字节的值,这个值(我们设为

varaddr)通常就是记录GUI TABLE基地址变量的地址

    6.分析USER32.DLL 的PE文件结构,找出这个DLL 的全局变量的起始地址(也就是.data

段的虚拟偏移(VirtualAddress)+USER32.DLL 的加载基地址).

    7.用第5 步找到的varaddr-(user32base+VirtualAddress),得到的值就是这个变量

在USER32.DLL 的全局变量中的相对偏移,我们记为VarOffset,

如果这个值>0,同时小于.data段的VirtualSize 那么说明成功.如果不成功我们再跳到第5 步

再从后往前重新找mov eax,xxxxxxxx指令.

    8.通过前面第四步(根据窗口所在的进程的进程号得到这个进程加载的 USER32.DLL 的基

地址)+VirtualAddress+VarOffset  我们就得到了目标进程中这个变量的地址,最后再调用

Toolhelp32ReadProcessMemory,就可以读出GUI TABLE 的基地址了.

    (注:由于不能找到直接调用UserRegisterWowHandlers  的办法,所以第七步从原理上

看并不能保证有100%的成功率,但通过我对多个不同系统不同版本的测试,目前的这个算法都还

是通用)

第八步:最后其实就是把*把密码算出来*这一节的算法实现就0K 了.不过要注意的是密码可能是

Unicode 格式的.

*最后的总结*

    所有的分析和技术细节都在上面了,这篇文章要用到PE文件格式,NAVITE API,反汇编等知

识如有不懂可以参考网上的相关的资料.

----------------------- Page 32-----------------------

    注一:文件补丁技术简单说就是分析目标程序的流层,找出程序本身获得密码框密码的代码,然

后在这个代码后面加上一个跳转跳到我们新增加的PE节中,在这个节中的代码就是取得密码并记

录到文件中,然后再跳回程序原来的流层.

    注二:其实要取得密码也可以这样做:发送 EW_SETPASSWORDCHAR 消息,取消 EDIT

控件的密码风格,然后再调用GetWindowText 函数取密码最后再恢复密码框属性,不过对于这

种办法,用户很可能会发现异常.使用Delphi/BCB 工具中的TEDIT 类,可以直接发消息,这时微

软的限制完全不起作用.

    注三:大多数版本的ZoneAlarm 是只防止OpenProcess 打开系统进程以及IE 的进程句

柄,对于OpenProcess 第三方程序默认中级安全级别下不拦.

    注四:程序中使用的LDE32 库,是国外的程序员开发的一个专门计算汇编指令长度的小工具,

网上有源代码可下载..

该库文件编译后只有600 多个字节.

    注五:还有一种按键记录技术是用一个死循环不停的调用 GetAsyncKeyState  和

GetKeyState 判断同一时间下每个按键的当前状态.

    该方法目前也很难被安全软件发现但还是有记录不准确,不能记录不按顺序输入的密码(当然

也不能记中文)等问题.

附:

1.看星号程序源代码

2.一个简单的密码框程序

3.测试系统的USER32.DLL

内存读取获得密码(原创)

;                  #--------------------------------------#          #

;                #   PassView                                  #       #

;              #                                              #    #

;            #                                                  #

;          #                       2007.1.1                       #

;            #                     codz: czy                  #     #

;             #------------------------------------------#         #

;test on winXPSP2,qqgame,qqlocalmsgpass,MSN,IE HTTPS/FTP,OE,RAR

                .386

                .model flat, stdcall

                option casemap :none         ; case sensitive

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;        Include 数据

----------------------- Page 33-----------------------

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include     \masm32\include\windows.inc

include     \masm32\include\kernel32.inc

include     \masm32\include\user32.inc

include     \masm32\include\gdi32.inc

includelib  \masm32\lib\kernel32.lib

includelib  \masm32\lib\user32.lib

includelib  \masm32\lib\gdi32.lib

CLIENT_ID STRUCT           ; sizeof = 8

       UniqueProcess          HANDLE          ?

       UniqueThread          HANDLE          ?

CLIENT_ID ENDS

THREAD_BASIC_INFORMATION STRUCT                  ; sizeof = 1ch

       ExitStatus                               DWORD             ?

       TebBaseAddress                          PVOID                ? ; PTEB

       ClientId                           CLIENT_ID         <> ; 8h

       AffinityMask                       DWORD                  ? ;

       Priority                          DWORD                  ?

       BasePriority                DWORD                  ?

THREAD_BASIC_INFORMATION ENDS

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;       子程序声明

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_ProcDlgMain          PROTO         :DWORD,:DWORD,:DWORD,:DWORD

ViewPass                    proto

_ProcessPeFile              proto :dword

GetUnknowVarOffset          proto

GetUser32Base               proto :dword

CheckOS                     proto

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;       Equ 数据

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.const

STATUS_SUCCESS                     equ           0

ThreadBasicInformation             equ           0

DLG_MAIN                           equ            1000

ID_PWD                             equ            1001

IDB_1                              equ            1

IDC_BMP                            equ            108

----------------------- Page 34-----------------------

RGB MACRO red, green, blue

        xor eax, eax

        mov al, blue     ; blue

        rol eax, 8

        mov al, green     ; green

        rol eax, 8

        mov al, red      ; red

ENDM

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;        数据段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.data

hWinMain           dd ?

vp                 db 'viewpass 0.1 codz czy',0

PASSLEN            DD ?

ENCODE             DD ?

WINTHREADID        DD ?

WINHAND            DD 0

unknownvar         DD ?

VirtualAddress     dd ?

VirtualSize        dd ?

VarOffset          dd ?

modbase            dd ?

lenoffset          dd ?

passoffset         dd ?

Tahoma             db 'Tahoma',0

editname           db 'EdIt',0

Richeditname       db 'RichEdit20W',0

dataname           db '.data   ',0

erros              db '不是吧,还在用WIN9X/NT4',0

nowin              db '找不到主窗体',0

nowin2             db '找不到子窗体中的密码窗口',0

errnowow           db '不能找到UserRegisterWowHandlers 函数地址',0

errnocode          db '不能找到UserRegisterWowHandlers 函数中相匹配的机器码',0

erropenthread      db '不能打开线程',0

errgetteb          db '打开线程但不能得到TEB',0

errnorealcid       db '不能得到TEB 中的RealClientID',0

errnounknowvar db '不能从user32.dll 中找到未知变量',0

passerr            db '密码太长或为空',0

tebformat          db 'TEB %x',0

varformat          db 'user32.dll gSharedInfo addr:%x',0

varformat2         db 'GUI TABLE in user modle addr:%08x',0

realcidformat      db 'real cid:%x',0

----------------------- Page 35-----------------------

Wndformat              db 'pass window hand:%x,win thread id:%x',0

wndcontformat          db 'win struct:%x',0

passaddrformat         db 'pass addr:%x',0

passlenformat          db 'pass len:%d',0

passformat             db 'pass decode     is:%x',0

user32dll              db 'user32.dll',0

Ntdll                  db        "NTDLL.DLL",0

_UserRegisterWowHandlers         db "UserRegisterWowHandlers",0

_ZwQueryInformationThread db "ZwQueryInformationThread",0

THREADHAND             DD 0

apiquerthread          dd ?

Pthreadinfo            dd ?

.data?

hInstance              dd ?

szBuffer               db 256 dup          (?)

buffervar              db 32 dup (?)

buffervar2             db 32 dup (?)

bufferteb              db 32 dup (?)

bufferPassWnd          db 32 dup (?)

bufferrealcid          db 32 dup (?)

bufferwndcont          db 32 dup (?)

bufferpassaddr         db 32 dup (?)

bufferpasslen          db 32 dup (?)

bufferpass1            db 128 dup (?)

bufferuni              db 256 dup (?)

classname              db 128 dup (?)

buffer1                db 128 dup (?)

buffercode             db 1024 dup (?)

                .code

;***********************************************************

*********

ViewPass    proc

            LOCAL parid:dword

            LOCAL threadinfo:THREAD_BASIC_INFORMATION

        ;invoke MessageBox,0,offset vp,offset vp,1

;--------------判断操作系统

        invoke CheckOS

        .if eax == 0

            ret

        .endif

----------------------- Page 36-----------------------

;---------------得到密码窗口句柄,以及线程句柄,进程句柄

            MOV EBX,WINHAND

            .if EBX!=NULL

               invoke GetWindowThreadProcessId,eBx,addr parid

               MOV     WINTHREADID,EAX

              ;invoke             wsprintf,offset          bufferPassWnd,offset

Wndformat,ebx,eax

              ;invoke MessageBox,0,offset bufferPassWnd,offset vp,1

            .else

                invoke     MessageBox,0,offset nowin2,offset vp,1

                ret

            .endif

;-------------根据窗口所在的进程的进程号得到这个进程加载的USER32.DLL 的基地址

        invoke GetUser32Base,parid

        mov     modbase,eax

;--------------根据窗口所在的线程得到该线程的TEB 地址

            invoke

OpenThread,THREAD_QUERY_INFORMATION,FALSE,WINTHREADID

            .if       eax != NULL

                   mov       THREADHAND,EAX

                       invoke         LoadLibrary,offset Ntdll

                   invoke                            GetProcAddress,eax,offset

_ZwQueryInformationThread

                   mov       apiquerthread,eax

                   push      0

                   push      sizeof THREAD_BASIC_INFORMATION

                   lea     ecx,threadinfo

                   push      ecx

                   push      ThreadBasicInformation

                   push      THREADHAND

                   call    apiquerthread

                   .IF EAX == STATUS_SUCCESS

                       lea ecx,threadinfo

                       mov esi,[ecx+4] ;得到TEB 了

                   .ELSE

                       invoke MessageBox,0,offset errgetteb,offset vp,1

                       ret

                .ENDIF

        .else

            invoke MessageBox,0,offset erropenthread,offset vp,1

----------------------- Page 37-----------------------

           ret

        .endif

       ;invoke wsprintf,offset bufferteb,offset tebformat,esi

       ;invoke MessageBox,0,offset bufferteb,offset vp,1

;-------------------------得到TEB 中的RealClientID

       add esi,6cch

       add esi,1ch

        invoke                Toolhelp32ReadProcessMemory,parid,esi,offset

buffer1,4,NULL

        .if eax == TRUE

              mov eax,offset buffer1

              mov eax,[eax]

              mov edi,eax

              .if eax !=NULL

              ;invoke wsprintf,offset bufferrealcid,offset realcidformat,eax

              ;invoke MessageBox,0,offset bufferrealcid,offset vp,1

              .else

               invoke MessageBox,0,offset errnorealcid,offset vp,1

              ret

              .endif

        .endif

       ;密码窗口句柄取低16位

       xor eax,eax

        mov    ebx,WINHAND

        mov    ax,bx

       add    ax,ax

       add    ax,bx ;3

       add    ax,ax ;6

       add    ax,ax ;12

        mov    ebx,eax

        pushad

        invoke GetUnknowVarOffset

        .if eax !=NULL

              mov eax,VarOffset

              add eax,modbase

              add eax,VirtualAddress

              ;invoke wsprintf,offset buffervar,offset varformat,eax

              ;invoke MessageBox,0,offset buffervar,offset vp,1

        .else

               invoke MessageBox,0,offset errnounknowvar,offset vp,1

----------------------- Page 38-----------------------

                ret

        .endif

        popad

        mov ecx,VarOffset

        add ecx,modbase

        add ecx,VirtualAddress

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                mov ecx,[ecx]

                ;push ecx

               ;invoke wsprintf,offset buffervar2,offset varformat2,ecx

               ;invoke MessageBox,0,offset buffervar2,offset vp,1

               ;pop ecx

        .endif

        add   ebx,ecx ;窗口句柄低16位*12+GUI TABLE BASE

        invoke                 Toolhelp32ReadProcessMemory,parid,ebx,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                mov ecx,[ecx]

        .endif

        sub ecx,edi ;减REALCLIENTID

        mov esi,ecx

        ;invoke wsprintf,offset bufferwndcont,offset wndcontformat,esi

        ;invoke MessageBox,0,offset bufferwndcont,offset vp,1

        add esi,passoffset

        invoke                  Toolhelp32ReadProcessMemory,parid,esi,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                mov esi,[ecx]

        .endif

        ;invoke wsprintf,offset bufferpassaddr,offset passaddrformat,esi

        ;invoke MessageBox,0,offset bufferpassaddr,offset vp,1

        ;得到密码长度

        mov    ecx,esi

        add   ecx,lenoffset

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,4,NULL

        .if eax == TRUE

----------------------- Page 39-----------------------

                mov ecx,offset buffer1

                mov ecx,[ecx]

                mov PASSLEN,ecx

        .endif

        .if ecx>0 && ecx <32

        ;invoke wsprintf,offset bufferpasslen,offset passlenformat,ecx

        ;invoke MessageBox,0,offset bufferpasslen,offset vp,1

        .else

        invoke MessageBox,0,offset passerr,offset vp,1

        ret

        .endif

        ;得到加密密码的变量

        mov      ecx,esi

        add     ecx,0ECh

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                MOV ECX,[ECX]

                xor edx,edx

                movzx edx,cl

                mov ENCODE,EDX

        .endif

        ;invoke wsprintf,offset bufferpass1,offset passformat,edx

        ;invoke MessageBox,0,offset bufferpass1,offset vp,1

        ;得到解密后的密码

        mov      ecx,esi

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                mov ecx,[ecx]

        .endif

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,4,NULL

        .if eax == TRUE

                mov ecx,offset buffer1

                mov ecx,[ecx]

        .endif

        mov ebx,ecx

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,PASSLEN,NULL

----------------------- Page 40-----------------------

        .if eax == TRUE

               mov esi,offset buffer1

        .endif

        MOV EDX,ENCODE

        mov cl,dl

        mov edi,PASSLEN

@@nextpass:

        CMP EDI,1

        JBE @@firstpass

        mov eax,esi

        add eax,edi

        mov dl,[eax-2]

        xor dl,[eax-1]

        xor dl,cl ;重要

        mov [eax-1],dl

        dec edi

       jmp @@nextpass

@@firstpass:

        or  cl,43h ;WHY?

        mov edx,offset buffer1

        xor [edx],cl

        ;密码可能是UNICODE 的

        invoke lstrlenA,edx

        .if eax<PASSLEN ;密码是UNICODE

        mov edx,PASSLEN

        add edx,edx

        mov ecx,ebx

        invoke                 Toolhelp32ReadProcessMemory,parid,ecx,offset

buffer1,edx,NULL

        .if eax == TRUE

               mov esi,offset buffer1

        .endif

        mov edi,PASSLEN

        add edi,edi

        MOV EDX,ENCODE

        mov cl,dl

@@nextpass2:

        CMP EDI,1

        JBE @@firstpass2

        mov eax,esi

        add eax,edi

----------------------- Page 41-----------------------

        mov dl,[eax-2]

        xor dl,[eax-1]

        xor dl,cl ;重要

        mov [eax-1],dl

        dec edi

        jmp @@nextpass2

@@firstpass2:

        or  cl,43h

        mov edx,offset buffer1

        xor [edx],cl

        ;invoke MessageBoxW,0,edx,edx,1

        invoke         SetDlgItemTextW,hWinMain,ID_PWD,edx

        .else

        mov ecx,offset buffer1

        add ecx,PASSLEN

        xor eax,eax

        MOV [ECX],eax

        invoke         SetDlgItemTextA,hWinMain,ID_PWD,offset buffer1

        invoke   RtlZeroMemory,offset buffer1,128

        .endif

            ret

ViewPass    endp

comment %

GetUnknowVarOffset       proc uses esi edi ebx

                    LOCAL     user32base:dword

                    LOCAL     varaddr:dword

        invoke LoadLibrary,offset user32dll

        mov      user32base,eax

        invoke _ProcessPeFile,user32base ;返回user32.dll 的.data段的虚拟偏移

        invoke                                 GetProcAddress,user32base,offset

_UserRegisterWowHandlers

        .if eax!=NULL

            invoke RtlMoveMemory,offset buffercode,eax,1000

            mov esi,offset buffercode

            xor ebx,ebx

@@nextcode:

            mov ax,word ptr [esi]

            .if ax==08C2h

                add esi,2

                mov al,byte ptr[esi]

                sub esi,2

----------------------- Page 42-----------------------

                .if al==00h

                    ;--找到 ret   08(C20800)后,倒过去找 mov   eax,xxxxxxxx   (B8

xxxxxxxx)

                    sub esi,5

                    sub ebx,5

@@nextcode2:

                    mov al,byte ptr [esi]

                    .if al==0B8h

                        inc esi

                        mov eax,dword ptr [esi]

                        dec esi

                        mov varaddr,eax

                    .else

                    @@nextb8:

                        dec esi

                        dec ebx

                        .if ebx>1

                           jmp @@nextcode2

                        .else ;找不到mov eax,xxxxxxxx

                            invoke MessageBox,0,offset errnocode,offset vp,1

                            xor eax,eax

                            ret

                        .endif

                    .endif

                .else

                    jmp @@findother

                .endif

            .else

@@findother:

                inc ebx

                inc esi

                .if ebx<1000d

                    jmp @@nextcode

                .else ;找不到ret 08 指令

                    invoke MessageBox,0,offset errnocode,offset vp,1

                    xor eax,eax

                    ret

                .endif

            .endif

        .else  ;找不到函数

         invoke MessageBox,0,offset errnowow,offset vp,1

         xor eax,eax

         ret

        .endif

----------------------- Page 43-----------------------

        ;正常情况varaddr 为USER32.DLL 全局变量中记录当前线程GUI TABLE R3 层

地址的变量的地址

        mov ecx,user32base

        add ecx,VirtualAddress ;得到内存中user32.dll 的全局变量的起始地址

        mov eax,varaddr

        sub eax,ecx ;变量的地址减全局变量起始地址

        ..if eax>0 && eax<VirtualSize ;变量的偏移大于0,小于变局变量总大小

        add eax,4

        mov VarOffset,eax

        .else

        jmp @@nextb8

        .endif

                    ret

GetUnknowVarOffset       endp

%

GetUnknowVarOffset       proc uses esi edi ebx

                    LOCAL     user32base:dword

                    LOCAL     varaddr:dword

                    LOCAL     optable[2048]:byte

                    LOCAL     oplen[256]:byte ;记录每个指令的长度,最多512 个

        invoke LoadLibrary,offset user32dll

        mov      user32base,eax

        invoke _ProcessPeFile,user32base ;返回user32.dll 的.data段的虚拟偏移

        invoke                                 GetProcAddress,user32base,offset

_UserRegisterWowHandlers

        .if eax!=NULL

            invoke RtlMoveMemory,offset buffercode,eax,1000

            mov esi,offset buffercode

;------------------------     利用LDE32库函数得到该函数中每个指令的长度:)

        lea  eax,optable

        push eax

        call disasm_init   ;解压缩'指令长度表'

        xor ebx,ebx

        lea edi,oplen

----------------------- Page 44-----------------------

@@nextcode:

        push esi

        lea  eax,optable

        push eax

        call disasm_main

        .if eax!=-1 && ebx<256

            mov cx,word ptr [esi]

            mov dl,byte ptr [esi+2]

            .if eax == 3 && cx == 08C2h && dl == 00         ;找到ret 08=C20800

            ;------------------- 然后反过去找 MOV         EAX,XXXXXXXX         =B8

XXXXXXXX

            dec ebx

@@nextcode2:

            mov AL,byte ptr [edi+ebx]

            movzx eax,al

            sub esi,eax

            mov cl,byte ptr [esi]

            .if al == 5 && cl == 0B8H

                        inc esi

                        mov eax,dword ptr [esi]

                        mov varaddr,eax

                        dec esi

            .else

@@nextb8:

                    dec ebx

                    .if ebx >1

                        jmp @@nextcode2

                    .else

                        jmp @@errcode

                    .endif

            .endif

            .else

            mov byte ptr [edi+ebx],al

            inc ebx

            add esi,eax

            jmp @@nextcode

            .endif

       .else ;找不到RET 08指令

@@errcode:

            invoke MessageBox,0,offset errnocode,offset vp,1

            xor eax,eax

            ret

----------------------- Page 45-----------------------

       .endif

;------------------------

        .else  ;找不到函数

            invoke MessageBox,0,offset errnowow,offset vp,1

            xor eax,eax

            ret

        .endif

        ;正常情况varaddr 为USER32.DLL 全局变量中记录当前线程GUI TABLE R3 层

地址的变量的地址

        mov ecx,user32base

        add ecx,VirtualAddress ;得到内存中user32.dll 的全局变量的起始地址

        mov eax,varaddr

        sub eax,ecx ;变量的地址减全局变量起始地址

        .if eax>0 && eax<VirtualSize ;变量的偏移大于0,小于全局变量总大小

        add eax,4

        mov VarOffset,eax

        .else

        jmp @@nextb8

        .endif

                    ret

GetUnknowVarOffset        endp

GetUser32Base      proc uses ebx esi edi remoteproid

            LOCAL hSnapshot:dword

            LOCAL modinfo:MODULEENTRY32

            LOCAL modname[256]:byte

        mov              modinfo.dwSize,sizeof MODULEENTRY32

        invoke

CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid

        mov       hSnapshot,eax

        invoke    Module32First,hSnapshot,addr modinfo

        .while eax

        lea      ecx,modinfo.szModule

        invoke    lstrcmpi,offset user32dll,ecx

        .if    eax == 0

                mov eax,modinfo.modBaseAddr

                ret

        .endif

----------------------- Page 46-----------------------

        invoke  Module32Next,hSnapshot,addr modinfo

        .endw

        invoke        CloseHandle,hSnapshot

               ret

GetUser32Base      endp

_ProcessPeFile         proc _lpPeHead

               local        @szBuffer[1024]:byte,@szSectionName[16]:byte

        mov         esi,_lpPeHead

               assume          esi:ptr IMAGE_DOS_HEADER

        add        esi,[esi].e_lfanew

               mov         edi,esi

               assume          edi:ptr IMAGE_NT_HEADERS

               ;movzx         ecx,[edi].FileHeader.Machine

               ;movzx         edx,[edi].FileHeader.NumberOfSections

               ;movzx         ebx,[edi].FileHeader.Characteristics

;***********************************************************

*********

; 循环显示每个节区的信息

;***********************************************************

*********

               movzx         ecx,[edi].FileHeader.NumberOfSections

               add        edi,sizeof IMAGE_NT_HEADERS

               assume          edi:ptr IMAGE_SECTION_HEADER

               .repeat

                       push        ecx

;***********************************************************

*********

; 节区名称

;***********************************************************

*********

                       invoke                             RtlZeroMemory,addr

@szSectionName,sizeof @szSectionName

                       push        esi

                       push        edi

                       mov         ecx,8

                       mov         esi,edi

                       lea       edi,@szSectionName

                       cld

                       @@:

                       lodsb

----------------------- Page 47-----------------------

                        .if       ! al

                                mov         al,' '

                        .endif

                        stosb

                        loop        @B

                        pop         edi

                        pop         esi

;***********************************************************

*********

            invoke lstrcmpi,offset dataname,addr @szSectionName

            .if eax == 0

                    push [edi].VirtualAddress

                    pop VirtualAddress

                    push dword ptr [edi+8]

                    pop VirtualSize

                    ret

                        .else

                        add         edi,sizeof IMAGE_SECTION_HEADER

                        .endif

;***********************************************************

*********

                        pop         ecx

                .untilcxz

                assume          edi:nothing

                ret

_ProcessPeFile          endp

CheckOS proc

    LOCAL verinfo:OSVERSIONINFO

    mov       verinfo.dwOSVersionInfoSize,sizeof OSVERSIONINFO

    invoke   GetVersionEx,addr verinfo

    .if    (verinfo.dwPlatformId       ==      VER_PLATFORM_WIN32_NT              &&

verinfo..dwMajorVersion == 5 && verinfo.dwMinorVersion == 1)

        mov eax,1 ;xp

        mov passoffset,0A4H

        mov lenoffset ,14H

    .elseif   (verinfo.dwPlatformId       ==    VER_PLATFORM_WIN32_NT             &&

verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 2)

        mov eax,1 ;2003

----------------------- Page 48-----------------------

        mov passoffset,0A0H

        mov lenoffset ,0CH

    .elseif   (verinfo.dwPlatformId     ==   VER_PLATFORM_WIN32_NT            &&

verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 0)

        mov eax,1 ;2000

        mov passoffset,98H

        mov lenoffset ,0cH

    .else

        invoke MessageBox,0,offset erros,offset vp,1

       xor eax,eax    ;9x,nt4

    .endif

        ret

CheckOS endp

_ProcDlgMain          proc        uses ebx edi esi, \

hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD

               local       @stPoint:POINT

               local       @hWindow

               local   hBrush :dword

           local   rect:RECT

           local   LogBrush:LOGBRUSH

               mov         eax,wMsg

               .if       eax == WM_CLOSE

                       invoke        EndDialog,hWnd,NULL

                       invoke        KillTimer,hWnd,1

               .elseif       eax == WM_INITDIALOG

                       push     hWnd

                       pop     hWinMain

                   invoke LoadIcon, hInstance, 1

                   invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL,

eax

                       invoke

SendDlgItemMessage,hWnd,ID_PWD,EM_SETREADONLY,TRUE,NULL

                       invoke

SetWindowPos,hWnd,HWND_TOPMOST,0,0,0,0,\

                                  SWP_NOMOVE or SWP_NOSIZE

                       invoke        SetTimer,hWnd,1,2000,NULL

                       invoke        LoadBitmap,hInstance,IDB_1

                       invoke

SendDlgItemMessage,hWnd,IDC_BMP,STM_SETIMAGE,IMAGE_BITMAP,eax

                       invoke  GetWindowLong,hWnd, GWL_EXSTYLE

----------------------- Page 49-----------------------

                       or      eax,80000h

                       invoke   SetWindowLong,hWnd, GWL_EXSTYLE, eax

                       invoke    SetLayeredWindowAttributes,hWnd,   0,   220,

02h;LWA_ALPHA

            .elseif      eax ==          WM_CTLCOLORSTATIC

                   RGB 0,0,0

                   invoke         SetBkColor,wParam,eax

                   invoke         SetTextColor,wParam,00aeaeaeh

                   invoke         GetStockObject,HOLLOW_BRUSH

                   ret

               .elseif       eax == WM_ERASEBKGND

                   mov LogBrush.lbStyle,BS_SOLID

                   RGB 0,0,0

                   mov LogBrush.lbColor,eax

                   invoke CreateBrushIndirect,addr LogBrush

                   mov hBrush,eax

                   invoke GetClientRect,hWnd,addr rect

                   invoke FillRect,wParam,addr rect,hBrush

                   mov eax,TRUE

                   ret

               .elseif       eax == WM_TIMER

                       invoke        GetCursorPos,addr @stPoint

                       invoke

WindowFromPoint,@stPoint.x,@stPoint.y

                       mov         @hWindow,eax

                       .if       eax !=        NULL

                               invoke

GetWindowLong,@hWindow,GWL_STYLE

                               .if (eax & ES_PASSWORD)

                   invoke GetClassName,@hWindow,offset classname,64

                   invoke lstrcmpi,offset classname,offset editname

                   .if eax == 0

                   mov eax,@hWindow

                   mov WINHAND,eax

                   invoke   ViewPass

                   .endif

                               .endif

                       .endif

               .else

;***********************************************************

;       注意:对话框的消息处理后,要返回TRUE,对没有处理的消息

;       要返回 FALSE

;***********************************************************

                       mov         eax,FALSE

----------------------- Page 50-----------------------

                       ret

               .endif

               mov          eax,TRUE

               ret

_ProcDlgMain          endp

include      \masm32\include\lde32bin.inc

;***********************************************************

start:

                invoke        GetModuleHandle,NULL

               mov          hInstance,eax

                invoke

DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0

                invoke        ExitProcess,NULL

end         start
2007-8-28 23:00
0
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
http://www.live-share.com/files/260630/Windows_NT____.txt.html
2007-8-28 23:05
0
雪    币: 224
活跃值: (147)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
10
自己重新整理会好点 呵呵 习惯而已
2007-8-28 23:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
简单的看了看8楼贴出的内容就吸引住我了,好东西,下载下来好好学习。
2007-8-29 00:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
这书好啊。
很傎得一看
2007-8-29 02:32
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
13
不错,收藏了
2007-8-29 13:58
0
雪    币: 199
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
好文!楼主多发呀.当下来学习一下.
2007-8-29 16:11
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
谢谢楼主的无私奉献,支持,顶!
2007-8-30 18:49
0
雪    币: 721
活跃值: (350)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
16
thank you for sharing with us...
2007-9-7 08:45
0
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
17
先下来看看,多谢
2007-9-8 11:02
0
雪    币: 5041
活跃值: (3439)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
18
多谢楼主分享
2007-9-9 11:43
0
雪    币: 8599
活跃值: (5065)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
大家下这个吧。重新整理了一个比较全面的,又添加了4篇重要发的分析。
上传的附件:
2007-9-10 23:56
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
感谢分享!!
2007-9-12 21:07
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
谢谢搂主 学习了!!!
2007-9-13 23:11
0
雪    币: 280
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
谢谢楼主分享!
2007-9-29 09:33
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
顶了,好东西啊
2007-10-25 16:15
0
雪    币: 466
活跃值: (165)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
24
支持一下~
有书签就好了
2007-11-20 20:44
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
谢谢楼主分享
2008-4-10 09:34
0
游客
登录 | 注册 方可回帖
返回
//