获取windows10下的SSDT及函数表(一)

简介:在之前介绍过的系统调用的基础上,以获取SSDT为目的,综合应用的角度去用代码实践。

前言

在之前,我们有详细介绍过关于windows的系统调用,今天的介绍也是既是对这篇文章的补充,也是一种小小的实践与结合,文章如下:

https://daliu.net/posts/20250105/

https://daliu.net/posts/20250106/

https://daliu.net/posts/20250108/

在之前介绍的过程中有一个概念非常重要,那就是系统服务函数的描述符表,也就是我们常说的SSDT,我们今天要做的就是如何通过代码来遍历出SSDT全部的函数。既然如此,我们依然要设定几个约束条件:

1. 因为考虑到需要驱动代码,所以,获取SSDT各个函数的地址以及函数的参数个数我们用驱动程序,也就是C语言完成;

2. 考虑到画面呈现以及跟驱动的调用,所以应用层我们采用GO语言完成;

3. 我们需要呈现的内容至少包括,服务函数序号,函数名称,函数在应用层的地址,函数在内核的地址,函数参数个数

驱动代码

通过上面所写,我们要获取函数的内核地址和参数,这个部分是可以在内核完成并获取的,所以我们采用驱动代码,但是在实现之前,我们先考虑下获取SSDT的步骤,按着步骤依次完成对应的功能即可。

第一步: 获取SSDT表地址

1. 解析pe文件,获取各区段地址

2. 通过硬编码以及区段地址获取目标地址

3. 通过目标地址获取SSDT地址

第二步: 解析SSDT获取函数地址

第三步: 存储函数地址,参数个数等(要返回应用层)

SSDT表地址获取

看上面步骤一定会比较疑惑,SSDT表为什么需要通过PE文件解析的方式获取,因为在64位操作系统里,SSDT表不再导出,所以我们没法直接通过代码或者api调用这个表,我们在双击调试的时候,通过windbg可以直接查看,是因为有这个符号文件,但是如果单单通过api是没有办法实现的。

所以,我们需要通过硬编码的方式,也就是直接内核文件上通过硬编码找到这个表的位置,但是具体找哪一段呢,我们在之前系统调用的时候介绍过,这个表在刚刚进入内核的调用服务函数KiSystemCall64中必然使用了这个表,那么我们只需找到那段调用这个表的代码的位置即可。

我们再次用IDA打开内核文件ntoskrnl.exe,通过KiSystemCall64这个函数,找到对应的SSDT表,如下图所示:

2

接下来要解决两个问题,1.获取哪一段硬编码,2.如何获取硬编码

获取硬编码

硬编码的选取并没有什么特别的,毕竟内核变化的不多,这段代码大概率不会变,那对应的机器码也就变化不多,不过尽量别选那些涉及跳转的,毕竟有些偏移有可能会改变。(如果不放心可以多试一些内核版本)

我们就选这段代码上面的那一段就可以,如何显示出硬编码,在IDA,Options菜单栏,选择general,然后会弹出这个对话框,在右侧Number of opcode bytes 里填入机器码展示的个数,指令一般最多也就8字节(不排除有长指令,那就是取指的时候CPU要跑两次)

3

确定之后,硬编码就如此显示了

4

然后选中上面的一段代码,按快捷键shift+E,会弹出一个要导出编码的对话框,可以选择第四个,就以c语言的字符数组方式显示,复制粘贴即可。

5

数组如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 定义ssdt的硬编码
unsigned char ssdt_chars[] =
{
  0x48, 0x8B, 0x45, 0xB0, 0x48, 0x8B, 0x4D, 0xB8, 0x48, 0x8B,
  0x55, 0xC0, 0xFB, 0x48, 0x89, 0x8B, 0x88, 0x00, 0x00, 0x00,
  0x89, 0x83, 0x80, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x84, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xA3, 0x90, 0x00, 0x00,
  0x00, 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25,
  0xFF, 0x0F, 0x00, 0x00
};
计算SSDT地址

通过上文的硬编码,我们事实上只能定位到这段调用SSDT表的代码地址,并没有获取真实的SSDT地址,所以我们需要进一步计算。

1
2
0x4C, 0x8D, 0x15, 0x05, 0x08, 0x9F, 0x00,  lea r10, KeServiceDescriptorTable
0x4C, 0x8D, 0x1D, 0x7E, 0xB9, 0x8E, 0x00   lea r11, KeServiceDescriptorTableShadow

以上两句是对应的代码,通过观察可以看出,前三个字节张的很像,也就是lea r10lea r11,那每条指令的后四个字节是什么呢,答案是偏移,这个偏移是相对于谁的呢,毕竟这只是汇编指令,总不能汇编指令的偏移也是针对镜像基址的,显示不可能。事实上,这个偏移值针对下一条指令的起始位置,向后的偏移大小。

这是一种CPU寻址方式,寄存器相对寻址,程序计数器PC(windows里就是EIP寄存器)存储的是下一条要执行的指令,偏移量就是相对于EIP寄存器的距离,所以上述编码中,0x009F0805就是相对于EIP寄存器,也就是下一条指令起始位置的偏移,由此,我们算是获得了SSDT的地址。

获取SSDT并保存

上面介绍了如何通过硬编码获取SSDT的地址,但是,别忘记一个事情就是,我们是要在PE文件里寻找硬编码,而且是在内存里已加载的PE文件获取目标地址,前提是需要知道PE文件的地址然后再解析PE文件并获取对应的分段,在去遍历硬编码。

