1
mirror of https://github.com/rapid7/metasploit-payloads synced 2024-11-26 17:41:08 +01:00

First pass of WMI support

Not quite working, but a good deal done.
This commit is contained in:
OJ 2014-01-16 13:34:15 +10:00
parent 5db4f962b9
commit af5d6bd908
13 changed files with 431 additions and 39 deletions

View File

@ -162,7 +162,7 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
#include <wininet.h>
/*! @brief When defined, debug output is enabled on Windows builds. */
//#define DEBUGTRACE 1
#define DEBUGTRACE 1
#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)

View File

@ -3,6 +3,7 @@
* @brief Definitions for ADSI functionality.
*/
#include "extapi.h"
#include "wshelpers.h"
#include "adsi.h"
#include "adsi_interface.h"
@ -10,41 +11,7 @@
#define DEFAULT_PAGE_SIZE 1000
/*!
* @brief Helper function that converts an ASCII string to a wide char string.
* @param lpValue ASCII string to convert.
* @param lpwValue Target memory for the converted string.
* @remark \c lpwValue must be freed by the caller using `free`.
* @returns Indication of success or failure.
*/
DWORD to_wide_string(LPSTR lpValue, LPWSTR* lpwValue)
{
size_t charsCopied = 0;
DWORD valueLength;
DWORD dwResult;
do
{
if (lpValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Value parameter missing", ERROR_INVALID_PARAMETER);
}
valueLength = lstrlenA(lpValue);
*lpwValue = (LPWSTR)malloc(sizeof(WCHAR)* (lstrlenA(lpValue) + 1));
if (*lpwValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
}
mbstowcs_s(&charsCopied, *lpwValue, valueLength + 1, lpValue, valueLength);
dwResult = ERROR_SUCCESS;
} while (0);
return dwResult;
}
/*!
* @brief Enumerate all the users in AD.
* @brief Perform an ASDI query against a domain.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the incoming \c Packet instance.
* @returns Indication of success or failure.

View File

@ -5,8 +5,6 @@
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_H
//DWORD request_adsi_user_enum(Remote *remote, Packet *packet);
//DWORD request_adsi_computer_enum(Remote *remote, Packet *packet);
DWORD request_adsi_domain_query(Remote *remote, Packet *packet);
#endif

View File

@ -1,7 +1,7 @@
/*!
* @file adsi_interface.h
* @brief Declarations for functions that deal directly with ADSI
* via the COM interfaces (hence the .cpp extension).
* via the COM interfaces.cpp extension).
*/
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_INTERFACE_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_ADSI_INTERFACE_H

View File

@ -14,6 +14,7 @@
#include "service.h"
#include "clipboard.h"
#include "adsi.h"
#include "wmi.h"
// this sets the delay load hook function, see DelayLoadMetSrv.h
EnableDelayLoadMetSrv();
@ -27,6 +28,7 @@ Command customCommands[] =
COMMAND_REQ("extapi_clipboard_get_data", request_clipboard_get_data),
COMMAND_REQ("extapi_clipboard_set_data", request_clipboard_set_data),
COMMAND_REQ("extapi_adsi_domain_query", request_adsi_domain_query),
COMMAND_REQ("extapi_wmi_query", request_wmi_query),
COMMAND_TERMINATOR
};

View File

@ -50,4 +50,11 @@
#define TLV_TYPE_EXT_ASDI_MAXRESULTS MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 60)
#define TLV_TYPE_EXT_ASDI_PAGESIZE MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 61)
#define TLV_TYPE_EXT_WMI_DOMAIN MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 65)
#define TLV_TYPE_EXT_WMI_QUERY MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 66)
#define TLV_TYPE_EXT_WMI_FIELD MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 67)
#define TLV_TYPE_EXT_WMI_VALUE MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 68)
#define TLV_TYPE_EXT_WMI_FIELDS MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 69)
#define TLV_TYPE_EXT_WMI_VALUES MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 70)
#endif

View File

