WMI技术介绍和应用——Instance/Method Provider
? ? ? ? 在《WMI技術介紹和應用——事件通知》一文中,我們提到了提供者(Provider)這個概念。我們還是要引入WMI的結構圖(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 我們在1這層的Native C/C++里可以看到若干Provider,這些Provider將各個實例或者事件信息通過WMI core傳遞到上層。微軟對這塊內容在MSDN上沒有給出詳細的例子,所以一開始摸索起來非常困難,以致我一度想放棄對其的研究。但是好在找到一本《Developing WMI Solutions》,該書非常詳細講解了WMI的一些書寫規則,雖然該書的網站已經不再維護,其事例代碼也找不到了。但是循著書中的講解,我還是將其摸索出來,以供大家參考。
instance provider
? ? ? ? MSDN上說Virtual Studio的ATL模板里有WMI向導,并且推薦大家使用向導去生成WMI工程。需要注意的是,不是每個版本的VS都有這個向導,像我環境中的VS2015則沒有,而VS2005則有。
? ? ? ? 我們先創建一個名字為Test_Instance的ATL工程
? ? ? ? Server type選擇Dynamic-link library (DLL),其他不選,我們將生成Test_Instance和Test_InstancePS兩個工程。Test_InstancePS工程我們不用關心,直接刪除就行了。我們選中Test_Instance工程,右擊并選擇Add,添加一個class。在向導里我們可以看到ATL下有WMI相關模板
? ? ? ? Name字段還是填寫Test_Instance,在Instance Provider的向導中,我們在short name項下填入我們需要實例化的類的名字TestClass
? ? ? ? 在WMI Class項中,我們不做任何修改,使用默認的root空間,其實最終我們真實使用的是root\default。
? ? ? ? 在Attribute項中,我們在Threading Model中選擇Both,其他可選項都勾選上。
? ? ? ? 通過向導的設定,我們的工程中新增了TestClass.cpp、TestClass.h和Test_Instance.mof三個文件。Test_Instance.mof是我們向WMI倉庫注冊使用的文件,其他則是我們的代碼文件。
? ? ? ? 向導已經幫我們在mof文件中生成了一大串內容。
#pragma autorecover#pragma namespace ("\\\\.\\root\\default")class Win32_ProviderEx : __Win32Provider
{[Description ( "Hosting Model, provides compatibility with Windows XP and Windows Server .NET. Do not override." ) , Override("HostingModel")]string HostingModel = "NetworkServiceHost";[Description("..."),Override("SecurityDescriptor")] string SecurityDescriptor; UInt32 version = 1;
} ;instance of Win32_ProviderEx as $TestClass
{Name = "TestClass" ; //Name is the key property for __Provider objects.//vendor|provider|version is the suggested format//to prevent name collisions.ClsId = "{7F1C3BD1-7732-403F-844F-CC6ED9CA85FE}" ; //provider GUIDDefaultMachineName = NULL; //NULL for local machineClientLoadableCLSID = NULL; //reservedImpersonationLevel = 0; //reservedInitializationReentrancy = 0; //Set of flags that provide information about serialization://0 = all initialization of this provider must be serialized//1 = all initializations of this provider in the same namespace must be serialized//2 = no initialization serialization is necessaryInitializeAsAdminFirst = FALSE; //Request to be fully initialized as "Admin" before //initializations begin for usersPerLocaleInitialization = FALSE; //Indicates whether the provider is initialized for each //locale if a user connects to the same namespace more //than once using different locales.PerUserInitialization = FALSE; //Indicates whether the provider is initialized once for each actual //Windows NT/Windows 2000 NTLM user making requests of the provider. //If set to FALSE, the provider is initialized once for all usersPure = TRUE; //A pure provider exists only to service requests from //applications and WMI. Most providers are pure providers.//Non-pure providers transition to the role of client after they have //finished servicing requests. UnloadTimeout = NULL; //Currently ignored//Use __CacheControl class in the root namespace to control provider unloading.//A string in the DMTF date/time format that specifies how long WMI //allows the provider to remain idle before it is unloaded.} ; instance of __InstanceProviderRegistration
{Provider = $TestClass;SupportsPut = "TRUE"; SupportsGet = "TRUE"; SupportsDelete = "TRUE"; SupportsEnumeration = "TRUE"; QuerySupportLevels = {"WQL:Associators","WQL:V1ProviderDefined","WQL:UnarySelect","WQL:References"};};instance of __MethodProviderRegistration
{Provider = $TestClass;
};
? ? ? ? 這一長串內容詳細大家可以參看MSDN,其中主要的是__InstanceProviderRegistration和__MethodProviderRegistration實例申明。instance of __InstanceProviderRegistration是說我們需要一個Instance類型Provider的注冊實例,instance of __MethodProviderRegistration是說我們需要一個Method類型Provider的注冊實例。它們的Provider都是我們之前使用instance of Win32_ProviderEx as $TestClass申明的實例。前者用于實例方面的使用,比如獲取對象、刪除對象、查詢信息等;后者用于方法方面的使用,比如我們之前說的Win32_Process的Create方法創建進程。
? ? ? ? 我們先修改mof文件,我們在文件末尾新增如下內容
[
dynamic : ToInstance,
provider("TestClass") : ToInstance, // uses the TestClass Provider
ClassContext("whatever!"), // information is dynamically// supported by the provider
DisplayName("ClassInstance"): ToInstance
] class ClassInstance
{
[key]
string name = "CIN";
[PropertyContext("ClassInstance_Member")]
string value = "CIV";
};
? ? ? ??其中非常重要的是provider字段填寫,它就是指向我們之前申明的provider。然后我們申明了一個ClassInstance的類,其有一個Key屬性的變量name,還有一個普通類型變量value。
? ? ? ? 我們在到TestClass.cpp中修改相關內容。首先我們需要將類名定義為我們在mof文件中定義的類名
//TODO: define provided class name, e.g.:
const static WCHAR * s_pMyClassName = L"ClassInstance";
? ? ? ? 為了支持WQL查詢,我們需要修改ExecQueryAsync方法,我們在其方法中新增如下邏輯
CComPtr<IWbemClassObject> sp_object;CComPtr<IWbemClassObject> pNewInst;hr = m_pClass->SpawnInstance(0, &pNewInst);if (FAILED(hr)) { return WBEM_E_INVALID_CLASS;}sp_object = pNewInst;CComVariant value;value.vt = VT_BSTR;value.bstrVal = CComBSTR("notepad.exe");sp_object->Put(L"name", 0, &value, 0);sp_object->Put(L"value", 0, &value, 0);hr = pResponseHandler->Indicate(1, &sp_object.p);hr = pResponseHandler->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL);
? ? ? ? 這段邏輯,我們通過SpawnInstance創建了mof定義的類的實例。然后通過Put方法,我們設置了其name和value變量的值。
? ? ? ? 代碼修改好后,我們編譯工程。工程編譯中,會對mof文件進行檢查和注冊。我們還可以手工使用Mofcomp工具對mof文件進行注冊。
? ? ? ? 使用regsvr32注冊編譯出來的dll文件。這樣我們的實例便可以查詢了,使用之前工程中的方法
CSynQueryData recvnotify(L"root\\default", L"SELECT * FROM ClassInstance");
recvnotify.ExcuteFun();
? ? ? ? 可以得到
method provider? ? ? ??
? ? ? ? 我們再說說Method Provider。一個類的方法有靜態和非靜態之分,我們在mof里定義的類也是如此。我們對上例中的ClassInstance類新增兩個方法
class ClassInstance
{
[key]
string name = "CIN";
[PropertyContext("ClassInstance_Member")]
string value = "CIV";[implemented]
void func();
[static, implemented]
void staticfunc();
};
? ? ? ? 其中func方法是非靜態方法,staticfunc是靜態方法。和C++中定義類似,靜態方法我們要使用類名+方法名調用;非靜態方法要使用對象調用。在WMI Provider中,執行方法的函數是ExecMethodAsync,我們對其進行修改
CComPtr<IWbemQualifierSet> pQualifierSet;hr = m_pClass->GetMethodQualifierSet(strMethodName, &pQualifierSet);if (SUCCEEDED(hr)) {hr = pQualifierSet->Get(L"static", 0, NULL, NULL);if (SUCCEEDED(hr)) {// static function. go on}else {CComPtr<IWbemClassObject> pInstance;hr = GetInstanceByPath(strObjectPath,&pInstance);if(FAILED(hr)) {// object is not existedreturn hr;}// object is existing. go on}}else{return hr;}
? ? ? ? 這段代碼,我們檢測函數是否使用了static修飾。如果是,則它是靜態方法,我們就不用做對象是否存在的檢查;如果不是靜態方法,我們就要對對象是否存在進行檢查,如果對象不存在,我們則應該返回相應錯誤,否則繼續執行。
? ? ? ? 我想模擬Win32_Process的Create方法啟動一個進程。我嘗試了CreateProcess、ShellExcute等方法,均不成功。后來逆向了一下Win32_Process所以在的dll文件,發現其中啟動進程是使用CreateProcessAsUser,且在此之前做了很多和用戶有關的操作。為了簡便,我們可以這樣書寫
HANDLE TokenHandle = NULL;OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, 1, &TokenHandle);STARTUPINFO StartupInfo;memset(&StartupInfo, 0 , sizeof(StartupInfo));StartupInfo.wShowWindow = SW_SHOWNORMAL;PROCESS_INFORMATION ProcessInformation;memset(&ProcessInformation, 0, sizeof(ProcessInformation));BOOL bRet = CreateProcessAsUser( TokenHandle ,L"C:\\windows\\notepad.exe",NULL , NULL,NULL,FALSE , NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB ,NULL,NULL,&StartupInfo,&ProcessInformation);
? ? ? ? 該過程我們獲取的權限和我們啟動查詢的進程中的線程相同。這樣我們便將notepad啟動起來。
? ? ? ? 我們對static方法的調用是這樣的
CExcuteMethod* excute_method = NULL;{ParamsMap params;excute_method = new CExcuteMethod(L"root\\default", L"ClassInstance", L"", L"staticfunc", L"", params);excute_method->ExcuteFun();}delete excute_method;
? ? ? ? 對非static方法的調用,我們則要傳入實例名
CExcuteMethod* excute_method = NULL;{ParamsMap params;excute_method = new CExcuteMethod(L"root\\default", L"ClassInstance", L"ClassInstance.name='notepad.exe'", L"staticfunc", L"", params);excute_method->ExcuteFun();}delete excute_method;
? ? ? ? 工程鏈接:http://pan.baidu.com/s/1geucuKb 密碼:pcwl
總結
以上是生活随笔為你收集整理的WMI技术介绍和应用——Instance/Method Provider的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WMI技术介绍和应用——事件通知
- 下一篇: WMI技术介绍和应用——Event Pr