C++程式安裝解除安裝WDM驅動

whatday發表於2013-07-31

編譯環境:VS2012 + WIN8 64

測試環境:VM WIN7

測試物件:WDM驅動 (sys檔案 和 inf檔案)

專案型別:Win32 Console Application

其它說明:程式碼來源於網路,經小修改而成,載入驅動方法還有很多(如SetupCopyOEMInf等OEM系列函式),但是逆向EzDriverInstaller驅動載入器,發現和下面程式碼邏輯基本一致,所以最終記錄以下程式碼,以便日後使用。

程式碼如下:

// WinInstallWin.cpp : 
//

#include "stdafx.h"

#include <windows.h>
#include <newdev.h>
#include <setupapi.h>
#include <locale.h>

#pragma comment(lib, "newdev.lib")
#pragma comment(lib, "setupapi.lib")

#ifndef MAX_DEVICE_ID_LEN
#define MAX_DEVICE_ID_LEN     200
#define MAX_DEVNODE_ID_LEN    MAX_DEVICE_ID_LEN
#define MAX_GUID_STRING_LEN   39          // 38 chars + terminator null
#define MAX_CLASS_NAME_LEN    32
#endif


WORD g_wVender = 0;
WORD g_wHardware = 0;
TCHAR g_strVender[20][64] = {0};
TCHAR g_strHardware[20][64] = {0};
TCHAR g_strHID[MAX_PATH+1] = {0};

//列印錯誤
VOID ShowErrorMsg(DWORD Count,LPCWSTR szData)
{
	printf("%d\n%s",&Count,&szData);
}

//過濾字元
VOID FindComma(LPSTR szData)
{
	WORD wLen = (WORD)strlen(szData);
	WORD wIdx;
	WORD wLoop; 
	CHAR szTmp[128] = {0};

	for (wIdx = 0, wLoop = 0; wLoop < wLen; wLoop++)
	{
		if (szData[wLoop] == ',')
			szData[wLoop] = '.';
		else if (szData[wLoop] == ' ')
			continue;
		szTmp[wIdx++] = szData[wLoop];
	}
	memcpy(szData, szTmp, wIdx*sizeof(char));
	szData[wIdx] = 0;
}

//去除字串左邊的空格
VOID StrLTrim(LPSTR szData)
{
	LPSTR ptr = szData;
	//判斷是否為空格
	while (isspace(*ptr))
			ptr++;

	if (strcmp(ptr, szData))
	{
		WORD wLen = (WORD)(strlen(szData) - (ptr - szData));
		memmove(szData, ptr, (wLen+1)*sizeof(char));
	}
}

//去除字串右邊的空格
VOID StrRTrim(LPSTR szData)
{
	LPSTR ptr  = szData;
	LPSTR pTmp = NULL;

	//debug模式下 使用isspace判斷中文 需要設定編碼
	#if defined(WIN32) && defined(_DEBUG)
    char* locale = setlocale( LC_ALL, ".OCP" );
    #endif 

	while (*ptr != 0)
	{
		//判斷是否為空格
		if (isspace(*ptr))
		{
			if (!pTmp)
				pTmp = ptr;
		}
		else
			pTmp = NULL;
		ptr++;
	}

	if (pTmp)
	{
		*pTmp = 0;
		memmove(szData, szData, strlen(szData) - strlen(pTmp));
	}
}

//從字串右邊開始擷取字串
VOID StrRight(LPSTR szData, WORD wCount)
{
	WORD wLen = (WORD)strlen(szData) - wCount;

	if (wCount > 0x7FFF)//負數
		wCount = 0;
	if (wCount >= (WORD)strlen(szData))
		return;

	memmove(szData, szData + wLen, wCount * sizeof(char));
	szData[wCount] = 0;
}

VOID ConvertGUIDToString(const GUID guid, LPSTR pData)
{
	CHAR szData[30] = {0};
	CHAR szTmp[3]   = {0};
	WORD wLoop;

	sprintf_s(pData, _countof(szData), "%04X-%02X-%02X-", guid.Data1, guid.Data2, guid.Data3);
	for (wLoop = 0; wLoop < 8; wLoop++)
	{
		if (wLoop == 2)
			strcat_s(szData, "-");
		sprintf_s(szTmp, _countof(szTmp), "%02X", guid.Data4[wLoop]);
		strcat_s(szData, szTmp);
	}

	memcpy(pData + strlen(pData), szData, strlen(szData));
}

