SSDT全称为System Service Descriptor Table,即系统服务描述符表。SSDT的作用就是把Ring3的WIN32 API函数和Ring0的内核API联系起来。R3下的一些API最终会对应ntdll.dll中的一个Nt开头的函数。将系统服务号放入EAX然后调用KiSystemService进入内核根据系统服务号得到对应系统服务的内核地址。HIPS,杀毒软件往往会通过SSDT HOOK实现监控系统的目的。
32位系统中SSDT是由内核Ntoskrnl.exe导出的一张表,导出符号为KeServiceDescriptorTable。在64位系统上是不导出的。
以下程序实现将ntdll.dll文件映射到内核空间并从导出表中获取导出函数地址,然后获取SSDT函数索引号。
具体操作流程如下:
1 调用InitializeObjectAttributes宏初始化文件对象,它主要初始化文件路径,内核下的文件路径需要在路径前面加上\??\
2 调用ZwCreateSection函数生成一个内存映射对象得到句柄hSection,该对象建立在由ZwOpenFile函数创建的文件对象基础上。该内存映射对象对视图读写具有SECTION_MAP_READ和SECTION_MAP_WRITE访问权限,同时设置内存映射方式为SEC_IMAGE,以PE中的SectionAlignment大小对齐映射文件
3 调用ZwMapViewOfSection函数把文件映射到内存得到lpMemory。该指针直接读写内存
4 调用ZwUnmapViewOfSection解除文件映射,进程句柄,lpMemory作为参数。
5 接下来根据PE文件结构获取导出函数地址并从导出函数中获取SSDT函数索引号,根据IMAGE_DOS_HEADER和IMAGE_NT_HEADERS计算出OptionalHeader,获取数据目录中导出表的RVA地址查找匹配的函数名称从AddressOfNamesOrdinal中获取索引值,32位系统下导出函数偏移1字节后的四字节就是SSDT函数索引号,64位系统下需要偏移4字节
获取SSDT函数地址
32系统上获取SSDT地址,直接获取Ntoskrnl.exe导出符号KeServiceDescriptorTable即可
64位系统下无法直接获取导出符号KeServiceDescriptorTable,从KiSystemCall64函数中进行内存扫描获取KeServiceDescriptorTable的地址
SSDT HOOK实现
64位系统Patch Guard机制把SSDT所在的内存作为重点保护对象,修改SSDT内存会触发Patch Guard导致系统蓝屏,64位系统下要使用SSDT HOOK必须绕过Patch Guard。
将SSDT表中存储的ZwQueryDriverFile函数地址修改成新函数的地址,当系统从SSDT中获取ZwQueryDirectoryFile函数地址的时候获取到的就是新函数的地址,其中,SSDT的内存是写保护属性的,所以采用MDL方式可以绕过写保护属性。
2 过滤驱动
设备栈中的成员是设备,IRP的传递方向是由上到下,新添加的设备总是附加在设备栈的顶部,最上层的设备最先获得IRP
键盘过滤驱动创建一个键盘设备将它附加在键盘类KbdClass设备栈上,当有键盘按下IRP消息的时候该设备最先接收到IRP消息,实现IRP HOOK。
创建过滤驱动设备的过程如下:
1 IoCreateDevice创建一个FILE_DEVICE_KEYBOARD键盘设备并调用IoCreateSymbolicLink为该设备创建一个符号链接。
2 调用 IoGetDeviceObjectPointer函数获取KeyboardClass0驱动设备对象
3 通过IoAttachDeviceToDeviceStack将创建的键盘设备附加到KeyboardClass0设备对象所在的设备栈顶
自定义键盘驱动捕获到IRP请求再把该IRP请求发送给操作系统
IRP读请求中处理流程如下:
1 调用IoCopyCurrentIrpStackLocationToNext将当前设备中的IRP复制到下一个设备中
2 调用IoSetCompletionRoutine函数设置完成回调函数,将在下一层驱动完成由IRP指定的操作请求时调用这个函数
3 调用IoCallDriver函数将IRP发送到下一个设备
完成回调函数的具体代码如下
任何时候设备栈底都会有一个键盘的IRP_MJ_READ请求处于挂起状态。一般的方式卸载键盘过滤驱动基本都有IRP_MJ_READ请求处于挂起未确定状态。这时若执行卸载驱动操作那么在以后按键需要这个IRP时却找不到对应驱动,从而导致计算机蓝屏。所以必须处理好这个挂起状态的IRP。
实现动态卸载过滤的方法是在设备增加一个DeviceExtension,并添加一个u1IrpInQuene变量来记录。卸载驱动的时候先调用IoDetachDevice,取消附加键盘设备。一直循环判断u1IrpInQuene是否为0,若不未0则一直循环等待。直到用户手动按键就会在IRP中返回。由于不附加键盘设备栈,所以也不会有新的IRP_MJ_READ产生,此时的u1IrpInQuene的值是0,可以正确卸载。
卸载程序:
参考资料:
看雪论坛:通俗解析IRP和I/O设备栈在内核程序中的作用https://bbs.pediy.com/thread-111559.htm
《黑客免杀攻防》
《黑客编程技术详解》
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据索引号, 从SSDT表中获取对应函数地址
pFunctionAddress
=
(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];
/
/
显示
DbgPrint(
"[%s][Index:%d][Address:0x%p]\n"
, pszFunctionName, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据索引号, 从SSDT表中获取对应函数地址
pFunctionAddress
=
(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];
/
/
显示
DbgPrint(
"[%s][Index:%d][Address:0x%p]\n"
, pszFunctionName, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
PSSDTEntry pServiceDescriptorTable
=
NULL;
ULONG ulOffset
=
0
;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据特征码, 从 KiSystemCall64 中获取 SSDT 地址
pServiceDescriptorTable
=
GetSSDTAddress();
/
/
根据索引号, 从SSDT表中获取对应函数偏移地址并计算出函数地址
ulOffset
=
pServiceDescriptorTable
-
>ServiceTableBase[ulSSDTFunctionIndex] >>
4
;
pFunctionAddress
=
(PVOID)((PUCHAR)pServiceDescriptorTable
-
>ServiceTableBase
+
ulOffset);
/
/
显示
DbgPrint(
"[%s][SSDT Addr:0x%p][Index:%d][Address:0x%p]\n"
, pszFunctionName, pServiceDescriptorTable, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
根据特征码, 从 KiSystemCall64 中获取 SSDT 地址
PVOID GetSSDTAddress()
{
PVOID pServiceDescriptorTable
=
NULL;
PVOID pKiSystemCall64
=
NULL;
UCHAR ulCode1
=
0
;
UCHAR ulCode2
=
0
;
UCHAR ulCode3
=
0
;
/
/
注意使用有符号整型
LONG
lOffset
=
0
;
/
/
获取 KiSystemCall64 函数地址
pKiSystemCall64
=
(PVOID)__readmsr(
0xC0000082
);
/
/
搜索特征码
4C8D15
for
(ULONG i
=
0
; i <
1024
; i
+
+
)
{
/
/
获取内存数据
ulCode1
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i));
ulCode2
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i
+
1
));
ulCode3
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i
+
2
));
/
/
判断
if
(
0x4C
=
=
ulCode1 &&
0x8D
=
=
ulCode2 &&
0x15
=
=
ulCode3)
{
/
/
获取偏移
lOffset
=
*
((PLONG)((PUCHAR)pKiSystemCall64
+
i
+
3
));
/
/
根据偏移计算地址
pServiceDescriptorTable
=
(PVOID)(((PUCHAR)pKiSystemCall64
+
i)
+
7
+
lOffset);
break
;
}
}
return
pServiceDescriptorTable;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
PSSDTEntry pServiceDescriptorTable
=
NULL;
ULONG ulOffset
=
0
;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据特征码, 从 KiSystemCall64 中获取 SSDT 地址
pServiceDescriptorTable
=
GetSSDTAddress();
/
/
根据索引号, 从SSDT表中获取对应函数偏移地址并计算出函数地址
ulOffset
=
pServiceDescriptorTable
-
>ServiceTableBase[ulSSDTFunctionIndex] >>
4
;
pFunctionAddress
=
(PVOID)((PUCHAR)pServiceDescriptorTable
-
>ServiceTableBase
+
ulOffset);
/
/
显示
DbgPrint(
"[%s][SSDT Addr:0x%p][Index:%d][Address:0x%p]\n"
, pszFunctionName, pServiceDescriptorTable, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
根据特征码, 从 KiSystemCall64 中获取 SSDT 地址
PVOID GetSSDTAddress()
{
PVOID pServiceDescriptorTable
=
NULL;
PVOID pKiSystemCall64
=
NULL;
UCHAR ulCode1
=
0
;
UCHAR ulCode2
=
0
;
UCHAR ulCode3
=
0
;
/
/
注意使用有符号整型
LONG
lOffset
=
0
;
/
/
获取 KiSystemCall64 函数地址
pKiSystemCall64
=
(PVOID)__readmsr(
0xC0000082
);
/
/
搜索特征码
4C8D15
for
(ULONG i
=
0
; i <
1024
; i
+
+
)
{
/
/
获取内存数据
ulCode1
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i));
ulCode2
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i
+
1
));
ulCode3
=
*
((PUCHAR)((PUCHAR)pKiSystemCall64
+
i
+
2
));
/
/
判断
if
(
0x4C
=
=
ulCode1 &&
0x8D
=
=
ulCode2 &&
0x15
=
=
ulCode3)
{
/
/
获取偏移
lOffset
=
*
((PLONG)((PUCHAR)pKiSystemCall64
+
i
+
3
));
/
/
根据偏移计算地址
pServiceDescriptorTable
=
(PVOID)(((PUCHAR)pKiSystemCall64
+
i)
+
7
+
lOffset);
break
;
}
}
return
pServiceDescriptorTable;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据索引号, 从SSDT表中获取对应函数地址
pFunctionAddress
=
(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];
/
/
显示
/
/
DbgPrint(
"[%s][Index:%d][Address:0x%p]\n"
, pszFunctionName, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
映射到内存
status
=
ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress,
0
,
1024
,
0
, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
if
(!NT_SUCCESS(status))
{
ZwClose(hSection);
ZwClose(hFile);
KdPrint((
"ZwMapViewOfSection Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
返回数据
*
phFile
=
hFile;
*
phSection
=
hSection;
*
ppBaseAddress
=
pBaseAddress;
return
status;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
/
/
Dos Header
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)pBaseAddress;
/
/
NT Header
PIMAGE_NT_HEADERS pNtHeaders
=
(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader
+
pDosHeader
-
>e_lfanew);
/
/
Export Table
PIMAGE_EXPORT_DIRECTORY pExportTable
=
(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader
+
pNtHeaders
-
>OptionalHeader.DataDirectory[
0
].VirtualAddress);
/
/
有名称的导出函数个数
ULONG ulNumberOfNames
=
pExportTable
-
>NumberOfNames;
/
/
导出函数名称地址表
PULONG lpNameArray
=
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNames);
PCHAR lpName
=
NULL;
/
/
开始遍历导出表
for
(ULONG i
=
0
; i < ulNumberOfNames; i
+
+
)
{
lpName
=
(PCHAR)((PUCHAR)pDosHeader
+
lpNameArray[i]);
/
/
判断是否查找的函数
if
(
0
=
=
_strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
{
/
/
获取导出函数地址
USHORT uHint
=
*
(USHORT
*
)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfNameOrdinals
+
2
*
i);
ULONG ulFuncAddr
=
*
(PULONG)((PUCHAR)pDosHeader
+
pExportTable
-
>AddressOfFunctions
+
4
*
uHint);
PVOID lpFuncAddr
=
(PVOID)((PUCHAR)pDosHeader
+
ulFuncAddr);
/
/
获取 SSDT 函数 Index
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
4
);
ulFunctionIndex
=
*
(ULONG
*
)((PUCHAR)lpFuncAddr
+
1
);
break
;
}
}
return
ulFunctionIndex;
}
/
/
获取 SSDT 函数地址
PVOID GetSSDTFunction(PCHAR pszFunctionName)
{
UNICODE_STRING ustrDllFileName;
ULONG ulSSDTFunctionIndex
=
0
;
PVOID pFunctionAddress
=
NULL;
RtlInitUnicodeString(&ustrDllFileName, L
"\\??\\C:\\Windows\\System32\\ntdll.dll"
);
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ulSSDTFunctionIndex
=
GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName);
/
/
根据索引号, 从SSDT表中获取对应函数地址
pFunctionAddress
=
(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];
/
/
显示
/
/
DbgPrint(
"[%s][Index:%d][Address:0x%p]\n"
, pszFunctionName, ulSSDTFunctionIndex, pFunctionAddress);
return
pFunctionAddress;
}
/
/
从 ntdll.dll 中获取 SSDT 函数索引号
ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
ULONG ulFunctionIndex
=
0
;
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
PVOID pBaseAddress
=
NULL;
/
/
内存映射文件
status
=
DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
if
(!NT_SUCCESS(status))
{
KdPrint((
"DllFileMap Error!\n"
));
return
ulFunctionIndex;
}
/
/
根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
ulFunctionIndex
=
GetIndexFromExportTable(pBaseAddress, pszFunctionName);
/
/
释放
ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
ZwClose(hSection);
ZwClose(hFile);
return
ulFunctionIndex;
}
/
/
内存映射文件
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE
*
phFile, HANDLE
*
phSection, PVOID
*
ppBaseAddress)
{
NTSTATUS status
=
STATUS_SUCCESS;
HANDLE hFile
=
NULL;
HANDLE hSection
=
NULL;
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
IO_STATUS_BLOCK iosb
=
{
0
};
PVOID pBaseAddress
=
NULL;
SIZE_T viewSize
=
0
;
/
/
打开 DLL 文件, 并获取文件句柄
InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status
=
ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if
(!NT_SUCCESS(status))
{
KdPrint((
"ZwOpenFile Error! [error code: 0x%X]"
, status));
return
status;
}
/
/
创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
status
=
ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL,
0
, PAGE_READWRITE,
0x1000000
, hFile);
if
(!NT_SUCCESS(status))
{
ZwClose(hFile);
KdPrint((
"ZwCreateSection Error! [error code: 0x%X]"
, status));
return
status;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)