驅動開發:通過MDL映射實現多次通信

来源:https://www.cnblogs.com/LyShark/archive/2023/04/29/17134274.html
-Advertisement-
Play Games

在前幾篇文章中`LyShark`通過多種方式實現了驅動程式與應用層之間的通信,這其中就包括了通過運用`SystemBuf`緩衝區通信,運用`ReadFile`讀寫通信,運用`PIPE`管道通信,以及運用`ASYNC`反向通信,這些通信方式在應對`一收一發`模式的時候效率極高,但往往我們需要實現一次性... ...


在前幾篇文章中LyShark通過多種方式實現了驅動程式與應用層之間的通信,這其中就包括了通過運用SystemBuf緩衝區通信,運用ReadFile讀寫通信,運用PIPE管道通信,以及運用ASYNC反向通信,這些通信方式在應對一收一發模式的時候效率極高,但往往我們需要實現一次性吐出多種數據,例如ARK工具中當我們枚舉內核模塊時,往往應用層常式中可以返回幾條甚至是幾十條結果,如下案例所示,這對於開發一款ARK反內核工具是必須要有的功能。

  • 那麼如何實現如上述功能呢?

其實,實現這類功能可以從兩個方面入手,但不論使用哪一種方式本質上都是預留一段緩衝區以此來給內核與應用層共用的區域,該區域內可用於交換數據,實現方式有兩種要麼在應用層分配空間,要麼在內核中分配,LyShark先帶大家在內核層實現,通過巧妙地運用MDL映射機制來實現通信需求。

  • MDL是什麼呢?

MDL記憶體讀寫是最常用的一種讀寫模式,是用於描述物理地址頁面的一個結構,簡單的官方解釋;記憶體描述符列表 (MDL) 是一個系統定義的結構,通過一系列物理地址描述緩衝區。執行直接I/O的驅動程式從I/O管理器接收一個MDL的指針,並通過MDL讀寫數據。一些驅動程式在執行直接I/O來滿足設備I/O控制請求時也使用MDL。

通過運用MDL的方式對同一塊物理記憶體同時映射到R0和R3,這樣我們只需要使用DeviceIoControl向驅動發送一個指針,通過對指針進行讀寫就可以實現數據的交換,本人在網路上找到瞭如下兩段被轉載的爛大街的片段,這兩段代碼明顯是存在缺陷的如果你也在尋找映射方法那麼不要被這兩段代碼坑了,多數人也根本沒有能力將其變為可用的,也就只能轉載,不知道哪個大哥挖的坑。

用戶態進程分配空間,內核態去映射。

// assume uva is a virtual address in user space, uva_size is its size
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
	MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
} __except(EXCEPTION_EXECUTE_HANDLER) {
	DbgPrint("error code = %d", GetExceptionCode);
}
PVOID kva = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
// use kva 
// …
 
MmUnlockPages(mdl);
IoFreeMdl(mdl);

內核態分配空間,用戶態進程去映射。

PVOID kva = ExAllocatePoolWithTag(NonPagedPool, 1024, (ULONG)'PMET');
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
	MmBuildMdlForNonPagedPool(mdl);
} __except(EXCEPTION_EXECUTE_HANDLER) {
	DbgPrint("error code = %d", GetExceptionCode);
}
 
PVOID uva = MmMapLockedPagesSpecifyCache(mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); 

如上的代碼看看就好摘出來只是要提醒大家這個是無法使用的,如下將進入本篇文章的正題。

以內核中開闢空間為例,首先在代碼中要做的就是定義一段非分頁記憶體#define FILE_DEVICE_EXTENSION 4096這段區域用於給全局變數使用,其次我們需要傳輸結構體那麼結構體中的成員就要事先定義好,例如此處使用StructAll來定義結構結構體成員變數如下所示,通過使用static將結構體定義為靜態,預先空出1024的記憶體空間並初始化為0,當然了這種方式是存在弊端的,例如最大隻支持1024個結構如果超過了則可能會溢出,當然最好的辦法是用戶空間開闢,在下次章節中再介紹。

// -------------------------------------------------
// MDL數據傳遞變數
// -------------------------------------------------

