FS寄存器,关于前一篇中获得当前PEB的一些疑点

2009年12月17日

FS寄存器指向当前活动线程的TEB结构(线程结构)

偏移 说明
000 指向SEH链指针
004 线程堆栈顶部
008 线程堆栈底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在内存中的镜像地址
020 进程PID
024 线程ID
02C 指向线程局部存储指针
030 PEB结构地址(进程结构)
034 上个错误号


得到KERNEL32.DLL基址的方法
assume fs:nothing ;打开FS寄存器
mov eax,fs:[30h] ;得到PEB结构地址
mov eax,[eax + 0ch] ;得到PEB_LDR_DATA结构地址
mov esi,[eax + 1ch] ;InInitializationOrderModuleList
lodsd ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址
mov edx,[eax + 8h] ;得到BaseAddress,既Kernel32.dll基址


 

问题一:

上一篇中获得当前PEB的代码如下

mov eax, dword ptr fs:[0x18]

mov eax, dword ptr [eax+0x30]

其实完全可以

move eax, dword ptr fs:[0x30]

测试过了,这两段代码是等效的

 

问题二:

既然fs指向当前TEB,那为什么要用

mov eax, dword ptr fs:[0x18]

来获得当前TEB地址呢

因为 move eax, dword ptr fs:[0x00]获得的是 fs:[0x00]的内容 而无法获取真正的fs对应的地址

 

kernel32里的 GetVersion 函数的逆向分析

2009年12月14日

今天在家调试程序,跟进了一个函数kernel32里面的GetVersion函数。最近刚好在学逆向。心一横,干脆把它逆了。

代码如下:

0043846A  |.  FF15 70F14500 call    dword ptr [<&KERNEL32.GetVersion>]                ;  kernel32.GetVersion

7C81126A >  64:A1 18000000       mov     eax, dword ptr fs:[18]         ; 获得当前线程的TEB地址
7C811270    8B48 30                       mov     ecx, dword ptr [eax+30]        ; 在TEB偏移30h处获得PEB地址
7C811273    8B81 B0000000        mov     eax, dword ptr [ecx+B0]
7C811279    0FB791 AC000000   movzx   edx, word ptr [ecx+AC]
7C811280    83F0 FE                       xor     eax, FFFFFFFE
7C811283    C1E0 0E                      shl     eax, 0E
7C811286    0BC2                            or      eax, edx
7C811288    C1E0 08                      shl     eax, 8
7C81128B    0B81 A8000000        or      eax, dword ptr [ecx+A8]
7C811291    C1E0 08         shl       eax, 8
7C811294    0B81 A4000000        or      eax, dword ptr [ecx+A4]
7C81129A    C3                                retn