获取基址

早先并没有了解过是否有windows的API来获取基址,即便有,也先假设我们不知道,但是之前介绍过获取模块基址的方法,如果能够利用这个方法是不是也会很好解决,毕竟即便是内核文件,它也还是PE文件,方法都是通过的。

显然,windows把一个关键的结构体也存放在的驱动里,我们在定义DriverEntry这个实例的时候,有一个很重要的参数就是_DRIVER_OBJECT 对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
typedef struct _DRIVER_OBJECT {
  CSHORT             Type;
  CSHORT             Size;
  PDEVICE_OBJECT     DeviceObject;
  ULONG              Flags;
  PVOID              DriverStart;
  ULONG              DriverSize;
  PVOID              DriverSection;
  PDRIVER_EXTENSION  DriverExtension;
  UNICODE_STRING     DriverName;
  PUNICODE_STRING    HardwareDatabase;
  PFAST_IO_DISPATCH  FastIoDispatch;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO    DriverStartIo;
  PDRIVER_UNLOAD     DriverUnload;
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;

这个结构体有一个参数叫DriverSection;虽然在官方文档里没有展开介绍(起码我找到),但是,经过前人师傅分享,这个参数其实就是_LDR_DATA_TABLE_ENTRY 这个结构体很熟悉吧,这个就是描述内存模块的那个双链表的结构体,因而我们可以通过遍历这个链表,遍历到自己这个镜像的模块。

然后进一步获取对应的imageBase,获取每个Section,不过我们不需要获取所有Section,只需要获取代码所处的那个Section就行,这个段代码在.text里,我们可以用代码定义一个如下的方法获取基址:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 根据模块名称获取当前进程的下对应模块的信息
PMODULE_INFO getProcessInfoByName(char* processName, PDRIVER_OBJECT pDrvierObject)
{
    //模块名称最中是以UNICODE_STRING呈现,所以需要把char*转化为UNICODE_STRING
    ANSI_STRING orignName = { 0 };
    RtlInitAnsiString(&orignName, processName);

    UNICODE_STRING ModuleName = { 0 };
    RtlAnsiStringToUnicodeString(&ModuleName, &orignName, TRUE);

    //通过当前驱动对象获取镜像对应结构体
    PLDR_DATA_TABLE_ENTRY pLdr = NULL;
    PLIST_ENTRY pListEntry = NULL;
    PLIST_ENTRY pCurrentListEntry = NULL;

    PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
    pLdr = (PLDR_DATA_TABLE_ENTRY)pDrvierObject->DriverSection;
    pListEntry = pLdr->InLoadOrderLinks.Flink;
    pCurrentListEntry = pListEntry->Flink;

    // 遍历双链表
    while (pCurrentListEntry != pListEntry) //前后不相等
    {
        //获取LDR_DATA_TABLE_ENTRY结构
        pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, 
            LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

        if (pCurrentModule->BaseDllName.Buffer != 0)
        {

            if (!RtlCompareUnicodeString(&ModuleName, 
            &(pCurrentModule->BaseDllName), TRUE))
            {
                
                DbgPrint("ModuleName = %wZ ModuleBase = %p ModuleEndBase = %p\r\n",
                    pCurrentModule->BaseDllName,
                    pCurrentModule->DllBase,
                    (LONGLONG)pCurrentModule->DllBase + pCurrentModule->SizeOfImage);
                MODULE_INFO moduleInfo = { 0 };
                moduleInfo.ModuleBase = pCurrentModule->DllBase;
                moduleInfo.ModuleSize = pCurrentModule->SizeOfImage;
                return &moduleInfo;

            }

        }
        pCurrentListEntry = pCurrentListEntry->Flink;
    }
    return NULL;
}

获取区段地址

获取基址之后还需要获取对应模块的某个区段的地址,毕竟如果能直接定位到区段就不需要从整个pe文件进行查找,获取区段地址的方法如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

// 根据模块地址及区段名称获取区段信息
void getSectionsAddrByImageBase(PVOID imageBase, char* sectionName, 
PSECTION_INFO sectionInfo)
{
    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS64 pImageNtHeader = (PIMAGE_NT_HEADERS64)(pImageDosHeader->e_lfanew + (ULONG64)pImageDosHeader);


    //区段头s的地址
    PIMAGE_OPTIONAL_HEADER64 pImageOptionalHeaders = &(pImageNtHeader->OptionalHeader);
    PIMAGE_SECTION_HEADER pFirstSectionHeader = pImageNtHeader->FileHeader.SizeOfOptionalHeader + (ULONG64)pImageOptionalHeaders;
    try
    {
        for (int i = 0; i < pImageNtHeader->FileHeader.NumberOfSections; i++)
        {
            PIMAGE_SECTION_HEADER pResSection = pFirstSectionHeader + i;

            // 判断如果名称相同,则返回区段地址
            if (strstr(pResSection->Name, sectionName))
            {
                DbgPrint("SectionName = %s SectionAddr = %p SectionSize = %d\r\n",
                    pResSection->Name,
                    pResSection->VirtualAddress + (ULONG64)imageBase,
                    pResSection->SizeOfRawData);

                //SECTION_INFO section = { 0 };
                sectionInfo->sectionAddr = (PULONG64)(pResSection->VirtualAddress + (ULONG64)imageBase);
                sectionInfo->sectionSize = pResSection->SizeOfRawData;

                return;
            }
      
        }
    }
    except(1)
    {
        DbgPrint("faild get sections");
    }

    return;
}

有一个地方需要注意:因为获取基址和区段地址需要很多结构体,而这些结构体默认都没有公开或者说在当前头文件里没有定义,所以为了方便,我们自行定义,涉及PE文件和双链表的结构体定义补充如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// 定义PE文件中的结构体
//0x40 bytes (sizeof)
typedef struct _IMAGE_DOS_HEADER
{
    USHORT e_magic;                                                         //0x0
    USHORT e_cblp;                                                          //0x2
    USHORT e_cp;                                                            //0x4
    USHORT e_crlc;                                                          //0x6
    USHORT e_cparhdr;                                                       //0x8
    USHORT e_minalloc;                                                      //0xa
    USHORT e_maxalloc;                                                      //0xc
    USHORT e_ss;                                                            //0xe
    USHORT e_sp;                                                            //0x10
    USHORT e_csum;                                                          //0x12
    USHORT e_ip;                                                            //0x14
    USHORT e_cs;                                                            //0x16
    USHORT e_lfarlc;                                                        //0x18
    USHORT e_ovno;                                                          //0x1a
    USHORT e_res[4];                                                        //0x1c
    USHORT e_oemid;                                                         //0x24
    USHORT e_oeminfo;                                                       //0x26
    USHORT e_res2[10];                                                      //0x28
    LONG e_lfanew;                                                          //0x3c
}IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;

//0x14 bytes (sizeof)
typedef struct _IMAGE_FILE_HEADER
{
    USHORT Machine;                                                         //0x0
    USHORT NumberOfSections;                                                //0x2
    ULONG TimeDateStamp;                                                    //0x4
    ULONG PointerToSymbolTable;                                             //0x8
    ULONG NumberOfSymbols;                                                  //0xc
    USHORT SizeOfOptionalHeader;                                            //0x10
    USHORT Characteristics;                                                 //0x12
}IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER;

//0x8 bytes (sizeof)
typedef struct _IMAGE_DATA_DIRECTORY
{
    ULONG VirtualAddress;                                                   //0x0
    ULONG Size;                                                             //0x4
}IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY;

//0xf0 bytes (sizeof)
typedef struct _IMAGE_OPTIONAL_HEADER64
{
    USHORT Magic;                                                           //0x0
    UCHAR MajorLinkerVersion;                                               //0x2
    UCHAR MinorLinkerVersion;                                               //0x3
    ULONG SizeOfCode;                                                       //0x4
    ULONG SizeOfInitializedData;                                            //0x8
    ULONG SizeOfUninitializedData;                                          //0xc
    ULONG AddressOfEntryPoint;                                              //0x10
    ULONG BaseOfCode;                                                       //0x14
    ULONGLONG ImageBase;                                                    //0x18
    ULONG SectionAlignment;                                                 //0x20
    ULONG FileAlignment;                                                    //0x24
    USHORT MajorOperatingSystemVersion;                                     //0x28
    USHORT MinorOperatingSystemVersion;                                     //0x2a
    USHORT MajorImageVersion;                                               //0x2c
    USHORT MinorImageVersion;                                               //0x2e
    USHORT MajorSubsystemVersion;                                           //0x30
    USHORT MinorSubsystemVersion;                                           //0x32
    ULONG Win32VersionValue;                                                //0x34
    ULONG SizeOfImage;                                                      //0x38
    ULONG SizeOfHeaders;                                                    //0x3c
    ULONG CheckSum;                                                         //0x40
    USHORT Subsystem;                                                       //0x44
    USHORT DllCharacteristics;                                              //0x46
    ULONGLONG SizeOfStackReserve;                                           //0x48
    ULONGLONG SizeOfStackCommit;                                            //0x50
    ULONGLONG SizeOfHeapReserve;                                            //0x58
    ULONGLONG SizeOfHeapCommit;                                             //0x60
    ULONG LoaderFlags;                                                      //0x68
    ULONG NumberOfRvaAndSizes;                                              //0x6c
    struct _IMAGE_DATA_DIRECTORY DataDirectory[16];                         //0x70
}IMAGE_OPTIONAL_HEADER64, * PIMAGE_OPTIONAL_HEADER64;

//0x108 bytes (sizeof)
typedef struct _IMAGE_NT_HEADERS64
{
    ULONG Signature;                                                        //0x0
    struct _IMAGE_FILE_HEADER FileHeader;                                   //0x4
    struct _IMAGE_OPTIONAL_HEADER64 OptionalHeader;                         //0x18
}IMAGE_NT_HEADERS64, * PIMAGE_NT_HEADERS64;

//0x28 bytes (sizeof)
typedef struct _IMAGE_SECTION_HEADER
{
    UCHAR Name[8];                                                          //0x0
    union
    {
        ULONG PhysicalAddress;                                              //0x8
        ULONG VirtualSize;                                                  //0x8
    } Misc;                                                                 //0x8
    ULONG VirtualAddress;                                                   //0xc
    ULONG SizeOfRawData;                                                    //0x10
    ULONG PointerToRawData;                                                 //0x14
    ULONG PointerToRelocations;                                             //0x18
    ULONG PointerToLinenumbers;                                             //0x1c
    USHORT NumberOfRelocations;                                             //0x20
    USHORT NumberOfLinenumbers;                                             //0x22
    ULONG Characteristics;                                                  //0x24
}IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER;


//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
    struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0
    struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x10
    struct _LIST_ENTRY InInitializationOrderLinks;                          //0x20
    VOID* DllBase;                                                          //0x30
    VOID* EntryPoint;                                                       //0x38
    ULONG SizeOfImage;                                                      //0x40
    struct _UNICODE_STRING FullDllName;                                     //0x48
    struct _UNICODE_STRING BaseDllName;                                     //0x58
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
获取SSDT地址

获取区段的地址和大小,也就知道寻找硬编码的起始和结束地址,硬编码也是知道的,剩下就是需要一个工具函数来在起始和结束地址寻找跟硬编码完全匹配的地址,工具函数定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 工具函数:通过起始地址及硬编码获取对应地址
PVOID findAddrByOpcode(char* opCode, PVOID start, PVOID end, ULONG codeSize)
{
    BOOLEAN findOp = FALSE;
    char* p;
    char* q;
    char* r;
    for (p = start; p != end; p++)
    {
        findOp = TRUE;
        for (q = p, r = opCode; q != p + codeSize; q++, r++)
        {
            if (*q != *r)
            {
                findOp = FALSE;
                break;
            }

        }
        if (findOp == TRUE)
        {
            break;
        }
    }
    return findOp ? p : NULL;
}

解析并保存函数

当获取到SSDT的地址之后,需要计算并保存函数地址,64位的函数地址计算方式跟x86不一样,之前有在系统调用的文章里介绍过,可以看这个链接:https://daliu.net/posts/20250108/#%E5%A4%84%E7%90%86%E6%9C%8D%E5%8A%A1%E5%8F%8Assdt

首先需要补充一下SSDT的定义,只有这样才能确保取值是正确的:

1
2
3
4
5
6
typedef struct _ServiceDescriptorEntry {
    PULONG32 ServiceTableBase;        //SystemService Dispatch Table 的基地址
    ULONG64 ServiceCounterTableBase; //包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由 sysenter  更新。
    ULONG64 NumberOfServices;         //由 ServiceTableBase描述的服务的数目。
    PUCHAR  ParamTableBase;         //包含每个系统服务参数字节数表的基地址-系统服务参数表
}SSDT, * PSSDT;

都知道,地址表存储的是一组4字节大小的数值,函数的真实地址是 ServiceTableBase + LONG(ServiceTableBase[i]) >> 4,注意,之所以需要在右移之前先转换成LONG形而不是继续用ULONG是因为,右移不是逻辑右移,是算数右移,也就是说要带符号的,我们把所有的值都经过计算成真实地址并保存,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 通过SSDT基址获取获取所有数据
PVOID getSSDT(PVOID pSSDTBase, PVOID buff)
{
    PSSDT pSSDT = (PSSDT)pSSDTBase;
    ULONG64 fucnNumber = pSSDT->NumberOfServices;
    PULONG32 pFuncBegin = pSSDT->ServiceTableBase;
    PUCHAR  pParamTableBase = pSSDT->ParamTableBase;

    PULONG64 begin = (PULONG64)buff;
    for (ULONG i = 0; i < fucnNumber; i++)
    {
        // 获取地址值,转换为LONG,位移运算才是算是位移:
        ULONG64 addr = (ULONG64)((((LONG32)(pFuncBegin[i])) >> 4) + (ULONG64)pFuncBegin);
        begin[i*2] = addr;
        // 获取参数个数
        ULONG64 param = ((ULONG64)(pParamTableBase[i]))/4;
        begin[i*2 + 1] = param;
    }
    return NULL;
}

应用层与驱动通信

考虑到方便测试,优先用c编写客户端进行通信测试,这样既比较容易通信,又不用切换IDE,驱动层代码中,我们就简单弄两个文件就好,一个是main.c用来搭建通信并调用SSDT获取的函数。另一个文件就是以上获取SSDT并保存SSDT的函数。

驱动代码串联

搭建这个通信的方式我们采用直接I/O方式,用DeviceIOControl的方式,这个在之前的这篇文章有些,可以点击查看,https://daliu.net/posts/20250102/#%E7%9B%B4%E6%8E%A5io%E6%96%B9%E5%BC%8F

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 定义CTL_CODE,也就是通信码
#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,0X801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN,0X802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

PDRIVER_OBJECT g_pDrvierObject;

//创建行为的派发函数
NTSTATUS
DispatchCreate(
    struct _DEVICE_OBJECT* DeviceObject,
    struct _IRP* Irp
)
{

    DbgPrint("IRP_MJ_CREATE");
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

void DriverUnload(PDRIVER_OBJECT pDrvierObject)
{
    //驱动退出部分
    UNICODE_STRING deviceSymbloName = RTL_CONSTANT_STRING(L"\\??\\newDevice");
    IoDeleteSymbolicLink(&deviceSymbloName);
    IoDeleteDevice(pDrvierObject->DeviceObject);
    DbgPrint("DriverUnload");
}

派发函数的定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 处理用DeviceIOControl这种方式通信的派发函数
NTSTATUS
DispatchControl(
    struct _DEVICE_OBJECT* DeviceObject,
    struct _IRP* Irp
)
{
    // 通过irp获取mdl,并通过mdl获取缓冲区地址
    PVOID buff = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG inBuffLen = ioStack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outBuffLen = ioStack->Parameters.DeviceIoControl.OutputBufferLength;

    //通过这个参数获取控制代码,所以在驱动层和用户层,上文中定义控制代码的两个宏都要写出来
    ULONG opCode = ioStack->Parameters.DeviceIoControl.IoControlCode;
    if (MSG_CODE_WRITE == opCode)
    {
        DbgPrint("InputBufferLength===%d", inBuffLen);
        DbgPrint("buff====%s", buff);
        Irp->IoStatus.Information = 1;
    }
    else
    {
        DbgPrint("InputBufferLength===%d", outBuffLen);
        DbgPrint("buff====%s", buff);
        
        // 获取SSDT的目标信息,主要就是这个函数
        getSSDTAll(g_pDrvierObject,buff);

        Irp->IoStatus.Information = outBuffLen;
    }

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

}

DriverEntry的入口函数如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvierObject, PUNICODE_STRING pRetPath)
{

    UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\newDevice");
    PDEVICE_OBJECT PDeviceObj = NULL;

    g_pDrvierObject = pDrvierObject;

    NTSTATUS status = IoCreateDevice(pDrvierObject, 
        0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, 
        FALSE, &PDeviceObj);

    if (!NT_SUCCESS(status))
    {
        DbgPrint("创建一个设备对象失败");
        return status;
    }
    //创建符号链接
    UNICODE_STRING deviceSymbloName = RTL_CONSTANT_STRING(L"\\??\\newDevice");
    status = IoCreateSymbolicLink(&deviceSymbloName, &deviceName);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("创建符号链接失败");
        return status;
    }
    //数据交互方式,直接I/O
    PDeviceObj->Flags |= DO_DIRECT_IO;
    //设置消息回调函数 派遣函数
    pDrvierObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDrvierObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;
    pDrvierObject->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}
