前几天去当面膜拜了女王大牛,果然****,还让我等了一个来小时。。。

在xp中ntdll.dll的调用都是通过sysenter切换到内核的,sysenter调用了KiFastCallEntry,而KiFastCallEntry函数则是在ssdt表找到相应的
函数地址,然后call,所以只要伪造一张原始的ssdt表,并且欺骗过KiFastCallEntry函数,这样ssdt hook就无效了。。。
先看KiFastCallEntry函数: 

代码:
nt!KiFastCallEntry:
80541790 b923000000      mov     ecx,23h
80541795 6a30            push    30h
80541797 0fa1            pop     fs
80541799 8ed9            mov     ds,cx
8054179b 8ec1            mov     es,cx
8054179d 648b0d40000000  mov     ecx,dword ptr fs:[40h] 
805417a4 8b6104          mov     esp,dword ptr [ecx+4]  
805417a7 6a23            push    23h
805417a9 52              push    edx  
805417aa 9c              pushfd
805417ab 6a02            push    2
805417ad 83c208          add     edx,8 
805417b0 9d              popfd
805417b1 804c240102      or      byte ptr [esp+1],2
805417b6 6a1b            push    1Bh
805417b8 ff350403dfff    push    dword ptr ds:[0FFDF0304h]
805417be 6a00            push    0
805417c0 55              push    ebp
805417c1 53              push    ebx
805417c2 56              push    esi
805417c3 57              push    edi
//ebx=_KPCR.SelfPcr
805417c4 648b1d1c000000  mov     ebx,dword ptr fs:[1Ch] 
805417cb 6a3b            push    3Bh
//esi=_KPCR.PrcbData.CurrentThread
805417cd 8bb324010000    mov     esi,dword ptr [ebx+124h]  
805417d3 ff33            push    dword ptr [ebx]
805417d5 c703ffffffff    mov     dword ptr [ebx],0FFFFFFFFh   
805417db 8b6e18          mov     ebp,dword ptr [esi+18h] 
805417de 6a01            push    1
805417e0 83ec48          sub     esp,48h
805417e3 81ed9c020000    sub     ebp,29Ch
805417e9 c6864001000001  mov     byte ptr [esi+140h],1
805417f0 3bec            cmp     ebp,esp
805417f2 758d            jne     nt!KiFastCallEntry2+0x49 (80541781)
805417f4 83652c00        and     dword ptr [ebp+2Ch],0
805417f8 f6462cff        test    byte ptr [esi+2Ch],0FFh
805417fc 89ae34010000    mov     dword ptr [esi+134h],ebp
80541802 0f8538feffff    jne     nt!Dr_FastCallDrSave (80541640)
80541808 8b5d60          mov     ebx,dword ptr [ebp+60h]
8054180b 8b7d68          mov     edi,dword ptr [ebp+68h]
8054180e 89550c          mov     dword ptr [ebp+0Ch],edx
80541811 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
80541818 895d00          mov     dword ptr [ebp],ebx
8054181b 897d04          mov     dword ptr [ebp+4],edi
8054181e fb              sti
//eax为函数序号,但是在KeServiceDescriptorTableShadow时是函数序号+1000h,
//所以在KeServiceDescriptorTableShadow经过下面运算ecx最终为10h,否则为0
8054181f 8bf8            mov     edi,eax
80541821 c1ef08          shr     edi,8
80541824 83e730          and     edi,30h
80541827 8bcf            mov     ecx,edi
//KTHREAD.ServiceTable gdi32.dll和user32.dll调用时,
//ServiceTable为KeServiceDescriptorTableShadow,
//ntdll.dll调用时ServiceTable为KeServiceDescriptorTable
//在KeServiceDescriptorTableShadow时+10正好得到win32.sys调用
80541829 03bee0000000    add     edi,dword ptr [esi+0E0h]   
8054182f 8bd8            mov     ebx,eax  
//保留三个字节,得到正真的序号
80541831 25ff0f0000      and     eax,0FFFh
//和ssdt或shadow ssdt总共项数比较,eax大则跳
80541836 3b4708          cmp     eax,dword ptr [edi+8] 
80541839 0f8333fdffff    jae     nt!KiBBTUnexpectedRange (80541572)
//判断是shadow ssdt还是ssdt,不跳则是shadow ssdt,跳则是ssdt
8054183f 83f910          cmp     ecx,10h 
80541842 751b            jne     nt!KiFastCallEntry+0xcf (8054185f)  
80541844 648b0d18000000  mov     ecx,dword ptr fs:[18h]
8054184b 33db            xor     ebx,ebx
8054184d 0b99700f0000    or      ebx,dword ptr [ecx+0F70h]
80541853 740a            je      nt!KiFastCallEntry+0xcf (8054185f)
80541855 52              push    edx
80541856 50              push    eax
80541857 ff1528c75580    call    dword ptr [nt!KeGdiFlushUserBatch (8055c728)]
8054185d 58              pop     eax
8054185e 5a              pop     edx
8054185f 64ff0538060000  inc     dword ptr fs:[638h]
80541866 8bf2            mov     esi,edx
80541868 8b5f0c          mov     ebx,dword ptr [edi+0Ch]
8054186b 33c9            xor     ecx,ecx
8054186d 8a0c18          mov     cl,byte ptr [eax+ebx]
//edi=KeServiceDescriptorTable->ServiceTableBase
//在这里patch 把edi指向构造的ssdt
80541870 8b3f            mov     edi,dword ptr [edi]  
//得到真实地址,edi为KiServiceTable,eax为函数序号  
80541872 8b1c87          mov     ebx,dword ptr [edi+eax*4] 
80541875 2be1            sub     esp,ecx
80541878 c1e902          shr     ecx,2
8054187a 8bfc            mov     edi,esp
8054187c 3b3574f73ff4    cmp     esi,dword ptr ds:[0F43FF774h]
80541882 0f83a8010000    jae     nt!KiSystemCallExit2+0x9f (80541a30)
//把参数复制到内核
80541888 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
//调用函数
8054188a ffd3            call    ebx
8054188c 8be5            mov     esp,ebp
8054188e 648b0d24010000  mov     ecx,dword ptr fs:[124h]
80541895 8b553c          mov     edx,dword ptr [ebp+3Ch]
80541898 899134010000    mov     dword ptr [ecx+134h],edx
KiFastCallEntry函数先通过fs寄存器的_KPCR得到了当前线程的ETHREAD,然后通过ETHREAD得到ServiceTable
这个就是main SDT或者是shadow sdt,在shadow sdt中要保留24位得到函数序号,之后根据main sdt或者shadow sdt
和函数序号得到函数地址,最后call这个函数。