@ -0,0 +1,77 @@
/*!
* @file wmi.c
* @brief Definitions for WMI request handling functionality.
*/
#include "extapi.h"
#include "wshelpers.h"
#include "wmi.h"
#include "wmi_interface.h"
/*!
* @brief Enumerate all the users in AD.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the incoming \c Packet instance.
* @returns Indication of success or failure.
* @remark Real error codes are returned to the caller via a response packet.
*/
DWORD request_wmi_query(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
LPSTR lpValue = NULL;
LPWSTR lpwDomain = NULL;
LPWSTR lpwQuery = NULL;
Packet * response = packet_create_response(packet);
do
{
if (!response)
{
BREAK_WITH_ERROR("[EXTAPI WMI] Unable to create response packet", ERROR_OUTOFMEMORY);
}
lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_WMI_DOMAIN);
if (!lpValue)
{
lpValue = "CIMV2";
}
dprintf("[EXTAPI WMI] Domain: %s", lpValue);
dwResult = to_wide_string(lpValue, &lpwDomain);
if (dwResult != ERROR_SUCCESS)
{
dprintf("[EXTAPI WMI] Failed to get Domain");
break;
}
lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_WMI_QUERY);
dprintf("[EXTAPI WMI] Query: %s", lpValue);
dwResult = to_wide_string(lpValue, &lpwQuery);
if (dwResult != ERROR_SUCCESS)
{
dprintf("[EXTAPI WMI] Failed to get Query");
break;
}
dprintf("[EXTAPI WMI] Beginning user enumeration");
dwResult = wmi_query(lpwDomain, lpwQuery, response);
dprintf("[EXTAPI WMI] Result of processing: %u (0x%x)", dwResult, dwResult);
} while (0);
if (lpwQuery)
{
free(lpwQuery);
}
if (lpwDomain)
{
free(lpwDomain);
}
dprintf("[EXTAPI WMI] Transmitting response back to caller.");
if (response)
{
packet_transmit_response(dwResult, remote, response);
}
return dwResult;
}

View File

@ -0,0 +1,10 @@
/*!
* @file wmi.h
* @brief Declarations for WMI request handlers.
*/
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_WMI_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_WMI_H
DWORD request_wmi_query(Remote *remote, Packet *packet);
#endif

View File