用户层测试

然后就是编写用户侧测试代码,这个在之前也详细展开介绍过,这里不特别说明,主要就是还是CTL_CODE,一定是要METHOD_IN_DIRECTMETHOD_OUT_DIRECT,因为是直接I/O的方式,应用层输入的内容,在驱动层是可以看到的,所以这也方便了一个事情,就是用户层可以发不同的调用命令,而驱动层都是可以根据需求而实现不同的服务,具体的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <Windows.h>
#include<iostream>
#include <string>

#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,0X801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN,0X802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

int main ()
{
    DWORD realRead = 0;
    DWORD realWrite = 0;

    //通过创建文件来调用,具体就不展开了
    HANDLE handle = CreateFileW(L"\\\\.\\newDevice",
         GENERIC_ALL, 0, NULL, OPEN_EXISTING, 
        FILE_ATTRIBUTE_SYSTEM, 0);

    if (handle)
    {
        PVOID buff = malloc(0X100000);
        memset(buff, 0, 0X100000);
        memcpy(buff, "newDevice", strlen("newDevice") + 1);
        
        // 调用
        DeviceIoControl(handle, MSG_CODE_READ, 
            NULL, 0, buff, 0X100000, 
            &realRead, NULL);

        PULONG64 begin = (PULONG64)buff;
        for (int i = 0; i < realRead/(2*sizeof(ULONG64)); i=i+2)
        {
            if (begin[i] == 0)
            {
                system("pause");
            }
            printf("address:%llx paramsnum:%llx number:%d\n\r", 
            begin[i],begin[i+1],i/2);
        }

        CloseHandle(handle);
        //方便观察
        system("pause");
    }
    else
    {
        printf("打开设备失败!");
    }
}

