忆杰的博客

忆杰的博客

和内核共享内存

Windows确实给我们提供了很多的内存共享方法,有同进程的, 有不同进程的, 各种方法都有, 我看kmdkit中介绍了一种和内核共享内存的方法, 确实很不错. 忍不住自己写了一下, 确实很好用. 看来以后用这种方法就可以做到和内核共享内存了, 利用注册表啊什么的共享还是有点欠缺实时性. 还是这个MDL共享内存好.

关于这个MDL, 我想如果要深挖的话, 估计有很多内容被挖掘出来, 但是目前也没有看到哪里有提到, 我倒. 先用着,套路就是这么个套路, 先申请一段不被分页的内存, 然后在调用IoAllocateMdl将一页内存转换成MDL. 转换好了以后还是虚拟地址, 然后调用MmBuildMdlForNonPagedPool将物理页面映射到系统地址空间, 但是显然我们需要在用户态使用这个页面, 所以还要调用MmMapLockedPagesSpecifyCache映射物理页映射到虚拟内存地址, 因为第二个参数是UserMode, 所以映射成功的话可以被用户态所访问到. 就是这样. 将一个页面映射成用户态也可以访问..

我倒. 但是这个有个关键性的问题. 就是这个MDL内部做的是什么动作? 虽然对于CPU的分页机制是比较了解了, 但是Windows在这几个函数内部到底做了些什么工作呢? 我想需要走的路还有很长啊!

说说这个代码的逻辑含义吧, 首先在用户态这边打开了内核创建的设备然后调用DeviceIoControl发送控制码, 发送完了以后如果程序执行成功的话会返回一片内存空间, 然后这边内存空间的前面部分就是一个FILETIME结构了, 然后转换这个结构.在一个Static中显示出来.

内核这边也是了, 上来就是老套路, 创建设备啊什么的, 然后等待用户态这边发送DeviceIoControl码, 在这里将一页内存映射成可以被用户态可以访问的状态, 并将页内存返回给用户态, 然后创建一个Timer对象, 每隔一秒不断的获取当前的时间写入这篇内存. 所以用户态这边才可以显示出时间.

这个内存映射很帅啊. 写内核态的代码就是牛, 如果技术够牛. 那得写出多少暴力的东西啊!

http://www.joenchen.com/JoenTools/DeviceIoControlIo.rar

看用户态的代码:

/*
	Windows内核下用户态和内核共享内存 3环代码
	编译方法参见makefile. TAB = 8
*/
#include <windows.h>
#include <stdio.h>
#include <windowsx.h>
#include "resource.h"

#define  SYS_LINK_NAME	"\\\\.\\SysLinkShareMemory"
#define  IOCTL_MEMORY	CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800,METHOD_BUFFERED, FILE_READ_ACCESS )
#define  TIMER_ID	100
#define  STATIC_EDIT	1000

#pragma comment(linker, "/Entry:Jmain")
#pragma comment(linker, "/subsystem:windows")

PVOID g_pShareMemory;
//===========================================================================
/*
	WM_INITDIALOG消息处理
*/
BOOL Test_OnInitDialog( HWND hWnd, HWND hwndFocus, LPARAM lParam ) {

	SetTimer(hWnd, TIMER_ID, 1000, NULL );

	return TRUE;
}
//===========================================================================
/*
	WM_TIMER消息
*/
void Test_OnTimer(HWND hWnd, UINT id) {
	SYSTEMTIME StTime;
	CHAR buf[128];

	if ( g_pShareMemory ) {
		FileTimeToSystemTime( (FILETIME*)g_pShareMemory, &StTime );
		wsprintf( buf, "%02d:%02d:%02d", StTime.wHour, StTime.wMinute, StTime.wSecond );

		SendDlgItemMessage( hWnd, STATIC_EDIT, WM_SETTEXT, 0, (LPARAM)buf );

	}
}
//===========================================================================
/*
	WM_CLOSE消息处理
*/
void Test_OnClose( HWND hWnd ) {

	KillTimer(hWnd, TIMER_ID );
	EndDialog( hWnd, 0 );
	ExitProcess(0);
}

BOOL WINAPI DialogProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {

	switch ( uMsg ) {
		HANDLE_MSG( hWnd, WM_CLOSE, Test_OnClose );
		HANDLE_MSG( hWnd, WM_INITDIALOG, Test_OnInitDialog );
		HANDLE_MSG( hWnd,WM_TIMER, Test_OnTimer);
	}
	return FALSE;
}

//===========================================================================
	//入口