BOOL AnsiToUnicode(LPCSTR Source, const WORD sLen, LPWSTR Destination, const WORD wLen)
{
	return MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Source, sLen, Destination, wLen);
}

BOOL UnicodeToAnsi(LPCWSTR Source, const WORD wLen, LPSTR Destination, const WORD sLen)
{
	return WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, Source, wLen, Destination, sLen, 0L, 0L);
}

// 初始化全域性變數
__inline VOID InitialGlobalVar()
{
	WORD wLoop;

	g_wVender = g_wHardware = 0;
	for (wLoop = 0; wLoop < 20; wLoop++)
	{
		RtlZeroMemory(g_strVender[wLoop], sizeof(TCHAR)*64);
		RtlZeroMemory(g_strHardware[wLoop], sizeof(TCHAR)*64);
	}
}

//安裝驅動功能
__inline BOOL IsInstalled()
{
	HDEVINFO hDevInfo = 0L;
	SP_DEVINFO_DATA spDevInfoData = {0L};
	WORD wIdx;
	BOOL bIsFound;

	//得到裝置資訊結構的控制程式碼
	hDevInfo = SetupDiGetClassDevs(0L, 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT);
	if (hDevInfo == INVALID_HANDLE_VALUE)
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetClassDevs"));
		return FALSE;
	}

	spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
	wIdx = 0;
	bIsFound = 0;
	while (++wIdx)
	{
		//找到所有的硬體裝置,並且可以得到所有的硬體裝置的詳細資訊
		if (SetupDiEnumDeviceInfo(hDevInfo, wIdx, &spDevInfoData))
		{
			LPTSTR ptr;
			LPBYTE pBuffer = NULL;
			DWORD dwData  = 0L;
			DWORD dwRetVal;
			DWORD dwBufSize = 0L;

			while (TRUE)
			{
				//可以在前面得到的指向某一個具體裝置資訊集合的指標中取出某一項資訊
				dwRetVal = SetupDiGetDeviceRegistryProperty(hDevInfo, &spDevInfoData, SPDRP_HARDWAREID,
					&dwData, (PBYTE)pBuffer, dwBufSize, &dwBufSize);
				if (!dwRetVal)
					dwRetVal = GetLastError();
				else
					break;
				if (dwRetVal == ERROR_INVALID_DATA)
					break;
				else if (dwRetVal == ERROR_INSUFFICIENT_BUFFER)
				{
					if (pBuffer)
						LocalFree(pBuffer);
					pBuffer = (LPBYTE)LocalAlloc(LPTR, dwBufSize);
				}
				else
				{
					ShowErrorMsg(dwRetVal, _T("SetupDiGetDeviceRegistryProperty"));
					//銷燬一個裝置資訊集合
					SetupDiDestroyDeviceInfoList(hDevInfo);
					return FALSE;
				}
			}

			if (dwRetVal == ERROR_INVALID_DATA) 
				continue;

			for (ptr = (LPTSTR)pBuffer; *ptr && (ptr < (LPTSTR)&pBuffer[dwBufSize]); ptr += _tcslen(ptr) + sizeof(TCHAR))
			{
				WORD wLoop;

				for (wLoop = 0; wLoop < g_wHardware; wLoop++)
				{
					if (!_tcscmp(g_strHardware[wLoop], ptr))
					{
						bIsFound = TRUE;
						break;
					}
				}
			}
			if (pBuffer)
				LocalFree(pBuffer);
			if (bIsFound)
				break;
		}
	}
	//銷燬一個裝置資訊集合
	SetupDiDestroyDeviceInfoList(hDevInfo);
	return bIsFound;
}

//尋找指定的節名 如果找到返回TRUE 反之返回FALSE
BOOL FindSectionName(FILE *pFile, const char *szKey)
{
	char szData[256] = {0};

	if (!pFile)
		return FALSE;

	//將檔案內部的位置指標重新指向一個流(資料流/檔案)的開頭
	rewind(pFile);
	//迴圈讀取檔案內容
	while (!feof(pFile))
	{
		//讀取一行
		fgets(szData, 255, pFile);
		//去除前後空格
		StrLTrim(szData);
		StrRTrim(szData);

		if (strcmp(szKey, szData) == 0)
			return TRUE;		
	}
	return FALSE;
}