以上代码执行之后,成功执行,但是最终不知道是否正确,我通过几个方法进行了验证,一个就是windbg调试,首先在驱动的代码中插入一个函数,DbgBreakPoint();当驱动运行到这个函数之后,就会断下来,然后你可以跟x64dbg一样进行调试。

6

然后我们按步骤运行代码,并且在最后获取SSDT之后,利用SSDT的函数地址计算函数地址,并在windbg反编译地址,成功获取函数名称的打印(u 加地址或者uf 加地址,就是反编译代码)

7

然后还有一个方法,更加边界,就是用工具进行比对,有一个厉害的工具叫PChunter,可以自己想办法获取下,打开里面找到SSDT表,跟我们自己打印的对比下,会发现没什么问题,那就是OK。

8

在用Go实现应用层效果前,为了方便,先把整段驱动层代码放到这里以方便大家查看:

main.c的文件代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include<ntifs.h>
#include "getSSDT.h"

#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,0X801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN,0X802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

PDRIVER_OBJECT g_pDrvierObject;

//创建行为的派发函数
NTSTATUS
DispatchCreate(
    struct _DEVICE_OBJECT* DeviceObject,
    struct _IRP* Irp
)
{

    DbgPrint("IRP_MJ_CREATE");
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

void DriverUnload(PDRIVER_OBJECT pDrvierObject)
{
    //驱动退出部分
    UNICODE_STRING deviceSymbloName = RTL_CONSTANT_STRING(L"\\??\\newDevice");
    IoDeleteSymbolicLink(&deviceSymbloName);
    IoDeleteDevice(pDrvierObject->DeviceObject);
    DbgPrint("DriverUnload");
}


//获取SSDT所有函数
PVOID getSSDTAll(PDRIVER_OBJECT pDrvierObject,PVOID orignbuff)
{
    //获取SSDT
    PVOID ssdtAddr = getSSDTBase(pDrvierObject);
    if (ssdtAddr == NULL)
    {
        DbgPrint("获取SSDT失败");
        return NULL;
    }
    PVOID buff =  ExAllocatePoolWithTag(PagedPool, 0x100000, 'SSDT');
    getSSDT(ssdtAddr,buff);
    if (buff == NULL)
    {
        DbgPrint("内存申请失败");
        return NULL;
    }
    // 复制内存
    memcpy(orignbuff, buff, 0x100000);
    ExFreePoolWithTag(buff, 'SSDT');
    return orignbuff;
}

NTSTATUS
DispatchControl(
    struct _DEVICE_OBJECT* DeviceObject,
    struct _IRP* Irp
)
{
    // 通过irp获取mdl,并通过mdl获取缓冲区地址
    PVOID buff = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG inBuffLen = ioStack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outBuffLen = ioStack->Parameters.DeviceIoControl.OutputBufferLength;

    //通过这个参数获取控制代码,所以在驱动层和用户层,上文中定义控制代码的两个宏都要写出来
    ULONG opCode = ioStack->Parameters.DeviceIoControl.IoControlCode;
    if (MSG_CODE_WRITE == opCode)
    {
        DbgPrint("InputBufferLength===%d", inBuffLen);
        DbgPrint("buff====%s", buff);
        Irp->IoStatus.Information = 1;
    }
    else
    {
        DbgPrint("InputBufferLength===%d", outBuffLen);
        DbgPrint("buff====%s", buff);

        getSSDTAll(g_pDrvierObject,buff);

        Irp->IoStatus.Information = outBuffLen;
    }

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

}


NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvierObject, PUNICODE_STRING pRetPath)
{

    UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\newDevice");
    PDEVICE_OBJECT PDeviceObj = NULL;

    g_pDrvierObject = pDrvierObject;

    NTSTATUS status = IoCreateDevice(pDrvierObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &PDeviceObj);

    if (!NT_SUCCESS(status))
    {
        DbgPrint("创建一个设备对象失败");
        return status;
    }
    //创建符号链接
    UNICODE_STRING deviceSymbloName = RTL_CONSTANT_STRING(L"\\??\\newDevice");
    status = IoCreateSymbolicLink(&deviceSymbloName, &deviceName);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("创建符号链接失败");
        return status;
    }
    //数据交互方式
    //PDeviceObj->Flags |= DO_BUFFERED_IO;
    PDeviceObj->Flags |= DO_DIRECT_IO;
    //设置消息回调函数 派遣函数
    pDrvierObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDrvierObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;
    pDrvierObject->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

