主頁 > 後端開發 > 驅動開發:通過MDL映射實作多次通信

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

2023-04-30 07:21:04 後端開發

在前幾篇文章中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
著作權宣告:本博客文章,除去特殊宣告 [轉載標注/參考文獻] 部分, [均為原創] 作品,禁止任何形式的轉載!

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/551489.html

標籤:其他

上一篇:P6818 [PA2013]Dzia?ka 題解

下一篇:返回列表

標籤雲
其他(158312) Python(38110) JavaScript(25398) Java(18011) C(15221) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7152) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5870) 数组(5741) R(5409) Linux(5334) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4565) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2432) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1964) Web開發(1951) HtmlCss(1929) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1857) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 驅動開發:通過MDL映射實作多次通信

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

    uj5u.com 2023-04-30 07:21:04 more
  • P6818 [PA2013]Dzia?ka 題解

    P6818 [PA2013]Dzia?ka 前言 我太菜了。。。。 對著 jiangly 大佬的題解研究了一下午研究了一下午才搞出來(淚目。 作為一個蒟蒻,我就詳細的講一下我對與本題的理解。 題意 本題的的題意描述的還是比較明了。 在二維坐標系中,輸入 $n$ 個點 $m$ 次詢問, 每次詢問,給出 ......

    uj5u.com 2023-04-30 07:15:44 more
  • Python 基于win32com客戶端實作Excel操作

    測驗環境 Python 3.6.2 代碼實作 非多執行緒場景下使用 新建并保存EXCEL import win32com.client from win32api import RGB def save_something_to_excel(result_file_path): excel_app = ......

    uj5u.com 2023-04-30 07:10:32 more
  • FFmpeg開發筆記(二)搭建Windows系統的開發環境

    由于Linux系統比較專業,個人電腦很少安裝Linux,反而大都安裝Windows系統,因此提高了FFmpeg的學習門檻,畢竟在Windows系統搭建FFmpeg的開發環境還是比較麻煩的。不過若有已經編譯好的Windows版本FFmpeg開發包,那就免去了繁瑣的Windows編譯程序,所以直接安裝已 ......

    uj5u.com 2023-04-30 07:10:24 more
  • XMake學習筆記(1):Windows(MSYS2)下MinGW-w64環境搭建和XMake安裝

    以前寫的C++基本都是C with STL,大多是面向程序的演算法題,或者比較小的專案,然后經常報各種編譯錯誤(對編譯原理不熟),經常把人搞到崩潰,搞不懂構建、鏈接之類的東西。 現在開始記錄一下XMake的學習筆記,記錄一些學習程序中踩的坑,在這篇文章,你將學習到Windows下利用MSYS2進行Mi ......

    uj5u.com 2023-04-30 07:10:13 more
  • 安裝Python

    轉載請注明 來源:http://www.eword.name/ Author:eword Email:[email protected] 安裝Python 一、查詢是否安裝了Python及安裝路徑 #查看當前Python版本 python --version Python 2.7.16 #查看當前所有 ......

    uj5u.com 2023-04-30 07:09:28 more
  • 安裝Python

    轉載請注明 來源:http://www.eword.name/ Author:eword Email:[email protected] 安裝Python 一、查詢是否安裝了Python及安裝路徑 #查看當前Python版本 python --version Python 2.7.16 #查看當前所有 ......

    uj5u.com 2023-04-30 07:03:56 more
  • 二分查找演算法講解及其C++代碼實作

    二分查找演算法是一種常用的查找演算法,也被稱為折半查找。它可以在有序的陣列或串列中快速查找需要的元素。 演算法描述: 首先確定陣列的中間位置mid=(left+right)/2; 然后將要查找的值key與中間位置的值進行比較; 如果key等于中間位置的值,則查找成功,回傳mid; 如果key小于中間位置的 ......

    uj5u.com 2023-04-28 15:01:01 more
  • springboot升級程序中踩坑定位分析記錄 | 京東云技術團隊

    因所負責的系統使用的spring框架版本5.1.5.RELEASE在線上出過一個偶發的小事故,最后定位為spring-context中的一個bug導致的。 ......

    uj5u.com 2023-04-28 15:00:46 more
  • springboot專案出現”java: 錯誤: 無效的源發行版:17“問題解決方

    下面是報錯頁面 問題決議 在我個人遇到此問題的情況下,出現此錯誤的原因是springboot的版本與java版本不一致 在spring3更新后,idea在創建springboot專案時會默認選擇spring3,哪怕你選擇的是java8的版本 idea默認選擇spring3 在你以java8創建spr ......

    uj5u.com 2023-04-28 15:00:17 more