//得到INF檔案中節的數量
__inline BOOL GetSectionData(FILE* pFile, const char* szKey, const char bIsVender)
{
	char szData[128] = {0};
	
	if (bIsVender)
		strcpy_s(szData, szKey);
	else
		sprintf_s(szData, _countof(szData), "[%s]", szKey);

	if (FindSectionName(pFile, szData) == FALSE)
		return FALSE;

	RtlZeroMemory(szData, sizeof(char)*128);
	while (!feof(pFile))
	{
		char *str = NULL;
		fgets(szData, 127, pFile);
		szData[strlen(szData)-1] = 0;
		StrLTrim(szData);
		StrRTrim(szData);
		if (!*szData)
			continue;
		if (szData[0] == ';')
			continue;

		if (strchr(szData, '['))
		{
			StrLTrim(szData);
			if (szData[0] != ';')
				return 1;
			else
				continue;
		}

		if (bIsVender)
			str = strchr(szData, '=');
		else
			str = strchr(szData, ',');

		if (*str)
		{
			char szTmp[128] = {0};
			WORD pos = (WORD)(str - szData + 1);

			StrRight(szData, (short)(strlen(szData)-pos));
			StrLTrim(szData);
			StrRTrim(szData);
			FindComma(szData);
			if (bIsVender)
			{
				AnsiToUnicode(szData, strlen(szData), g_strVender[g_wVender++], 64);
			}
			else
			{
				AnsiToUnicode(szData, strlen(szData), g_strHardware[g_wHardware++], 64);
			}
		}/* end if */
	}
	return TRUE;
}

//得到INF檔案相關資料
BOOL GetINFData(FILE *pFile)
{
	WORD wLoop;

	if (!g_wVender || !g_wHardware)
		InitialGlobalVar();
	if (GetSectionData(pFile, "[Manufacturer]", TRUE) == FALSE)
		return 0;
							   
	for (wLoop = 0; wLoop < g_wVender; wLoop++)
	{
		CHAR szVender[64] = {0};
		UnicodeToAnsi(g_strVender[wLoop], _tcslen(g_strVender[wLoop]), szVender, 64);
		GetSectionData(pFile, szVender, FALSE);
	}
	if (g_wHardware != 0)
	{
		if (IsInstalled() == TRUE)//如果已經安裝
			return FALSE;
		else
			return TRUE;
	}
	return FALSE;
}