getSSDT.c代码如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#include<ntifs.h>

// 定义ssdt的硬编码
unsigned char ssdt_chars[] =
{
  0x48, 0x8B, 0x45, 0xB0, 0x48, 0x8B, 0x4D, 0xB8, 0x48, 0x8B,
  0x55, 0xC0, 0xFB, 0x48, 0x89, 0x8B, 0x88, 0x00, 0x00, 0x00,
  0x89, 0x83, 0x80, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x84, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xA3, 0x90, 0x00, 0x00,
  0x00, 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25,
  0xFF, 0x0F, 0x00, 0x00
};

// 定义一个存储模块信息的结构体
typedef struct _MODULE_INFO
{
    PULONG64 ModuleBase;
    ULONG64 ModuleSize;
}MODULE_INFO, *PMODULE_INFO;

// 定义一个存储区段信息的结构体
typedef struct _SECTION_INFO
{
    PULONG64 sectionAddr;
    ULONG64 sectionSize;
}SECTION_INFO, *PSECTION_INFO;

// 定义PE文件中的结构体


//0x40 bytes (sizeof)
typedef struct _IMAGE_DOS_HEADER
{
    USHORT e_magic;                                                         //0x0
    USHORT e_cblp;                                                          //0x2
    USHORT e_cp;                                                            //0x4
    USHORT e_crlc;                                                          //0x6
    USHORT e_cparhdr;                                                       //0x8
    USHORT e_minalloc;                                                      //0xa
    USHORT e_maxalloc;                                                      //0xc
    USHORT e_ss;                                                            //0xe
    USHORT e_sp;                                                            //0x10
    USHORT e_csum;                                                          //0x12
    USHORT e_ip;                                                            //0x14
    USHORT e_cs;                                                            //0x16
    USHORT e_lfarlc;                                                        //0x18
    USHORT e_ovno;                                                          //0x1a
    USHORT e_res[4];                                                        //0x1c
    USHORT e_oemid;                                                         //0x24
    USHORT e_oeminfo;                                                       //0x26
    USHORT e_res2[10];                                                      //0x28
    LONG e_lfanew;                                                          //0x3c
}IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;