解析:
          mov     eax, dword ptr fs:[18]       
          FS里面存档的是当前线程环境块(_TEB结构),_TEB结构如下:
 kd> dt _TEB
   ntdll!_TEB
   +0×000 NtTib            : _NT_TIB
   +0×01c EnvironmentPointer : Ptr32 Void
   +0×020 ClientId         : _CLIENT_ID
   +0×028 ActiveRpcHandle  : Ptr32 Void
   +0×02c ThreadLocalStoragePointer : Ptr32 Void
   +0×030 ProcessEnvironmentBlock : Ptr32 _PEB
   +0×034 LastErrorValue   : Uint4B
   +0×038 CountOfOwnedCriticalSections : Uint4B
   +0×03c CsrClientThread  : Ptr32 Void
   +0×040 Win32ThreadInfo  : Ptr32 Void
   +0×044 User32Reserved   : [26] Uint4B
   +0×0ac UserReserved     : [5] Uint4B
   +0×0c0 WOW32Reserved    : Ptr32 Void
   +0×0c4 CurrentLocale    : Uint4B
   +0×0c8 FpSoftwareStatusRegister : Uint4B
   +0×0cc SystemReserved1  : [54] Ptr32 Void
   +0×1a4 ExceptionCode    : Int4B
   +0×1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
   +0×1bc SpareBytes1      : [24] UChar
   +0×1d4 GdiTebBatch      : _GDI_TEB_BATCH
   +0×6b4 RealClientId     : _CLIENT_ID
   +0×6bc GdiCachedProcessHandle : Ptr32 Void
   +0×6c0 GdiClientPID     : Uint4B
   +0×6c4 GdiClientTID     : Uint4B
   +0×6c8 GdiThreadLocalInfo : Ptr32 Void
   +0×6cc Win32ClientInfo  : [62] Uint4B
   +0×7c4 glDispatchTable  : [233] Ptr32 Void
   +0xb68 glReserved1    
 : [29] Uint4B
   +0xbdc glReserved2      : Ptr32 Void
   +0xbe0 glSectionInfo    : Ptr32 Void
   +0xbe4 glSection        : Ptr32 Void
   +0xbe8 glTable          : Ptr32 Void
   +0xbec glCurrentRC      : Ptr32 Void
   +0xbf0 glContext        : Ptr32 Void
   +0xbf4 LastStatusValue  : Uint4B
   +0xbf8 StaticUnicodeString : _UNICODE_STRING
   +0xc00 StaticUnicodeBuffer : [261] Uint2B
   +0xe0c DeallocationStack : Ptr32 Void
   +0xe10 TlsSlots         : [64] Ptr32 Void
   +0xf10 TlsLinks         : _LIST_ENTRY
   +0xf18 Vdm              : Ptr32 Void
   +0xf1c ReservedForNtRpc : Ptr32 Void
   +0xf20 DbgSsReserved    : [2] Ptr32 Void
   +0xf28 HardErrorsAreDisabled : Uint4B
   +0xf2c Instrumentation  : [16] Ptr32 Void
   +0xf6c WinSockData      : Ptr32 Void
   +0xf70 GdiBatchCount    : Uint4B
   +0xf74 InDbgPrint       : UChar
   +0xf75 FreeStackOnTermination : UChar
   +0xf76 HasFiberData     : UChar
   +0xf77 IdealProcessor   : UChar
   +0xf78 Spare3           : Uint4B
   +0xf7c ReservedForPerf  : Ptr32 Void
   +0xf80 ReservedForOle   : Ptr32 Void
   +0xf84 WaitingOnLoaderLock : Uint4B
   +0xf88 Wx86Thread       : _Wx86ThreadState
   +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
   +0xf98 ImpersonationLocale : Uint4B
   +0xf9c IsImpersonating  : Uint4B
   +0xfa0 NlsCache         : Ptr32 Void
   +0xfa4 pShimData        : Ptr32 Void
   +0xfa8 HeapVirtualAffinity : Uint4B
   +0xfac CurrentTransactionHandle : Ptr32 Void
   +0xfb0 ActiveFrame      : Ptr32 _TEB_ACTIVE_FRAME
   +0xfb4 SafeThunkCall    : UChar
   +0xfb5 BooleanSpare     : [3] UChar

          显然,偏移量18位置在_NT_TIB这个结构里, _NT_TIB如下:
kd> dt _NT_TIB
ntdll!_NT_TIB
   +0×000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0×004 StackBase        : Ptr32 Void
   +0×008 StackLimit       : Ptr32 Void
   +0×00c SubSystemTib     : Ptr32 Void
   +0×010 FiberData        : Ptr32 Void
   +0×010 Version          : Uint4B
   +0×014 ArbitraryUserPointer : Ptr32 Voi
d
   +0×018 Self             : Ptr32 _NT_TIB
         
          OK,这样我们就知道dword ptr fs:[18]其实是   +0×018 Self    : Ptr32 _NT_TIB,
          就是说这句话实际上是把当前线程TEB地址放入了eax中

          mov     ecx, dword ptr [eax+30] 
          把TEB基址偏移30h处的4个字节放入ecx  , +0×030 ProcessEnvironmentBlock : Ptr32 _PEB
          即获得当前线程PEB地址,放入ecx
         
         mov     eax, dword ptr [ecx+B0]
         要知道这是什么意思就需要先查看_PEB结构,_PEB结构如下:
kd> dt _PEB
ntdll!_PEB
   +0×000 InheritedAddressSpace : UChar
   +0×001 ReadImageFileExecOptions : UChar
   +0×002 BeingDebugged    : UChar
   +0×003 SpareBool        : UChar
   +0×004 Mutant           : Ptr32 Void
   +0×008 ImageBaseAddress : Ptr32 Void
   +0×00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0×010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0×014 SubSystemData    : Ptr32 Void
   +0×018 ProcessHeap      : Ptr32 Void
   +0×01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0×020 FastPebLockRoutine : Ptr32 Void
   +0×024 FastPebUnlockRoutine : Ptr32 Void
   +0×028 EnvironmentUpdateCount : Uint4B
   +0×02c KernelCallbackTable : Ptr32 Void
   +0×030 SystemReserved   : [1] Uint4B
   +0×034 AtlThunkSListPtr32 : Uint4B
   +0×038 FreeList         : Ptr32 _PEB_FREE_BLOCK
   +0×03c TlsExpansionCounter : Uint4B
   +0×040 TlsBitmap        : Ptr32 Void
   +0×044 TlsBitmapBits    : [2] Uint4B
   +0×04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0×050 ReadOnlySharedMemoryHeap : Ptr32 Void
   +0×054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0×058 AnsiCodePageData : Ptr32 Void
   +0×05c OemCodePageData  : Ptr32 Void
   +0×060 UnicodeCaseTableData : Ptr32 Void
   +0×064 NumberOfProcessors : Uint4B
   +0×068 NtGlobalFlag     : Uint4B
   +0×070 CriticalSectionTimeout : _LARGE_INTEGER
   +0×078 HeapSegmentReserve : Uint4B
   +0×07c HeapSegmentCommit : Uint4B
   +0×080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0×084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0×088 NumberOfHeaps    : Uint4B
   +0×08c MaximumNumberOfHeaps : Uint4B
   +0×090 ProcessHeaps    
: Ptr32 Ptr32 Void
   +0×094 GdiSharedHandleTable : Ptr32 Void
   +0×098 ProcessStarterHelper : Ptr32 Void
   +0×09c GdiDCAttributeList : Uint4B
   +0×0a0 LoaderLock       : Ptr32 Void
   +0×0a4 OSMajorVersion   : Uint4B
   +0×0a8 OSMinorVersion   : Uint4B
   +0×0ac OSBuildNumber    : Uint2B
   +0×0ae OSCSDVersion     : Uint2B
   +0×0b0 OSPlatformId     : Uint4B
   +0×0b4 ImageSubsystem   : Uint4B
   +0×0b8 ImageSubsystemMajorVersion : Uint4B
   +0×0bc ImageSubsystemMinorVersion : Uint4B
   +0×0c0 ImageProcessAffinityMask : Uint4B
   +0×0c4 GdiHandleBuffer  : [34] Uint4B
   +0×14c PostProcessInitRoutine : Ptr32     void 
   +0×150 TlsExpansionBitmap : Ptr32 Void
   +0×154 TlsExpansionBitmapBits : [32] Uint4B
   +0×1d4 SessionId        : Uint4B
   +0×1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0×1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0×1e8 pShimData        : Ptr32 Void
   +0×1ec AppCompatInfo    : Ptr32 Void
   +0×1f0 CSDVersion       : _UNICODE_STRING
   +0×1f8 ActivationContextData : Ptr32 Void
   +0×1fc ProcessAssemblyStorageMap : Ptr32 Void
   +0×200 SystemDefaultActivationContextData : Ptr32 Void
   +0×204 SystemAssemblyStorageMap : Ptr32 Void
   +0×208 MinimumStackCommit : Uint4B
           这句话就是说把当前PEB的OSPlatformId放入eax中
 
           movzx   edx, word ptr [ecx+AC]
            +0×0ac OSBuildNumber    : Uint2B是2个字节,要放入edx则需要字节扩展。从word扩展到dword,高位填0.
            这句话就是说把当前PEB的OSBuildNumber放到edx中。

          xor          eax, FFFFFFFE
          PEB的OSPlatformId异或FFFFFFFE,存入eax中

          shl     eax, 0E
          eax寄存器逻辑左移14位
          or      eax, edx
          eax与edx或操作,然后保存到eax中
          shl     eax, 8
          eax寄存器逻辑左移8位
          or      eax, dword ptr [ecx+A8]
         eax或 OSMinorVersion,然后保存到eax
          shl       eax, 8
          eax寄存器逻辑左移8位
          or      eax, dword ptr [ecx+A4]
          eax或 OSMajorVersion,然后保存到eax
          retn
 
          返回值在eax中
 
 
 