//實質性的安裝驅動
__inline BOOL InstallClassDriver(LPCTSTR theINFName)
{
	GUID guid = {0};
	SP_DEVINFO_DATA spDevData = {0};
	HDEVINFO hDevInfo = 0L;
	TCHAR className[MAX_CLASS_NAME_LEN] = {0};
	LPTSTR pHID = NULL;
	WORD wLoop;
	BOOL bRebootRequired;

	//取得此驅動的GUID值
	if (!SetupDiGetINFClass(theINFName, &guid, className, MAX_CLASS_NAME_LEN, 0))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetINFClass"));
		return FALSE;
	}

	//建立裝置資訊塊列表
	hDevInfo = SetupDiCreateDeviceInfoList(&guid, 0);
	if (hDevInfo == INVALID_HANDLE_VALUE)
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiCreateDeviceInfoList"));
		return FALSE;
	}

	spDevData.cbSize = sizeof(SP_DEVINFO_DATA);
	//建立裝置資訊塊
	if (!SetupDiCreateDeviceInfo(hDevInfo, className, &guid, 0L, 0L, DICD_GENERATE_ID, &spDevData))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiCreateDeviceInfo"));
		//銷燬一個裝置資訊集合
		SetupDiDestroyDeviceInfoList(hDevInfo);
		return FALSE;
	}

	for (wLoop = 0; wLoop < g_wHardware; wLoop++)
	{
		if (pHID)
			LocalFree(pHID);

		pHID = (LPTSTR)LocalAlloc(LPTR, _tcslen(g_strHardware[wLoop])*2*sizeof(TCHAR));
		if (!pHID)
		{
			ShowErrorMsg(GetLastError(), _T("LocalAlloc"));
			//銷燬一個裝置資訊集合
			SetupDiDestroyDeviceInfoList(hDevInfo);
			return FALSE;
		}

		_tcscpy_s(pHID, _tcslen(g_strHardware[wLoop])*2, g_strHardware[wLoop]);
		//設定硬體ID
		if (!SetupDiSetDeviceRegistryProperty(hDevInfo, &spDevData, SPDRP_HARDWAREID, (PBYTE)pHID,
			(DWORD)(_tcslen(g_strHardware[wLoop])*2*sizeof(TCHAR))))
		{
			ShowErrorMsg(GetLastError(), _T("SetupDiSetDeviceRegistryProperty"));
			//銷燬一個裝置資訊集合
			SetupDiDestroyDeviceInfoList(hDevInfo);
			LocalFree(pHID);
			return FALSE;
		}
		//呼叫相應的類程式來註冊裝置
		if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDevInfo, &spDevData))
		{
			ShowErrorMsg(GetLastError(), _T("SetupDiCallClassInstaller"));
			//銷燬一個裝置資訊集合
			SetupDiDestroyDeviceInfoList(hDevInfo);
			LocalFree(pHID);
			return FALSE;
		}

		bRebootRequired = FALSE;
		//安裝更新和硬體ID相匹配的驅動程式
		if (!UpdateDriverForPlugAndPlayDevices(0L, g_strHardware[wLoop], theINFName, 
			INSTALLFLAG_FORCE, &bRebootRequired))
		{
			DWORD dwErrorCode = GetLastError();
			//呼叫相應的類程式來移除裝置
			if (!SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, &spDevData))
				ShowErrorMsg(GetLastError(), _T("SetupDiCallClassInstaller(Remove)"));
			ShowErrorMsg((WORD)dwErrorCode, _T("UpdateDriverForPlugAndPlayDevices"));
			//銷燬一個裝置資訊集合
			SetupDiDestroyDeviceInfoList(hDevInfo);
			LocalFree(pHID);
			return FALSE;
		}
		LocalFree(pHID);
		pHID = NULL;
	}
	//銷燬一個裝置資訊集合
	SetupDiDestroyDeviceInfoList(hDevInfo);
	_tprintf(_T("Install Successed\n"));
	return TRUE;
}

// 安裝WDM驅動的測試工作
BOOL StartInstallWDMDriver(LPCTSTR theInfName)
{
	HDEVINFO hDevInfo = 0L;
	GUID guid = {0L};
	SP_DEVINSTALL_PARAMS spDevInst = {0L};
	TCHAR strClass[MAX_CLASS_NAME_LEN] = {0L};

	//取得此驅動的GUID值
	if (!SetupDiGetINFClass(theInfName, &guid, strClass, MAX_CLASS_NAME_LEN, 0))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetINFClass"));
		return FALSE;
	}

	//得到裝置資訊結構的控制程式碼
	hDevInfo = SetupDiGetClassDevs(&guid, 0L, 0L, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_PROFILE);
	if (!hDevInfo)
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetClassDevs"));
		return FALSE;
	}

	
	spDevInst.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
	//獲得指定裝置的安裝資訊
	if (!SetupDiGetDeviceInstallParams(hDevInfo, 0L, &spDevInst))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetDeviceInstallParams"));
		return FALSE;
	}

	spDevInst.Flags   = DI_ENUMSINGLEINF;
	spDevInst.FlagsEx = DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
	_tcscpy_s(spDevInst.DriverPath, _countof(spDevInst.DriverPath), theInfName);

	//為裝置資訊集或者是一個實際的裝置資訊單元設定或清除類安裝引數
	if (!SetupDiSetDeviceInstallParams(hDevInfo, 0, &spDevInst))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiSetDeviceInstallParams"));
		return FALSE;
	}

	//獲取這個裝置的驅動程式資訊列表
	if (!SetupDiBuildDriverInfoList(hDevInfo, 0, SPDIT_CLASSDRIVER))
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiDeviceInstallParams"));
		return FALSE;
	}

	//銷燬一個裝置資訊集合
	SetupDiDestroyDeviceInfoList(hDevInfo);

	//進入安裝裝置驅動函式
	return InstallClassDriver(theInfName);
}