//0x14 bytes (sizeof)
typedef struct _IMAGE_FILE_HEADER
{
    USHORT Machine;                                                         //0x0
    USHORT NumberOfSections;                                                //0x2
    ULONG TimeDateStamp;                                                    //0x4
    ULONG PointerToSymbolTable;                                             //0x8
    ULONG NumberOfSymbols;                                                  //0xc
    USHORT SizeOfOptionalHeader;                                            //0x10
    USHORT Characteristics;                                                 //0x12
}IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER;

//0x8 bytes (sizeof)
typedef struct _IMAGE_DATA_DIRECTORY
{
    ULONG VirtualAddress;                                                   //0x0
    ULONG Size;                                                             //0x4
}IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY;

//0xf0 bytes (sizeof)
typedef struct _IMAGE_OPTIONAL_HEADER64
{
    USHORT Magic;                                                           //0x0
    UCHAR MajorLinkerVersion;                                               //0x2
    UCHAR MinorLinkerVersion;                                               //0x3
    ULONG SizeOfCode;                                                       //0x4
    ULONG SizeOfInitializedData;                                            //0x8
    ULONG SizeOfUninitializedData;                                          //0xc
    ULONG AddressOfEntryPoint;                                              //0x10
    ULONG BaseOfCode;                                                       //0x14
    ULONGLONG ImageBase;                                                    //0x18
    ULONG SectionAlignment;                                                 //0x20
    ULONG FileAlignment;                                                    //0x24
    USHORT MajorOperatingSystemVersion;                                     //0x28
    USHORT MinorOperatingSystemVersion;                                     //0x2a
    USHORT MajorImageVersion;                                               //0x2c
    USHORT MinorImageVersion;                                               //0x2e
    USHORT MajorSubsystemVersion;                                           //0x30
    USHORT MinorSubsystemVersion;                                           //0x32
    ULONG Win32VersionValue;                                                //0x34
    ULONG SizeOfImage;                                                      //0x38
    ULONG SizeOfHeaders;                                                    //0x3c
    ULONG CheckSum;                                                         //0x40
    USHORT Subsystem;                                                       //0x44
    USHORT DllCharacteristics;                                              //0x46
    ULONGLONG SizeOfStackReserve;                                           //0x48
    ULONGLONG SizeOfStackCommit;                                            //0x50
    ULONGLONG SizeOfHeapReserve;                                            //0x58
    ULONGLONG SizeOfHeapCommit;                                             //0x60
    ULONG LoaderFlags;                                                      //0x68
    ULONG NumberOfRvaAndSizes;                                              //0x6c
    struct _IMAGE_DATA_DIRECTORY DataDirectory[16];                         //0x70
}IMAGE_OPTIONAL_HEADER64, * PIMAGE_OPTIONAL_HEADER64;