@ -0,0 +1,256 @@
/*!
* @file wmi_interface.h
* @brief Declarations for functions that deal directly with WMI
* via the COM interfaces (hence the .cpp extension).
*/
extern "C" {
#include "extapi.h"
#include <inttypes.h>
#include "wmi_interface.h"
}
#include <WbemCli.h>
#include <comutil.h>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "comsuppw.lib")
#define PATH_SIZE 512
#define FIELD_SIZE 512
#define ENUM_TIMEOUT 5000
/*! @brief The GUID of the Directory Search COM object. */
//static const IID IID_IDirectorySearch = { 0x109BA8EC, 0x92F0, 0x11D0, { 0xA7, 0x90, 0x00, 0xC0, 0x4F, 0xD8, 0xD5, 0xA8 } };
/*!
* @brief Perform a WMI query.
* @param lpwDomain Name of the domain that is to be queried.
* @param lpwQuery The filter to use when reading objects (LDAP style).
* @param response The response \c Packet to add the results to.
*/
DWORD wmi_query(LPCWSTR lpwDomain, LPWSTR lpwQuery, Packet* response)
{
HRESULT hResult;
WCHAR cbPath[PATH_SIZE];
swprintf_s(cbPath, PATH_SIZE - 1, L"root\\%s", lpwDomain);
if ((hResult = CoInitializeEx(NULL, COINIT_MULTITHREADED)) == S_OK)
{
IWbemLocator* pLocator = NULL;
IWbemServices* pServices = NULL;
IEnumWbemClassObject* pEnumerator = NULL;
IWbemClassObject* pObj = NULL;
Tlv* valueTlvs = NULL;
char* values = NULL;
VARIANT** fields = NULL;
do
{
if (FAILED(hResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0)))
{
dprintf("[WMI] Failed to initialize security: %x", hResult);
break;
}
if (FAILED(hResult = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator))))
{
dprintf("[WMI] Failed to create WbemLocator: %x", hResult);
break;
}
if (FAILED(hResult = pLocator->ConnectServer(cbPath, NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pServices)))
{
dprintf("[WMI] Failed to create WbemServices at %S: %x", cbPath, hResult);
break;
}
if (FAILED(hResult = pServices->ExecQuery(L"WQL", lpwQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
{
dprintf("[WMI] Failed to create Enumerator for query %S: %x", lpwQuery, hResult);
break;
}
ULONG numFound;
if (FAILED(hResult = pEnumerator->Next(ENUM_TIMEOUT, 1, &pObj, &numFound)))
{
dprintf("[WMI] Failed to get the first query element: %x", lpwQuery, hResult);
break;
}
// get the names of the fields out of the first object before doing anything else.
LPSAFEARRAY pFieldArray = NULL;
if (FAILED(hResult = pObj->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pFieldArray)))
{
dprintf("[WMI] Failed to get field names: %x", hResult);
break;
}
// lock the array
if (FAILED(hResult = SafeArrayLock(pFieldArray)))
{
dprintf("[WMI] Failed to get array dimension: %x", hResult);
break;
}
do
{
dprintf("[WMI] Array dimensions: %u", SafeArrayGetDim(pFieldArray);
// this array is just one dimension, let's get the bounds of the first dimension
LONG lBound, uBound;
if (FAILED(hResult = SafeArrayGetLBound(pFieldArray, 1, &lBound))
|| FAILED(hResult = SafeArrayGetLBound(pFieldArray, 1, &uBound)))
{
dprintf("[WMI] Failed to get array dimensions: %x", hResult);
break;
}
LONG fieldCount = uBound - lBound + 1;
dprintf("[WMI] Query results in %u fields", fieldCount);
fields = (VARIANT**)malloc(fieldCount * sizeof(VARIANT**));
valueTlvs = (Tlv*)malloc(fieldCount * sizeof(Tlv));
values = (char*)malloc(fieldCount * FIELD_SIZE);
memset(fields, 0, fieldCount * sizeof(VARIANT**));
memset(valueTlvs, 0, fieldCount * sizeof(Tlv));
memset(values, 0, fieldCount * FIELD_SIZE);
for (LONG i = 0; i < fieldCount; ++i)
{
char* fieldName = values + (i * FIELD_SIZE);
LONG indices[2] = { 0, i };
SafeArrayPtrOfIndex(pFieldArray, indices, (void**)&fields[i]);
_bstr_t bstr(fields[i]->bstrVal);
strncpy_s(fieldName, FIELD_SIZE, (const char*)bstr, FIELD_SIZE - 1);
valueTlvs[i].header.type = TLV_TYPE_EXT_WMI_FIELD;
valueTlvs[i].header.length = (UINT)strlen(fieldName) + 1;
valueTlvs[i].buffer = (PUCHAR)fieldName;
dprintf("[WMI] Added header field: %s", fieldName);
}
// add the field names to the packet
packet_add_tlv_group(response, TLV_TYPE_EXT_WMI_FIELDS, valueTlvs, fieldCount);
// with that horrible pain out of the way, let's actually grab the data
do
{
if (FAILED(hResult))
{
break;
}
memset(valueTlvs, 0, fieldCount * sizeof(Tlv));
memset(values, 0, fieldCount * FIELD_SIZE);
for (LONG i = 0; i < fieldCount; ++i)
{
char* value = values + (i * FIELD_SIZE);
valueTlvs[i].header.type = TLV_TYPE_EXT_WMI_VALUE;
valueTlvs[i].buffer = (PUCHAR)value;
VARIANT varValue;
VariantInit(&varValue);
_bstr_t field(fields[i]->bstrVal);
if (SUCCEEDED(pObj->Get(field, 0, &varValue, NULL, NULL)))
{
_variant_t v(varValue);
switch (v.vt)
{
case VT_BOOL:
strncpy_s(value, FIELD_SIZE, v.boolVal == VARIANT_TRUE ? "true" : "false", FIELD_SIZE - 1);
break;
case VT_INT:
_snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRId32, (INT)v);
break;
case VT_INT_PTR:
_snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRId64, (INT_PTR)v);
break;
case VT_UINT:
_snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRIu32, (UINT)v);
break;
case VT_UINT_PTR:
_snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRIu64, (UINT_PTR)v);
break;
case VT_BSTR:
case VT_LPSTR:
case VT_LPWSTR:
// not sure if this is correct
strncpy_s(value, FIELD_SIZE, (char*)(_bstr_t)v.bstrVal, FIELD_SIZE - 1);
break;
// TODO more types, such as floats, dates, etc.
default:
// ignore the value for other types
break;
}
}
valueTlvs[i].header.length = (UINT)strlen(value) + 1;
dprintf("[WMI] Added value for %s: %s", (char*)_bstr_t(fields[i]->bstrVal), value);
}
// add the field values to the packet
packet_add_tlv_group(response, TLV_TYPE_EXT_WMI_VALUES, valueTlvs, fieldCount);
pObj->Release();
pObj = NULL;
} while ((hResult = pEnumerator->Next(ENUM_TIMEOUT, 1, &pObj, &numFound)) != WBEM_S_FALSE);
} while (0);
SafeArrayUnlock(pFieldArray);
if (SUCCEEDED(hResult))
{
hResult = S_OK;
}
} while (0);
if (fields)
{
free(fields);
}
if (values)
{
free(values);
}
if (valueTlvs)
{
free(valueTlvs);
}
if (pObj)
{
pObj->Release();
}
if (pEnumerator)
{
pEnumerator->Release();
}
if (pServices)
{
pServices->Release();
}
if (pLocator)
{
pLocator->Release();
}
CoUninitialize();
dprintf("[WMI] Things appeard to go well!");
}
else
{
dprintf("[WMI] Failed to initialize COM");
}
return (DWORD)hResult;
}

