2015年3月13日星期五

遍历32位系统的SSDT函数

#include <ntifs.h>
#include <windef.h>


/*
好久不玩SSDT了,因为微软不建议,甚至抵触,特别是在64位系统上。
但是有时候,知道原理对于分析问题和解决功能还是有好处的,

所以有此文。

SSDT再分析。

首先这是系统的一个机制。

其次是32位的系统导出了一个变量。
0: kd> x nt!_KeServiceDescriptorTable
8055d700          nt!KeServiceDescriptorTable = <no type information>

这个变量是啥类型呢?
REGMON的一个版本的代码,声明如下:
typedef struct _SRVTABLE {
PVOID           *ServiceTable;
ULONG           LowCall;      
ULONG           HiCall;
PVOID    *ArgTable;
} SRVTABLE, *PSRVTABLE;

不过我们可以这样看:
0: kd> dps 8055d700 L 8
8055d700  80505570 nt!KiServiceTable
8055d704  00000000
8055d708  0000011c
8055d70c  805059e4 nt!KiArgumentTable
8055d710  00000000
8055d714  00000000
8055d718  00000000
8055d71c  00000000

再下就是这样看:
0: kd> dps 80505570 L 11c
80505570  805a5664 nt!NtAcceptConnectPort
80505574  805f23ea nt!NtAccessCheck
80505578  805f5c20 nt!NtAccessCheckAndAuditAlarm
8050557c  805f241c nt!NtAccessCheckByType
80505580  805f5c5a nt!NtAccessCheckByTypeAndAuditAlarm
80505584  805f2452 nt!NtAccessCheckByTypeResultList
80505588  805f5c9e nt!NtAccessCheckByTypeResultListAndAuditAlarm
8050558c  805f5ce2 nt!NtAccessCheckByTypeResultListAndAuditAlarmByHandle
80505590  80616e80 nt!NtAddAtom
...

再下就是本文了。

功能:枚举SSDT。有的是分析PE文件的,觉得不太好。

made by correy
made at 2014.11.22


再看看一个宏:
#define SYSCALL_INDEX(_Function) (*(PULONG)((PUCHAR)_Function+1))
这是求一个函数的INDEX,输入是函数名/函数地址,不过这个函数特殊必须是SSDT。
0: kd> u nt!ZwQuerySystemInformation
nt!ZwQuerySystemInformation:
80501b0c b8ad000000      mov     eax,0ADh
80501b11 8d542404        lea     edx,[esp+4]
80501b15 9c              pushfd
80501b16 6a08            push    8
80501b18 e8f40a0400      call    nt!KiSystemService (80542611)
80501b1d c21000          ret     10h
nt!ZwQuerySystemTime:
80501b20 b8ae000000      mov     eax,0AEh
80501b25 8d542404        lea     edx,[esp+4]
也就是说函数的地址前移一个字节就是函数INDEX。这个宏的定义是一样的。

made at 2015.03.13
*/


#define TAG 'tset' //test


#if defined(_WIN64)
//啥也没有。
#else
//这里的内容摘自:REGMON的某个版本的代码。


// Definition for system call service table
typedef struct _SRVTABLE {
PVOID           *ServiceTable;
ULONG           LowCall;      
ULONG           HiCall;
PVOID    *ArgTable;
} SRVTABLE, *PSRVTABLE;


extern PSRVTABLE KeServiceDescriptorTable;// Pointer to the image of the system service table

// Macro for easy hook/unhook.
// On X86 implementations of Zw* functions, the DWORD following the first byte is the system call number, so we reach into the Zw function passed as a parameter, and pull the number out.
// This makes system call hooking dependent ONLY on the Zw* function implementation not changing.
//#if defined(_ALPHA_)
//#define SYSCALL(_function)  KeServiceDescriptorTable->ServiceTable[ (*(PULONG)_function)  & 0x0000FFFF ]
//#else
//#define SYSCALL(_function)  KeServiceDescriptorTable->ServiceTable[ *(PULONG)((PUCHAR)_function+1)]
//#endif

#endif


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
 
 
}


VOID enumerate_SSDT()
{
    ULONG i = 0;

    for ( ; i < KeServiceDescriptorTable->HiCall ;i++ )
    {
        KdPrint(("第%d个函数的地址是:0x%p.\n", i, KeServiceDescriptorTable->ServiceTable[i]));

        /*
        这里要做的还有更多,如参数的个数,函数的名字,函数所在的模块(区分是不是被劫持)。
        根据函数的名字找到ID等。
        */
    }
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    enumerate_SSDT();

    return 0;
}


-------------------------------------------------------------------------------------------------------------------------

#include <ntifs.h>
#include <windef.h>
#include <Aux_klib.h> //source里面要有TARGETLIBS=$(DDK_LIB_PATH)\Aux_klib.lib

/*
made by correy
made at 2015.04.11
*/