//0x108 bytes (sizeof)
typedef struct _IMAGE_NT_HEADERS64
{
    ULONG Signature;                                                        //0x0
    struct _IMAGE_FILE_HEADER FileHeader;                                   //0x4
    struct _IMAGE_OPTIONAL_HEADER64 OptionalHeader;                         //0x18
}IMAGE_NT_HEADERS64, * PIMAGE_NT_HEADERS64;

//0x28 bytes (sizeof)
typedef struct _IMAGE_SECTION_HEADER
{
    UCHAR Name[8];                                                          //0x0
    union
    {
        ULONG PhysicalAddress;                                              //0x8
        ULONG VirtualSize;                                                  //0x8
    } Misc;                                                                 //0x8
    ULONG VirtualAddress;                                                   //0xc
    ULONG SizeOfRawData;                                                    //0x10
    ULONG PointerToRawData;                                                 //0x14
    ULONG PointerToRelocations;                                             //0x18
    ULONG PointerToLinenumbers;                                             //0x1c
    USHORT NumberOfRelocations;                                             //0x20
    USHORT NumberOfLinenumbers;                                             //0x22
    ULONG Characteristics;                                                  //0x24
}IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER;


//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
    struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0
    struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x10
    struct _LIST_ENTRY InInitializationOrderLinks;                          //0x20
    VOID* DllBase;                                                          //0x30
    VOID* EntryPoint;                                                       //0x38
    ULONG SizeOfImage;                                                      //0x40
    struct _UNICODE_STRING FullDllName;                                     //0x48
    struct _UNICODE_STRING BaseDllName;                                     //0x58
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

// 定义SSDT结构体

typedef struct _ServiceDescriptorEntry {
    PULONG32 ServiceTableBase;        //SystemService Dispatch Table 的基地址
    ULONG64 ServiceCounterTableBase; //包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由 sysenter  更新。
    ULONG64 NumberOfServices;         //由 ServiceTableBase描述的服务的数目。
    PUCHAR  ParamTableBase;         //包含每个系统服务参数字节数表的基地址-系统服务参数表
}SSDT, * PSSDT;


// 工具函数:通过起始地址及硬编码获取对应地址
PVOID findAddrByOpcode(char* opCode, PVOID start, PVOID end, ULONG codeSize)
{
    BOOLEAN findOp = FALSE;
    char* p;
    char* q;
    char* r;
    for (p = start; p != end; p++)
    {
        findOp = TRUE;
        for (q = p, r = opCode; q != p + codeSize; q++, r++)
        {
            if (*q != *r)
            {
                findOp = FALSE;
                break;
            }

        }
        if (findOp == TRUE)
        {
            break;
        }
    }
    return findOp ? p : NULL;
}

// 根据模块名称获取当前进程的下对应模块的信息
PMODULE_INFO getProcessInfoByName(char* processName, PDRIVER_OBJECT pDrvierObject)
{
    ANSI_STRING orignName = { 0 };
    RtlInitAnsiString(&orignName, processName);

    UNICODE_STRING ModuleName = { 0 };
    RtlAnsiStringToUnicodeString(&ModuleName, &orignName, TRUE);

    //通过当前驱动对象获取镜像对应结构体
    PLDR_DATA_TABLE_ENTRY pLdr = NULL;
    PLIST_ENTRY pListEntry = NULL;
    PLIST_ENTRY pCurrentListEntry = NULL;

    PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
    pLdr = (PLDR_DATA_TABLE_ENTRY)pDrvierObject->DriverSection;
    pListEntry = pLdr->InLoadOrderLinks.Flink;
    pCurrentListEntry = pListEntry->Flink;

    while (pCurrentListEntry != pListEntry) //前后不相等
    {
        //获取LDR_DATA_TABLE_ENTRY结构
        pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

        if (pCurrentModule->BaseDllName.Buffer != 0)
        {

            if (!RtlCompareUnicodeString(&ModuleName, &(pCurrentModule->BaseDllName), TRUE))
            {
                
                DbgPrint("ModuleName = %wZ ModuleBase = %p ModuleEndBase = %p\r\n",
                    pCurrentModule->BaseDllName,
                    pCurrentModule->DllBase,
                    (LONGLONG)pCurrentModule->DllBase + pCurrentModule->SizeOfImage);
                MODULE_INFO moduleInfo = { 0 };
                moduleInfo.ModuleBase = pCurrentModule->DllBase;
                moduleInfo.ModuleSize = pCurrentModule->SizeOfImage;
                return &moduleInfo;

            }

        }
        pCurrentListEntry = pCurrentListEntry->Flink;
    }
    return NULL;
}