哈哈,阳光总在风雨后

2009年12月13日

呵呵,生活终归还是美好的。

虽然造物弄人,但只要坚持,一般结果都还是好的。

我也比较服我自己。

我们又在一起了。

搂着自己很爱的女孩是一种幸福。

 

【天书笔记】C函数传参过程

2009年12月13日

 一.C函数调用与堆栈的关系

      C语言通过堆栈将参数传入函数内部

      push和pop的时候esp用于指向栈顶——栈顶总是栈中地址最小的位置。push则esp减少,pop则esp增加。

二.函数调用规则

      定义:函数调用规则是指调用者和被调用函数之间传递参数及返回参数的方法。

                  Windows上常用的有Pascal方式,WINAPI(_stdcall)方式以及C方式(_cdecl)。

      1._cdecl方式

                 参数入栈:参数从右到左依次入栈

                 清理方式:函数返回后,调用者负责清理堆栈。这种调用会生成较大的可执行程序。

      2._stdcall方式(WINAPI方式)

                 参数入栈:参数从右到左依次入栈

                 清理方式:被调用函数在返回前自行清理堆栈。生成代码比_cdecl方式小。

      3.Pascal方式

                 入栈方式:参数从左到右依次入栈

                 清理方式:被调用函数在返回前自行清理堆栈

                 不支持可变参数的函数调用

       此外Windows内核中还有常见的_fastcall快速调用;c++中有_thiscall方式。

       任何调用方式,最终返回值都是写入eax,然后返回。外部从eax中取得返回值。

三.技术细节 

     1. 标准C的_cdecl调用方式

                 调用者把参数反序压入堆栈中

                 调用函数

                 调用者把堆栈清理复原

    2._cdecl被调用函数

                  a.保存ebp。ebp总是被用来保存当前函数执行前的esp,执行完毕后我们用ebp回复esp。上层函数也用ebp作同样的事。所以先把ebp压入堆栈,返回之前弹出,避免ebp被我们改动。

                  b.保存esp到ebp

                               push     ebp                                      ;保存ebp

                               move     ebp,    esp                         ;把esp放入ebp     此时esp=ebp         都是这次函数调用时的栈顶

                  c.在堆栈中腾出一个区域用来保存局部变量。esp减去一个值,这样就等于压入了一堆变量。回复的时候只要把esp恢复成ebp中保存的值即可。

                  d.保存ebx,esi,edi到堆栈中,函数调用完了以后恢复。

                                sub       esp,         0cch                   ;esp向下移动一个范围,等同于在堆栈中放入一片新空间来存放局部变量

                                push     ebx                                      ;保存 ebx,esi,edi 三个寄存器      

                                push     esi

                                push     edi

                  e.局部变量区域初始化成全0xCCCCCCCC。0cch实际上是int 3指令的机器码,这是一个断点中断指令。因为局部变量不可能被执行,执行了必然有错,这时发生中断来提示开发者。这是VC编译debug版本的特有操作。

                                lea         edi,         [ebp-0cch]           ; 即 edi = ebp-0xcc

                                mov       ecx,         33h                       ;总长cch,每次stos操作时4h,所以操作次数=CCh/4h=33h

                                mov       eax,         0cccccccch          ;将被写入的内容  

                                rep stos dword ptr [edi]                    ;串写入。stos将eax数据写入edi同时edi加4,rep则是重复执行ecx次

                  f.做函数该做的事情ebp+12字节为第二个参数ebp+8为第一个参数,ebp+4是返回地址

                 &nbs