#define TAG  'tset' //test
#pragma pack(1)
typedef struct ServiceDescriptorEntry{
    unsigned int *ServiceTableBase;
    unsigned int *ServiceCounterTableBase; //Used only in checked build
    unsigned int NumberOfServices;
    unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
extern __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
#define SYSCALL_INDEX(_Function) (*(PULONG)((PUCHAR)_Function+1)) //获取SSDT函数的ID,X64没有测试和观察。
#define SYSTEMSERVICE(index) KeServiceDescriptorTable.ServiceTableBase[index]

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
}

PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);

PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    USHORT OrdinalNumber;
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    PULONG Addr;
    LONG High;
    LONG Low;
    LONG Middle;
    LONG Result;
    ULONG ExportSize;
    PVOID FunctionAddress = 0;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    PAGED_CODE();
    __try
    {
        FunctionAddress = *(PVOID *)DllBase;
        FunctionAddress = 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FunctionAddress;
    } 
    //确保DllBase可以访问。否则蓝屏。
    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return NULL;
    }
   
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.   
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.   
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;
    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {       
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }
    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }
    OrdinalNumber = NameOrdinalTableBase[Middle];
    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }
    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));
    return FunctionAddress;
}

PVOID get_module_image_base(__in UCHAR * name)
{
    NTSTATUS status = 0;
    ULONG  modulesSize;
    AUX_MODULE_EXTENDED_INFO * modules;
    ULONG  numberOfModules;
    ULONG i;
    PIMAGE_EXPORT_DIRECTORY pied = 0;
    PVOID ImageBase = 0;
    status = AuxKlibInitialize();
    if (!NT_SUCCESS( status ))
    {
        KdPrint(( "AuxKlibInitialize fail %d\n", status));
        return ImageBase;
    }   
   
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), NULL);// Get the required array size.
    if (!NT_SUCCESS(status) || modulesSize == 0) {
        return ImageBase;
    }
   
    numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);// Calculate the number of modules.
       
    modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool, modulesSize, TAG);// Allocate memory to receive data.
    if (modules == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        return ImageBase;
    }
    RtlZeroMemory(modules, modulesSize);
   
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), modules);// Obtain the module information.
    if (!NT_SUCCESS(status)) {
        ExFreePoolWithTag(modules,TAG);
        return ImageBase;
    }
    for (i = 0;i<numberOfModules;i++)
    {
        UCHAR * pfilename = modules[i].FullPathName + modules[i].FileNameOffset;
        if (_stricmp(pfilename, name) == 0)
        {
            ImageBase = modules[i].BasicInfo.ImageBase;
            break;
        }
    }
    ExFreePoolWithTag(modules,TAG);
    return ImageBase;
}

ULONG get_ssdt_function_index(IN char * function_name)
    /*
    功能:获取SSDT函数的调用号。
          这里支持内核没有导出的SSDT的函数,即ntoskrnl.exe没有导出,而ntdll.dll导出的,在SSDT里有的函数。
    */
{
    PVOID ImageBase = 0;
    PVOID FunctionAddress;
    ANSI_STRING test  = RTL_CONSTANT_STRING(function_name);
    ImageBase = get_module_image_base("ntdll.dll");
    if (ImageBase == 0)
    {
        return -1;
    }
    FunctionAddress = MiFindExportedRoutineByName (ImageBase, &test);
    if (FunctionAddress == 0)
    {
        return -1;
    }      
    return SYSCALL_INDEX(FunctionAddress);
}

BOOL get_function_name_by_ssdt_index  (int index, OUT PANSI_STRING AnsiImageRoutineName)
/*++
--*/
{
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    ULONG Ordinals = 0;
    ULONG ExportSize;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    PVOID DllBase = 0;
    BOOL B = FALSE ;
    PAGED_CODE();
    if (AnsiImageRoutineName == NULL || AnsiImageRoutineName->Buffer == NULL)
    {
        return FALSE;
    }
    DllBase = get_module_image_base("ntdll.dll");
    if (DllBase == 0)
    {
        return FALSE;
    }
    //确保DllBase可以访问。否则蓝屏。
    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return FALSE;
    }
   
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.   
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.   
    while (Ordinals < ExportDirectory->NumberOfNames) // Lookup the desired name in the name table using a binary search.
    {   
        PCHAR RoutineName = (PCHAR)DllBase + NameTableBase[Ordinals];
        //如果是Nt开头的函数。
        if (_strnicmp(RoutineName, "nt", 2) == 0)// || _strnicmp(RoutineName, "zw", 2) == 0
        {
            USHORT OrdinalNumber = NameOrdinalTableBase[Ordinals];
            // Index into the array of RVA export addresses by ordinal number.
            PULONG Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
            PVOID FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
            //这个函数的地址的第二个字节的INT等于index,就退出,并返回返函数的名字。
            if (SYSCALL_INDEX(FunctionAddress) == index)
            {
                RtlCopyMemory(AnsiImageRoutineName->Buffer, RoutineName, strlen(RoutineName));
                //KdPrint(("function_name:%s.\n", RoutineName));
                B = TRUE;
                break;
            }
        }
        Ordinals++;
    }   
    return B;
}

