2020-07-24 09:39:02 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0
|
|
|
|
*
|
2021-01-30 16:45:26 +01:00
|
|
|
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
2020-07-24 09:39:02 +02:00
|
|
|
*/
|
|
|
|
|
2021-07-28 20:20:09 +02:00
|
|
|
#include "rundll32.h"
|
2021-07-28 13:50:40 +02:00
|
|
|
#include "adapter.h"
|
2021-07-28 20:20:09 +02:00
|
|
|
#include "main.h"
|
2021-07-28 13:50:40 +02:00
|
|
|
#include "logger.h"
|
2021-07-28 20:20:09 +02:00
|
|
|
#include "resource.h"
|
2021-07-28 13:50:40 +02:00
|
|
|
#include <Windows.h>
|
|
|
|
#include <shellapi.h>
|
2021-07-28 20:20:09 +02:00
|
|
|
#include <Shlwapi.h>
|
2021-07-28 13:50:40 +02:00
|
|
|
#include <cfgmgr32.h>
|
|
|
|
#include <objbase.h>
|
|
|
|
#include <assert.h>
|
2020-10-31 11:55:26 +01:00
|
|
|
|
2021-07-28 20:20:09 +02:00
|
|
|
#ifdef MAYBE_WOW64
|
|
|
|
|
|
|
|
_Return_type_success_(return != FALSE)
|
|
|
|
static BOOL
|
|
|
|
AppendToBuffer(_Inout_ LPWSTR *Buffer, _In_ CONST WCHAR Addition, _Inout_ SIZE_T *BufferPos, _Inout_ SIZE_T *BufferLen)
|
|
|
|
{
|
|
|
|
SIZE_T NewPos;
|
|
|
|
if (FAILED(SIZETAdd(*BufferPos, sizeof(Addition), &NewPos)))
|
|
|
|
return FALSE;
|
|
|
|
if (NewPos >= *BufferLen)
|
|
|
|
{
|
|
|
|
SIZE_T NewLen;
|
|
|
|
if (FAILED(SIZETMult(NewPos, 3, &NewLen)))
|
|
|
|
return FALSE;
|
|
|
|
LPWSTR NewBuffer = ReZalloc(*Buffer, NewLen);
|
|
|
|
if (!NewBuffer)
|
|
|
|
return FALSE;
|
|
|
|
*Buffer = NewBuffer;
|
|
|
|
*BufferLen = NewLen;
|
|
|
|
}
|
|
|
|
SIZE_T NewIndex = *BufferPos / sizeof(**Buffer);
|
|
|
|
if (*Buffer + NewIndex < *Buffer)
|
|
|
|
return FALSE;
|
|
|
|
(*Buffer)[NewIndex] = Addition;
|
|
|
|
*BufferPos = NewPos;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
static _Return_type_success_(return != NULL)
|
|
|
|
_Post_maybenull_
|
|
|
|
LPWSTR
|
|
|
|
ArgvToCommandLineW(_In_ SIZE_T ArgCount, ...)
|
|
|
|
{
|
|
|
|
LPWSTR Output = NULL;
|
|
|
|
SIZE_T BufferPos = 0, BufferLen = 0;
|
|
|
|
# define Append(Char) \
|
|
|
|
do \
|
|
|
|
{ \
|
|
|
|
if (!AppendToBuffer(&Output, Char, &BufferPos, &BufferLen)) \
|
|
|
|
goto cleanupBuffer; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
va_list Args;
|
|
|
|
va_start(Args, ArgCount);
|
|
|
|
for (SIZE_T i = 0; i < ArgCount; ++i)
|
|
|
|
{
|
|
|
|
LPCWSTR Arg = va_arg(Args, LPCWSTR);
|
|
|
|
SIZE_T ArgLen = wcslen(Arg);
|
|
|
|
if (ArgLen >= DWORD_MAX >> 3)
|
|
|
|
goto cleanupBuffer;
|
|
|
|
if (i)
|
|
|
|
Append(L' ');
|
|
|
|
Append(L'"');
|
|
|
|
for (SIZE_T j = 0;; ++j)
|
|
|
|
{
|
|
|
|
SIZE_T NumberBackslashes = 0;
|
|
|
|
|
|
|
|
while (j < ArgLen && Arg[j] == L'\\')
|
|
|
|
{
|
|
|
|
++j;
|
|
|
|
++NumberBackslashes;
|
|
|
|
}
|
|
|
|
if (j >= ArgLen)
|
|
|
|
{
|
|
|
|
for (SIZE_T k = 0; k < NumberBackslashes * 2; ++k)
|
|
|
|
Append(L'\\');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (Arg[j] == L'"')
|
|
|
|
{
|
|
|
|
for (SIZE_T k = 0; k < NumberBackslashes * 2 + 1; ++k)
|
|
|
|
Append(L'\\');
|
|
|
|
Append(Arg[j]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (SIZE_T k = 0; k < NumberBackslashes; ++k)
|
|
|
|
Append(L'\\');
|
|
|
|
Append(Arg[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Append(L'"');
|
|
|
|
}
|
|
|
|
va_end(Args);
|
|
|
|
return Output;
|
|
|
|
|
|
|
|
cleanupBuffer:
|
|
|
|
Free(Output);
|
|
|
|
return NULL;
|
|
|
|
# undef Append
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _PROCESS_STDOUT_STATE
|
|
|
|
{
|
|
|
|
HANDLE Stdout;
|
|
|
|
LPWSTR Response;
|
|
|
|
DWORD ResponseCapacity;
|
|
|
|
} PROCESS_STDOUT_STATE;
|
|
|
|
|
|
|
|
_Return_type_success_(return != ERROR_SUCCESS)
|
|
|
|
static DWORD WINAPI
|
|
|
|
ProcessStdout(_Inout_ PROCESS_STDOUT_STATE *State)
|
|
|
|
{
|
|
|
|
for (DWORD Offset = 0, MaxLen = State->ResponseCapacity - 1; Offset < MaxLen;)
|
|
|
|
{
|
|
|
|
DWORD Size;
|
|
|
|
if (FAILED(DWordMult(MaxLen - Offset, sizeof(WCHAR), &Size)))
|
|
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
|
|
if (!ReadFile(State->Stdout, State->Response + Offset, Size, &Size, NULL))
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
if (Size % sizeof(WCHAR))
|
|
|
|
return ERROR_INVALID_DATA;
|
|
|
|
Offset += Size / sizeof(WCHAR);
|
|
|
|
State->Response[Offset] = 0;
|
|
|
|
}
|
|
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD WINAPI
|
|
|
|
ProcessStderr(_In_ HANDLE Stderr)
|
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
WCHAR Msg[0x200], Buf[0x220], LevelRune;
|
|
|
|
DWORD64 Timestamp;
|
|
|
|
DWORD SizeRead;
|
|
|
|
WINTUN_LOGGER_LEVEL Level;
|
2021-07-28 20:20:09 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
if (!ReadFile(Stderr, Buf, sizeof(Buf), &SizeRead, NULL) || !SizeRead)
|
2021-07-28 20:20:09 +02:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
if (SizeRead % sizeof(*Buf))
|
|
|
|
return ERROR_INVALID_DATA;
|
2021-10-12 07:21:31 +02:00
|
|
|
Msg[0] = Buf[SizeRead / sizeof(*Buf) - 1] = L'\0';
|
|
|
|
if (swscanf_s(Buf, L"[%c %I64u] %[^\n]", &LevelRune, 1, &Timestamp, Msg, (DWORD)_countof(Msg)) != 3 || !Msg[0])
|
|
|
|
return ERROR_INVALID_DATA;
|
|
|
|
if (!((Level = WINTUN_LOG_INFO, LevelRune == L'+') || (Level = WINTUN_LOG_WARN, LevelRune == L'-') ||
|
|
|
|
(Level = WINTUN_LOG_ERR, LevelRune == L'!')))
|
|
|
|
return ERROR_INVALID_DATA;
|
|
|
|
Logger(Level, Timestamp, Msg);
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static _Return_type_success_(return != FALSE)
|
|
|
|
BOOL
|
|
|
|
ExecuteRunDll32(
|
|
|
|
_In_z_ LPCWSTR Function,
|
|
|
|
_In_z_ LPCWSTR Arguments,
|
|
|
|
_Out_z_cap_c_(ResponseCapacity) LPWSTR Response,
|
|
|
|
_In_ DWORD ResponseCapacity)
|
|
|
|
{
|
|
|
|
WCHAR WindowsDirectory[MAX_PATH];
|
|
|
|
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
|
|
|
|
{
|
|
|
|
LOG_LAST_ERROR(L"Failed to get Windows folder");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
WCHAR RunDll32Path[MAX_PATH];
|
|
|
|
if (!PathCombineW(RunDll32Path, WindowsDirectory, L"Sysnative\\rundll32.exe"))
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_BUFFER_OVERFLOW);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD LastError;
|
|
|
|
WCHAR RandomTempSubDirectory[MAX_PATH];
|
|
|
|
if (!ResourceCreateTemporaryDirectory(RandomTempSubDirectory))
|
|
|
|
{
|
|
|
|
LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
WCHAR DllPath[MAX_PATH] = { 0 };
|
2021-10-12 07:21:31 +02:00
|
|
|
if (!PathCombineW(DllPath, RandomTempSubDirectory, L"setupapihost.dll"))
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
|
|
|
LastError = ERROR_BUFFER_OVERFLOW;
|
|
|
|
goto cleanupDirectory;
|
|
|
|
}
|
|
|
|
LPCWSTR WintunDllResourceName;
|
|
|
|
switch (NativeMachine)
|
|
|
|
{
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
2021-10-12 07:21:31 +02:00
|
|
|
WintunDllResourceName = L"setupapihost-amd64.dll";
|
2021-07-28 20:20:09 +02:00
|
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ARM64:
|
2021-10-12 07:21:31 +02:00
|
|
|
WintunDllResourceName = L"setupapihost-arm64.dll";
|
2021-07-28 20:20:09 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG(WINTUN_LOG_ERR, L"Unsupported platform 0x%x", NativeMachine);
|
|
|
|
LastError = ERROR_NOT_SUPPORTED;
|
|
|
|
goto cleanupDirectory;
|
|
|
|
}
|
|
|
|
if (!ResourceCopyToFile(DllPath, WintunDllResourceName))
|
|
|
|
{
|
|
|
|
LastError = LOG(WINTUN_LOG_ERR, L"Failed to copy resource %s to %s", WintunDllResourceName, DllPath);
|
|
|
|
goto cleanupDelete;
|
|
|
|
}
|
|
|
|
size_t CommandLineLen = 10 + MAX_PATH + 2 + wcslen(Arguments) + 1 + wcslen(Function) + 1;
|
|
|
|
LPWSTR CommandLine = AllocArray(CommandLineLen, sizeof(*CommandLine));
|
|
|
|
if (!CommandLine)
|
|
|
|
{
|
|
|
|
LastError = GetLastError();
|
|
|
|
goto cleanupDelete;
|
|
|
|
}
|
|
|
|
if (_snwprintf_s(
|
|
|
|
CommandLine,
|
|
|
|
CommandLineLen,
|
|
|
|
_TRUNCATE,
|
|
|
|
L"rundll32 \"%.*s\",%s %s",
|
|
|
|
MAX_PATH,
|
|
|
|
DllPath,
|
|
|
|
Function,
|
|
|
|
Arguments) == -1)
|
|
|
|
{
|
|
|
|
LOG(WINTUN_LOG_ERR, L"Command line too long");
|
|
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
|
|
goto cleanupDelete;
|
|
|
|
}
|
|
|
|
HANDLE StreamRStdout = INVALID_HANDLE_VALUE, StreamRStderr = INVALID_HANDLE_VALUE,
|
|
|
|
StreamWStdout = INVALID_HANDLE_VALUE, StreamWStderr = INVALID_HANDLE_VALUE;
|
|
|
|
if (!CreatePipe(&StreamRStdout, &StreamWStdout, &SecurityAttributes, 0) ||
|
|
|
|
!CreatePipe(&StreamRStderr, &StreamWStderr, &SecurityAttributes, 0))
|
|
|
|
{
|
|
|
|
LastError = LOG_LAST_ERROR(L"Failed to create pipes");
|
|
|
|
goto cleanupPipes;
|
|
|
|
}
|
|
|
|
if (!SetHandleInformation(StreamWStdout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
|
|
|
|
!SetHandleInformation(StreamWStderr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
|
|
|
|
{
|
|
|
|
LastError = LOG_LAST_ERROR(L"Failed to set handle info");
|
|
|
|
goto cleanupPipes;
|
|
|
|
}
|
|
|
|
if (ResponseCapacity)
|
|
|
|
Response[0] = 0;
|
|
|
|
PROCESS_STDOUT_STATE ProcessStdoutState = { .Stdout = StreamRStdout,
|
|
|
|
.Response = Response,
|
|
|
|
.ResponseCapacity = ResponseCapacity };
|
|
|
|
HANDLE ThreadStdout = NULL, ThreadStderr = NULL;
|
|
|
|
if ((ThreadStdout = CreateThread(NULL, 0, ProcessStdout, &ProcessStdoutState, 0, NULL)) == NULL ||
|
|
|
|
(ThreadStderr = CreateThread(NULL, 0, ProcessStderr, StreamRStderr, 0, NULL)) == NULL)
|
|
|
|
{
|
|
|
|
LastError = LOG_LAST_ERROR(L"Failed to spawn readers");
|
|
|
|
goto cleanupThreads;
|
|
|
|
}
|
|
|
|
STARTUPINFOW si = { .cb = sizeof(STARTUPINFO),
|
|
|
|
.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
|
|
|
|
.wShowWindow = SW_HIDE,
|
|
|
|
.hStdOutput = StreamWStdout,
|
|
|
|
.hStdError = StreamWStderr };
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (!CreateProcessW(RunDll32Path, CommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
|
|
|
|
{
|
|
|
|
LastError = LOG_LAST_ERROR(L"Failed to create process: %s", CommandLine);
|
|
|
|
goto cleanupThreads;
|
|
|
|
}
|
|
|
|
LastError = ERROR_SUCCESS;
|
|
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
cleanupThreads:
|
|
|
|
if (ThreadStderr)
|
|
|
|
{
|
|
|
|
CloseHandle(StreamWStderr);
|
|
|
|
StreamWStderr = INVALID_HANDLE_VALUE;
|
|
|
|
WaitForSingleObject(ThreadStderr, INFINITE);
|
|
|
|
CloseHandle(ThreadStderr);
|
|
|
|
}
|
|
|
|
if (ThreadStdout)
|
|
|
|
{
|
|
|
|
CloseHandle(StreamWStdout);
|
|
|
|
StreamWStdout = INVALID_HANDLE_VALUE;
|
|
|
|
WaitForSingleObject(ThreadStdout, INFINITE);
|
|
|
|
DWORD ThreadResult;
|
|
|
|
if (!GetExitCodeThread(ThreadStdout, &ThreadResult))
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_LAST_ERROR(L"Failed to retrieve stdout reader result");
|
2021-07-28 20:20:09 +02:00
|
|
|
else if (ThreadResult != ERROR_SUCCESS)
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_ERROR(ThreadResult, L"Failed to read process output");
|
2021-07-28 20:20:09 +02:00
|
|
|
CloseHandle(ThreadStdout);
|
|
|
|
}
|
|
|
|
cleanupPipes:
|
|
|
|
CloseHandle(StreamRStderr);
|
|
|
|
CloseHandle(StreamWStderr);
|
|
|
|
CloseHandle(StreamRStdout);
|
|
|
|
CloseHandle(StreamWStdout);
|
|
|
|
Free(CommandLine);
|
|
|
|
cleanupDelete:
|
|
|
|
DeleteFileW(DllPath);
|
|
|
|
cleanupDirectory:
|
|
|
|
RemoveDirectoryW(RandomTempSubDirectory);
|
|
|
|
return RET_ERROR(TRUE, LastError);
|
|
|
|
}
|
|
|
|
|
2021-10-12 07:21:31 +02:00
|
|
|
static _Return_type_success_(return != FALSE)
|
|
|
|
BOOL
|
|
|
|
InvokeClassInstaller(_In_ LPCWSTR Action, _In_ LPCWSTR Function, _In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LOG(WINTUN_LOG_INFO, L"Spawning native process to %s instance", Action);
|
|
|
|
|
2021-10-14 08:37:50 +02:00
|
|
|
WCHAR InstanceId[MAX_DEVICE_ID_LEN];
|
2021-10-12 07:21:31 +02:00
|
|
|
DWORD RequiredChars = _countof(InstanceId);
|
|
|
|
if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredChars, &RequiredChars))
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LOG_LAST_ERROR(L"Failed to get adapter instance ID");
|
|
|
|
return FALSE;
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
2021-10-12 07:21:31 +02:00
|
|
|
LPWSTR Arguments = ArgvToCommandLineW(1, InstanceId);
|
2021-07-28 20:20:09 +02:00
|
|
|
if (!Arguments)
|
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
SetLastError(LOG_ERROR(ERROR_INVALID_PARAMETER, L"Command line too long"));
|
|
|
|
return FALSE;
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
|
|
|
DWORD LastError;
|
2021-10-12 07:21:31 +02:00
|
|
|
WCHAR Response[8 + 1];
|
|
|
|
if (!ExecuteRunDll32(Function, Arguments, Response, _countof(Response)))
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_LAST_ERROR(L"Error executing worker process: %s", Arguments);
|
2021-07-28 20:20:09 +02:00
|
|
|
goto cleanupArguments;
|
|
|
|
}
|
|
|
|
int Argc;
|
|
|
|
LPWSTR *Argv = CommandLineToArgvW(Response, &Argc);
|
2021-10-12 07:21:31 +02:00
|
|
|
if (Argc < 1)
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response);
|
2021-07-28 20:20:09 +02:00
|
|
|
goto cleanupArgv;
|
|
|
|
}
|
|
|
|
LastError = wcstoul(Argv[0], NULL, 16);
|
|
|
|
cleanupArgv:
|
|
|
|
LocalFree(Argv);
|
|
|
|
cleanupArguments:
|
|
|
|
Free(Arguments);
|
2021-10-12 07:21:31 +02:00
|
|
|
return RET_ERROR(TRUE, LastError);
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_Use_decl_annotations_
|
|
|
|
BOOL
|
2021-10-12 07:21:31 +02:00
|
|
|
RemoveInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
return InvokeClassInstaller(L"remove", L"RemoveInstance", DevInfo, DevInfoData);
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_Use_decl_annotations_
|
|
|
|
BOOL
|
2021-10-12 07:21:31 +02:00
|
|
|
EnableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
return InvokeClassInstaller(L"enable", L"EnableInstance", DevInfo, DevInfoData);
|
|
|
|
}
|
|
|
|
|
|
|
|
_Use_decl_annotations_
|
|
|
|
BOOL
|
|
|
|
DisableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
|
|
|
|
{
|
|
|
|
return InvokeClassInstaller(L"disable", L"DisableInstance", DevInfo, DevInfoData);
|
|
|
|
}
|
|
|
|
|
|
|
|
_Use_decl_annotations_
|
|
|
|
BOOL
|
|
|
|
CreateInstanceWin7ViaRundll32(LPWSTR InstanceId)
|
|
|
|
{
|
|
|
|
LOG(WINTUN_LOG_INFO, L"Spawning native process to create instance");
|
|
|
|
|
2021-07-28 20:20:09 +02:00
|
|
|
DWORD LastError;
|
2021-10-14 08:37:50 +02:00
|
|
|
WCHAR Response[MAX_DEVICE_ID_LEN + 1];
|
2021-10-12 07:21:31 +02:00
|
|
|
if (!ExecuteRunDll32(L"CreateInstanceWin7", L"", Response, _countof(Response)))
|
2021-07-28 20:20:09 +02:00
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_LAST_ERROR(L"Error executing worker process");
|
|
|
|
goto cleanup;
|
2021-07-28 20:20:09 +02:00
|
|
|
}
|
|
|
|
int Argc;
|
|
|
|
LPWSTR *Argv = CommandLineToArgvW(Response, &Argc);
|
|
|
|
if (Argc < 2)
|
|
|
|
{
|
2021-10-12 07:21:31 +02:00
|
|
|
LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response);
|
2021-07-28 20:20:09 +02:00
|
|
|
goto cleanupArgv;
|
|
|
|
}
|
|
|
|
LastError = wcstoul(Argv[0], NULL, 16);
|
2021-10-12 07:21:31 +02:00
|
|
|
if (LastError == ERROR_SUCCESS)
|
2021-10-14 08:37:50 +02:00
|
|
|
wcsncpy_s(InstanceId, MAX_DEVICE_ID_LEN, Argv[1], _TRUNCATE);
|
2021-07-28 20:20:09 +02:00
|
|
|
cleanupArgv:
|
|
|
|
LocalFree(Argv);
|
2021-10-12 07:21:31 +02:00
|
|
|
cleanup:
|
2021-07-28 20:20:09 +02:00
|
|
|
return RET_ERROR(TRUE, LastError);
|
2020-11-02 16:28:51 +01:00
|
|
|
}
|
2020-07-24 09:39:02 +02:00
|
|
|
#endif
|