api: build the bridge from WoW64 to native in

SetupAPI fails to create a device in WoW64 processes. x86 (and arm)
wintun.dll pack the amd64 and arm64 wintun.dll now, and use rundll32 to
create a native process to do the job where required.

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2020-10-16 13:30:51 +02:00 committed by Jason A. Donenfeld
parent 2d20564f0a
commit 9a16d4e3cc
3 changed files with 175 additions and 20 deletions

View File

@ -9,8 +9,20 @@
#define WAIT_FOR_REGISTRY_TIMEOUT 10000 /* ms */
#define MAX_POOL_DEVICE_TYPE (MAX_POOL + 8) /* Should accommodate a pool name with " Tunnel" appended */
#if defined(_M_IX86)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_I386
#elif defined(_M_AMD64)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_AMD64
#elif defined(_M_ARM)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_ARMNT
#elif defined(_M_ARM64)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_ARM64
#else
# error Unsupported architecture
#endif
static _locale_t Locale;
static USHORT NativeMachine = IMAGE_FILE_PROCESS;
WINTUN_STATUS
AdapterGetDrvInfoDetail(
@ -324,6 +336,22 @@ void
AdapterInit()
{
Locale = _wcreate_locale(LC_ALL, L"");
#if defined(_M_IX86) || defined(_M_ARM)
typedef BOOL(WINAPI * IsWow64Process2_t)(
_In_ HANDLE hProcess, _Out_ USHORT * pProcessMachine, _Out_opt_ USHORT * pNativeMachine);
HANDLE Kernel32;
IsWow64Process2_t IsWow64Process2;
USHORT ProcessMachine;
if ((Kernel32 = GetModuleHandleW(L"kernel32.dll")) == NULL ||
(IsWow64Process2 = (IsWow64Process2_t)GetProcAddress(Kernel32, "IsWow64Process2")) == NULL ||
!IsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine))
{
BOOL IsWoW64;
NativeMachine =
IsWow64Process(GetCurrentProcess(), &IsWoW64) && IsWoW64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_PROCESS;
}
#endif
}
void
@ -1203,6 +1231,90 @@ cleanupMutex:
return Result;
}
static WINTUN_STATUS
CreateTemporaryDirectory(_Out_cap_c_(MAX_PATH) WCHAR *RandomTempSubDirectory)
{
WCHAR WindowsDirectory[MAX_PATH];
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
return LOG_LAST_ERROR(L"Failed to get Windows folder");
WCHAR WindowsTempDirectory[MAX_PATH];
if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
return ERROR_BUFFER_OVERFLOW;
UCHAR RandomBytes[32] = { 0 };
#pragma warning(suppress : 6387)
if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
return LOG_LAST_ERROR(L"Failed to generate random");
WCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1];
for (int i = 0; i < sizeof(RandomBytes); ++i)
swprintf_s(&RandomSubDirectory[i * 2], 3, L"%02x", RandomBytes[i]);
if (!PathCombineW(RandomTempSubDirectory, WindowsTempDirectory, RandomSubDirectory))
return ERROR_BUFFER_OVERFLOW;
if (!CreateDirectoryW(RandomTempSubDirectory, SecurityAttributes))
return LOG_LAST_ERROR(L"Failed to create temporary folder");
return ERROR_SUCCESS;
}
#if defined(_M_IX86) || defined(_M_ARM)
static WINTUN_STATUS
ExecuteRunDll32(_In_z_ const WCHAR *Arguments)
{
WCHAR WindowsDirectory[MAX_PATH];
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
return LOG_LAST_ERROR(L"Failed to get Windows folder");
WCHAR RunDll32Path[MAX_PATH];
if (!PathCombineW(RunDll32Path, WindowsDirectory, L"Sysnative\\rundll32.exe"))
return ERROR_BUFFER_OVERFLOW;
DWORD Result;
WCHAR RandomTempSubDirectory[MAX_PATH];
if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder"), Result;
WCHAR DllPath[MAX_PATH] = { 0 };
if (!PathCombineW(DllPath, RandomTempSubDirectory, L"wintun.dll"))
{
Result = ERROR_BUFFER_OVERFLOW;
goto cleanupDirectory;
}
if ((Result = ResourceCopyToFile(
DllPath, NativeMachine == IMAGE_FILE_MACHINE_ARM64 ? L"wintun-arm64.dll" : L"wintun-amd64.dll")) !=
ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to copy resource");
goto cleanupDelete;
}
HANDLE Heap = GetProcessHeap();
size_t CommandLineLen = 10 + MAX_PATH + 2 + wcslen(Arguments) + 1;
WCHAR *CommandLine = HeapAlloc(Heap, 0, CommandLineLen * sizeof(WCHAR));
if (!CommandLine)
{
LOG(WINTUN_LOG_ERR, L"Out of memory");
Result = ERROR_OUTOFMEMORY;
goto cleanupDelete;
}
_snwprintf_s(CommandLine, CommandLineLen, _TRUNCATE, L"rundll32 \"%.*s\",%s", MAX_PATH, DllPath, Arguments);
/* TODO: Create stdio pipes to intercept logged messages. */
STARTUPINFOW si = { .cb = sizeof(STARTUPINFO), .dwFlags = STARTF_USESHOWWINDOW, .wShowWindow = SW_HIDE };
PROCESS_INFORMATION pi;
if (!CreateProcessW(RunDll32Path, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
Result = LOG_LAST_ERROR(L"Creating process failed");
goto cleanupCommandLine;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
cleanupCommandLine:
HeapFree(Heap, 0, CommandLine);
cleanupDelete:
DeleteFileW(DllPath);
cleanupDirectory:
RemoveDirectoryW(RandomTempSubDirectory);
return Result;
}
#endif
WINTUN_STATUS WINAPI
WintunCreateAdapter(
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
@ -1211,27 +1323,39 @@ WintunCreateAdapter(
_Out_ WINTUN_ADAPTER **Adapter,
_Inout_ BOOL *RebootRequired)
{
#if defined(HAVE_EV) || defined(HAVE_WHQL)
WCHAR WindowsDirectory[MAX_PATH];
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
return LOG_LAST_ERROR(L"Failed to get Windows folder");
WCHAR WindowsTempDirectory[MAX_PATH];
if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
return ERROR_BUFFER_OVERFLOW;
UCHAR RandomBytes[32] = { 0 };
# pragma warning(suppress : 6387)
if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
return LOG_LAST_ERROR(L"Failed to generate random");
WCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1];
for (int i = 0; i < sizeof(RandomBytes); ++i)
swprintf_s(&RandomSubDirectory[i * 2], 3, L"%02x", RandomBytes[i]);
WCHAR RandomTempSubDirectory[MAX_PATH];
if (!PathCombineW(RandomTempSubDirectory, WindowsTempDirectory, RandomSubDirectory))
return ERROR_BUFFER_OVERFLOW;
if (!CreateDirectoryW(RandomTempSubDirectory, SecurityAttributes))
return LOG_LAST_ERROR(L"Failed to create temporary folder");
#if defined(_M_IX86) || defined(_M_ARM)
if (NativeMachine != IMAGE_FILE_PROCESS)
{
LOG(WINTUN_LOG_INFO, L"Spawning native process for the job");
WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN];
WCHAR Arguments[15 + MAX_POOL + 3 + MAX_ADAPTER_NAME + 2 + MAX_GUID_STRING_LEN + 1];
_snwprintf_s(
Arguments,
_countof(Arguments),
_TRUNCATE,
RequestedGUID ? L"CreateAdapter \"%.*s\" \"%.*s\" %.*s" : L"CreateAdapter \"%.*s\" \"%.*s\"",
MAX_POOL,
Pool,
MAX_ADAPTER_NAME,
Name,
RequestedGUID ? StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr)) : 0,
RequestedGUIDStr);
DWORD Result = ExecuteRunDll32(Arguments);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Error executing worker process");
return Result;
}
return WintunGetAdapter(Pool, Name, Adapter);
}
#endif
#if defined(HAVE_EV) || defined(HAVE_WHQL)
DWORD Result = ERROR_SUCCESS;
WCHAR RandomTempSubDirectory[MAX_PATH];
if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder"), Result;
WCHAR CatPath[MAX_PATH] = { 0 };
WCHAR SysPath[MAX_PATH] = { 0 };
WCHAR InfPath[MAX_PATH] = { 0 };
@ -1278,6 +1402,26 @@ cleanupDirectory:
WINTUN_STATUS WINAPI
WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequired)
{
#if defined(_M_IX86) || defined(_M_ARM)
if (NativeMachine != IMAGE_FILE_PROCESS)
{
LOG(WINTUN_LOG_INFO, L"Spawning native process for the job");
WCHAR GuidStr[MAX_GUID_STRING_LEN];
WCHAR Arguments[14 + MAX_GUID_STRING_LEN + 1];
_snwprintf_s(
Arguments,
_countof(Arguments),
_TRUNCATE,
L"DeleteAdapter %.*s",
StringFromGUID2(&Adapter->CfgInstanceID, GuidStr, _countof(GuidStr)),
GuidStr);
DWORD Result = ExecuteRunDll32(Arguments);
if (Result != ERROR_SUCCESS)
LOG(WINTUN_LOG_ERR, L"Error executing worker process");
return Result;
}
#endif
HDEVINFO DevInfo;
SP_DEVINFO_DATA DevInfoData;
DWORD Result = GetDevInfoData(&Adapter->CfgInstanceID, &DevInfo, &DevInfoData);

View File

@ -149,9 +149,13 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<ResourceCompile>
<AdditionalIncludeDirectories>..\$(Configuration)\$(WintunPlatform);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\$(Configuration)\$(WintunPlatform);..\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Platform)'=='Win32' Or '$(Platform)'=='x64' Or '$(Configuration)|$(Platform)'=='Debug|ARM64'">HAVE_EV;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="Exists('$(OutDir)whql\')">HAVE_WHQL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='Win32'">_M_IX86=600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_M_AMD64=100;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM'">_M_ARM=7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">_M_ARM64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
<AdditionalDependencies>Bcrypt.lib;Cfgmgr32.lib;Crypt32.lib;Iphlpapi.lib;newdev.lib;ntdll.lib;Setupapi.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>

View File

@ -18,6 +18,13 @@ wintun-whql.inf RCDATA "whql\\wintun.inf"
wintun-whql.sys RCDATA "whql\\wintun.sys"
#endif
#if defined(_M_IX86)
wintun-amd64.dll RCDATA "amd64\\wintun.dll"
#endif
#if defined(_M_IX86) || defined(_M_ARM)
wintun-arm64.dll RCDATA "arm64\\wintun.dll"
#endif
#define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x)