p;g.恢复ebx,esi,edi,esp,ebp

                                pop        edi                                        ;回复 edi, esi, ebx

                                pop        esi

                                pop        ebx

 

                                mov        esp,         ebp                        ;恢复上层函数原有的ebp和esp

                                pop        ebp

                                ret

四.实例

                void    myfunction(int a, int b)

                {

                           int   c = a+b; 

                }

  我自己用VS2005反汇编了一下

                void function(int a, int b)

                {

                          00411390  push        ebp                        ;保存上一级函数的ebp,作为返回地址

                          00411391  mov         ebp,esp                 ;保存当前栈顶

                          00411393  sub          esp,0CCh              ;esp  = esp – 0cch

                          00411399  push        ebx                         ;压栈ebx, esi, edi

                          0041139A  push        esi  

                          0041139B  push        edi  

                          0041139C  lea         edi,[ebp-0CCh]       ;edi = ebp-0cch

                          004113A2  mov         ecx,33h                    ;串指令执行次数

                          004113A7  mov         eax,0CCCCCCCCh  ; 串指令填充的内容

                          004113AC  rep stos    dword ptr es:[edi]    ;重复执行33h次,将eax写入edi并且edi加4 (ebp-0cch到ebp)

                         int c = a+b;

                         004113AE  mov         eax,dword ptr [a]          ; eax = a

                         004113B1  add         eax,dword ptr [b]           ;eax  += b

                         004113B4  mov         dword ptr [c],eax           ; c  = eax

               }

              004113B7  pop         edi                                            ; 恢复 edi,esi,ebx 

              004113B8  pop         esi  

              004113B9  pop         ebx  

                                                                                                      ;当前esp位于ebp-0cch

              004113BA  mov         esp,ebp                                   ;恢复esp到当前函数的栈顶ebp,即可跳过局部变量区

              004113BC  pop         ebp                                           ;恢复ebp为上一级函数ebp

              004113BD  ret              

>                

或许我就不该这么做,我想骂人,但不想骂你

2009年12月9日

 /////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

我受不了了

杯具

一切都用C++注释的方式来表示吧

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

你走了,这里剩下我一人。

我要去外面疯一圈

/////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////

我就不该容许自己有爱情。。。。。。。。。

 

 

这一刻,我告别了单身

2009年12月7日

好多年没有这样喜欢一个女孩,真的。

今天我们告别了单身,以至于我不知道改以怎样的方式来证实这一刻。

和一个自己喜欢的女孩在一起,这样的故事在我身上已经5年没有发生过了。

希望我的博客帮我记住这一刻。

希望我和她能走得远一些。

希望!!!


谢谢织织。

另外谢谢樊爽,李菲。。。。。

选择或者抉择

2009年12月5日

 我已承受不起失败,3岁不是很大的差别,但如果你不理解,或者你并不在意这一切,那么我的付出就是没有意义的。

生理决定心理,心理决定行为。我无意中说出了这么一句,或许她真的对我是有影响的。

我已无法不顾一切的付出,我必须核算我的付出风险。

我从你那里没有得到一丝丝信号,连个毛也没有。

我不得不重新考虑我该怎么做。我必须给自己留以余地。

理智必须压倒一切,虽然这很难。

我宁可不看你一眼,扭头落泪,继而狂奔。也不能葬送了我自己。

88,我要做我自己。自己的失败才是真的失败。

如果有一天你发现我,请你不要惊讶我这么快就做了决定。

要知道,作决定是很痛苦的,我也不例外。


我多么希望能和你一起看那轮圆圆的月亮从东方升起,晚霞照耀着西边“巨乳峰”。

我多么希望能和你一起周末走进影院,哪怕反复看那部《2012》。

我多么希望能和你一起在厨房做一些小东西吃,哪怕它并不好吃。


只要我喜欢,一切都可以。


也许有一天你会抱怨我的沉默。

也许有一天我们还会再遇见。

当然,也许你没有觉得什么不同。


在程序的世界里,没有织女。