// 保存一段非分頁記憶體,用於給全局變數使用
#define FILE_DEVICE_EXTENSION 4096

// 定義重覆結構(迴圈傳遞)
typedef struct
{
  char username[256];
  char password[256];
  int count;
}StructAll;

static StructAll ptr[1024] = { 0 };

為了能夠達到輸出結構體的效果這裡我定義一個ShowProcess用於模擬當前系統內進程數,並自動填充為特定的數據,此處結構體內部count成員則用於標註當前共有多少個結構體,用於在用戶層讀取判斷,當然了這種方式的另一個弊端就是浪費空間,因為每一個結構體中都存在一個被填充為0的整數類型。但如果只是實現功能的話其實也不是那麼重要。

// 模擬進程列表賦值測試
int ShowProcess(int process_count)
{
  memset(ptr, 0, sizeof(StructAll) * process_count);
  int x = 0;

  for (; x < process_count + 1; x++)
  {
    strcpy_s(ptr[x].username, 256, "lyshark");
    strcpy_s(ptr[x].password, 256, "123456");
  }

  // 設置總共有多少個結構體,並返回結構體個數
  ptr[0].count = x;
  return x;
}

內核態映射: 當定義好如上這些方法時,接下來就是最重要的驅動映射部分了,如下代碼所示,首先當用戶調用派遣時第一個執行的函數是ShowProcess()它用於獲取到當前系統中有多少個進程,接著通過sizeof(MyData) * count計算出當前MyData需要分配的記憶體池大小並返回給pool_size,調用ExAllocatePool分配一塊非分頁內核空間,創建IoAllocateMdlMDL映射,將數據MmMapLockedPagesSpecifyCache映射到用戶空間,最後將指針pShareMM_User返回給用戶態。

  • ShowProcess(715) 獲取當前進程數,並返回數量
  • sizeof(MyData) * count 計算得到結構體長度
  • ExAllocatePool(NonPagedPool, pool_size) 分配非分頁記憶體,長度是pool_size
  • IoAllocateMdl() 分配MDL空間,並放入內核態
  • MmMapLockedPagesSpecifyCache() 將內核態指針映射到用戶態
  • RtlCopyMemory(pShareMM_SYS, &ptr, sizeof(ptr[0]) * count) 將總進程數放入到count計數變數內
  • *(PVOID *)pIrp->AssociatedIrp.SystemBuffer = pShareMM_User 直接將指針傳遞給用戶態
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

// 獲取到當前列表數據
int count = ShowProcess(715);
long pool_size = sizeof(MyData) * count;

DbgPrint("總進程數: %d  分配記憶體池大小: %d \n", count, pool_size);

__try
{
  // 分配內核空間
  PVOID pShareMM_SYS = ExAllocatePool(NonPagedPool, pool_size);
  RtlZeroMemory(pShareMM_SYS, pool_size);

  // 創建MDL
  PMDL pShareMM_MDL = IoAllocateMdl(pShareMM_SYS, pool_size, FALSE, FALSE, NULL);
  MmBuildMdlForNonPagedPool(pShareMM_MDL);

  // 將內核空間映射到用戶空間
  PVOID pShareMM_User = MmMapLockedPagesSpecifyCache(pShareMM_MDL, UserMode, MmCached, NULL, FALSE, NormalPagePriority);

  // 拷貝發送數據
  RtlCopyMemory(pShareMM_SYS, &ptr, sizeof(ptr[0]) * count);

  DbgPrint("[lyshark] 用戶地址空間: 0x%x \n", pShareMM_User);
  DbgPrint("[lyshark] 內核地址空間: 0x%p \n", pShareMM_SYS);

  // 將字元串指針發送給應用層
  *(PVOID *)pIrp->AssociatedIrp.SystemBuffer = pShareMM_User;

  // ExFreePool(pShareMM_SYS);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
  break;
}
status = STATUS_SUCCESS;
break;

用戶態讀取數據: 與內核層一致,用戶層同樣需要定義StructAll結構體用於接收內核中返回過來的結構,而重要的代碼則是接收部分,通過IoControl發送控制碼,並得到ptr記憶體指針,此處區域就是內核態分配過的指針,用戶只需要通過迴圈的方式依次讀出裡面的數據即可。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

