WMI Request to remote host using Win API

Just starting out? Need help? Post your questions and find answers here.
tatanas
Enthusiast
Enthusiast
Posts: 204
Joined: Wed Nov 06, 2019 10:28 am
Location: France

WMI Request to remote host using Win API

Post by tatanas »

Hi !

Because I hijacked a thread (same topic) for my problem, I make my own.
I'm trying to get information from a remote host through WMI request with Win API functions.
I've found some PB code working for local requests and C++ code working for remote requests. But I can't adapt this code in Purebasic (I'm new in PB - coming from Autoit).

C++ code : https://pastebin.com/XAcDpLRH

Non working (remote) PB code :
If you want to test it in local, just replace the user and password parameter in ConnectServer() by 0 and *userAcct parameter in CoSetProxyBlanket() by 0 to (or "Null").

Code: Select all

; #WBEM_INFINITE = $FFFFFFFF
#COINIT_MULTITHREADED               = 0
#RPC_C_AUTHN_LEVEL_CONNECT          = 2     ;Authenticates the credentials of the client only when the client establishes a relationship with the server.
#RPC_C_AUTHN_LEVEL_CALL             = 3     ;Authenticates only at the beginning of each remote procedure call when the server receives the request.
#EOAC_NONE                          = 0     ;No Authentication capability flags are set
#RPC_C_AUTHN_WINNT                  = 10    ;Use the Microsoft NT LAN Manager (NTLM) SSP.
; #RPC_C_AUTHN_DEFAULT                  = $FFFFFFFF
#RPC_C_AUTHZ_NONE                   = 0     ;The server performs no authorization. Currently, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_GSS_SCHANNEL, And RPC_C_AUTHN_GSS_KERBEROS all use only RPC_C_AUTHZ_NONE.
#RPC_C_IMP_LEVEL_IMPERSONATE        = 3     ;The server process can impersonate the client's security context while acting on behalf of the client. When impersonating at this level, the impersonation token can only be passed across one machine boundary (can be used to access local resources such as files).
#wbemFlagReturnImmediately          = 16    ;Causes the call to return immediately.
#wbemFlagForwardOnly                = 32    ;Causes a forward-only enumerator to be returned. Forward-only enumerators are generally much faster and use less memory than conventional enumerators, but they do not allow calls to SWbemObject.Clone_.
#CLSCTX_INPROC_SERVER               = $1
#wbemFlagConnectUseMaxWait          = $80

#CLSCTX_REMOTE_SERVER = $10

CompilerIf #PB_Compiler_Unicode
    Macro ptr(String) : @String : EndMacro
CompilerElse
    Macro ptr(String) : ansi2bstr(String) : EndMacro
CompilerEndIf

;=======================================
Procedure.i ansi2bstr(String$)
   size.i = MultiByteToWideChar_(#CP_ACP, 0, String$, Len(String$), 0, 0)
   If Size
    Dim unicode.u(size)
    MultiByteToWideChar_(#CP_ACP, 0, String$, Len(String$), @unicode(), size)   ;#CP_ACP
    For Counter.i = 0 To size
        tmp.s + Hex(unicode(Counter), #PB_Unicode)
    Next
    EndIf
    ProcedureReturn SysAllocString_(@unicode())
EndProcedure
;=======================================
Procedure.i bstr2string(bstr.i)
    Shared result.s
    result = PeekS(bstr, -1, #PB_Unicode)
    ProcedureReturn @result
EndProcedure
;=======================================


Procedure.s WMI_GetProcessOwner(Servername$, Processname$, user$, password$)
   Result$ = ""
   Select Servername$
      Case "" : Namespace$ = "\\.\root\CIMV2"
      Default : Namespace$ = "\\" + RemoveString(Servername$, "\") + "\root\CIMV2"
   EndSelect


Structure COAUTHIDENTITY
   *User
   UserLength.l
   *Domain
   DomainLength.l
   *Password
   PasswordLength.l
   Flags.l
EndStructure

domain$ = "domainname"

#SEC_WINNT_AUTH_IDENTITY_UNICODE = $2
authIdent.COAUTHIDENTITY
authIdent\PasswordLength = Len(password$)
authIdent\Password = @password$
authIdent\Flags = #SEC_WINNT_AUTH_IDENTITY_UNICODE
authIdent\User = @user$
authIdent\UserLength = Len(user$)
authIdent\Domain = @domain$
authIdent\DomainLength = Len(domain$)

*userAcct.COAUTHIDENTITY
*userAcct = @authIdent


   If CoInitializeEx_(0, #COINIT_MULTITHREADED) = 0         
      If CoInitializeSecurity_(0, -1, 0, 0, #RPC_C_AUTHN_LEVEL_DEFAULT, #RPC_C_IMP_LEVEL_IMPERSONATE, 0, #EOAC_NONE, 0) = 0             
         If CoCreateInstance_(?CLSID_WbemLocator, #Null, #CLSCTX_INPROC_SERVER, ?IID_IWbemLocator, @pLocator.IWbemLocator) = 0

            If pLocator\ConnectServer(ptr(Namespace$), @user$, @password$, #Null, #wbemFlagConnectUseMaxWait, #Null, #Null, @pService.IWbemServices) = 0
               
               If CoSetProxyBlanket_(pService, #RPC_C_AUTHN_WINNT, #RPC_C_AUTHZ_NONE, 0, #RPC_C_AUTHN_LEVEL_CALL, #RPC_C_IMP_LEVEL_IMPERSONATE, *userAcct, #EOAC_NONE) = 0
;                   pService\queryinterface(?IID_IUnknown,@pUnknown.IUnknown)
;
;                   If CoSetProxyBlanket_(pUnknown, #RPC_C_AUTHN_NONE, #RPC_C_AUTHZ_NONE, 0, #RPC_C_AUTHN_LEVEL_NONE, #RPC_C_IMP_LEVEL_IMPERSONATE, 0, #EOAC_NONE) = 0

                     If pService\ExecQuery(ptr("WQL"), ptr("SELECT * FROM Win32_Process"), #wbemFlagReturnImmediately|#wbemFlagForwardOnly, #Null, @pEnumerator.IEnumWbemClassObject) = 0

                        If CoSetProxyBlanket_(pEnumerator, #RPC_C_AUTHN_WINNT, #RPC_C_AUTHZ_NONE, 0, #RPC_C_AUTHN_LEVEL_CALL, #RPC_C_IMP_LEVEL_IMPERSONATE, *userAcct, #EOAC_NONE) = 0

                           pEnumerator\reset()
   ;                         NbrReturned.i = 1
   ;                         While NbrReturned
   ;                            pEnumerator\Next(#WBEM_INFINITE, 1, @pclsObj.IWbemClassObject, @NbrReturned)
   ;                            ;Get Processname
   ;                            If pclsObj\Get(ptr("Name"), 0, @vtReturn.VARIANT, 0, 0) = 0          ;Debug PeekS(bstr2string(vtReturn\bstrVal))
   ;                               Found.i = #False
   ;                               If LCase(PeekS(bstr2string(vtReturn\bstrVal))) = Processname$
   ;                                  Found = #True
   ;                                  ;Get Processpath
   ;                                  If pclsObj\Get(ptr("__PATH"), 0, @vtPath.VARIANT, 0, 0) = 0  ;Debug PeekS(bstr2string(vtPath\bstrVal))
   ;                                                                                               ;Get Processowner
   ;                                     If pService\ExecMethod(vtPath\bstrVal, ptr("GetOwner"), 0, 0, 0, @pResult.IWbemClassObject, 0) = 0
   ;                                        If pResult\Get(ptr("User"), 0, @vtUser.VARIANT, 0, 0) = 0
   ;                                           Result$ = PeekS(bstr2string(vtUser\bstrVal))
   ;                                           VariantClear_(vtUser)
   ;                                        EndIf
   ;                                        pResult\release()   
   ;                                     EndIf   
   ;                                     VariantClear_(vtPath)       
   ;                                  EndIf
   ;                                  Break
   ;                               EndIf     
   ;                               VariantClear_(vtReturn)
   ;                               If Found = #False : Result$ = "Error - Unable to find the process" : EndIf
   ;                            EndIf
   ;                         Wend
   ;                         pclsObj\release()   

                        Else
                           
                        EndIf


                     Else
                        Result$ = "Error - Unable to retrieve the objects"
                     EndIf
;                      pUnknown\release()
;                   Else
;                      Result$ = "Error - Unable to set the IUnknown security"                           
;                   EndIf
               Else
                  Result$ = "Error - Unable to set the proxy security"
               EndIf   
               pService\release()
            Else
               Result$ = "Error - Unable to connect to CIMV2"
            EndIf
            pLocator\release()
         Else
            Result$ = "Error - Unable to create a WbemLocator"
         EndIf
      Else
         Result$ = "Error - Unable to set the security values for the process"
      EndIf
      CoUninitialize_()
   Else
      Result$ = "Error - Unable to launch COM"
   EndIf   
   ProcedureReturn Result$

   DataSection
     CLSID_IEnumWbemClassObject:
     ;1B1CAD8C-2DAB-11D2-B604-00104B703EFD
     Data.l $1B1CAD8C
     Data.w $2DAB, $11D2
     Data.b $B6, $04, $00, $10, $4B, $70, $3E, $FD
     IID_IEnumWbemClassObject:
     ;7C857801-7381-11CF-884D-00AA004B2E24
     Data.l $7C857801
     Data.w $7381, $11CF
     Data.b $88, $4D, $00, $AA, $00, $4B, $2E, $24
     CLSID_WbemLocator:
     ;4590f811-1d3a-11d0-891f-00aa004b2e24
     Data.l $4590F811
     Data.w $1D3A, $11D0
     Data.b $89, $1F, $00, $AA, $00, $4B, $2E, $24
     IID_IWbemLocator:
     ;dc12a687-737f-11cf-884d-00aa004b2e24
     Data.l $DC12A687
     Data.w $737F, $11CF
     Data.b $88, $4D, $00, $AA, $00, $4B, $2E, $24
     IID_IUnknown:
     ;00000000-0000-0000-C000-000000000046
     Data.l $00000000
     Data.w $0000, $0000
     Data.b $C0, $00, $00, $00, $00, $00, $00, $46

;       IID_IWbemRefresher:
;       Data.l $49353C99 : Data.w $516B, $11D1 : Data.b $AE, $A6, $00, $C0, $4F, $B6, $88, $20
;       CLSID_WbemRefresher:
;       Data.l $C71566F2 : Data.w $561E, $11D1 : Data.b $AD, $87, $00, $C0, $4F, $D8, $FD, $FF
;       IID_IWbemConfigureRefresher:
;       Data.l $49353C92 : Data.w $516B, $11D1 : Data.b $AE, $A6, $00, $C0, $4F, $B6, $88, $20
;       IID_IWbemObjectAccess:
;       Data.l $49353C9A : Data.w $516B, $11D1 : Data.b $AE, $A6, $00, $C0, $4F, $B6, $88, $20
   EndDataSection   
EndProcedure



;=======================================
;=======================================
;=======================================
Servername$ = "computername"
Processname$ = "processusname"

Debug WMI_GetProcessOwner(Servername$, Processname$, "user", "password")
I wish to draw your attention to the CoSetProxyBlanket_ function. It seems the important parameter is the *userAcct pointer to the COAUTHIDENTITY structure. This structure hosts the credentials for the remote connection. I suspect a second parameter from preventing the connection : the '0' in this same function. It is a OLECHAR *pServerPrincName (msdn). I don't know if it is really mandatory.
I hope someone will be able to make it works.

Thanks for your time.
Last edited by tatanas on Tue Nov 12, 2019 8:28 am, edited 1 time in total.
Windows 10 Pro x64
PureBasic 6.04 x64
Marc56us
Addict
Addict
Posts: 1479
Joined: Sat Feb 08, 2014 3:26 pm

Re: WMI Request to remote host using Win API

Post by Marc56us »

:idea:

The local WMI program goes much faster than the remote WMI program, so it depends on the type of information you need to report and how often, but since you are the administrator of your local network, it is sometimes easier to create an "agent" that you place on the client machine or that you call via the "logon script". This is the classic way of doing inventory.
This agent could run the local WMI request and then send the result to the server. See even in a database on the server.

Also, when it comes to device status information, SNMP is also interesting. I didn't do it in PB but about fifteen years ago, I had scripts (shell under FreeBSD, but we can do it under any OS) that would send me the printer counters of the whole company in a few seconds (at the time, these printers had no remote administration tools and many printers were very remote.)

:wink:
tatanas
Enthusiast
Enthusiast
Posts: 204
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: WMI Request to remote host using Win API

Post by tatanas »

Just an idea : should I use prototypes ? (Can't test for now)
Windows 10 Pro x64
PureBasic 6.04 x64
Post Reply