我擅自闯入尘世,寻找千百度,可你却不属于人间。

也许上天注定不会有结果。


为自己惋惜。。。。。。

而我只能在这个真正属于我的小角落里记录下这一时刻。


我仍会为你付出

因为我的感觉还在

我只是不能不计风险

我多么希望你能够注意到我的一切

我多么希望你能融入我的世界


可是一切随缘吧

地上牛郎天上星

天上之女地上锦

接下来交给缘分吧

 

原来我的心还活着

2009年12月5日

        我越发的离不开你,看见你我就会很高兴,和你发短信我都会很踏实,和你做任何事,哪怕是逛街,我都情绪高涨。

        已经好多年没有遇到能让我有这种感觉的女孩了。我发自内心的高兴,既为了我遇见你,也为了我的心原来还活着。我不知道是不是在电影院里你激活了我,在或者第一次见面就真的为后面的情节埋下了伏笔。爱就是害怕失去,害怕失去现有的一切,我在自己身上找到了这种东西,我有些不敢和你说,我怕失去。我承受不起。

        你的眼神,你柔软的语调,你那老成的造型和小孩的言语之间的差异,没有一样不吸引着我。我真的掉进去了。谢谢你给我带来的一切,也许你并没有在意,但我很感激。

敏感很痛苦,敏感的人要看似不敏感那无疑更痛苦

2009年12月4日

    一颗敏感的心就像一个高精度传感器,能感知许多变化。

    

    处理这些变化很痛苦。要处理这些变化而不让别人知晓自己捕获到了这些变化,则无疑是更痛苦的。

    

    我已经很久没有捕获到这中变化了,但捕获到了又有什么用,我依然处理不了。

    

    我仅仅是我而已,能做的很有限,希望你能明白。

用LIST_ENTRY双向链表实现文件操作的监控 ,写于08年

2009年12月4日

 这两个星期天天都在搞这个,无数个错误被修正过后,做些笔记以备以后查阅。

        为了实现机密文档的保护,文档管理系统必须对所有打开的文件进行监控,因此我们的过滤驱动需要知道当前运行时刻有几个文件被打开,它们分别被打开了几次(同一个文件可能被多次打开)。

            用户态和内核态之间的关系

                         用户态调用CreateFile函数,子系统将请求发给IO管理器,IO管理器会用该函数的

                 参数生成一个IRP,将此IRP发往底层。过滤驱动截获到IRP,开始对其操作。过滤驱动

                 操作完了之后会继续往下发,就是调用更下层驱动程序。当下层所有操作完成之后,底

                 层的完成信息会向上反。这有点像函数的嵌套调用,从最内层函数开始一级一级的返回,

                 但这只是像。下面是过滤驱动截获返回的CreateFile时的操作。

