• Win64 驱动内核编程-18.SSDT


    SSDT

     学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019

     学习资料:WIN64内核编程基础 胡文亮

       SSDT(系统服务描述表),刚开始接触什么进程保护XXX啥的,都是看到在说SSDT(虽然说目前很多杀软都已经采用稳定简单的回调姿势了)。这次就弄清楚两个问题:

        如何在内核里动态获得 SSDT 的基址;

    如何在内核里动态获得 SSDT 函数的地址;

        在 WIN32 下,第一个问题就根本不是问题,因为 KeServiceDescriptorTable 直接被导

    出了。但是 WIN64 下 KeServiceDescriptorTable 没有被导出。所以必须搜索得到它的地址。

    反汇编:uf KisystemCall64


        上面看到,貌似直接反汇编uf KiSystemServiceRepeat 试了下,一样可以找到SSDT基址,但是问题是,KiSystemServiceRepeat这个东西的地址找不到,而KiSystemCall64的地址直接可以直接读取指定的 msr 得出。

    通过读取 C0000082 寄存器,能够得到 KiSystemCall64 的地址,然后从

    KiSystemCall64 的地址开始,往下搜索 0x500 字节左右(特征码是 4c8d15),就能得到

    KeServiceDescriptorTable 的地址了。同理,我们换一下特征码(4c8d1d),就能获得

    KeServiceDescriptorTableShadow 的地址了。


    然后是资料里面带的两个SSDT基址的函数:

        方法1(这个方法蓝屏了,我直接用的方法2)

    ULONGLONG GetKeServiceDescriptorTable64()
    {
    char KiSystemServiceStart_pattern[13] = "x8BxF8xC1xEFx07x83xE7x20x25xFFx0Fx00x00";
    ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;
    ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;
    UNICODE_STRING Symbol;
    ULONGLONG i, tbl_address, b;
    for (i = 0; i < CodeScanEnd - CodeScanStart; i++)
    {
    if (!memcmp((char*)(ULONGLONG)CodeScanStart +i, (char*)KiSystemServiceStart_pattern,13))
    { 
    for (b = 0; b < 50; b++)
    {
    tbl_address = ((ULONGLONG)CodeScanStart+i+b);
    if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)
    return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);
    }
    }
    }
    return 0;
    }
    方法2

    ULONGLONG MyGetKeServiceDescriptorTable64() 
    {
    PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
        PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
    PUCHAR i = NULL;
    UCHAR b1=0,b2=0,b3=0;
    ULONG templong=0;
    ULONGLONG addr=0;
    for(i=StartSearchAddress;i<EndSearchAddress;i++)
    {
    if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
    {
    b1=*i;
    b2=*(i+1);
    b3=*(i+2);
    if( b1==0x4c && b2==0x8d && b3==0x15 ) //4c8d15
    {
    memcpy(&templong,i+3,4);
    addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
    return addr;
    }
    }
    }
    return 0;
    }

        接下来就是讲述 SSDT 函数地址了。在获得地址之前,需要知道 SSDT 函数的 INDEX。获得这个 INDEX 的方法很简单,直接在 RING3 读取 NTDLL 的内容即可。使用 WINDBG 的方法如下:随便创建一个进程,然后使用 WINDBG 附加:


        可以看到两次反汇编的结果几乎完全相同,唯一不同的地方是第二句。XXh 就是此函数的 index。知道 INDEX 之后,就可以计算地址了(资料上也是给了两个方法,建议使用方法2)。

    方法1

    VOID Initxxxx()
    {
    UCHAR strShellCode[36]="x48x8BxC1x4Cx8Dx12x8BxF8xC1xEFx07x83xE7x20x4Ex8Bx14x17x4Dx63x1Cx82x49x8BxC3x49xC1xFBx04x4Dx03xD3x49x8BxC2xC3";
    /*
    mov rax, rcx ;rcx=index
    lea r10,[rdx] ;rdx=ssdt
    mov edi,eax
    shr edi,7
    and edi,20h
    mov r10, qword ptr [r10+rdi]
    movsxd r11,dword ptr [r10+rax*4]
    mov rax,r11
    sar r11,4
    add r10,r11
    mov rax,r10
    ret
    */
    scfn=ExAllocatePool(NonPagedPool,36);
    memcpy(scfn,strShellCode,36);
    }
    ULONGLONG GetSSDTFunctionAddress64(ULONGLONG NtApiIndex)
    {
    ULONGLONG ret=0;
    ULONGLONG ssdt= MyGetKeServiceDescriptorTable64();
    if(scfn==NULL)
    Initxxxx();
    ret=scfn(NtApiIndex, ssdt);
    return ret;
    }

    方法2

    typedef struct _SYSTEM_SERVICE_TABLE{
    PVOID  	ServiceTableBase; 
    PVOID  	ServiceCounterTableBase; 
    ULONGLONG  	NumberOfServices; 
    PVOID  	ParamTableBase; 
    } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;
     
    ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
    {
    LONG dwTemp=0;
    ULONGLONG qwTemp=0,stb=0,ret=0;
    PSYSTEM_SERVICE_TABLE ssdt=(PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
    stb=(ULONGLONG)(ssdt->ServiceTableBase);
    qwTemp = stb + 4 * Index;
    dwTemp = *(PLONG)qwTemp;
    dwTemp = dwTemp >> 4;
    ret = stb + (LONG64)dwTemp;
    return ret;
    }
     

        最后,总结一下 WIN32 和 WIN64 在 SSDT 方面的不同(我直接截图过来)。大家可以把 SSDT(其实 SHADOWSSDT 同理)想像成一排保险柜,每个柜子都有编号(从 开始),柜子的长度为四字节,每个柜子里都放了一个 LONG 数据。但不同的是,WIN32 的“柜子”里放的数据是某个函数的绝对地址,而 WIN64 的“柜子”里放的数据是某个函数的偏移地址。这个偏移地址要经过一定的计算才能变成绝对地址。


    测试代码:

    #include <ntddk.h>
    #include <windef.h>
    #include "MyDriver.h"    
     
    #pragma intrinsic(__readmsr)
    typedef UINT64 (__fastcall *SCFN)(UINT64,UINT64);
    SCFN scfn;
     
     
    ULONGLONG MyGetKeServiceDescriptorTable64() 
    {
    PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
        PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
    PUCHAR i = NULL;
    UCHAR b1=0,b2=0,b3=0;
    ULONG templong=0;
    ULONGLONG addr=0;
    for(i=StartSearchAddress;i<EndSearchAddress;i++)
    {
    if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
    {
    b1=*i;
    b2=*(i+1);
    b3=*(i+2);
    if( b1==0x4c && b2==0x8d && b3==0x15 ) //4c8d15
    {
    memcpy(&templong,i+3,4);
    addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
    return addr;
    }
    }
    }
    return 0;
    }
     
    VOID Initxxxx()
    {
    UCHAR strShellCode[36]="x48x8BxC1x4Cx8Dx12x8BxF8xC1xEFx07x83xE7x20x4Ex8Bx14x17x4Dx63x1Cx82x49x8BxC3x49xC1xFBx04x4Dx03xD3x49x8BxC2xC3";
    /*
    mov rax, rcx ;rcx=index
    lea r10,[rdx] ;rdx=ssdt
    mov edi,eax
    shr edi,7
    and edi,20h
    mov r10, qword ptr [r10+rdi]
    movsxd r11,dword ptr [r10+rax*4]
    mov rax,r11
    sar r11,4
    add r10,r11
    mov rax,r10
    ret
    */
    scfn=ExAllocatePool(NonPagedPool,36);
    memcpy(scfn,strShellCode,36);
    }
     
    ULONGLONG GetSSDTFunctionAddress64(ULONGLONG NtApiIndex)
    {
    ULONGLONG ret=0;
    ULONGLONG ssdt= MyGetKeServiceDescriptorTable64();
    if(scfn==NULL)
    Initxxxx();
    ret=scfn(NtApiIndex, ssdt);
    return ret;
    }
     
    typedef struct _SYSTEM_SERVICE_TABLE{
    PVOID  	ServiceTableBase; 
    PVOID  	ServiceCounterTableBase; 
    ULONGLONG  	NumberOfServices; 
    PVOID  	ParamTableBase; 
    } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;
     
    ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
    {
    LONG dwTemp=0;
    ULONGLONG qwTemp=0,stb=0,ret=0;
    PSYSTEM_SERVICE_TABLE ssdt=(PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
    stb=(ULONGLONG)(ssdt->ServiceTableBase);
    qwTemp = stb + 4 * Index;
    dwTemp = *(PLONG)qwTemp;
    dwTemp = dwTemp >> 4;
    ret = stb + (LONG64)dwTemp;
    return ret;
    }
     
    VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
    {
    UNICODE_STRING strLink;
    RtlInitUnicodeString(&strLink, LINK_NAME);
    IoDeleteSymbolicLink(&strLink);
    IoDeleteDevice(pDriverObj->DeviceObject);
    }
     
    NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }
     
    NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }
     
    NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PIO_STACK_LOCATION pIrpStack;
    ULONG uIoControlCode;
    PVOID pIoBuffer;
    ULONG uInSize;
    ULONG uOutSize;
    pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    switch(uIoControlCode)
    {
    ;
    }
    if(status == STATUS_SUCCESS)
    pIrp->IoStatus.Information = uOutSize;
    else
    pIrp->IoStatus.Information = 0;
    pIrp->IoStatus.Status = status;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return status;
    }
     
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
    {
    NTSTATUS status = STATUS_SUCCESS;
    UNICODE_STRING ustrLinkName;
    UNICODE_STRING ustrDevName;  
    PDEVICE_OBJECT pDevObj;
    pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
    pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
    pDriverObj->DriverUnload = DriverUnload;
    RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
    status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
    if(!NT_SUCCESS(status))	return status;
    if(IoIsWdmVersionAvailable(1, 0x10))
    RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
    else
    RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
    status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);  
    if(!NT_SUCCESS(status))
    {
    IoDeleteDevice(pDevObj); 
    return status;
    }
    //test
    DbgPrint("[method 1]SSDT: %llx
    ",MyGetKeServiceDescriptorTable64());
     
    DbgPrint("[method 1]NtOpenProcess: %llx
    ",GetSSDTFunctionAddress64(0x23));	//WIN7X64 HARDCODE
    DbgPrint("[method 1]NtTerminateProcess: %llx
    ",GetSSDTFunctionAddress64(0x29));	//WIN7X64 HARDCODE
     
    DbgPrint("[method 2]NtOpenProcess: %llx
    ",GetSSDTFunctionAddress64_2(0x23));	//WIN7X64 HARDCODE
    DbgPrint("[method 2]NtTerminateProcess: %llx
    ",GetSSDTFunctionAddress64_2(0x29));//WIN7X64 HARDCODE
    //test
    return STATUS_SUCCESS;
    }
    执行结果:


  • 相关阅读:
    如何在Ubuntu Unity上修改应用程序图标
    Ubuntu添加PPA源
    Ubuntu14.04下Unity桌面托盘图标显示问题
    apt-get用法
    解决ubuntu无法调整和保存屏幕亮度的问题
    终端调测命令易用性的改进
    关于Linux系统basename函数缺陷的思考
    基于VLAN的二三层转发
    【译】编写可重入和线程安全的代码
    Writing Reentrant and Thread-Safe Code(译:编写可重入和线程安全的代码)
  • 原文地址:https://www.cnblogs.com/csnd/p/12062009.html
Copyright © 2020-2023  润新知