所以可以在 
代码:
80541868 8b5f0c          mov     ebx,dword ptr [edi+0Ch]
8054186b 33c9            xor     ecx,ecx
8054186d 8a0c18          mov     cl,byte ptr [eax+ebx]
//edi=KeServiceDescriptorTable->ServiceTableBase
//在这里patch 把edi指向构造的ssdt
80541870 8b3f            mov     edi,dword ptr [edi]  
这里inline hook

完整代码如下:
代码:
#include "kernelfun.h"
#include <ntimage.h>

#define SEC_IMAGE    0x01000000
typedef struct {
        WORD    offset:12;
        WORD    type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;


DWORD *oldssdt;
DWORD patchAddress;
DWORD nowSsdt;
PVOID lpRet;

//得到系统内核模块基址
DWORD FoundSystemModule(BOOL bKernel,char *sysFileName)
{
        DWORD    dwNeededSize,rc;
        PMODULES    pModules=(PMODULES)&pModules;
        PCHAR    pKernelName;
        DWORD kernelBase;
        DWORD i;

        rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,4,&dwNeededSize);
        if (rc==STATUS_INFO_LENGTH_MISMATCH) 
        {
                pModules=(MODULES *)ExAllocatePool(PagedPool,dwNeededSize);
                rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,dwNeededSize,NULL);
                if (!NT_SUCCESS(rc))
                {
                        DbgPrint("ZwQuerySystemInformation failed");
                        return 0;
                }
        } 
        else
        {
                DbgPrint("ZwQuerySystemInformation failed");
                return 0;

        }

        if(bKernel)
        {
                pKernelName=pModules->smi[0].ModuleNameOffset+pModules->smi[0].ImageName;
                strcpy(sysFileName,pKernelName);
                kernelBase=(DWORD)pModules->smi[0].Base;
                return kernelBase;
        }
        for (i=0;(pModules->dwNumberOfModules)>i;i++)
        {
                pKernelName=pModules->smi[i].ModuleNameOffset+pModules->smi[i].ImageName;
                if(_stricmp(pKernelName,sysFileName)==0)
                {
                        kernelBase=(DWORD)pModules->smi[i].Base;
                        return kernelBase;
                }
        }

        return 0;
}