处理IRP_MJ_CREATE的核心代码():

         {
    //返回的FsContext为空时,不予以处理
    if(FileObject->FsContext == NULL)
    {
     ////////////////////////////////////////DbgPrint("\n\n\n SfCreate: FileObject->FsContext 为 NULL\n\n\n");
     status = Irp->IoStatus.Status;

     DbgPrint("\n SfCreate() : 建立链表的第一个节点时遭遇 FsContext 为 NULL   \n",status);

     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     return status;
    }

      //第一次打开链表时,链表为空,上面for语句无法运行

    if(IsListEmpty(&my_list_head))
    {
     PFILE_CONTEXT FileCtx = (PFILE_CONTEXT)ExAllocatePoolWithTag(PagedPool,sizeof(FILE_CONTEXT),SFLT_POOL_TAG);
     if(FileCtx == NULL)
      return STATUS_INSUFFICIENT_RESOURCES;

     FileCtx->FsContext = FileObject->FsContext;
     FileCtx->RefCount = 1;

     ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);//多线程安全插入方式

     DbgPrint("\n\n SfCreate() : 创建链表的第一个节点:\n ");
     DbgPrint("SfCreate() : 第一个节点中 FileCtx->FsContext: %x , FileCtx->RefCount: %u \n\n\n",FileCtx->FsContext,FileCtx->RefCount);

     status = Irp->IoStatus.Status;
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     return status;
    }

    //搜索链表,查看文件是否已经打开(链表非空时,此搜索才可以进行)
    for(p=my_list_head.Flink; p!=&my_list_head; p=p->Flink)
    {
     PFILE_CONTEXT Elem = CONTAINING_RECORD(p,FILE_CONTEXT,ListEntry);

     //文件已打开,计数器加一
     if(Elem->FsContext == FileObject->FsContext)
     {
      KIRQL irql;
     
      DbgPrint("\n\n\nSfCreate() : 遇到已打开过的文件 , 打印Elem节点信息如下 :\n ListEntry: %s, FsContext: %x, RefCount: %u\n",Elem->ListEntry,Elem->FsContext,Elem->RefCount);

      if( FileObject->FsContext == NULL)
       DbgPrint( "SfCreate() : FileObject->FsContext 为 空");

      KeAcquireSpinLock(&my_list_lock,&irql);          
      Elem->RefCount += 1;    
      KeReleaseSpinLock(&my_list_lock,irql);

      ////////////////////////////////////////DbgPrint("\n文件第 %u 次打开\n",Elem->RefCount);

      status = Irp->IoStatus.Status;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
      return status;
     } 
    }


    //文件未打开,填充节点并插入链表

    { 
     //节点内存分配
     PFILE_CONTEXT FileCtx = (PFILE_CONTEXT)ExAllocatePoolWithTag(PagedPool,sizeof(FILE_CONTEXT),SFLT_POOL_TAG);
     if(FileCtx == NULL)
     {
      DbgPrint("\n\n SfCreate() : 文件第一次打开时,内存分配失败");
      return STATUS_INSUFFICIENT_RESOURCES;
     }

     //节点内容填充
     FileCtx->FsContext = FileObject->FsContext;
     FileCtx->RefCount = 1;

     //插入节点到链表
     ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);//多线程安全插入方式
    }
   }      

大体思路说明:

        获取文件对象,确认它是对一个文件的操作(不是就就忽略,直接返回)。然后获取其FsContext,这是文件的全局唯一标识,一个文件不管打开多少次,它只有一个FsContext,不同文件的FsContext也不一样。如果链表为空,直接用这个FsContext填充数据结构,创立一个节点,然后插入链表,之后返回。如果链表不为空,则遍历搜索链表,如果查找到FsContext值一样的节点则说明该文件打开过,只需将该节点数据结构中的RefCount++。如果链表中没有搜索到相应节点,那么就要新建一个节点插入链表。

         既然监控就不能只监控打开的,自然也要监控关闭的。

         当关闭一个文件窗口的时候,IO管理器会发送一个IRP_MJ_CLEANUP。在处理这个消息的时候,我们需要做的仅仅是从链表中找到那个文件,将其RefCount–。当RefCount值为0的时候,IO管理器就会发送一个IRP_MJ_CLOSE,截获到这个消息,我们就可以将这个文件对应的节点从链表中删除了。

        OK!That’s all.

关键用法

    A.链表的遍历搜索

                   if(IsListEmpty(&my_list_head))       {    插入第一个节点 返回     }

                   for(p=my_list_head.Flink; p!=&my_list_head; p=p->Flink)    

                  从循环体内推出说明没找到,这就需要我们新建节点了

 

                  关键是要优先处理空链表的情况

    B.链表的插入删除操作

                      ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);

                      删除LST_ENTRY p所指向的节点,第一个参数需要用前一个节点的LIST_ENTRY

                       ExInterlockedRemoveHeadList(p->Blink,&my_list_lock);

    C.IRQL锁与互斥体

                     KeAcquireSpinLock(&my_list_lock,&Irql);
                     Elem->RefCount -= 1;
                     KeReleaseSpinLock(&my_list_lock,Irql);

                     它通过提高IRQL来使其他线程无法执行,解决同步问题

                       ExAcquireFastMutex( &gSfilterAttachLock );                                       

                      ExReleaseFastMutex( &gSfilterAttachLock );

                      这个要相对轻量级