驅動通信:通過PIPE管道與內核層通信

来源:https://www.cnblogs.com/LyShark/archive/2022/10/01/16746250.html
-Advertisement-
Play Games

在本人前一篇博文`《驅動開發:通過ReadFile與內核層通信》`詳細介紹瞭如何使用應用層`ReadFile`系列函數實現內核通信,本篇將繼續延申這個知識點,介紹利用`PIPE`命名管道實現應用層與內核層之間的多次通信方法。 ...


在本人前一篇博文《驅動開發:通過ReadFile與內核層通信》詳細介紹瞭如何使用應用層ReadFile系列函數實現內核通信,本篇將繼續延申這個知識點,介紹利用PIPE命名管道實現應用層與內核層之間的多次通信方法。

  • 什麼是PIPE管道?

在Windows編程中,數據重定向需要用到管道PIPE,管道是一種用於在進程間共用數據的機制,通常由兩端組成,數據從一端流入則必須從令一端流出,也就是一讀一寫,利用這種機制即可實現進程間直接通信。管道的本質其實是一段共用記憶體區域,多數情況下管道是用於應用層之間的數據交換的,其實驅動中依然可以使用命名管道實現應用層與內核層的直接通信。

那麼如何在內核中創建一個管道?請看以下代碼片段,以及MSDN針對函數的解析。

  • InitializeObjectAttributes

    • 初始化一個OBJECT_ATTRIBUTES結構,它設置將被打開的對象句柄的屬性。然後調用方可以將一個指向該結構的指針傳遞給實際打開句柄的常式。
  • ZwCreateFile

    • 該函數的作用時創建或打開一個已經存在的文件,在這裡其實是打開objAttr這個文件。
  • KeInitializeEvent

    • 將事件對象初始化為同步 (單個服務) 或通知類型事件,並將其設置為已發出信號或未發出信號的狀態。
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文件,然後將事件對象初始化為同步狀態。

接下來就是如何將數據發送給應用層的問題,發送問題可以調用ZwWriteFile這個內核函數,如下我們實現的效果是將一個char類型的字元串傳輸給應用層。

// 將數據傳到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);
}

// 將數據傳到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);

			// 發送到應用層
			ReportToR3(report, 4096);
			ExFreePool(report);
		}
	}

	DbgPrint("驅動載入成功 \n");
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

內核中創建了命名管道,客戶端就需要創建一個相同名稱的管道,並通過ReadFile函數讀取管道中的數據,應用層核心代碼如下所示:

#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("內核層數據: %s \n", buffer);

				free(buffer_tmp);
				free(buffer);
			}
		}
	}

	system("pause");
	return 0;
}

至此將驅動簽名後運行,並迅速打開應用層程式等待同步發送事件,即可得到如下返回結果。

此處有必要解釋一下為什麼會寫出錯誤,很簡單這段代碼並沒有控制何時觸發事件,導致兩邊不同步,因為只是一個案例用於演示管道的應用方法,所以大家不要太較真,如果不想出錯誤這段代碼還有很多需要改進的地方。

管道不僅可以傳輸字元串完全可以傳輸結構體數據,如下我們定義一個Networkreport結構體,並通過管道的方式多次傳輸給應用層,這部分傳輸模式適合用於驅動中一次性突出多個結構體,例如進程列表的輸出,ARK工具中的驅動列表輸出等功能的實現。

驅動層完整代碼

#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);
}

// 將數據傳到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);

      // 發送到應用層
      ReportToR3(report, 4096);
      ExFreePool(report);
    }
  }

  DbgPrint("驅動載入成功 \n");
  Driver->DriverUnload = UnDriver;
  return STATUS_SUCCESS;
}

應用層完整代碼

#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("輸出數據: %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;
}