// -------------------------------------------------
// MDL數據傳遞變數
// -------------------------------------------------
// 定義重覆結構(迴圈傳遞)
typedef struct
{
  char username[256];
  char password[256];
  int count;
}StructAll;

// 直接輸出迴圈結構體
StructAll *ptr;

// 派遣命令
DriveControl.IoControl(IOCTL_IO_MDLStructAll, 0, 0, &ptr, sizeof(PVOID), 0);

printf("共用記憶體地址: %x \n", ptr);

long size = ptr[0].count;

std::cout << "得到結構體總數: " << size << std::endl;

for (int x = 0; x < size; x++)
{
  std::cout << "計數器: " << x << std::endl;
  std::cout << "用戶名: " << ptr[x].username << std::endl;
  std::cout << "密碼: " << ptr[x].password << std::endl;
  std::cout << std::endl;
}

如上就是內核層與應用層的部分代碼功能分析,接下來我將完整代碼分享出來,大家可以自行測試效果。

驅動程式WinDDK.sys完整代碼;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#define _CRT_SECURE_NO_WARNINGS
#include <ntifs.h>
#include <windef.h>

// 定義符號鏈接,一般來說修改為驅動的名字即可
#define DEVICE_NAME        L"\\Device\\WinDDK"
#define LINK_NAME          L"\\DosDevices\\WinDDK"
#define LINK_GLOBAL_NAME   L"\\DosDevices\\Global\\WinDDK"

// 定義驅動功能號和名字,提供介面給應用程式調用
#define IOCTL_IO_MDLStructAll   CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 保存一段非分頁記憶體,用於給全局變數使用
#define FILE_DEVICE_EXTENSION 4096

// 定義傳遞結構體
typedef struct
{
	int uuid;
	char szUname[1024];
}MyData;

// -------------------------------------------------
// MDL數據傳遞變數
// -------------------------------------------------

// 定義重覆結構(迴圈傳遞)
typedef struct
{
	char username[256];
	char password[256];
	int count;
}StructAll;

static StructAll ptr[1024] = { 0 };

// 模擬進程列表賦值測試
int ShowProcess(int process_count)
{
	memset(ptr, 0, sizeof(StructAll) * process_count);
	int x = 0;

	for (; x < process_count + 1; x++)
	{
		strcpy_s(ptr[x].username, 256, "hello lyshark.com");
		strcpy_s(ptr[x].password, 256, "123456");
	}

	// 設置總共有多少個結構體,並返回結構體個數
	ptr[0].count = x;
	return x;
}

// 驅動綁定預設派遣函數
NTSTATUS DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
	_pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
	_pIrp->IoStatus.Information = 0;
	IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
	return _pIrp->IoStatus.Status;
}

// 驅動卸載的處理常式
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	if (pDriverObj->DeviceObject)
	{
		UNICODE_STRING strLink;

		// 刪除符號連接和設備
		RtlInitUnicodeString(&strLink, LINK_NAME);
		IoDeleteSymbolicLink(&strLink);
		IoDeleteDevice(pDriverObj->DeviceObject);
		DbgPrint("[kernel] # 驅動已卸載 \n");
	}
}