//得到并保存原始的ssdt
BOOL SaveOldSddt(PVOID hModule,DWORD dwKSDT,DWORD dwKernelBase)
{

        PDWORD    pService;
        DWORD dwKiServiceTable;

        IMAGE_DOS_HEADER *dosHeader;
        IMAGE_NT_HEADERS *ntHeader;
        PIMAGE_BASE_RELOCATION    pbr;
        PIMAGE_FIXUP_ENTRY    pfe;  
        DWORD dwPointer;
        DWORD point;
        BOOL    bFirstChunk=TRUE;
        DWORD i;
        dosHeader=(IMAGE_DOS_HEADER*)hModule;



        ntHeader=(IMAGE_NT_HEADERS*)((DWORD)hModule+dosHeader->e_lfanew);

        pbr=(PIMAGE_BASE_RELOCATION )(ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress+(DWORD)hModule);
        pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));
        while(bFirstChunk||pbr->VirtualAddress)
        {
                bFirstChunk=FALSE;

                for (i=0;((pbr->SizeOfBlock-8)/2)>i;i++)
                {
                        if(pfe->type==IMAGE_REL_BASED_HIGHLOW)
                        {
                                dwPointer=pbr->VirtualAddress+pfe->offset;

                                point=*(DWORD*)(dwPointer+(DWORD)hModule)-(DWORD)ntHeader->OptionalHeader.ImageBase;
                                if(point==dwKSDT)
                                {

                                        if(*(USHORT*)(dwPointer+(DWORD)hModule-2)==0x05c7)
                                        {
                                                dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointer+4)-ntHeader->OptionalHeader.ImageBase;
                                                i=0;
                                                for (pService=(PDWORD)((DWORD)hModule+dwKiServiceTable);
                                                        *pService-ntHeader->OptionalHeader.ImageBase<ntHeader->OptionalHeader.ImageBase;
                                                        pService++,i++)
                                                {
                                                        oldssdt[i]=*pService-ntHeader->OptionalHeader.ImageBase+dwKernelBase;

                                                }

                                        }



                                }
                        }
                        pfe++;

                }
                pbr=(PIMAGE_BASE_RELOCATION)((DWORD)pbr+pbr->SizeOfBlock);
                pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));

        }

        return TRUE;
}

//计算mov [rem],rem的[rem]
DWORD GetFlag(PVOID lpBase)
{
        IMAGE_DOS_HEADER *dosHeader;
        IMAGE_NT_HEADERS *ntHeader;
        IMAGE_EXPORT_DIRECTORY* exportTable;
        DWORD* pfunctionAddresses;
        DWORD* pfunctionNames;
        WORD* pfunctionOrdinals;
        DWORD functionOrdinal;
        DWORD Base, i, functionAddress;
        DWORD dwKSDT;
        char* functionName;

        dosHeader=(IMAGE_DOS_HEADER*)lpBase;
        ntHeader=(IMAGE_NT_HEADERS*)((DWORD)lpBase+dosHeader->e_lfanew);
        exportTable=(IMAGE_EXPORT_DIRECTORY*)((DWORD)lpBase+ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

        pfunctionAddresses=(DWORD*)((DWORD)lpBase+exportTable->AddressOfFunctions);
        pfunctionNames=(DWORD*)((DWORD)lpBase+exportTable->AddressOfNames);
        pfunctionOrdinals=(WORD*)((DWORD)lpBase+exportTable->AddressOfNameOrdinals);
        Base=exportTable->Base;
        for(i=0;i<exportTable->NumberOfFunctions;i++)
        {
                functionName=(char*)((DWORD)lpBase+pfunctionNames[i]);
                functionOrdinal=pfunctionOrdinals[i]+Base-1;
                functionAddress = (DWORD)( (DWORD)lpBase + pfunctionAddresses[functionOrdinal]);
                if(_stricmp(functionName,"KeServiceDescriptorTable")==0)
                {
                        dwKSDT=functionAddress-(DWORD)lpBase;
                        return dwKSDT;
                }

        }
        return 0;

}


//映射文件进内存得到ssdt并保存起来
BOOL GetOldSsdt(PUNICODE_STRING kernelFileName,DWORD kernelBase) 
{
        NTSTATUS status;
        HANDLE  hSection, hFile;
        DWORD dwKSDT;
        PVOID BaseAddress = NULL;
        SIZE_T size=0;
        IO_STATUS_BLOCK iosb;
        OBJECT_ATTRIBUTES oa = {sizeof oa, 0, kernelFileName, OBJ_CASE_INSENSITIVE};
        status=ZwOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
        if(!NT_SUCCESS(status))
        {
                DbgPrint("ZwOpenFile failed\n");
                return FALSE;
        }
        oa.ObjectName = 0;

        status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,PAGE_EXECUTE, SEC_IMAGE, hFile);
        if(!NT_SUCCESS(status))
        {
                DbgPrint("ZwCreateSection failed\n");
                return FALSE;
        }

        status=ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 1000, 0, &size, (SECTION_INHERIT)1, MEM_TOP_DOWN, PAGE_READWRITE); 
        if(!NT_SUCCESS(status))
        {
                DbgPrint("ZwMapViewOfSection failed\n");
                return FALSE;
        }
        ZwClose(hFile);
        dwKSDT=GetFlag(BaseAddress);
        if(dwKSDT==0)
        {
                DbgPrint("GetFlag failed\n");
                return FALSE;
        }
        DbgPrint("dwKSDT:%x\n",dwKSDT);
        if(!SaveOldSddt(BaseAddress,dwKSDT,kernelBase))
        {
                DbgPrint("GetOldSddt failed\n");
                return FALSE;
        }

        ZwClose(hSection);
        return TRUE;
}

