SSDT表结构的深入学习
阅读原文时间:2023年07月12日阅读:2

SSDT表的知识目录:

A、了解SSDT结构

B、由SSDT索引号获取当前函数地址

C、如何获取索引号

D、获取起源地址-判断SSDT是否被HOOK

E、如何向内核地址写入自己代码

A、了解SSDT结构

SSDT的全称是 System  ServicesDescriptor Table--系统服务描述符表,是由 ntoskrnl.exe导出KeServiceDescriptorTable 这个表,是全局的,声明导出即可在编程中使用。

typedef  struct  ServiceDescriptorEntry

{

   unsigned  int  *ServiceTableBase;       //SystemService Dispatch Table 的基地址

   unsigned  int  *ServiceCounterTable(0);

//包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由 sysenter 更新。

   unsigned  int   NumberOfServices;     //由 ServiceTableBase描述的服务的数目。

   unsigned  char  *ParamTableBase;

//包含每个系统服务参数字节数表的基地址-系统服务参数表

 } SSDT_Entry;

用windbg 了解SSDT结构,命令:

dp    nt!KeServiceDescriptorTable

B、由SSDT索引号获取当前函数地址

[[KeServiceDescriptorTable基址] + index*4]

C、如何获取SSDT索引号

用工具查看如:狙剑、火眼等

D、获取起源地址-判断SSDT是否被HOOK

MmGetSystemRoutineAddress函数,得到系统导出函数的地址

包含头文件Wdm.h 或Ntddk.h

//NTKERNELAPI
PVOID
MmGetSystemRoutineAddress (
    __in    PUNICODE_STRING   SystemRoutineName
    )
/*++

Routine Description:

    This functionreturns the address of the argument function pointer if
    it is in the kernelor HAL, NULL if it is not.

Arguments:

    SystemRoutineName -Supplies the name of the desired routine.

Return Value:

    Non-NULL functionpointer if successful.  NULL if not.

Environment:

    Kernel mode,PASSIVE_LEVEL, arbitrary process context.

--*/
{
    PKTHREADCurrentThread;
    NTSTATUS Status;
    PKLDR_DATA_TABLE_ENTRY DataTableEntry;
    ANSI_STRINGAnsiString;
    PLIST_ENTRYNextEntry;
    UNICODE_STRINGKernelString;
    UNICODE_STRINGHalString;
    PVOIDFunctionAddress;
    LOGICAL Found;
    ULONGEntriesChecked;

    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

    EntriesChecked = 0;
    FunctionAddress =NULL;

    KernelString.Buffer= (const PUSHORT) KERNEL_NAME;
    KernelString.Length= sizeof (KERNEL_NAME) - sizeof (WCHAR);
   KernelString.MaximumLength = sizeof KERNEL_NAME;

    HalString.Buffer =(const PUSHORT) HAL_NAME;
    HalString.Length =sizeof (HAL_NAME) - sizeof (WCHAR);
   HalString.MaximumLength = sizeof HAL_NAME;

    do {
        Status =RtlUnicodeStringToAnsiString (&AnsiString,
                                               SystemRoutineName,
                                               TRUE);

        if (NT_SUCCESS(Status)) {
            break;
        }

       KeDelayExecutionThread (KernelMode, FALSE,(PLARGE_INTEGER)&MmShortTime);

    } while (TRUE);

    //
    // Arbitraryprocess context so prevent suspend APCs now.
    //

    CurrentThread =KeGetCurrentThread ();
    KeEnterCriticalRegionThread(CurrentThread);
   ExAcquireResourceSharedLite (&PsLoadedModuleResource, TRUE);

    //
    // Check only thekernel and the HAL for exports.
    //

    NextEntry =PsLoadedModuleList.Flink;
    while (NextEntry !=&PsLoadedModuleList) {

        Found = FALSE;

        DataTableEntry= CONTAINING_RECORD(NextEntry,
                                          KLDR_DATA_TABLE_ENTRY,
                                          InLoadOrderLinks);

        if (RtlEqualUnicodeString(&KernelString,
                                  &DataTableEntry->BaseDllName,
                                  TRUE)) {

            Found =TRUE;
           EntriesChecked += 1;

        }
        else if(RtlEqualUnicodeString (&HalString,
                                       &DataTableEntry->BaseDllName,
                                       TRUE)){

            Found =TRUE;
           EntriesChecked += 1;
        }

        if (Found ==TRUE) {

           FunctionAddress = MiFindExportedRoutineByName(DataTableEntry->DllBase,
                                                         &AnsiString);

            if(FunctionAddress != NULL) {
                break;
            }

            if (EntriesChecked== 2) {
                break;
            }
        }

        NextEntry =NextEntry->Flink;
    }

   ExReleaseResourceLite (&PsLoadedModuleResource);
   KeLeaveCriticalRegionThread (CurrentThread);

    RtlFreeAnsiString(&AnsiString);

    returnFunctionAddress;
}
//WRK中的源码