// IRP_MJ_CREATE 對應的處理常式,一般不用管它
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	DbgPrint("[kernel] # 驅動處理常式載入 \n");
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// IRP_MJ_CLOSE 對應的處理常式,一般不用管它
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	DbgPrint("[kernel] # 關閉派遣 \n");
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// IRP_MJ_DEVICE_CONTROL 對應的處理常式,驅動最重要的函數
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
	PIO_STACK_LOCATION pIrpStack;
	ULONG uIoControlCode;
	PVOID pIoBuffer;
	ULONG uInSize;
	ULONG uOutSize;

	// 獲得IRP里的關鍵數據
	pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

	// 獲取控制碼
	uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;

	// 輸入和輸出的緩衝區(DeviceIoControl的InBuffer和OutBuffer都是它)
	pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;

	// EXE發送傳入數據的BUFFER長度(DeviceIoControl的nInBufferSize)
	uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;

	// EXE接收傳出數據的BUFFER長度(DeviceIoControl的nOutBufferSize)
	uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

	// 對不同控制信號的處理流程
	switch (uIoControlCode)
	{
	// 測試MDL傳輸多次結構體
	case IOCTL_IO_MDLStructAll:
	{
		// 獲取到當前列表數據
		int count = ShowProcess(715);
		long pool_size = sizeof(MyData) * count;

		DbgPrint("總進程數: %d  分配記憶體池大小: %d \n", count, pool_size);

		__try
		{
			// 分配內核空間
			PVOID pShareMM_SYS = ExAllocatePool(NonPagedPool, pool_size);
			RtlZeroMemory(pShareMM_SYS, pool_size);

			// 創建MDL
			PMDL pShareMM_MDL = IoAllocateMdl(pShareMM_SYS, pool_size, FALSE, FALSE, NULL);
			MmBuildMdlForNonPagedPool(pShareMM_MDL);

			// 將內核空間映射到用戶空間
			PVOID pShareMM_User = MmMapLockedPagesSpecifyCache(pShareMM_MDL, UserMode, MmCached, NULL, FALSE, NormalPagePriority);

			// 拷貝發送數據
			RtlCopyMemory(pShareMM_SYS, &ptr, sizeof(ptr[0]) * count);

			DbgPrint("[lyshark.com] 用戶地址空間: 0x%x \n", pShareMM_User);
			DbgPrint("[lyshark.com] 內核地址空間: 0x%p \n", pShareMM_SYS);

			// 將字元串指針發送給應用層
			*(PVOID *)pIrp->AssociatedIrp.SystemBuffer = pShareMM_User;

			// ExFreePool(pShareMM_SYS);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
			break;
		}
		status = STATUS_SUCCESS;
		break;
	}
	}

	// 設定DeviceIoControl的*lpBytesReturned的值(如果通信失敗則返回0長度)
	if (status == STATUS_SUCCESS)
	{
		pIrp->IoStatus.Information = uOutSize;
	}
	else
	{
		pIrp->IoStatus.Information = 0;
	}

	// 設定DeviceIoControl的返回值是成功還是失敗
	pIrp->IoStatus.Status = status;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return status;
}

// 驅動的初始化工作
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
	NTSTATUS status = STATUS_SUCCESS;
	UNICODE_STRING ustrLinkName;
	UNICODE_STRING ustrDevName;
	PDEVICE_OBJECT pDevObj;

	// 初始化其他派遣
	for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		DbgPrint("初始化派遣: %d \n", i);
		pDriverObj->MajorFunction[i] = DefaultDispatch;
	}

	// 設置分發函數和卸載常式
	pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
	pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
	pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
	pDriverObj->DriverUnload = DriverUnload;

	// 創建一個設備
	RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);

	// FILE_DEVICE_EXTENSION 創建設備時,指定設備擴展記憶體的大小,傳一個值進去,就會給設備分配一塊非頁面記憶體。
	status = IoCreateDevice(pDriverObj, sizeof(FILE_DEVICE_EXTENSION), &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 判斷支持的WDM版本,其實這個已經不需要了,純屬WIN9X和WINNT並存時代的殘留物
	if (IoIsWdmVersionAvailable(1, 0x10))
	{
		RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
	}
	else
	{
		RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
	}

	// 創建符號連接
	status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("創建符號鏈接失敗 \n");
		IoDeleteDevice(pDevObj);
		return status;
	}
	DbgPrint("[kernel] # hello lyshark.com \n");

	// 返回載入驅動的狀態(如果返回失敗,驅動講被清除出內核空間)
	return STATUS_SUCCESS;
}

應用層客戶端程式lyshark.exe完整代碼;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include <iostream>
#include <Windows.h>
#include <vector>

#pragma comment(lib,"user32.lib")
#pragma comment(lib,"advapi32.lib")

// 定義驅動功能號和名字,提供介面給應用程式調用
#define IOCTL_IO_MDLStructAll   0x805

