NT式驱动的动态加载

以下均为学习后的个人见解,若果有误,欢迎纠正,谢谢

NT式驱动的加载分为手动加载和动态加载,因为本片文章主要讲解动态加载,所以大体概念讲解一下手动加载

手动加载:

第一步是在注册表中添加新项,路径为HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\驱动名称

第二步是添加子键,DisplayName、ErrorControl、Group、ImagePath、Start、Type

第三步是命令行方式启动,net start/stop 驱动名称

然后在设备管理器下查看隐藏设备,也可以应用DbgView查看内核发送的调试信息

动态加载:

下面是结合书本和MSDN,把注册表系列的操作在代码中体现出来,从而直接调用执行文件以实现驱动的动态加载,代码中如果有不正确的地方,希望你们指正出来

main函数开头第一个调用了RegOperation()函数,该函数就是用来实现注册表项的实现,源码如下:

BOOL RegOperation()
{
	BOOL Ret = FALSE;
	HKEY hKeyCreate;
	LPCTSTR subKeyPath = "SYSTEM\\CurrentControlSet\\Services\\HelloDDK"; 
	LONG bRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, 
							subKeyPath,
							0,
							NULL,
							REG_OPTION_NON_VOLATILE,
							KEY_ALL_ACCESS,
							NULL,
							&hKeyCreate,
							NULL);
	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to create a hkey: %d\n", bRet);
		goto beforeLeave;
	}

	bRet = RegSetValueEx(hKeyCreate, 
						"DisplayName", 
						0,
						REG_SZ,
						(const BYTE*)"HelloDDK",
						sizeof("HelloDDK"));
	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set DisplayName to the key: %d\n", bRet);
		goto beforeLeave;
	}

	DWORD value = 1;
	bRet = RegSetValueEx(hKeyCreate, 
						"ErrorControl", 
						0,
						REG_DWORD,
						(const BYTE*)&value,
						sizeof(value));

	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set ErrorControl to the key: %d\n", bRet);
		goto beforeLeave;
	}

	bRet = RegSetValueEx(hKeyCreate, 
						"Group", 
						0,
						REG_SZ,
						NULL,
						0);

	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set Group to the key: %d\n", bRet);
		goto beforeLeave;
	}

	LPCSTR imagePath = (LPCSTR)"\\??\\D:\\Vs programs\\LoadNTDriver\\LoadNTDriver\\HelloDDK.sys";
	bRet = RegSetValueEx(hKeyCreate, 
						"ImagePath", 
						0,
						REG_EXPAND_SZ,
						(const BYTE*)imagePath,
						strlen(imagePath)+1);

	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set ImagePath to the key: %d\n", bRet);
		goto beforeLeave;
	}

	value = 3;
	bRet = RegSetValueEx(hKeyCreate, 
						"Start", 
						0,
						REG_DWORD,
						(const BYTE*)&value,
						sizeof(value));

	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set Start to the key: %d\n", bRet);
		goto beforeLeave;
	}

	value = 1;
	bRet = RegSetValueEx(hKeyCreate, 
						"Type", 
						0,
						REG_DWORD,
						(const BYTE*)&value,
						sizeof(value));

	if(bRet != ERROR_SUCCESS)
	{
		Ret = FALSE;
		printf("failed to set Type to the key: %d\n", bRet);
		goto beforeLeave;
	}
	Ret = TRUE;
beforeLeave:
	RegCloseKey(hKeyCreate);

	return Ret;
}

该函数执行成功后,就可以直接加载驱动了。设备驱动程序的动态加载主要由SCM系统组件来完成,该组件提供许多服务,例如启动、停止和控制服务。所以应用SCM系统组件加载和卸载驱动分为四个步奏:

1、为NT驱动创建新的服务

2、开启服务

3、关闭服务

4、删除NT驱动创建的服务

其中第一步我调用LoadNTDriver()函数,形参顾名思义了,创建并开启新的服务项,源代码如下:

BOOL LoadNTDriver(char* lpszDriverName, LPCSTR lpszDriverPath)
{
	BOOL bRet = FALSE;
	LPSTR lpszImagePath = (LPSTR)malloc(MAX_PATH);
	memset(lpszImagePath, 0, sizeof(lpszImagePath));

	GetFullPathName(lpszDriverPath, MAX_PATH, lpszImagePath, NULL);
	printf("%s\n", lpszImagePath);
	//打开SCM管理器
	SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if(hSCManager == NULL)
	{
		printf("open scmanager failed\n");
		bRet = FALSE;
		goto beforeLeave;
	}
	printf("open scmanager successed\n");
	
	SC_HANDLE hServiceDDK = CreateService(hSCManager, (LPCSTR)lpszDriverName, (LPCSTR)lpszDriverName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,											SERVICE_ERROR_IGNORE, lpszImagePath, NULL, NULL, NULL, NULL, NULL);
	if(hServiceDDK == NULL)
	{
		DWORD dwResult = GetLastError();
		if(dwResult != ERROR_IO_PENDING && dwResult != ERROR_SERVICE_EXISTS)
		{
			printf("create service failed:%d\n", dwResult);
			bRet = FALSE;
			goto beforeLeave;
		}
		else
		{
			if(dwResult == ERROR_SERVICE_EXISTS)
				printf("createService failed beacause of the service existed\n");
			else
				printf("createService failed beacause of the service pended\n");
		}
		hServiceDDK = OpenService(hSCManager, lpszDriverName, SERVICE_ALL_ACCESS);
		if(hServiceDDK == NULL)
		{
			printf("open service failed\n");
			bRet = FALSE;
			goto beforeLeave;
		}
		printf("open service successed\n");
	}
	else
		printf("create service successed\n");
	if(!StartService(hServiceDDK, NULL, NULL))
	{
		if(GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
		{
			printf("start service failed:%d\n", GetLastError());
			bRet = FALSE;
			goto beforeLeave;
		}
		else
		{
			if(GetLastError() == ERROR_IO_PENDING)
			{
				printf("start service failed beacause of pended\n");
				bRet = FALSE;
				goto beforeLeave;
			}
			else
			{
				printf("start service failed beacause of running\n");
				bRet = TRUE;
				goto beforeLeave;
			}
		}
	}
	bRet = TRUE;
beforeLeave:
	if(hServiceDDK)
	{
		CloseServiceHandle(hServiceDDK);
	}
	if(hSCManager)
	{
		CloseServiceHandle(hSCManager);
	}
	return bRet;
}

在关闭和删除服务之前我调用了TestDriver()函数完成驱动的测试,源代码如下:

void TestDriver()
{
	//测试驱动程序
	HANDLE hDevice = CreateFile("\\\\.\\HelloDDK", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
	if(hDevice == INVALID_HANDLE_VALUE)
	{
		printf("create device failed: %d\n", GetLastError());
	}
	else
	{
		printf("create device successed\n");
	}
	CloseHandle(hDevice);
}

所谓的驱动测试,只是生成文件句柄。最后就是调用UnLoadNTDriver()函数删除服务,

BOOL UnLoadNTDriver(char* lpszDriverName)
{
	BOOL bRet = FALSE;
	SC_HANDLE hServiceMgr = NULL;
	SC_HANDLE hServiceDDK = NULL;
	SERVICE_STATUS svrSta;

	hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if(hServiceMgr == NULL)
	{
		//打开管理器失败
		bRet = FALSE;
		printf("failed to open SCManager\n");
		goto beforeLeave;
	}
	else
	{
		printf("successed to open SCM\n");
	}
	hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
	if(hServiceDDK == NULL)
	{
		bRet = FALSE;
		printf("failed to open service\n");
		goto beforeLeave;
	}
	else
	{
		printf("successed to open service\n");
	}

	if(!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &svrSta))
	{
		bRet = FALSE;
		printf("failed to stop the service\n");
		goto beforeLeave;
	}
	else
	{
		printf("successed to stop the service\n");
	}
	if(!DeleteService(hServiceDDK))	
	{
		bRet = FALSE;
		printf("failed to delete the service\n");
		goto beforeLeave;
	}
	else
	{
		printf("successed to delete the service\n");
	}
	bRet = TRUE;
beforeLeave:
	if(hServiceDDK)
	{
		CloseServiceHandle(hServiceDDK);
	}
	if(hServiceMgr)
	{
		CloseServiceHandle(hServiceMgr);
	}
	return bRet;

}

在主程序做一些断点,可以看到在完成LoadNTDriver()函数的时候,观察设备管理器的隐藏设备,会发现HelloDDK的驱动已经加载完毕,也能观察到注册表中的变化,在UnLoadNTDriver()函数中可以查看隐藏设备,理论上是HelloDDK已经不见了。

但是我个人的实验结果是:
应用DbgView能够查看到内核发送的调试信息,但是在查看隐藏设备的时候,有时候成功,有时候失败,调试的时候防止上次的结果影响,均为重启计算机,到现在还没有想清楚为什么会是这样?具体原因还是过段时间,等深入驱动知识再来解答,也诚心希望大牛来帮我解决一下,谢谢了。

实验环境和工具:

XP系统、VS2008、DbgView,驱动文件是之前生成好的,驱动文件的生成环境参见上篇文章的末尾,谢谢。

  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值