Вопросы по технологии COM WMI (pure C,ASM)

Тема в разделе "WASM.WIN32", создана пользователем M0rg0t, 23 сен 2020.

  1. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Собственно, сабж. Нужна платная консультация по некоторым сорцам технологии СОМ и WMI, язык - чистый Си либо Асм. В консультацию должны входить объяснения по коду (почему тут так, что значит эта строка), т.е. откомментировать код и ответить на возникающие вопросы.

    Бюджет обсуждается.

    Сам не могу разобраться, везде либо павершелл либо сошарп либо еще какая-то высокоуровневая ересь.
     
    Последнее редактирование: 24 сен 2020
  2. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Так а что разбираться то? Wmi-провайдеры - это просто COM-классы с определенными интерфейсами.
     
  3. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Rel, не могу там некоторые нюансы понять, что и зачем и как.
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Так залей сорсы сюда, щас разберемся, если Инде своими визорами и анклавами нас на флуд отвлекать не будет.
     
  5. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Rel, допустим вот такой код https://pastebin.com/5Bh8ZBhc
    мне уже 1 человек прояснил некоторые моменты в лс, но все равно надо изучать много матчасти, чтобы понять от и до. Тут идет как бы отслеживание запуска новой службы, и оно не пашет. А заменить на новый процесс - пашет. Почему так. В чем логика вообще.
     
  6. Vicshann

    Vicshann Member

    Публикаций:
    0
    Регистрация:
    22 сен 2020
    Сообщения:
    36
    M0rg0t нравится это.
  7. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    В коде идет не отслеживание запуска службы, а отслеживание добавления службы. Для отслеживания запуска (изменения состояния) нужно использовать __InstanceModificationEvent событие.

    В COM нет ничего сложного (если ты только не создаешь некоторые ActiveX библиотеки). Вот пример COM библиотеки на чистом C, без каких-либо платформозависимых функций. Там еще в теме есть примеры работы на асме. Если есть вопросы по COM можешь тут задавать, я отвечу.
     
    M0rg0t, TermoSINteZ и Aiks нравится это.
  8. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    То есть Win32_Service не работает, но работает Win32_Process? А уже ответили, я не успел((
     
  9. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Я не могу понять основную логику этого WMI . Откуда что берется. Вот допустим, отследил новый Win32_Process. Смотрю в справке, у него есть метод GetOwner. И откуда его вызывать? Из EventSink ? Или как-то иначе? И что туда передавать, буфер или он должен вернуть буфер? Вот ссылка https://docs.microsoft.com/en-us/windows/win32/api/wbemcli/nf-wbemcli-iwbemservices-execmethod .
     
  10. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Ты получаешь уведомление в IWbemObjectSink::Indicate в которое приходит массив объектов __InstanceCreationEvent. Каждый объект имеет свойство TargetInstance которое содержит копию созданного объекта, в твоем случае это экземпляр Win32_Process:
    Ты запрашиваешь у __InstanceCreationEvent объекта свойство TragetInstance через IWbemClassObject::Get, у которого ты и будешь вызывать нужные тебе методы.
    Для использование метода IWbemServices::ExecMethod необходимо передавать путь к объекту у которого вызывается метод. Для этого у экземпляра Win32_Process необходимо прочитать свойство __Path:
    После этого можно уже вызывать метод GetOwner IWbemServices::ExecMethod и в ppOutParams получать выходные параметры, т.к. метод имеет только [out] параметры. Затем у ppOutParams запрашиваются нужные параметры через IWbemClassObject::Get.

    В псевдокоде, без проверок ошибок и параметров:
    Код (C++):
    1. VARIANT vObj;        // Объект Win32_Process
    2. VARIANT vPath;        // Путь до объекта (моникер)
    3. VARIANT vtUser;        // Выходные параметры метода Win32_Process::GetOwner
    4. VARIANT vtDomain;
    5.  
    6. IWbemClassObject *pOutParams = NULL;
    7. IWbemClassObject *pInstance;
    8.  
    9. // Инициализируем т.к. IWbemClassObject::Get имеет [in][out] тип
    10. VariantInit(&vObj);
    11. VariantInit(&vPath);
    12. VariantInit(&vtUser);
    13. VariantInit(&vtDomain);
    14.  
    15. // Получаем экземпляр Win32_Process
    16. apObjArray[i]->Get(L"TargetInstance", 0, &vObj, NULL, NULL);
    17.  
    18. // Запрашиваем IWbemClassObject у этого объекта
    19. vObj.punkVal->QueryInterface(&pInstance);
    20.  
    21. // Получаем моникер т.к. ExecMethod требует его
    22. pInstance->Get(L"__PATH", 0, &vPath, NULL, NULL);
    23.  
    24. // Вызываем метод. В pOutParams попадают выходные параметры
    25. this->m_pSvc->ExecMethod(vPath.bstrVal, L"GetOwner", 0, NULL, NULL, &pOutParams, NULL);
    26.  
    27. // Извлекаем выходные параметры. Они будут лежать в vt.bstrVal
    28. pOutParams->Get(L"User", 0, &vtUser, NULL, NULL);
    29. pOutParams->Get(L"Domain", 0, &vtDomain, NULL, NULL);
    30.  
     
    M0rg0t нравится это.
  11. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Спасибо, но до конца все равно не понятно.
    Почему приходит массив, откуда он берется? Допустим, создался 1 процесс, так должен прийти 1 объект, или они накапливаются?
    Эта строка все равно непонятна. Что такое в данном случае this и m_pSvc ? У меня есть прототип вида IWbemObjectSink *this , но там нет таких методов . Вот весь код

    Код (C):
    1. HRESULT STDMETHODCALLTYPE EventSink_Indicate(IWbemObjectSink *this, long lObjectCount, IWbemClassObject **apObjArray)
    2. {
    3.        for (int i = 0; i < lObjectCount; i++)
    4.     {
    5.         IWbemClassObject *pIWbemClassObject = apObjArray[i];
    6.         VARIANT vcn;
    7.         HRESULT hr;
    8.         if (!(hr = pIWbemClassObject->lpVtbl->Get(pIWbemClassObject, L"__Class", 0, &vcn, NULL, NULL)))
    9.         {
    10.             if (vcn.vt == VT_BSTR)
    11.                 printf("%ls\n", vcn.bstrVal);
    12.             VariantClear(&vcn);
    13.         }
    14.         else
    15.             printf("error: 0x%Xh\n", hr);
    16.         if (!(hr = pIWbemClassObject->lpVtbl->Get(pIWbemClassObject, L"TargetInstance", 0, &vcn, NULL, NULL)))
    17.         {
    18.             IUnknown *pUnk = vcn.punkVal;
    19.             IWbemClassObject *pIWbemClassObject1;
    20.             if (!(hr = pUnk->lpVtbl->QueryInterface(pUnk, &IID_IWbemClassObject, (void **)&pIWbemClassObject1)))
    21.             {
    22.                 VARIANT vcn1;
    23.                
    24.                 if (!(hr = pIWbemClassObject1->lpVtbl->Get(pIWbemClassObject1, L"Name", 0, &vcn1, NULL, NULL)))
    25.                 {
    26.                     printf("%ls\t", vcn1.bstrVal);
    27.                     if (lstrcmpW(vcn1.bstrVal,L"notepad.exe") == 0)
    28.                         {
    29.                         VARIANT vPath;        // Путь до объекта (моникер)
    30.                         VariantInit(&vPath);
    31.  
    32.                         pIWbemClassObject1->lpVtbl->Get(pIWbemClassObject1,L"__PATH",0,&vPath,NULL,NULL);
    33.                         //this->m_pSvc->ExecMethod(vPath.bstrVal, L"GetOwner", 0, NULL, NULL, &pOutParams, NULL);
    34.                    
    35.                         //pIWbemClassObject->lpVtbl-> Откуда это взять?
    36.                         }
    37.                        VariantClear(&vcn1);
    38.                 }
    39.             }
    40.             VariantClear(&vcn);
    41.         }
    42.         else
    43.             printf("error: 0x%Xh\n", hr);
    44.     }
    45.  
    46.     return WBEM_S_NO_ERROR;
    47. }
    --- Сообщение объединено, 24 сен 2020 ---
    Thetrik, подумал еще - разве что делать вот это
    Код (C++):
    1. IWbemServices *pSvc = NULL;
    глобальной переменной, доступной из этих методов; правильно это или нет?
     
  12. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    У тебя стоит WITHIN 1 это означает что WMI будет периодически с периодом в 1 секунду чекать события. В этот интервал может произойти несколько событий - поэтому в колбек приходит массив.
    Это IWbemServices объект. Просто его можно сохранить внутри объекта принимающего колбеки. Пишу с телефона - неудобно отвечать.
     
    M0rg0t нравится это.
  13. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Как будет время, напиши пожалуйста, как. Я сделал как глобальную переменную, и вроде работает, но интересно как с указателем.

    И еще вопрос. Стоит ли попробовать все это дело реализовать на том же VB6 ? Который скрывает часть абстракции, или нет смысла? Просто не могу въехать в логику этого СОМ, обычный винапи прост, вызвал функцию, выделил память. Тут же какая-то ерунда, вызвать метод, найти метод, классы эти. Мб это потому, что я не знаю ООП, или хз.
     
  14. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Ну вообще да, если у тебя нет чем то обоснованного требования писать на С. Ну помимо VB6 с этим вполне нормально работает дотнет или пауершелл.
     
  15. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Rel, сам проект должен быть на Си, просто подумал , мб первоначальная реализация на чем-то высокоуровневом поможет лучше понять это все. Ну, к примеру, иногда что-то пишу на РНР, сам алгоритм , РоС, а потом уже переписываю на Си.
     
  16. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    В принципе можешь оставить глобальную переменную. Я бы сделал так, добавил в конструктор аргумент и в класс свойство:
    Код (C++):
    1. class EventSink : public IWbemObjectSink
    2. {
    3.     ...
    4.     IWbemServices *m_pSvc;
    5.  
    6. public:
    7.  
    8.     EventSink(IWbemServices* pSvc) { m_lRef = 0; m_pSvc = pSvc; m_pSvc->AddRef(); }
    9.    ~EventSink() { bDone = true; m_pSvc->Release(); }
    Создаешь соответственно так:
    Код (C++):
    1. EventSink* pSink = new EventSink(pSvc);
    Можешь на VB6 на нем проще, но там много абстракции срывается, поэтому разобраться и переписать на C будет непросто. Это не логика COM такая, это логика WMI такая. Обычно в COM все проще, а WMI основан на COM и вот этот поиск методов и т.п. это "заморочки" WMI. Посмотри к примеру работу с Direct3D - вот работа с COM.
    --- Сообщение объединено, 25 сен 2020 ---
    Кстати на VB6 код будет такой:
    Код (Text):
    1. Private WithEvents m_cSink  As SWbemSink
    2. Private m_cSvc   As SWbemServices
    3.  
    4. Private Sub Form_Load()
    5.  
    6.     Set m_cSvc = GetObject("winmgmts:\\.\root\cimv2")
    7.     Set m_cSink = New SWbemSink
    8.     m_cSvc.ExecNotificationQueryAsync m_cSink, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'"
    9.    
    10. End Sub
    11.  
    12. Private Sub m_cSink_OnObjectReady( _
    13.             ByVal objWbemObject As WbemScripting.ISWbemObject, _
    14.             ByVal objWbemAsyncContext As WbemScripting.ISWbemNamedValueSet)
    15.     Dim sUser   As String
    16.     Dim sDomain As String
    17.    
    18.     objWbemObject.TargetInstance.GetOwner sUser, sDomain
    19.    
    20.     Debug.Print sUser, sDomain
    21.        
    22. End Sub
    23.  
    В референсах нужно подключить WMI.
     
    M0rg0t нравится это.
  17. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Thetrik, а если брать чистый Си. Вот эта функция объявлена так
    Код (C):
    1. HRESULT STDMETHODCALLTYPE EventSink_Indicate(IWbemObjectSink *this, long lObjectCount, IWbemClassObject **apObjArray)
    Я могу ее как то переопределить, добавив туда IWbemServices четвертым параметром , или нельзя?

    И еще. Вот допустим у меня код отслеживает процессы. Если я хочу добавить еще и службы, как быть? Т.е. я пишу код для процессов
    Код (C):
    1.  
    2. IWbemObjectSink* pStubSink = NULL;
    3. pStubUnk->lpVtbl->QueryInterface(pStubUnk, &IID_IWbemObjectSink, (void **) &pStubSink);
    4. ..вырезано
    5.     hres = pSvc->lpVtbl->ExecNotificationQueryAsync(
    6. pSvc,
    7.         bstrLanguage,
    8.         bstrQuery,
    9.         WBEM_FLAG_SEND_STATUS,
    10.         NULL,
    11.         pStubSink
    12. );
    13.  
    А следующий как? Такое же самое написать, просто создав новый объект IWbemObjectSink (и другой запрос).
    Но куда оно все будет передаваться, ведь функция EventSink_Indicate одна. Нужно уже в ней сделать разделение на процесс/служба/диск (что там еще приходит), или создавать новую функцию вида EventSink_Indicate_0 ?

    georgela, ?
     
  18. georgela

    georgela Member

    Публикаций:
    0
    Регистрация:
    13 окт 2019
    Сообщения:
    51
    M0rg0t, Thetrik, а если брать чистый Си. Вот эта функция объявлена так
    Код (C):
    1. HRESULT STDMETHODCALLTYPE EventSink_Indicate(IWbemObjectSink *this, long lObjectCount, IWbemClassObject **apObjArray)
    Я могу ее как то переопределить, добавив туда IWbemServices четвертым параметром , или нельзя?

    И еще. Вот допустим у меня код отслеживает процессы. Если я хочу добавить еще и службы, как быть? Т.е. я пишу код для процессов
    Код (C):
    1. IWbemObjectSink* pStubSink = NULL;
    2. pStubUnk->lpVtbl->QueryInterface(pStubUnk, &IID_IWbemObjectSink, (void **) &pStubSink);
    3. ..вырезано
    4.     hres = pSvc->lpVtbl->ExecNotificationQueryAsync(
    5. pSvc,
    6.         bstrLanguage,
    7.         bstrQuery,
    8.         WBEM_FLAG_SEND_STATUS,
    9.         NULL,
    10.         pStubSink
    11. );
    А следующий как? Такое же самое написать, просто создав новый объект IWbemObjectSink (и другой запрос).
    Но куда оно все будет передаваться, ведь функция EventSink_Indicate одна. Нужно уже в ней сделать разделение на процесс/служба/диск (что там еще приходит), или создавать новую функцию вида EventSink_Indicate_0 ?
     
    Последнее редактирование модератором: 25 сен 2020
  19. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    так я хочу конкретную тему изучить , а ты хочешь в целом от и до. Тебе надо самому изучать по тому же курсу от Отус или от Нарвахи, а вопросы писать на форуме, и будет норм.
    Эта тема (WMI) очень сложная, черт ногу сломит.
     
  20. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    875
    Нельзя - это интерфейс. У тебя структура EventSink, вот в нее добавь поле просто и обращайся к нему через this->m_pSvc. COM объект это просто структура где первым полем идет указатель на интерфейс. COM метод - это просто функция/процедура которая первым параметром принимает указатель на эту структуру. Вот добавляй какие-тебе нужно поля в структуру, а в колбеке обращайся к ним. Если хочешь полный пример - кидай весь код на С.

    Ну можешь либо просто с тем же Sink'ом ассоциировать - это уже зависит от архитектуры. Если с тем же Sink'ом будешь делать то все события и по процессам и по сервисам будут первым параметром принимать указатель на одну и туже структуру, если по разным то разные. Я бы делал так, если семантика обработки событий разная - делать с разными объектами, если одна и та же - то с одним и тем же.

    Если с тем же Sink'ом будешь делать то определять по типу apObjArray, если с другим то уже по типу Sink'а. К примеру если у тебя разные Sink'и то заведи там поле m_Tag которое будет определять какие события ловит Sink, если Sink один один и тот же то по типу класса TargetInstance (__CLASS).
    --- Сообщение объединено, 25 сен 2020 ---
    georgela, к чему тут это?
     
    M0rg0t нравится это.