现在的位置: 首页 > 综合 > 正文

SSDT表结构的深入学习

2018年02月08日 ⁄ 综合 ⁄ 共 4828字 ⁄ 字号 评论关闭

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

抱歉!评论已关闭.