在本人前一篇博文 《驅動開發(fā):通過ReadFile與內核層通信》
詳細介紹了如何使用應用層 ReadFile
系列函數(shù)實現(xiàn)內核通信,本篇將繼續(xù)延申這個知識點,介紹利用 PIPE
命名管道實現(xiàn)應用層與內核層之間的多次通信方法。
- 什么是PIPE管道?
在Windows編程中,數(shù)據(jù)重定向需要用到管道PIPE,管道是一種用于在進程間共享數(shù)據(jù)的機制,通常由兩端組成,數(shù)據(jù)從一端流入則必須從令一端流出,也就是一讀一寫,利用這種機制即可實現(xiàn)進程間直接通信。管道的本質其實是一段共享內存區(qū)域,多數(shù)情況下管道是用于應用層之間的數(shù)據(jù)交換的,其實驅動中依然可以使用命名管道實現(xiàn)應用層與內核層的直接通信。
那么如何在內核中創(chuàng)建一個管道?請看以下代碼片段,以及MSDN針對函數(shù)的解析。
-
InitializeObjectAttributes
- 初始化一個
OBJECT_ATTRIBUTES
結構,它設置將被打開的對象句柄的屬性。然后調用方可以將一個指向該結構的指針傳遞給實際打開句柄的例程。
- 初始化一個
-
ZwCreateFile
- 該函數(shù)的作用時創(chuàng)建或打開一個已經(jīng)存在的文件,在這里其實是打開
objAttr
這個文件。
- 該函數(shù)的作用時創(chuàng)建或打開一個已經(jīng)存在的文件,在這里其實是打開
-
KeInitializeEvent
- 將事件對象初始化為同步 (單個服務) 或通知類型事件,并將其設置為已發(fā)出信號或未發(fā)出信號的狀態(tài)。
HANDLE g_hClient;
IO_STATUS_BLOCK g_ioStatusBlock;
KEVENT g_event;
VOID NdisMSleep(IN ULONG MicrosecondsToSleep);
// 初始化管道
void init()
{
UNICODE_STRING uniName;
OBJECT_ATTRIBUTES objAttr;
RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\LySharkPipeConn");
InitializeObjectAttributes(&objAttr, &uniName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!g_hClient)
{
return;
}
KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);
}
原理就是打開 \\DosDevices\\Pipe\\LySharkPipeConn
文件,然后將事件對象初始化為同步狀態(tài)。
接下來就是如何將數(shù)據(jù)發(fā)送給應用層的問題,發(fā)送問題可以調用 ZwWriteFile
這個內核函數(shù),如下我們實現(xiàn)的效果是將一個 char
類型的字符串傳輸給應用層。
// 將數(shù)據(jù)傳到R3應用層
// LyShark
VOID ReportToR3(char* m_parameter, int lent)
{
if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL)))
{
DbgPrint("寫出錯誤");
}
}
內核層的核心代碼就是如上這些,將這些整合在一起完整代碼如下所示:
#include <ntifs.h>
#include <ndis.h>
#include <stdio.h>
HANDLE g_hClient;
IO_STATUS_BLOCK g_ioStatusBlock;
KEVENT g_event;
VOID NdisMSleep(IN ULONG MicrosecondsToSleep);
// 初始化管道
void init()
{
UNICODE_STRING uniName;
OBJECT_ATTRIBUTES objAttr;
RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\LySharkPipeConn");
InitializeObjectAttributes(&objAttr, &uniName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!g_hClient)
{
return;
}
KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);
}
// 將數(shù)據(jù)傳到R3應用層
// LyShark
VOID ReportToR3(char* m_parameter, int lent)
{
if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL)))
{
DbgPrint("寫出錯誤");
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動卸載成功 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
init();
// 延時3秒
NdisMSleep(3000000);
DbgPrint("hello lyshark \n");
for (int x = 0; x < 10; x++)
{
// 分配空間
char *report = (char*)ExAllocatePoolWithTag(NonPagedPool, 4096, 'lysh');
if (report)
{
RtlZeroMemory(report, 4096);
RtlCopyMemory(report, "hello lyshark", 13);
// 發(fā)送到應用層
ReportToR3(report, 4096);
ExFreePool(report);
}
}
DbgPrint("驅動加載成功 \n");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
內核中創(chuàng)建了命名管道,客戶端就需要創(chuàng)建一個相同名稱的管道,并通過 ReadFile
函數(shù)讀取管道中的數(shù)據(jù),應用層核心代碼如下所示:
#include <iostream>
#include <windows.h>
int main(int argc, char *argv[])
{
HANDLE hPipe = CreateNamedPipe(TEXT("\\\\.\\Pipe\\LySharkPipeConn"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, NULL);
if (INVALID_HANDLE_VALUE == hPipe)
{
return false;
}
const int size = 1024 * 10;
char buf[size];
DWORD rlen = 0;
while (true)
{
//if (ConnectNamedPipe(hPipe, NULL) != NULL)
// PowerBy: LyShark.com
if (1)
{
if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE)
{
continue;
}
else
{
//接收信息
char* buffer_tmp = (char*)&buf;
// 拷貝前半部分,不包括 buffer_data
char* buffer = (char*)malloc(size);
memcpy(buffer, buffer_tmp, size);
printf("內核層數(shù)據(jù): %s \n", buffer);
free(buffer_tmp);
free(buffer);
}
}
}
system("pause");
return 0;
}
至此將驅動簽名后運行,并迅速打開應用層程序等待同步發(fā)送事件,即可得到如下返回結果。
此處有必要解釋一下為什么會寫出錯誤,很簡單這段代碼并沒有控制何時觸發(fā)事件,導致兩邊不同步,因為只是一個案例用于演示管道的應用方法,所以大家不要太較真,如果不想出錯誤這段代碼還有很多需要改進的地方。
管道不僅可以傳輸字符串完全可以傳輸結構體數(shù)據(jù),如下我們定義一個 Networkreport
結構體,并通過管道的方式多次傳輸給應用層,這部分傳輸模式適合用于驅動中一次性突出多個結構體,例如進程列表的輸出,ARK工具中的驅動列表輸出等功能的實現(xiàn)。
驅動層完整代碼
#include <ntifs.h>
#include <ndis.h>
#include <stdio.h>
HANDLE g_hClient;
IO_STATUS_BLOCK g_ioStatusBlock;
KEVENT g_event;
typedef struct
{
int type;
unsigned long address;
unsigned long buffer_data_len;
char buffer_data[0];
}Networkreport;
VOID NdisMSleep(IN ULONG MicrosecondsToSleep);
// 初始化管道
void init()
{
UNICODE_STRING uniName;
OBJECT_ATTRIBUTES objAttr;
RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\LySharkPipeConn");
InitializeObjectAttributes(&objAttr, &uniName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!g_hClient)
{
return;
}
KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);
}
// 將數(shù)據(jù)傳到R3應用層
// PowerBy: LyShark.com
VOID ReportToR3(Networkreport* m_parameter, int lent)
{
if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL)))
{
DbgPrint("寫出錯誤");
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動卸載成功 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
init();
// 延時3秒
NdisMSleep(3000000);
DbgPrint("hello lyshark \n");
for (int x = 0; x < 10; x++)
{
// 分配空間
Networkreport *report = (Networkreport*)ExAllocatePoolWithTag(NonPagedPool, 4096, 'lysh');
if (report)
{
RtlZeroMemory(report, 4096);
report->type = x;
report->address = 401000 + x;
report->buffer_data_len = 13;
// 定位到結構體最后一個元素上
unsigned char * tmp = (unsigned char *)report + sizeof(Networkreport);
memcpy(tmp, "hello lyshark", 13);
// 發(fā)送到應用層
ReportToR3(report, 4096);
ExFreePool(report);
}
}
DbgPrint("驅動加載成功 \n");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
應用層完整代碼 文章來源:http://www.zghlxwxcb.cn/news/detail-828422.html
#include <iostream>
#include <windows.h>
typedef struct
{
int type;
unsigned long address;
unsigned long buffer_data_len;
char *buffer_data;
}Networkreport;
int main(int argc, char *argv[])
{
HANDLE hPipe = CreateNamedPipe(TEXT("\\\\.\\Pipe\\LySharkPipeConn"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, NULL);
if (INVALID_HANDLE_VALUE == hPipe)
{
return false;
}
const int size = 1024 * 10;
char buf[size];
DWORD rlen = 0;
while (true)
{
//if (ConnectNamedPipe(hPipe, NULL) != NULL)
if (1 == 1)
{
if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE)
{
continue;
}
else
{
//接收信息
Networkreport* buffer_tmp = (Networkreport*)&buf;
SIZE_T buffer_len = sizeof(Networkreport) + buffer_tmp->buffer_data_len;
// 拷貝前半部分,不包括 buffer_data
Networkreport* buffer = (Networkreport*)malloc(buffer_len);
memcpy(buffer, buffer_tmp, buffer_len);
// 對后半部 分配空間
// By: LyShark
char* data = (char*)malloc(buffer->buffer_data_len);
unsigned char* tmp = (unsigned char *)buffer + sizeof(Networkreport) - 4;
memcpy(data, tmp, buffer->buffer_data_len);
printf("輸出數(shù)據(jù): %s \n", data);
printf("地址: %d \n", buffer_tmp->address);
printf("長度: %d \n", buffer_tmp->type);
printf("輸出長度: %d \n", buffer_tmp->buffer_data_len);
free(data);
free(buffer);
}
}
}
system("pause");
return 0;
}
結構體一次性輸出多個,效果如下所示: 文章來源地址http://www.zghlxwxcb.cn/news/detail-828422.html
到了這里,關于驅動開發(fā):通過PIPE管道與內核層通信的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!