結構體一次性輸出多個,效果如下所示:

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/16746250.html
版權聲明:本博客文章與代碼均為學習時整理的筆記,文章 [均為原創] 作品,轉載請 [添加出處] ,您添加出處是我創作的動力!
轉載文章,請遵守《中華人民共和國著作權法》相關規定或遵守《署名CC BY-ND 4.0國際》禁止演繹規範,合理合規,攜帶原創出處轉載。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2022-10-01 ALLOWED_HOSTS "ALLOWED_HOSTS"的含義: 它是指允許放在“ALLOWED_HOSTS”的主機進行訪問後端 “ALLOWED_HOSTS”的說明: 它一般預設是“127.0.0.1”,如果添加上其他的主機的話,其他主機也可訪問此項目的view,但是此時, ...
  • 2022-10-01 結果集的含義: 結果集就是在查詢過程中,返回的一個列表的集合。 常見的結果集在一些函數中查詢會返回,具體包括的函數有: filter、exclude、order_by 結果集的兩大特性: (1)惰性 指的是查詢時,如果不用則在mysql日誌中不會有查詢記錄。反之,如果使用(一般 ...
  • 相信在大部分的web項目中都會有導出導入Excel的需求,之前我也寫過一篇導出單個sheet工作表的文章,沒看過的小伙伴可以去看哈,鏈接也給大家放出來了:導出單個sheet 但是在我們日常的工作中,需求往往沒這麼簡單,可能需要將數據按類型分類導出或者數據量過大,需要分多張表導出等等。遇到類似的需求該 ...
  • 相信在大部分的web項目中都會有導出導入Excel的需求,今天我們就來看看如何用Java代碼去實現 用POI導出Excel表格。 一、pom引用 pom文件中,添加以下依賴 查看代碼 <!--Excel工具--> <dependency> <groupId>org.apache.poi</group ...
  • 總結: LinkedList繼承自List,具備有序性 LinkedList繼承自Deque,具備鏈表關聯性 LinkedList集合進行增刪改查操作底層實際是操作Node節點的前後鏈接關係 LinkedList進行增刪操作時,僅需要操作節點的前後鏈接關係,因此效率較ArrayList高 Linke ...
  • python中使用[]來截取字元串,語法: 字元串[起始位置:結束位置] 一、起始位置:結束位置 先看幾個例子: s = 'python' print(s) #輸出 python 直接輸出字元串 #從前面截取 print(s[1]) #輸出 y 根據下標取字元 print(s[:]) #輸出 pyt ...
  • 在開發過程中,像側邊欄這種功能的版塊,我們在很多頁面都需要使用到的時候,我們則需要在視圖函數中書寫重覆的代碼,這樣很繁瑣,我們可以將側邊欄製成inclusion_tag,後面我們需要用到側邊欄功能時,只需要導入即可! 將側邊欄製成inclusion_tag的步驟: 1.在應用下創建一個名字必須叫te ...
  • 2022-10-01 關聯查詢: 在Django項目中使用ORM模式設置表後,進行關聯查詢,即兩個表直接有聯繫的查詢。 方式: 可以通過主表查詢從表,也可以通過從表查詢主表。 方式一實例: (1)查詢編號為1的圖書中的人物 前提環境,進入pycharm,進入虛擬環境、進入shell環境。 首先,需要 ...
一周排行
    -Advertisement-
    Play Games
  • JWT(JSON Web Token)是一種用於在網路應用之間傳遞信息的開放標準(RFC 7519)。它使用 JSON 對象在安全可靠的方式下傳遞信息,通常用於身份驗證和信息交換。 在Web API中,JWT通常用於對用戶進行身份驗證和授權。當用戶登錄成功後,伺服器會生成一個Token並返回給客戶端 ...
  • 老周在幾個世紀前曾寫過樹莓派相關的 iOT 水文,之所以沒寫 Nano Framework 相關的內容,是因為那時候這貨還不成熟,可玩性不高。不過,這貨現在已經相對完善,老周都把它用在項目上了——第一個是自製的智能插座,這個某寶上50多塊可以買到,搜“esp32 插座”就能找到。一種是 86 型盒子 ...
  • 引言 上一篇我們創建了一個Sample.Api項目和Sample.Repository,並且帶大家熟悉了一下Moq的概念,這一章我們來實戰一下在xUnit項目使用依賴註入。 Xunit.DependencyInjection Xunit.DependencyInjection 是一個用於 xUnit ...
  • 在 Avalonia 中,樣式是定義控制項外觀的一種方式,而控制項主題則是一組樣式和資源,用於定義應用程式的整體外觀和感覺。本文將深入探討這些概念,並提供示例代碼以幫助您更好地理解它們。 樣式是什麼? 樣式是一組屬性,用於定義控制項的外觀。它們可以包括背景色、邊框、字體樣式等。在 Avalonia 中,樣 ...
  • 在處理大型Excel工作簿時,有時候我們需要在工作表中凍結窗格,這樣可以在滾動查看數據的同時保持某些行或列固定不動。凍結窗格可以幫助我們更容易地導航和理解複雜的數據集。相反,當你不需要凍結窗格時,你可能需要解凍它們以獲得完整的視野。 下麵將介紹如何使用免費.NET庫通過C#實現凍結Excel視窗以鎖 ...
  • .NET 部署 IIS 的簡單步驟一: 下載 dotnet-hosting-x.y.z-win.exe ,下載地址:.NET Downloads (Linux, macOS, and Windows) (microsoft.com) .NET 部署 IIS 的簡單步驟二: 選擇對應的版本,點擊進入詳 ...
  • 拓展閱讀 資料庫設計工具-08-概覽 資料庫設計工具-08-powerdesigner 資料庫設計工具-09-mysql workbench 資料庫設計工具-10-dbdesign 資料庫設計工具-11-dbeaver 資料庫設計工具-12-pgmodeler 資料庫設計工具-13-erdplus ...
  • 初識STL STL,(Standard Template Library),即"標準模板庫",由惠普實驗室開發,STL中提供了非常多對信息學奧賽很有用的東西。 vector vetor是STL中的一個容器,可以看作一個不定長的數組,其基本形式為: vector<數據類型> 名字; 如: vector ...
  • 前言 最近自己做了個 Falsk 小項目,在部署上伺服器的時候,發現雖然不乏相關教程,但大多都是將自己項目代碼複製出來,不講核心邏輯,不太簡潔,於是將自己部署的經驗寫成內容分享出來。 uWSGI 簡介 uWSGI: 一種實現了多種協議(包括 uwsgi、http)並能提供伺服器搭建功能的 Pytho ...
  • 1 文本Embedding 將整個文本轉化為實數向量的技術。 Embedding優點是可將離散的詞語或句子轉化為連續的向量,就可用數學方法來處理詞語或句子,捕捉到文本的語義信息,文本和文本的關係信息。 ◉ 優質的Embedding通常會讓語義相似的文本在空間中彼此接近 ◉ 優質的Embedding相 ...