VOID enumerate_SSDT_32()
{
    ULONG i = 0;
    for ( ; i < KeServiceDescriptorTable.NumberOfServices ;i++ )
    {
        CHAR RoutineName[MAX_PATH] = {0};
        ANSI_STRING AnsiImageRoutineName = {0};
        AnsiImageRoutineName.Buffer = RoutineName;
        AnsiImageRoutineName.MaximumLength = MAX_PATH;
        get_function_name_by_ssdt_index  (i, &AnsiImageRoutineName);
        RtlInitAnsiString(&AnsiImageRoutineName, AnsiImageRoutineName.Buffer);
        KdPrint(("index:%d, address:0x%p, name:%Z.\n", i, KeServiceDescriptorTable.ServiceTableBase[i], &AnsiImageRoutineName));
        /*
        这里要做的还有更多,如参数的个数,函数的名字,函数所在的模块,原始的地址。
        */
    }
}

DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    ULONG index = 0;
    PVOID p_NtCreateMutant = 0;
    KdBreakPoint();
    DriverObject->DriverUnload = Unload;   
    //index = get_ssdt_function_index("NtCreateMutant");
    //p_NtCreateMutant = (PVOID)SYSTEMSERVICE(index);//注意这个是Nt*的函数。
    enumerate_SSDT_32();
    return status;   
}

/*
0: kd> !process 0 0 explorer.exe
PROCESS 81d4d588  SessionId: 0  Cid: 0658    Peb: 7ffde000  ParentCid: 0604
    DirBase: 02940200  ObjectTable: e149da90  HandleCount: 379.
    Image: explorer.exe
0: kd> .process /r /p 81d4d588 
Implicit process is now 81d4d588
.cache forcedecodeuser done
Loading User Symbols
................................................................
........................
0: kd> u ntdll!ntcreatefile
ntdll!ZwCreateFile:
7c92d0ae b825000000      mov     eax,25h
7c92d0b3 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d0b8 ff12            call    dword ptr [edx]
7c92d0ba c22c00          ret     2Ch
*/

/*
1: kd> uf nt!Zwcreatefile
Flow analysis was incomplete, some code may be missing
nt!ZwCreateFile:
fffff800`018d8680 488bc4          mov     rax,rsp
fffff800`018d8683 fa              cli
fffff800`018d8684 4883ec10        sub     rsp,10h
fffff800`018d8688 50              push    rax
fffff800`018d8689 9c              pushfq
fffff800`018d868a 6a10            push    10h
fffff800`018d868c 488d05dd270000  lea     rax,[nt!KiServiceLinkage (fffff800`018dae70)]
fffff800`018d8693 50              push    rax
fffff800`018d8694 b852000000      mov     eax,52h 注意这个。
fffff800`018d8699 e9225f0000      jmp     nt!KiServiceInternal (fffff800`018de5c0)
nt!KiServiceInternal:
fffff800`018de5c0 4883ec08        sub     rsp,8
fffff800`018de5c4 55              push    rbp
fffff800`018de5c5 4881ec58010000  sub     rsp,158h
fffff800`018de5cc 488dac2480000000 lea     rbp,[rsp+80h]
fffff800`018de5d4 48899dc0000000  mov     qword ptr [rbp+0C0h],rbx
fffff800`018de5db 4889bdc8000000  mov     qword ptr [rbp+0C8h],rdi
fffff800`018de5e2 4889b5d0000000  mov     qword ptr [rbp+0D0h],rsi
fffff800`018de5e9 fb              sti
fffff800`018de5ea 65488b1c2588010000 mov   rbx,qword ptr gs:[188h]
fffff800`018de5f3 0f0d8bd8010000  prefetchw [rbx+1D8h]
fffff800`018de5fa 0fb6bbf6010000  movzx   edi,byte ptr [rbx+1F6h]
fffff800`018de601 40887da8        mov     byte ptr [rbp-58h],dil
fffff800`018de605 c683f601000000  mov     byte ptr [rbx+1F6h],0
fffff800`018de60c 4c8b93d8010000  mov     r10,qword ptr [rbx+1D8h]
fffff800`018de613 4c8995b8000000  mov     qword ptr [rbp+0B8h],r10
fffff800`018de61a 4c8d1d3d010000  lea     r11,[nt!KiSystemServiceStart (fffff800`018de75e)]
fffff800`018de621 41ffe3          jmp     r11
1: kd> !process 0 0 explorer.exe
PROCESS fffffa801ad91910
    SessionId: 1  Cid: 06a4    Peb: 7fffffdd000  ParentCid: 0680
    DirBase: 66ef5000  ObjectTable: fffff8a0011323b0  HandleCount: 755.
    Image: explorer.exe
1: kd> .process /p /r fffffa801ad91910
Implicit process is now fffffa80`1ad91910
.cache forcedecodeuser done
Loading User Symbols
................................................................
................................................................
...........
1: kd> uf ntdll!NtCreateFile
ntdll!ZwCreateFile:
00000000`77051860 4c8bd1          mov     r10,rcx
00000000`77051863 b852000000      mov     eax,52h 注意这个
00000000`77051868 0f05            syscall
00000000`7705186a c3              ret
知道64位SSDT的结构及地址,根据上面的示例不难写出X64的相应的例子。
*/

没有评论:

发表评论