// 根据模块地址及区段名称获取区段信息
void getSectionsAddrByImageBase(PVOID imageBase, char* sectionName, PSECTION_INFO sectionInfo)
{
    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS64 pImageNtHeader = (PIMAGE_NT_HEADERS64)(pImageDosHeader->e_lfanew + (ULONG64)pImageDosHeader);


    //区段头s的地址
    PIMAGE_OPTIONAL_HEADER64 pImageOptionalHeaders = &(pImageNtHeader->OptionalHeader);
    PIMAGE_SECTION_HEADER pFirstSectionHeader = pImageNtHeader->FileHeader.SizeOfOptionalHeader + (ULONG64)pImageOptionalHeaders;
    try
    {
        for (int i = 0; i < pImageNtHeader->FileHeader.NumberOfSections; i++)
        {
            PIMAGE_SECTION_HEADER pResSection = pFirstSectionHeader + i;

            // 判断如果名称相同,则返回区段地址
            if (strstr(pResSection->Name, sectionName))
            {
                DbgPrint("SectionName = %s SectionAddr = %p SectionSize = %d\r\n",
                    pResSection->Name,
                    pResSection->VirtualAddress + (ULONG64)imageBase,
                    pResSection->SizeOfRawData);

                //SECTION_INFO section = { 0 };
                sectionInfo->sectionAddr = (PULONG64)(pResSection->VirtualAddress + (ULONG64)imageBase);
                sectionInfo->sectionSize = pResSection->SizeOfRawData;

                return;
            }
      
        }
    }
    except(1)
    {
        DbgPrint("faild get sections");
    }

    return;
}


// 通过区段,模块名称及硬编码获取SSDT基址
PVOID getSSDTBase(PDRIVER_OBJECT pDrvierObject)
{
    //  获取模块信息
    PMODULE_INFO pModuleInfo = getProcessInfoByName("ntoskrnl.exe", pDrvierObject);

    if (pModuleInfo == NULL)
    {
        return NULL;
    }
    
    // 通过模块地址获取区段信息
    SECTION_INFO sectionInfo = { 0 };
    getSectionsAddrByImageBase(pModuleInfo->ModuleBase, "text",&sectionInfo);

    // 获取SSDT基址
    PVOID pSSDTflag = findAddrByOpcode(
        ssdt_chars,
        sectionInfo.sectionAddr,
        (PVOID)((ULONG64)(sectionInfo.sectionAddr) + sectionInfo.sectionSize),
        sizeof(ssdt_chars));

    PUCHAR pSSDTBase = (PUCHAR)pSSDTflag + sizeof(ssdt_chars);
    if (pSSDTBase[0] == 0x4C && pSSDTBase[1] == 0x8D && pSSDTBase[2] == 0x15
        && pSSDTBase[7] == 0x4C && pSSDTBase[8] == 0x8D && pSSDTBase[9] == 0x1D)
    {
            PULONG64 pSSDTAddr = *(PULONG32)(pSSDTBase + 3) + pSSDTBase+7;
            return pSSDTAddr;
    }

    return NULL;
    
}

// 通过SSDT基址获取获取所有数据
PVOID getSSDT(PVOID pSSDTBase, PVOID buff)
{
    PSSDT pSSDT = (PSSDT)pSSDTBase;
    ULONG64 fucnNumber = pSSDT->NumberOfServices;
    PULONG32 pFuncBegin = pSSDT->ServiceTableBase;
    PUCHAR  pParamTableBase = pSSDT->ParamTableBase;

    PULONG64 begin = (PULONG64)buff;
    for (ULONG i = 0; i < fucnNumber; i++)
    {
        // 获取地址值,转换为LONG,位移运算才是算是位移:
        ULONG64 addr = (ULONG64)((((LONG32)(pFuncBegin[i])) >> 4) + (ULONG64)pFuncBegin);
        begin[i*2] = addr;
        // 获取参数个数
        ULONG64 param = ((ULONG64)(pParamTableBase[i]))/4;
        begin[i*2 + 1] = param;
    }
    return NULL;
}

方便函数调用再加一个头文件,getSSDT.h

1
2
3
4
5
#pragma once
#include<ntifs.h>

PVOID getSSDTBase(PDRIVER_OBJECT pDrvierObject);
PVOID getSSDT(PVOID pSSDTBase, PVOID buff);

因为篇幅过长,这篇就先介绍到这里,后面再写一篇会详细展开介绍如何通过go来实现应用层需要的内容。

updatedupdated2025-01-212025-01-21