int Jmain() {
	HANDLE hFile = 0;
	BOOL bRet;
	DWORD dwByteRead;

	//打开设备
	hFile = CreateFile( SYS_LINK_NAME, GENERIC_READ | GENERIC_WRITE, 0, 0,
	                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0  );
	if ( hFile == INVALID_HANDLE_VALUE ) {
		printf( "打开设备错误了!\n" );
		return -1;
	} 

	bRet = DeviceIoControl(hFile, IOCTL_MEMORY, NULL, 0, &g_pShareMemory,
		sizeof(g_pShareMemory), &dwByteRead, NULL);
	if ( !bRet ) {
		printf( "DeviceIoControl 错误!\n" );
		return -1;
	}

	DialogBox ( GetModuleHandle( NULL ), MAKEINTRESOURCE ( IDD_DIALOG1 ), NULL, &DialogProc );

	return 0;
}

内核态的代码:

/*
	Windows内核 用户态和内核共享内存 0环代码
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

#define  DEVICE_NAME	L"\\Device\\DevJoenDevice"
#define  SYS_LINK_NAME	L"\\??\\SysLinkShareMemory"
#define  IOCTL_MEMORY	CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800,METHOD_BUFFERED, FILE_READ_ACCESS )

typedef struct tagDeviceExt {
	PDEVICE_OBJECT	pDeviceObj;
	UNICODE_STRING	USzDeviceName;
	UNICODE_STRING	USzSysLinkName;
}DEVICEEXT, *PDEVICEEXT;

PVOID g_pMdl;
PVOID g_pShareMemory;
PVOID g_pUserAddress;
ULONG g_TimerFlags;
//===========================================================================
//清理资源
VOID CleanUp( PDEVICE_OBJECT pDeviceObj ) {

	if ( g_TimerFlags ) {
		//停止定时器过程
		IoStopTimer( pDeviceObj );
	}

	if ( g_pUserAddress != NULL && g_pMdl != NULL ) {
		//释放前面开放给用户模式的内存映像
		MmUnmapLockedPages( g_pUserAddress, g_pMdl );
		g_pUserAddress = NULL;
	}

	if ( g_pMdl != NULL ) {
		IoFreeMdl( g_pMdl );			//是否MDL
		g_pMdl = NULL;
	}

	if ( g_pShareMemory != NULL ) {
		ExFreePool( g_pShareMemory );		//释放内存
		g_pShareMemory = NULL;
	}
}

//===========================================================================
//清理资源例程
NTSTATUS DispatchCleanUp( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {

	CleanUp( pDeviceObj );
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	KdPrint( ( "DispatchCleanUp例程调用完成!\n" ) );
	return STATUS_SUCCESS;
}

//===========================================================================
//	驱动卸载例程
VOID DriverUnLoad( PDRIVER_OBJECT pDriverObj ) {
	PDEVICEEXT pDeviceExt;
	PDEVICE_OBJECT pNextDeviceObj;

	pNextDeviceObj = pDriverObj->DeviceObject;

	while ( pNextDeviceObj != NULL ) {
		pDeviceExt = pNextDeviceObj->DeviceExtension;

		IoDeleteDevice( pDeviceExt->pDeviceObj );
		IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName );
		pNextDeviceObj = pNextDeviceObj->NextDevice;

		KdPrint( ( "删除%wZ设备成功!\n", &pDeviceExt->USzSysLinkName ) );
	}
}

//===========================================================================
//	时钟例程IRQL = DISPATCH_LEVEL
VOID TimerRoutine( PDEVICE_OBJECT pDeviceObj,  PVOID pContext ) {
	LARGE_INTEGER liTimer;

	KeQuerySystemTime( &liTimer );
	ExSystemTimeToLocalTime( &liTimer, g_pShareMemory );

	//KdPrint(( "%X%X\n", liTimer.HighPart, liTimer.LowPart ));
}

//===========================================================================
//	所有不关心的IRP处理
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return STATUS_SUCCESS;
}

//===========================================================================
//	写请求的过滤
NTSTATUS DispatchDeviceIoControl( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	ULONG ulInBufLen;
	ULONG ulOutBufLen;
	NTSTATUS Status;
	ULONG Code, dwContext;
	PVOID pUserAddress = NULL;
	PVOID pOutputBuf = NULL;
	PDEVICEEXT pDeviceExt;
	PIO_STACK_LOCATION Stack;

	Stack = IoGetCurrentIrpStackLocation( pIrp );
	//输入缓冲区大小
	ulInBufLen = Stack->Parameters.DeviceIoControl.InputBufferLength;
	//输出缓冲区大小
	ulOutBufLen = Stack->Parameters.DeviceIoControl.OutputBufferLength;
	//IO控制码
	Code = Stack->Parameters.DeviceIoControl.IoControlCode;

	Status = STATUS_INVALID_DEVICE_REQUEST;
	pIrp->IoStatus.Information = 0;
	pDeviceExt = ( PDEVICEEXT )pDeviceObj->DeviceExtension;

	do {
		//测试是否是符合要求的控制码
		if ( Code != IOCTL_MEMORY ) {
			Status = STATUS_INVALID_DEVICE_REQUEST;
			break;
			//测试缓冲区是否足够
		} else if ( ulOutBufLen < sizeof( PVOID ) ) {
			Status = STATUS_BUFFER_TOO_SMALL;
			break;
		}

		g_pShareMemory = ExAllocatePool( NonPagedPool, PAGE_SIZE );

		if ( !g_pShareMemory ) {
			KdPrint( ( "内存分配失败!\n" ) );
			break;
		}

		//将一页内存组织成MDL准备传递给用户层使用
		g_pMdl = IoAllocateMdl( g_pShareMemory, PAGE_SIZE, FALSE, FALSE, NULL );

		if ( !g_pMdl ) {
			KdPrint( ( "MDL分配失败!\n" ) );
			break;
		}

		//将MDL描述的物理页面集合映射到系统地址空间(4G虚拟地址空间的高2G部分)
		MmBuildMdlForNonPagedPool( g_pMdl );

		__try {
			//调用此函数有可能会产生异常, 需要在__try中使用
			//映射物理页到虚拟内存地址(用户态)可以被使用
			g_pUserAddress = MmMapLockedPagesSpecifyCache( g_pMdl, UserMode, MmCached,
				NULL, FALSE, NormalPagePriority );

			if ( !g_pUserAddress ) {
				KdPrint( ( "映射MDL地址时遇到错误!\n" ) );
			} else {
				KdPrint( ( "映射MDL地址成功:%p!\n", g_pUserAddress ) );
			}

			//初始化内核定时器, 1S一次
			Status = IoInitializeTimer( pDeviceObj, TimerRoutine, &dwContext );

			if ( !NT_SUCCESS( Status ) ) {
				KdPrint( ( "初始化Timer失败!\n" ) );
				break;
			}

			//启动定时器
			IoStartTimer( pDeviceObj );

			g_TimerFlags = TRUE;
			KdPrint( ( "启动定时器成功!\n" ) );

			//将共享地址写入用户层传入的缓冲区
			pOutputBuf = pIrp->AssociatedIrp.SystemBuffer;
			*( ( ULONG* )pOutputBuf ) = ( ULONG )g_pUserAddress;

			Status = STATUS_SUCCESS;
			pIrp->IoStatus.Information = ulOutBufLen;

		}__except( EXCEPTION_EXECUTE_HANDLER ) {
			Status = STATUS_UNSUCCESSFUL;
			KdPrint( ( "DeviceIoControl中遇到异常!\n" ) );
		}
	} while ( FALSE );

	//如果前面发生了错误
	if ( !NT_SUCCESS( Status ) ) {
		CleanUp( pDeviceObj );
	}

	pIrp->IoStatus.Status = Status;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return Status;
}

//===========================================================================
//	驱动入口函数
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	ULONG i;
	NTSTATUS Status;
	PDEVICEEXT pDeviceExt;
	PDEVICE_OBJECT pDeviceObj;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME );
	UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYS_LINK_NAME );

	Status = IoCreateDevice( pDriverObj, sizeof( DEVICEEXT ), &USzDeviceName,
		FILE_DEVICE_UNKNOWN, 0, 0, &pDeviceObj );

	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "创建设备失败!\n" ) );
		return Status;
	}

	Status = IoCreateSymbolicLink( &USzSysLinkName, &USzDeviceName );

	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "创建符号链接失败!\n" ) );
	}

	//采用直接IO方式
	pDeviceObj->Flags |= DO_BUFFERED_IO;

	//设置设备扩展属性
	pDeviceExt = pDeviceObj->DeviceExtension;
	pDeviceExt->USzDeviceName = USzDeviceName;
	pDeviceExt->USzSysLinkName = USzSysLinkName;
	pDeviceExt->pDeviceObj = pDeviceObj;

	for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
		pDriverObj->MajorFunction[i] = &DispatchRoutine;
	}

	//设置分发函数
	pDriverObj->DriverUnload = &DriverUnLoad;
	pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = &DispatchDeviceIoControl;
	pDriverObj->MajorFunction[IRP_MJ_CLEANUP] = &DispatchCleanUp;
	return Status;
}

 

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

您必须登录 才能进行评论。