// 解除安裝WDM驅動
VOID UninstallWDMDriver(LPCTSTR theHardware)
{
	SP_DEVINFO_DATA spDevInfoData = {0};
	HDEVINFO hDevInfo = 0L;
	WORD wIdx, wCount = 0;

	//得到裝置資訊結構的控制程式碼
	hDevInfo = SetupDiGetClassDevs(0L, 0L, 0L, DIGCF_ALLCLASSES | DIGCF_PRESENT);
	if (hDevInfo == INVALID_HANDLE_VALUE)
	{
		ShowErrorMsg(GetLastError(), _T("SetupDiGetClassDevs"));
		return;
	}

	wIdx = 0;
	while (TRUE)
	{
		spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
		//找到所有的硬體裝置,並且可以得到所有的硬體裝置的詳細資訊
		if (SetupDiEnumDeviceInfo(hDevInfo, wIdx, &spDevInfoData))
		{
			char Buffer[2048] = {0};

			//可以在前面得到的指向某一個具體裝置資訊集合的指標中取出某一項資訊
			if (SetupDiGetDeviceRegistryProperty(hDevInfo, &spDevInfoData, SPDRP_HARDWAREID,
				0L, (PBYTE)Buffer, 2048, 0L))
			{
				if (!_tcscmp(theHardware, (LPTSTR)Buffer))
				{
					//從系統中刪除一個註冊的裝置介面
					if (!SetupDiRemoveDevice(hDevInfo, &spDevInfoData))
						ShowErrorMsg(GetLastError(), _T("SetupDiRemoveDevice"));          
					wCount++;
				}
			}
		}
		else
			break;
		wIdx++;
	}

	if (wCount != 0)
		_tprintf(_T("UnInstall Successed\n"));

	//銷燬一個裝置資訊集合
	SetupDiDestroyDeviceInfoList(hDevInfo);
	InitialGlobalVar();
	return;
}

//INF檔案路徑
const LPTSTR g_pInfPath = _T("C:\\Windows\\System32\\DriverStore\\FileRepository\\mydriver1.inf_x86_neutral_15204d1ef3d409a0\\mydriver1.inf");

//入口函式
int _tmain(int argc, _TCHAR* argv[])
{
	CHAR szInfPath[MAX_PATH] = {0};
	UnicodeToAnsi(g_pInfPath, _tcslen(g_pInfPath), szInfPath, MAX_PATH);
	FILE* pInf;
	errno_t err;

	if ((err=fopen_s(&pInf, szInfPath, "r"))!=0)
	{
		_tprintf(_T("can not open file %s\n"), g_pInfPath);
		return 0;
	}

	// 獲取INF檔案資料
	GetINFData(pInf);
	fclose(pInf);

	// 安裝WDM驅動
	if(_tcscmp(argv[1], TEXT("-Install"))==0)
	{
		if (StartInstallWDMDriver(g_pInfPath) == FALSE)
		{
			_tprintf(_T("Start Install WMD Driver failed\n"));
			return 0;
		}
	}
	// 解除安裝WDM驅動
	else if(_tcscmp(argv[1], TEXT("-UnInstall"))==0)
	{
		for (WORD wLoop = 0; wLoop < g_wHardware; wLoop++)
			UninstallWDMDriver(g_strHardware[wLoop]);
	}
	
	return 1;  
}

具體的SYS 和 INF檔案 可參考:http://blog.csdn.net/whatday/article/details/9384577

測試結果:

把相應EXE及其SYS INF檔案拷貝的WIN7虛擬機器,開啟CMD執行WinInstallWin.exe -Install 顯示安裝成功如圖:


再用DeviceTree檢視結果:

可以看到裝置已經新增成功

在CMD中執行 WinInstallWin.exe -UnInstall 顯示解除安裝成功 如圖:

再次使用DeviceTree檢視裝置 需要重新整理一下DeviceTree 結果如圖:

先前的裝置已經解除安裝掉了,到此WDM驅動的安裝解除安裝就全部結束了。

相關文章