//构建新的Ssdt
BOOL SetNewSsdt()
{
        UNICODE_STRING kernelFileName;
        char systemFile[80];
        DWORD kernelBase;
        int count;

        kernelBase=FoundSystemModule(TRUE,systemFile);
        if(kernelBase==0)
        {
                DbgPrint("get kernel base failed\n");
                return FALSE;
        } 
        if(_stricmp(systemFile,"ntkrnlpa.exe")==0)
        {
                RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntkrnlpa.exe");
        }
        else if(_stricmp(systemFile,"ntoskrnl.exe")==0)
        {
                RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntoskrnl.exe");

        }
        else
        {
                return FALSE;
        }
        count=KeServiceDescriptorTable->NumberOfServices;
        nowSsdt=(DWORD)KeServiceDescriptorTable->ServiceTableBase;
        oldssdt=(DWORD*)ExAllocatePool(NonPagedPool,count*4);
        if(oldssdt==NULL)
        {
                DbgPrint("allocate memory failed\n");
                return FALSE;
        }
        KdPrint(("oldssdt:%X\n",oldssdt));

        if(!GetOldSsdt(&kernelFileName,kernelBase))
        {
                DbgPrint("save ssdt failed\n");
                return FALSE;
        }
        return TRUE;


}


__declspec(naked)void UseOldSsdt()
{
        _asm
        {
                pushfd ;
                pushad ;
                mov  eax,nowSsdt;
                cmp eax,[edi];
                jnz shadow;//判断是不是shadow ssdt
                popad;
                popfd;
                mov     cl,byte ptr [eax+ebx];
                mov     edi,oldssdt;
                jmp [lpRet];        
shadow:
                popad;
                popfd;
                mov     cl,byte ptr [eax+ebx];
                mov     edi,dword ptr [edi];
                jmp [lpRet];

        }
        

};




BOOL HookKiFastCallEntry(BOOL bHook)
{
        BYTE jmpCode[5]={0xe9,0x00,0x00,0x00,0x00};
        BYTE oldCode[5]={0x8a,0x0c,0x18,0x8b,0x3f};
        BYTE *functionAddress;
        int i;
        if(bHook)
        {
                _asm
                {
                        pushad;
                        mov ecx, 0x176;
                        rdmsr;
                        mov functionAddress, eax;
                        popad;
                }
                for(i=0;i<264;i++)
                {
                        if(memcmp(&functionAddress[i],oldCode,5)==0)
                        {
                                patchAddress=(DWORD)&functionAddress[i];
                                KdPrint(("patch address:%X\n",patchAddress));
                                break;
                        }
                }
                if(i==264)
                        return FALSE;
                *(DWORD*)&jmpCode[1]=(DWORD)UseOldSsdt-(patchAddress+5);
                lpRet=(PVOID)(patchAddress+5);
                _asm
                {
                        CLI  ;                 
                        MOV    EAX, CR0  ;    
                        AND EAX, NOT 10000H ;
                        MOV    CR0, EAX;        
                }
                memcpy((PVOID)patchAddress,jmpCode,5);
                _asm 
                {
                        MOV    EAX, CR0;        
                        OR    EAX, 10000H;            
                        MOV    CR0, EAX ;              
                        STI;                    
                }

        }
        else
        {
                _asm
                {
                        CLI  ;                 
                        MOV    EAX, CR0  ;    
                        AND EAX, NOT 10000H ;
                        MOV    CR0, EAX;        
                }
                memcpy((PVOID)patchAddress,oldCode,5);
                _asm 
                {
                        MOV    EAX, CR0;        
                        OR    EAX, 10000H;            
                        MOV    CR0, EAX ;              
                        STI;                    
                }

        }
        return TRUE;




}


VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{    
        HookKiFastCallEntry(FALSE);
        DbgPrint("ROOTKIT: OnUnload called\n");
}


NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
        theDriverObject->DriverUnload = OnUnload;
        if(SetNewSsdt())
        {
                HookKiFastCallEntry(TRUE);

        }
        

        return STATUS_SUCCESS;
}
假如要anit shadow ssdt hook 只要同时hook KiFastCallEntry和KiSystemService俩函数就可以了。。。

又度过了一个没有情人的情人节了。。。。