1.LoadImageNotifyRoutine
一个dll的加载过程是这样的
我们以LoadLibraryA("1.dll")为例
LoadLibraryA
--LoadLibraryExA
--LoadLibraryExW
--BasepLoadLibraryAsDataFile
--MapViewOfFileEx
--NtMapViewOfSection
--MmMapViewOfSection
--MiMapViewOfImageSection

PAGE:004A6869                 mov     esi, [esi]
PAGE:004A686B                 shl     esi, 0Ch
PAGE:004A686E                cmp     ds:_PsImageNotifyEnabled, 0
PAGE:004A6875                 mov     [ebp+var_4], esi
PAGE:004A6878                 jnz     loc_51AD80

判断PsImageNotifyEnabled
PAGEDATA:005B7B38 _PsImageNotifyEnabled db 0   
      if ( PsImageNotifyEnabled )
      {
            PsCallImageNotifyRoutines(v49, v38, &v56);
      }
PsCallImageNotifyRoutines将会依次执行PspLoadImageNotifyRoutine指向的
notifyroutine

清除方法
1: 将PsImageNotifyEnabled=0
优点,比较方便,但是PsImageNotifyEnabled是非导出变量
只能通过MiMapViewOfImageSection或者DbgkCreateThread定位
但是上面2个函数仍然是非导出函数,如果做安全工具的话可以使用PDB解析大法
不然的话嘛...

2:PsRemoveLoadImageNotifyRoutine
标准卸载函数,但是仍然存在缺点,需要知道notifyroutine函数地址,
以及存在2000系统不能解析PsRemoveLoadImageNotifyRoutine的问题

3.自己清除PspLoadImageNotifyRoutine指向的内存地址
.data:0048A480 _PspLoadImageNotifyRoutine db    0      ; DATA XREF: PsSetLoadImageNotifyRoutine(x)+20
.data:0048A480                                         ; PsRemoveLoadImageNotifyRoutine(x)+Ao ...
.data:0048A481                 db    0
.data:0048A482                 db    0

定位可以通过导出的函数PsSetLoadImageNotifyRoutine
lkd> u PsSetLoadImageNotifyRoutine 
nt!PsSetLoadImageNotifyRoutine:
8062e97b 8bff            mov     edi,edi
8062e97d 55              push    ebp
8062e97e 8bec            mov     ebp,esp
8062e980 53              push    ebx
8062e981 57              push    edi
8062e982 33ff            xor     edi,edi
8062e984 57              push    edi
8062e985 ff7508          push    dword ptr [ebp+8]
8062e988 e8437a0100      call    nt!ExAllocateCallBack (806463d0)
8062e98d 8bd8            mov     ebx,eax
8062e98f 3bdf            cmp     ebx,edi
8062e991 7507            jne     nt!PsSetLoadImageNotifyRoutine+0x1f (8062e99a)
8062e993 b89a0000c0      mov     eax,0C000009Ah
8062e998 eb2a            jmp     nt!PsSetLoadImageNotifyRoutine+0x49 (8062e9c4)
8062e99a 56              push    esi
8062e99b be80245680      mov     esi,offset nt!PspLoadImageNotifyRoutine (80562480)
8062e9a0 6a00            push    0
8062e9a2 53              push    ebx
8062e9a3 56              push    esi

另外也可以使用PsRemoveLoadImageNotifyRoutine定位
lkd> u PsRemoveLoadImageNotifyRoutine
nt!PsRemoveLoadImageNotifyRoutine:
8062e9ec 8bff            mov     edi,edi
8062e9ee 55              push    ebp
8062e9ef 8bec            mov     ebp,esp
8062e9f1 53              push    ebx
8062e9f2 56              push    esi
8062e9f3 57              push    edi
8062e9f4 33db            xor     ebx,ebx
8062e9f6 bf80245680      mov     edi,offset nt!PspLoadImageNotifyRoutine (80562480)
8062e9fb 57              push    edi
8062e9fc e8df7a0100      call    nt!ExReferenceCallBackBlock (806464e0)
8062ea01 8bf0            mov     esi,eax
8062ea03 85f6            test    esi,esi

相对而言 PsRemoveLoadImageNotifyRoutine的距离更加接近
不过存在2000系统不能解析PsRemoveLoadImageNotifyRoutine的问题

4.patch MiMapViewOfImageSection
更好的办法是patch PsCallImageNotifyRoutines
缺点同样是函数不被导出,存在定位困难的问题

在偶的机器上面
lkd> dd PspLoadImageNotifyRoutine
80562480  e100c2cf e100e0c7 e15e28ff 00000000
80562490  00000000 00000000 00000000 00000000



2.CreateThreadNotifyRoutine
CreateThread
--NtCreateThread
--PspCreateThread

PAGE:00523434                 mov     [ebp+var_58], offset _PspCreateThreadNotifyRoutine
PAGE:0052343B                 mov     [ebp+var_84], 8
PAGE:00523445
PAGE:00523445 loc_523445:                             ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+6C379j
PAGE:00523445                 push    [ebp+var_58]
PAGE:00523448                 call    _ExReferenceCallBackBlock@4 ; ExReferenceCallBackBlock(x)
PAGE:0052344D                 mov     [ebp+var_78], eax
PAGE:00523450                 cmp     eax, edi
PAGE:00523452                 jz      short loc_523475
PAGE:00523454                 push    eax
PAGE:00523455                 call    _ExGetCallBackBlockRoutine@4 ; ExGetCallBackBlockRoutine(x)
PAGE:0052345A                 push    1
PAGE:0052345C                 push    dword ptr [esi+1F0h]
PAGE:00523462                 push    dword ptr [esi+1ECh]
PAGE:00523468                 call    eax


在PspCreateThread里面执行PspCreateThreadNotifyRoutine指向的notifyroutine
偶没找到CreateThread的enabled变量,
那么看起来只有3种方法使用了

lkd> dd PspCreateThreadNotifyRoutine
805624a0  e192e317 00000000 00000000 00000000
805624b0  00000000 00000000 00000000 00000000


3.CreateProcessNotifyRoutine几乎和CreateThreadNotifyRoutine完全一样
PAGE:00523372                 mov     [ebp+var_54], offset _PspCreateProcessNotifyRoutine
PAGE:00523379                 mov     [ebp+var_7C], 8
PAGE:00523380
PAGE:00523380 loc_523380:                             ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x)+6C2AEj
PAGE:00523380                 push    [ebp+var_54]
PAGE:00523383                 call    _ExReferenceCallBackBlock@4 ; ExReferenceCallBackBlock(x)
PAGE:00523388                 mov     edi, eax
PAGE:0052338A                 test    edi, edi
PAGE:0052338C                 jz      short loc_5233AD
PAGE:0052338E                 push    edi
PAGE:0052338F                 call    _ExGetCallBackBlockRoutine@4 ; ExGetCallBackBlockRoutine(x)
PAGE:00523394                 push    1
PAGE:00523396                 push    dword ptr [ebx+84h]
PAGE:0052339C                 push    dword ptr [ebx+14Ch]
PAGE:005233A2                 call    eax

lkd> dd PspCreateProcessNotifyRoutine
805624e0  e15df187 e192e32f 00000000 00000000
805624f0  00000000 00000000 00000000 00000000