class cDrvCtrl
{
public:
	cDrvCtrl()
	{
		m_pSysPath = NULL;
		m_pServiceName = NULL;
		m_pDisplayName = NULL;
		m_hSCManager = NULL;
		m_hService = NULL;
		m_hDriver = INVALID_HANDLE_VALUE;
	}
	~cDrvCtrl()
	{
		CloseServiceHandle(m_hService);
		CloseServiceHandle(m_hSCManager);
		CloseHandle(m_hDriver);
	}

	// 安裝驅動
	BOOL Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName)
	{
		m_pSysPath = pSysPath;
		m_pServiceName = pServiceName;
		m_pDisplayName = pDisplayName;
		m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
		if (NULL == m_hSCManager)
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		m_hService = CreateServiceA(m_hSCManager, m_pServiceName, m_pDisplayName,
			SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
			m_pSysPath, NULL, NULL, NULL, NULL, NULL);
		if (NULL == m_hService)
		{
			m_dwLastError = GetLastError();
			if (ERROR_SERVICE_EXISTS == m_dwLastError)
			{
				m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
				if (NULL == m_hService)
				{
					CloseServiceHandle(m_hSCManager);
					return FALSE;
				}
			}
			else
			{
				CloseServiceHandle(m_hSCManager);
				return FALSE;
			}
		}
		return TRUE;
	}

	// 啟動驅動
	BOOL Start()
	{
		if (!StartServiceA(m_hService, NULL, NULL))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 關閉驅動
	BOOL Stop()
	{
		SERVICE_STATUS ss;
		GetSvcHandle(m_pServiceName);
		if (!ControlService(m_hService, SERVICE_CONTROL_STOP, &ss))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 移除驅動
	BOOL Remove()
	{
		GetSvcHandle(m_pServiceName);
		if (!DeleteService(m_hService))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 打開驅動
	BOOL Open(PCHAR pLinkName)
	{
		if (m_hDriver != INVALID_HANDLE_VALUE)
			return TRUE;
		m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
		if (m_hDriver != INVALID_HANDLE_VALUE)
			return TRUE;
		else
			return FALSE;
	}

	// 發送控制信號
	BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes)
	{
		DWORD dw;
		BOOL b = DeviceIoControl(m_hDriver, CTL_CODE_GEN(dwIoCode), InBuff, InBuffLen, OutBuff, OutBuffLen, &dw, NULL);
		if (RealRetBytes)
			*RealRetBytes = dw;
		return b;
	}
private:

	// 獲取服務句柄
	BOOL GetSvcHandle(PCHAR pServiceName)
	{
		m_pServiceName = pServiceName;
		m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
		if (NULL == m_hSCManager)
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
		if (NULL == m_hService)
		{
			CloseServiceHandle(m_hSCManager);
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}

	// 獲取控制信號對應字元串
	DWORD CTL_CODE_GEN(DWORD lngFunction)
	{
		return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
	}

public:
	DWORD m_dwLastError;
	PCHAR m_pSysPath;
	PCHAR m_pServiceName;
	PCHAR m_pDisplayName;
	HANDLE m_hDriver;
	SC_HANDLE m_hSCManager;
	SC_HANDLE m_hService;
};

void GetAppPath(char *szCurFile)
{
	GetModuleFileNameA(0, szCurFile, MAX_PATH);
	for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--)
	{
		if (szCurFile[i] == '\\')
		{
			szCurFile[i + 1] = '\0';
			break;
		}
	}
}

// -------------------------------------------------
// MDL數據傳遞變數
// -------------------------------------------------
// 定義重覆結構(迴圈傳遞)
typedef struct
{
	char username[256];
	char password[256];
	int count;
}StructAll;

int main(int argc, char *argv[])
{
	cDrvCtrl DriveControl;

	// 設置驅動名稱
	char szSysFile[MAX_PATH] = { 0 };
	char szSvcLnkName[] = "WinDDK";;
	GetAppPath(szSysFile);
	strcat(szSysFile, "WinDDK.sys");

	// 安裝並啟動驅動
	DriveControl.Install(szSysFile, szSvcLnkName, szSvcLnkName);
	DriveControl.Start();

	// 打開驅動的符號鏈接
	DriveControl.Open("\\\\.\\WinDDK");

	// 直接輸出迴圈結構體
	StructAll *ptr;

	// 派遣命令
	DriveControl.IoControl(IOCTL_IO_MDLStructAll, 0, 0, &ptr, sizeof(PVOID), 0);

	printf("[LyShark.com] 共用記憶體地址: %x \n", ptr);

	long size = ptr[0].count;

	std::cout << "得到結構體總數: " << size << std::endl;

	for (int x = 0; x < size; x++)
	{
	std::cout << "計數器: " << x << std::endl;
	std::cout << "用戶名: " << ptr[x].username << std::endl;
	std::cout << "密碼: " << ptr[x].password << std::endl;
	std::cout << std::endl;
	}

	// 關閉符號鏈接句柄
	CloseHandle(DriveControl.m_hDriver);

	// 停止並卸載驅動
	DriveControl.Stop();
	DriveControl.Remove();

	system("pause");
	return 0;
}

手動編譯這兩個程式,將驅動簽名後以管理員身份運行lyshark.exe客戶端,此時屏幕中即可看到滾動輸出效果,如此一來就實現了迴圈傳遞參數的目的。

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17134274.html
版權聲明:本博客文章,除去特殊聲明 [轉載標註/參考文獻] 部分, [均為原創] 作品,禁止任何形式的轉載!

文章版權所有 © 孤風洗劍 (王瑞) 保留著作權,請遵守《中華人民共和國著作權法》相關規定,未經著作權人書面許可禁止任何形式的轉載,如若侵權必將竭盡全力打擊!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在開發用的台式機上啟用了 Windows 的 Hyper-V 虛擬化功能,利用虛擬機運行了一臺 Windows Server 2022 和 一臺 Ubuntu Server,為了方便別的機器直接訪問這兩台虛擬機,所以網路採用了外部網路橋接的模式,讓虛擬機和物理機保持在了同一網段。 為了實現在這一 ...
  • ==資料庫==1、創建資料庫create database [IF NOT EXISTS] 資料庫名; 2、刪除資料庫drop database [IF EXISTS] 資料庫名; 3、切換資料庫select database(); 4、查詢資料庫show databases; —————————— ...
  • 1、四層結構 viewer --> datasources(DataSourceCollection類型) --> datasource --> entities(EntityCollection類型) --> entity 需要學習的方向是:只需要註意每個層與層之間的關係和entity實例如何創建 ...
  • 簡介 模板方法模式(Template Method Pattern)也叫模板模式,是一種行為型模式。它定義了一個抽象公開類,包含基本的演算法骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變演算法的結構,只是重定義該演算法的某些特定步驟。不同的子類以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不 ...
  • 轉載請註明 來源:http://www.eword.name/ Author:eword Email:[email protected] 安裝Python 一、查詢是否安裝了Python及安裝路徑 #查看當前Python版本 python --version Python 2.7.16 #查看當前所有 ...
  • B/S結構系統的會話機制(session) 每博一文案 你跑得快,22歲有個家,身邊全是贊嘆,你跑得慢,30歲還在路上追求夢想。有的人為了車,房拼了一輩子, 有的人買輛摩托車走遍了大好江山。你想成為怎樣的人,過怎樣的生活,只要你不後悔就行。 並不是所有人都能在早上七點鐘起床的,也別拿一碗飯來衡量一個 ...
  • 本文首發於公眾號:Hunter後端 原文鏈接:Django筆記三十三之緩存操作 這一節介紹一下如何在 Django 中使用 redis 做緩存操作。 在 Django 中可以有很多種方式做緩存,比如資料庫,比如伺服器文件,或者記憶體,這裡介紹用的比較多的使用 redis 作為緩存。 這篇筆記主要內容如 ...
  • 大部分程式員走入編程世界第一個學習的語言就是C語言。 作為一門古老的編程語言,c語言擁有48年的發展歷程。 為什麼要學習 C語言? C語言是學習電腦程式設計語言的入門語言。最全面的編程面試網站 C語言是一門偏底層的語言,學好它,可以讓你更好的瞭解電腦。 學會了C語言,你就能學習現在任何的高級編程 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...