E、如何向内核地址写入自己代码

在后期版本的操作系统中,要求SSDT是只读的,因此任何合法的程序都不可能修改这个表,不过聪明的大牛们还是想出了修改SSDT表的方法。

方法一:更改注册表(需要重启)

HKLM\SYSTEM\CurrentControlset\Control\SessionManger\MemoryManagement\EnforceWriteProtection= 0

HKLM\SYSTEM\CurrentControlset\Control\SessionManger\MemoryManagement\DisablePagingExecutive= 1

方法二:修改CR0寄存器的第1位即wp位置0

//关闭内存保护
_asm
{
    push eax
    mov eax, CR0
    and eax, 0FFFEFFFFh
    mov CR0, eax
    pop eax
} 

//恢复内存保护
_asm
{
    push eax
    mov eax, CR0
    or eax , not 0FFFEFFFFh
    mov CR0, eax
    pop eax
} 

方法三:利用MDL(Memory Descriptor List)来绕过写保护

//MDL reference defined inntddk.h
typedef struct  _MDL{
                      Struct  _MDL   *Next;
                      CSHORT  Size;
                      CSHORT  MdlFlags;
                      Struct  _EPROCESS  *Process;
                      PVOID   MappedSystemVa;
                      PVOID   StartVa;
                      ULONG   ByteCount;
                      ULONG   ByteOffset;
}MDL,*PMDL; 

//MDL Flags
#define    MDL_MAPPED_TO_SYSTEM_VA             0x0001
#define    MDL_PAGES_LOCKED                    0x0002
#define    MDL_SOURCE_IS_NONPAGED_POOL         0x0004
#define    MDL_ALLOCATED_FIXED_SIZE            0x0008
#define MDL_PARTIAL                         0x0010
#define    MDL_PARTIAL_HAS_BEEN_MAPPED         0x0020
#define    MDL_IO_PAGE_READ                    0x0040
#define    MDL_WRITE_OPERATION                 0x0080
#define    MDL_PARENT_MAPPED_SYSTEM_VA         0x0100
#define MDL_LOCK_HELD                       0x0200
#define MDL_PHYSICAL_VIEW                   0x0400
#define MDL_IO_SPACE                        0x0800
#define    MDL_NETWORK_HEADER                  0x1000
#define    MDL_MAPPING_CAN_FAIL                0x2000
#define    MDL_ALLOCATED_MUST_SUCCEED          0x4000 

//声明
#pragma pack(1)
typedef struct  ServiceDescriptorEntry{
                                        unsigned int*   ServiceTableBase;
                    unsigned int*   ServiceCounterTableBase;
                    unsigned int    NumberOfService;
                    unsigned char*  ParamTableBase;
}SSDT_Entry;
#pragma pack()

//导出SSDT表
_declspec(dllimport)SSDT_Entry KeServiceDescriptorTable;

//保存原始的系统调用地址
PMDL g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
//创建MDL
g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable, ServiceTableBase, KeServcieDescriptorTable.NumberOfService*4);
if(!g_pmdlSystemCall)
{
    return STATUS_UNSUCCESSFUL;
}

//构建非分页内存
MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
//改变MDL的标志
g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA ;
//锁定内存
MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);

参考资料:

《Windows内核的安全防护》推荐

http://blog.csdn.net/evi10r/article/details/6840564

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章