以下均为学习后的个人见解,若果有误,欢迎纠正,谢谢
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,驱动文件是之前生成好的,驱动文件的生成环境参见上篇文章的末尾,谢谢。