View File

@ -0,0 +1,11 @@
/*!
* @file wmi_interface.h
* @brief Declarations for functions that deal directly with WMI
* via the COM interfaces.
*/
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_WMI_INTERFACE_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_WMI_INTERFACE_H
DWORD wmi_query(LPCWSTR lpwDomain, LPWSTR lpwQuery, Packet* response);
#endif

View File

@ -0,0 +1,40 @@
/*!
* @file wshelpers.h
* @brief Declarations for wide-string helper functions.
*/
#include "extapi.h"
#include "wshelpers.h"
/*!
* @brief Helper function that converts an ASCII string to a wide char string.
* @param lpValue ASCII string to convert.
* @param lpwValue Target memory for the converted string.
* @remark \c lpwValue must be freed by the caller using `free`.
* @returns Indication of success or failure.
*/
DWORD to_wide_string(LPSTR lpValue, LPWSTR* lpwValue)
{
size_t charsCopied = 0;
DWORD valueLength;
DWORD dwResult;
do
{
if (lpValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Value parameter missing", ERROR_INVALID_PARAMETER);
}
valueLength = lstrlenA(lpValue);
*lpwValue = (LPWSTR)malloc(sizeof(WCHAR)* (lstrlenA(lpValue) + 1));
if (*lpwValue == NULL)
{
BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY);
}
mbstowcs_s(&charsCopied, *lpwValue, valueLength + 1, lpValue, valueLength);
dwResult = ERROR_SUCCESS;
} while (0);
return dwResult;
}

View File

@ -0,0 +1,10 @@
/*!
* @file wshelpers.h
* @brief Declarations for wide-string helper functions.
*/
#ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_WSHELPERS_H
#define _METERPRETER_SOURCE_EXTENSION_EXTAPI_WSHELPERS_H
DWORD to_wide_string(LPSTR lpValue, LPWSTR* lpwValue);
#endif

View File

@ -114,6 +114,7 @@
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -142,6 +143,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -174,6 +176,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..\..\source\ReflectiveDLLInjection\common;..\..\source\extensions\extapi;..\..\source\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -207,6 +210,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..\..\source\ReflectiveDLLInjection\common;..\..\source\extensions\extapi;..\..\source\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -245,6 +249,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -294,6 +299,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -345,6 +351,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BufferSecurityCheck>false</BufferSecurityCheck>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -394,6 +401,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BufferSecurityCheck>false</BufferSecurityCheck>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -430,6 +438,9 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<ClCompile Include="..\..\source\extensions\extapi\extapi.c" />
<ClCompile Include="..\..\source\extensions\extapi\service.c" />
<ClCompile Include="..\..\source\extensions\extapi\window.c" />
<ClCompile Include="..\..\source\extensions\extapi\wmi.c" />
<ClCompile Include="..\..\source\extensions\extapi\wmi_interface.cpp" />
<ClCompile Include="..\..\source\extensions\extapi\wshelpers.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\source\extensions\extapi\adsi.h" />
@ -439,6 +450,9 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
<ClInclude Include="..\..\source\extensions\extapi\extapi.h" />
<ClInclude Include="..\..\source\extensions\extapi\service.h" />
<ClInclude Include="..\..\source\extensions\extapi\window.h" />
<ClInclude Include="..\..\source\extensions\extapi\wmi.h" />
<ClInclude Include="..\..\source\extensions\extapi\wmi_interface.h" />
<ClInclude Include="..\..\source\extensions\extapi\wshelpers.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\backcompat\backcompat.vcxproj">