example: rewrite and replace api's debug rundll32 functionality
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
0d7b9c7319
commit
9a937c7a49
@ -101,11 +101,6 @@
|
|||||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||||
<IgnoreImportLibrary>true</IgnoreImportLibrary>
|
<IgnoreImportLibrary>true</IgnoreImportLibrary>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
|
||||||
<LocalDebuggerCommand>rundll32.exe</LocalDebuggerCommand>
|
|
||||||
<LocalDebuggerCommandArguments>$(OutDir)$(TargetName)$(TargetExt),DoThingsForDebugging</LocalDebuggerCommandArguments>
|
|
||||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions>_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
@ -171,7 +166,6 @@ lib.exe /def:nci.def /out:"$(IntDir)nci.lib" /machine:$(PlatformTarget) /nologo
|
|||||||
<ClInclude Include="ntldr.h" />
|
<ClInclude Include="ntldr.h" />
|
||||||
<ClInclude Include="registry.h" />
|
<ClInclude Include="registry.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="session.h" />
|
|
||||||
<ClInclude Include="wintun.h" />
|
<ClInclude Include="wintun.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -58,9 +58,6 @@
|
|||||||
<ClInclude Include="entry.h">
|
<ClInclude Include="entry.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="session.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="namespace.c">
|
<ClCompile Include="namespace.c">
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "adapter.h"
|
#include "adapter.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "session.h"
|
|
||||||
#include "wintun.h"
|
#include "wintun.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@ -17,12 +16,6 @@
|
|||||||
|
|
||||||
#if defined(ACCEPT_WOW64) || defined(_DEBUG)
|
#if defined(ACCEPT_WOW64) || defined(_DEBUG)
|
||||||
|
|
||||||
static
|
|
||||||
#ifndef _DEBUG
|
|
||||||
const
|
|
||||||
#endif
|
|
||||||
BOOL WriteToConsole = FALSE;
|
|
||||||
|
|
||||||
static DWORD
|
static DWORD
|
||||||
WriteFormatted(_In_ DWORD StdHandle, _In_z_ const WCHAR *Template, ...)
|
WriteFormatted(_In_ DWORD StdHandle, _In_z_ const WCHAR *Template, ...)
|
||||||
{
|
{
|
||||||
@ -38,9 +31,6 @@ WriteFormatted(_In_ DWORD StdHandle, _In_z_ const WCHAR *Template, ...)
|
|||||||
(void *)&FormattedMessage,
|
(void *)&FormattedMessage,
|
||||||
0,
|
0,
|
||||||
&Arguments);
|
&Arguments);
|
||||||
if (WriteToConsole)
|
|
||||||
WriteConsoleW(GetStdHandle(StdHandle), FormattedMessage, Len, &SizeWritten, NULL);
|
|
||||||
else
|
|
||||||
WriteFile(GetStdHandle(StdHandle), FormattedMessage, Len * sizeof(WCHAR), &SizeWritten, NULL);
|
WriteFile(GetStdHandle(StdHandle), FormattedMessage, Len * sizeof(WCHAR), &SizeWritten, NULL);
|
||||||
LocalFree(FormattedMessage);
|
LocalFree(FormattedMessage);
|
||||||
va_end(Arguments);
|
va_end(Arguments);
|
||||||
@ -158,30 +148,4 @@ VOID __stdcall DeleteDriver(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int n
|
|||||||
Done();
|
Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
VOID __stdcall DoThingsForDebugging(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
|
|
||||||
{
|
|
||||||
# pragma EXPORT
|
|
||||||
UNREFERENCED_PARAMETER(hwnd);
|
|
||||||
UNREFERENCED_PARAMETER(hinst);
|
|
||||||
UNREFERENCED_PARAMETER(lpszCmdLine);
|
|
||||||
UNREFERENCED_PARAMETER(nCmdShow);
|
|
||||||
|
|
||||||
AllocConsole();
|
|
||||||
WriteToConsole = TRUE;
|
|
||||||
Init();
|
|
||||||
GUID TestGuid = { 0xdeadbabe, 0xcafe, 0xbeef, { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } };
|
|
||||||
WINTUN_ADAPTER *Adapter;
|
|
||||||
BOOL RebootRequired;
|
|
||||||
assert(WintunCreateAdapter(L"Wintun", L"Test", &TestGuid, &Adapter, &RebootRequired) == ERROR_SUCCESS);
|
|
||||||
assert(!RebootRequired);
|
|
||||||
TUN_SESSION *Session;
|
|
||||||
assert(WintunStartSession(Adapter, WINTUN_MIN_RING_CAPACITY, &Session) == ERROR_SUCCESS);
|
|
||||||
WintunEndSession(Session);
|
|
||||||
assert(WintunDeleteAdapter(Adapter, TRUE, &RebootRequired) == ERROR_SUCCESS);
|
|
||||||
assert(!RebootRequired);
|
|
||||||
system("pause");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "elevate.h"
|
#include "elevate.h"
|
||||||
#include "entry.h"
|
#include "entry.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "session.h"
|
|
||||||
#include "wintun.h"
|
#include "wintun.h"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "wintun.h"
|
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
typedef struct _TUN_SESSION TUN_SESSION;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_START_SESSION_FUNC
|
|
||||||
*/
|
|
||||||
WINTUN_STATUS WINAPI
|
|
||||||
WintunStartSession(
|
|
||||||
_In_ const WINTUN_ADAPTER *Adapter,
|
|
||||||
_In_ DWORD Capacity,
|
|
||||||
_Out_ TUN_SESSION **Session);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_END_SESSION_FUNC
|
|
||||||
*/
|
|
||||||
void WINAPI
|
|
||||||
WintunEndSession(_In_ TUN_SESSION *Session);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_GET_READ_WAIT_EVENT_FUNC
|
|
||||||
*/
|
|
||||||
HANDLE WINAPI
|
|
||||||
WintunGetReadWaitEvent(_In_ TUN_SESSION *Session);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_RECEIVE_PACKET_FUNC
|
|
||||||
*/
|
|
||||||
WINTUN_STATUS WINAPI
|
|
||||||
WintunReceivePacket(_In_ TUN_SESSION *Session, _Out_bytecapcount_(*PacketSize) BYTE **Packet, _Out_ DWORD *PacketSize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_RECEIVE_RELEASE_FUNC
|
|
||||||
*/
|
|
||||||
void WINAPI
|
|
||||||
WintunReceiveRelease(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_ALLOCATE_SEND_PACKET
|
|
||||||
*/
|
|
||||||
WINTUN_STATUS WINAPI
|
|
||||||
WintunAllocateSendPacket(
|
|
||||||
_In_ TUN_SESSION *Session,
|
|
||||||
_In_ DWORD PacketSize,
|
|
||||||
_Out_bytecapcount_(PacketSize) BYTE **Packet);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copydoc WINTUN_SEND_PACKET
|
|
||||||
*/
|
|
||||||
void WINAPI
|
|
||||||
WintunSendPacket(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet);
|
|
@ -3,16 +3,31 @@
|
|||||||
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../api/wintun.h"
|
#include <winsock2.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ws2ipdef.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <ip2string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "wintun.h"
|
||||||
|
|
||||||
static WINTUN_GET_VERSION_FUNC WintunGetVersion;
|
|
||||||
static WINTUN_SET_LOGGER_FUNC WintunSetLogger;
|
|
||||||
static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
|
static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
|
||||||
static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter;
|
static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter;
|
||||||
|
static WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
|
||||||
|
static WINTUN_ENUM_ADAPTERS_FUNC WintunEnumAdapters;
|
||||||
|
static WINTUN_FREE_ADAPTER_FUNC WintunFreeAdapter;
|
||||||
|
static WINTUN_GET_ADAPTER_FUNC WintunGetAdapter;
|
||||||
|
static WINTUN_GET_ADAPTER_DEVICE_OBJECT_FUNC WintunGetAdapterDeviceObject;
|
||||||
|
static WINTUN_GET_ADAPTER_GUID_FUNC WintunGetAdapterGUID;
|
||||||
|
static WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
|
||||||
|
static WINTUN_GET_ADAPTER_NAME_FUNC WintunGetAdapterName;
|
||||||
|
static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName;
|
||||||
|
static WINTUN_GET_VERSION_FUNC WintunGetVersion;
|
||||||
|
static WINTUN_SET_LOGGER_FUNC WintunSetLogger;
|
||||||
static WINTUN_START_SESSION_FUNC WintunStartSession;
|
static WINTUN_START_SESSION_FUNC WintunStartSession;
|
||||||
static WINTUN_END_SESSION_FUNC WintunEndSession;
|
static WINTUN_END_SESSION_FUNC WintunEndSession;
|
||||||
|
static WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
|
||||||
static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
|
static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
|
||||||
static WINTUN_RECEIVE_RELEASE_FUNC WintunReceiveRelease;
|
static WINTUN_RECEIVE_RELEASE_FUNC WintunReceiveRelease;
|
||||||
static WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
|
static WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
|
||||||
@ -59,9 +74,8 @@ ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *LogLine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DWORD
|
static DWORD
|
||||||
LogLastError(_In_z_ const WCHAR *Prefix)
|
LogError(_In_z_ const WCHAR *Prefix, _In_ DWORD Error)
|
||||||
{
|
{
|
||||||
DWORD Error = GetLastError();
|
|
||||||
WCHAR *SystemMessage = NULL, *FormattedMessage = NULL;
|
WCHAR *SystemMessage = NULL, *FormattedMessage = NULL;
|
||||||
FormatMessageW(
|
FormatMessageW(
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||||
@ -84,7 +98,6 @@ LogLastError(_In_z_ const WCHAR *Prefix)
|
|||||||
ConsoleLogger(WINTUN_LOG_ERR, FormattedMessage);
|
ConsoleLogger(WINTUN_LOG_ERR, FormattedMessage);
|
||||||
LocalFree(FormattedMessage);
|
LocalFree(FormattedMessage);
|
||||||
LocalFree(SystemMessage);
|
LocalFree(SystemMessage);
|
||||||
SetLastError(Error);
|
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +113,7 @@ Log(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *Format, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static BOOL WINAPI
|
static BOOL WINAPI
|
||||||
CtrlHandler(DWORD CtrlType)
|
CtrlHandler(_In_ DWORD CtrlType)
|
||||||
{
|
{
|
||||||
switch (CtrlType)
|
switch (CtrlType)
|
||||||
{
|
{
|
||||||
@ -109,6 +122,7 @@ CtrlHandler(DWORD CtrlType)
|
|||||||
case CTRL_CLOSE_EVENT:
|
case CTRL_CLOSE_EVENT:
|
||||||
case CTRL_LOGOFF_EVENT:
|
case CTRL_LOGOFF_EVENT:
|
||||||
case CTRL_SHUTDOWN_EVENT:
|
case CTRL_SHUTDOWN_EVENT:
|
||||||
|
Log(WINTUN_LOG_INFO, L"Cleaning up and shutting down...");
|
||||||
HaveQuit = TRUE;
|
HaveQuit = TRUE;
|
||||||
SetEvent(QuitEvent);
|
SetEvent(QuitEvent);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -116,143 +130,249 @@ CtrlHandler(DWORD CtrlType)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD WINAPI
|
static void
|
||||||
TestAdapter(_Inout_ DWORD_PTR Index)
|
PrintPacket(_In_ const BYTE *Packet, _In_ DWORD PacketSize)
|
||||||
{
|
{
|
||||||
/* Create adapter. */
|
if (PacketSize < 20)
|
||||||
WCHAR AdapterName[MAX_ADAPTER_NAME];
|
|
||||||
_snwprintf_s(
|
|
||||||
AdapterName,
|
|
||||||
_countof(AdapterName),
|
|
||||||
_TRUNCATE,
|
|
||||||
L"test-%d.%d-%zu",
|
|
||||||
WINTUN_VERSION_MAJ,
|
|
||||||
WINTUN_VERSION_MIN,
|
|
||||||
Index);
|
|
||||||
const GUID AdapterGuid = { 0xeef7ebf,
|
|
||||||
WINTUN_VERSION_MAJ,
|
|
||||||
WINTUN_VERSION_MIN,
|
|
||||||
{ (BYTE)Index & 0xff, 0xa, 0x33, 0xbf, 0x5c, 0x8, 0x4a, 0xc6 } };
|
|
||||||
while (!HaveQuit)
|
|
||||||
{
|
{
|
||||||
WINTUN_ADAPTER_HANDLE Adapter;
|
Log(WINTUN_LOG_INFO, L"Received packet without room for an IP header");
|
||||||
BOOL RebootRequired = FALSE;
|
return;
|
||||||
DWORD Result = WintunCreateAdapter(L"Example", AdapterName, &AdapterGuid, &Adapter, &RebootRequired);
|
|
||||||
if (Result != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Log(WINTUN_LOG_ERR, L"%s adapter creation failed.\n", AdapterName);
|
|
||||||
return Result;
|
|
||||||
}
|
}
|
||||||
|
BYTE IpVersion = Packet[0] >> 4, Proto;
|
||||||
DWORDLONG WintunVersion = WintunGetVersion();
|
WCHAR Src[46], Dst[46];
|
||||||
Log(WINTUN_LOG_INFO,
|
if (IpVersion == 4)
|
||||||
L"%s adapter created (Wintun %d.%d.%d.%d, reboot: %d).\n",
|
|
||||||
AdapterName,
|
|
||||||
(WintunVersion >> 48) & 0xffff,
|
|
||||||
(WintunVersion >> 32) & 0xffff,
|
|
||||||
(WintunVersion >> 16) & 0xffff,
|
|
||||||
(WintunVersion >> 0) & 0xffff,
|
|
||||||
RebootRequired ? 1 : 0);
|
|
||||||
|
|
||||||
WINTUN_SESSION_HANDLE Session;
|
|
||||||
HANDLE WaitHandles[2] = { NULL, QuitEvent };
|
|
||||||
Result = WintunStartSession(Adapter, 0x100000, &Session, &WaitHandles[0]);
|
|
||||||
if (Result != ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
Log(WINTUN_LOG_ERR, L"%s session creation failed.\n", AdapterName);
|
RtlIpv4AddressToStringW((struct in_addr *)&Packet[12], Src);
|
||||||
goto cleanupAdapter;
|
RtlIpv4AddressToStringW((struct in_addr *)&Packet[16], Dst);
|
||||||
|
Proto = Packet[9];
|
||||||
|
Packet += 20, PacketSize -= 20;
|
||||||
}
|
}
|
||||||
|
else if (IpVersion == 6 && PacketSize < 40)
|
||||||
|
{
|
||||||
|
Log(WINTUN_LOG_INFO, L"Received packet without room for an IP header");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (IpVersion == 6)
|
||||||
|
{
|
||||||
|
RtlIpv6AddressToStringW((struct in6_addr *)&Packet[8], Src);
|
||||||
|
RtlIpv6AddressToStringW((struct in6_addr *)&Packet[24], Dst);
|
||||||
|
Proto = Packet[6];
|
||||||
|
Packet += 40, PacketSize -= 40;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(WINTUN_LOG_INFO, L"Received packet that was not IP");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Proto == 1 && PacketSize >= 8 && Packet[0] == 0)
|
||||||
|
Log(WINTUN_LOG_INFO, L"Received IPv%d ICMP echo reply from %s to %s", IpVersion, Src, Dst);
|
||||||
|
else
|
||||||
|
Log(WINTUN_LOG_INFO, L"Received IPv%d proto 0x%x packet from %s to %s", IpVersion, Proto, Src, Dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static USHORT
|
||||||
|
IPChecksum(BYTE *Buffer, DWORD Len)
|
||||||
|
{
|
||||||
|
ULONG Sum = 0;
|
||||||
|
for (; Len > 1; Len -= 2, Buffer += 2)
|
||||||
|
Sum += *(USHORT *)Buffer;
|
||||||
|
if (Len)
|
||||||
|
Sum += *Buffer;
|
||||||
|
Sum = (Sum >> 16) + (Sum & 0xffff);
|
||||||
|
Sum += (Sum >> 16);
|
||||||
|
return (USHORT)(~Sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
MakeICMP(_Inout_ BYTE Packet[28])
|
||||||
|
{
|
||||||
|
memset(Packet, 0, 28);
|
||||||
|
Packet[0] = 0x45;
|
||||||
|
*(USHORT *)&Packet[2] = htons(28);
|
||||||
|
Packet[8] = 255;
|
||||||
|
Packet[9] = 1;
|
||||||
|
*(ULONG *)&Packet[12] = htonl((10 << 24) | (6 << 16) | (7 << 8) | (8 << 0)); /* 10.6.7.8 */
|
||||||
|
*(ULONG *)&Packet[16] = htonl((10 << 24) | (6 << 16) | (7 << 8) | (7 << 0)); /* 10.6.7.7 */
|
||||||
|
*(USHORT *)&Packet[10] = IPChecksum(Packet, 20);
|
||||||
|
Packet[20] = 8;
|
||||||
|
*(USHORT *)&Packet[22] = IPChecksum(&Packet[20], 8);
|
||||||
|
Log(WINTUN_LOG_INFO, L"Sending IPv4 ICMP echo request to 10.6.7.8 from 10.6.7.7");
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI
|
||||||
|
ReceivePackets(_Inout_ DWORD_PTR SessionPtr)
|
||||||
|
{
|
||||||
|
WINTUN_SESSION_HANDLE Session = (WINTUN_SESSION_HANDLE)SessionPtr;
|
||||||
|
HANDLE WaitHandles[] = { WintunGetReadWaitEvent(Session), QuitEvent };
|
||||||
|
|
||||||
while (!HaveQuit)
|
while (!HaveQuit)
|
||||||
{
|
{
|
||||||
BYTE *Packet;
|
BYTE *Packet;
|
||||||
DWORD PacketSize;
|
DWORD PacketSize;
|
||||||
Result = WintunReceivePacket(Session, &Packet, &PacketSize);
|
DWORD Result = WintunReceivePacket(Session, &Packet, &PacketSize);
|
||||||
switch (Result)
|
switch (Result)
|
||||||
{
|
{
|
||||||
case ERROR_SUCCESS:
|
case ERROR_SUCCESS:
|
||||||
// TODO: Process packet.
|
PrintPacket(Packet, PacketSize);
|
||||||
WintunReceiveRelease(Session, Packet);
|
WintunReceiveRelease(Session, Packet);
|
||||||
continue;
|
continue;
|
||||||
case ERROR_NO_MORE_ITEMS:
|
case ERROR_NO_MORE_ITEMS:
|
||||||
if (WaitForMultipleObjects(_countof(WaitHandles), WaitHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
|
if (WaitForMultipleObjects(_countof(WaitHandles), WaitHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
|
||||||
continue;
|
continue;
|
||||||
goto cleanupSession;
|
return ERROR_SUCCESS;
|
||||||
|
default:
|
||||||
|
LogError(L"Packet read failed", Result);
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
Log(WINTUN_LOG_ERR, L"%s packet read failed (Code 0x%08X).\n", AdapterName, Result);
|
|
||||||
goto cleanupSession;
|
|
||||||
}
|
|
||||||
cleanupSession:
|
|
||||||
WintunEndSession(Session);
|
|
||||||
cleanupAdapter:
|
|
||||||
WintunDeleteAdapter(Adapter, TRUE, &RebootRequired);
|
|
||||||
}
|
}
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI
|
||||||
|
SendPackets(_Inout_ DWORD_PTR SessionPtr)
|
||||||
|
{
|
||||||
|
WINTUN_SESSION_HANDLE Session = (WINTUN_SESSION_HANDLE)SessionPtr;
|
||||||
|
while (!HaveQuit)
|
||||||
|
{
|
||||||
|
BYTE *Packet;
|
||||||
|
WintunAllocateSendPacket(Session, 28, &Packet);
|
||||||
|
MakeICMP(Packet);
|
||||||
|
WintunSendPacket(Session, Packet);
|
||||||
|
|
||||||
|
switch (WaitForSingleObject(QuitEvent, 1000 /* 1 second */))
|
||||||
|
{
|
||||||
|
case WAIT_ABANDONED:
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMODULE
|
||||||
|
InitializeWintun(void)
|
||||||
|
{
|
||||||
|
HMODULE Wintun =
|
||||||
|
LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
if (!Wintun)
|
||||||
|
return NULL;
|
||||||
|
#define X(Name, Type) ((Name = (Type)GetProcAddress(Wintun, #Name)) == NULL)
|
||||||
|
if (X(WintunCreateAdapter, WINTUN_CREATE_ADAPTER_FUNC) || X(WintunDeleteAdapter, WINTUN_DELETE_ADAPTER_FUNC) ||
|
||||||
|
X(WintunDeleteDriver, WINTUN_DELETE_DRIVER_FUNC) || X(WintunEnumAdapters, WINTUN_ENUM_ADAPTERS_FUNC) ||
|
||||||
|
X(WintunFreeAdapter, WINTUN_FREE_ADAPTER_FUNC) || X(WintunGetAdapter, WINTUN_GET_ADAPTER_FUNC) ||
|
||||||
|
X(WintunGetAdapterDeviceObject, WINTUN_GET_ADAPTER_DEVICE_OBJECT_FUNC) ||
|
||||||
|
X(WintunGetAdapterGUID, WINTUN_GET_ADAPTER_GUID_FUNC) ||
|
||||||
|
X(WintunGetAdapterLUID, WINTUN_GET_ADAPTER_LUID_FUNC) ||
|
||||||
|
X(WintunGetAdapterName, WINTUN_GET_ADAPTER_NAME_FUNC) ||
|
||||||
|
X(WintunSetAdapterName, WINTUN_SET_ADAPTER_NAME_FUNC) || X(WintunGetVersion, WINTUN_GET_VERSION_FUNC) ||
|
||||||
|
X(WintunSetLogger, WINTUN_SET_LOGGER_FUNC) || X(WintunStartSession, WINTUN_START_SESSION_FUNC) ||
|
||||||
|
X(WintunEndSession, WINTUN_END_SESSION_FUNC) || X(WintunGetReadWaitEvent, WINTUN_GET_READ_WAIT_EVENT_FUNC) ||
|
||||||
|
X(WintunReceivePacket, WINTUN_RECEIVE_PACKET_FUNC) || X(WintunReceiveRelease, WINTUN_RECEIVE_RELEASE_FUNC) ||
|
||||||
|
X(WintunAllocateSendPacket, WINTUN_ALLOCATE_SEND_PACKET_FUNC) || X(WintunSendPacket, WINTUN_SEND_PACKET_FUNC))
|
||||||
|
#undef X
|
||||||
|
{
|
||||||
|
DWORD Result = GetLastError();
|
||||||
|
FreeLibrary(Wintun);
|
||||||
|
SetLastError(Result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
return Wintun;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
Log(WINTUN_LOG_INFO, L"Wintun Test v%d.%d\n", WINTUN_VERSION_MAJ, WINTUN_VERSION_MIN);
|
HMODULE Wintun = InitializeWintun();
|
||||||
|
|
||||||
HMODULE Wintun =
|
|
||||||
LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
||||||
if (!Wintun)
|
if (!Wintun)
|
||||||
return LogLastError(L"Failed to load wintun.dll");
|
return LogError(L"Failed to initialize Wintun", GetLastError());
|
||||||
DWORD Result;
|
WintunSetLogger(ConsoleLogger);
|
||||||
if ((WintunGetVersion = (WINTUN_GET_VERSION_FUNC)GetProcAddress(Wintun, "WintunGetVersion")) == NULL ||
|
Log(WINTUN_LOG_INFO, L"Wintun library loaded");
|
||||||
(WintunSetLogger = (WINTUN_SET_LOGGER_FUNC)GetProcAddress(Wintun, "WintunSetLogger")) == NULL ||
|
|
||||||
(WintunCreateAdapter = (WINTUN_CREATE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunCreateAdapter")) == NULL ||
|
|
||||||
(WintunDeleteAdapter = (WINTUN_DELETE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunDeleteAdapter")) == NULL ||
|
|
||||||
(WintunStartSession = (WINTUN_START_SESSION_FUNC)GetProcAddress(Wintun, "WintunStartSession")) == NULL ||
|
|
||||||
(WintunEndSession = (WINTUN_END_SESSION_FUNC)GetProcAddress(Wintun, "WintunEndSession")) == NULL ||
|
|
||||||
(WintunReceivePacket = (WINTUN_RECEIVE_PACKET_FUNC)GetProcAddress(Wintun, "WintunReceivePacket")) == NULL ||
|
|
||||||
(WintunReceiveRelease = (WINTUN_RECEIVE_RELEASE_FUNC)GetProcAddress(Wintun, "WintunReceiveRelease")) == NULL ||
|
|
||||||
(WintunAllocateSendPacket =
|
|
||||||
(WINTUN_ALLOCATE_SEND_PACKET_FUNC)GetProcAddress(Wintun, "WintunAllocateSendPacket")) == NULL ||
|
|
||||||
(WintunSendPacket = (WINTUN_SEND_PACKET_FUNC)GetProcAddress(Wintun, "WintunSendPacket")) == NULL)
|
|
||||||
{
|
|
||||||
Result = LogLastError(L"Failed to get wintun.dll entries");
|
|
||||||
goto cleanupWintun;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
DWORD Result;
|
||||||
HaveQuit = FALSE;
|
HaveQuit = FALSE;
|
||||||
QuitEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
QuitEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||||
if (!QuitEvent)
|
if (!QuitEvent)
|
||||||
{
|
{
|
||||||
Result = LogLastError(L"Failed to create event");
|
Result = LogError(L"Failed to create event", GetLastError());
|
||||||
goto cleanupWintun;
|
goto cleanupWintun;
|
||||||
}
|
}
|
||||||
WintunSetLogger(ConsoleLogger);
|
|
||||||
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
|
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
|
||||||
{
|
{
|
||||||
Result = LogLastError(L"Failed to set console handler");
|
Result = LogError(L"Failed to set console handler", GetLastError());
|
||||||
goto cleanupQuit;
|
goto cleanupQuit;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE Workers[MAXIMUM_WAIT_OBJECTS] = { 0 };
|
GUID ExampleGuid = { 0xdeadbabe, 0xcafe, 0xbeef, { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } };
|
||||||
for (size_t i = 0; i < _countof(Workers); ++i)
|
WINTUN_ADAPTER_HANDLE Adapter;
|
||||||
if (!Workers[i])
|
Result = WintunGetAdapter(L"Example", L"Demo", &Adapter);
|
||||||
|
if (Result != ERROR_SUCCESS)
|
||||||
|
Result = WintunCreateAdapter(L"Example", L"Demo", &ExampleGuid, &Adapter, NULL);
|
||||||
|
if (Result != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
Workers[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TestAdapter, (LPVOID)i, 0, NULL);
|
LogError(L"Failed to create adapter", Result);
|
||||||
if (!Workers[i])
|
goto cleanupQuit;
|
||||||
{
|
|
||||||
Result = LogLastError(L"Failed to create thread");
|
|
||||||
goto cleanupWorkers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORDLONG Version = WintunGetVersion();
|
||||||
|
Log(WINTUN_LOG_INFO,
|
||||||
|
L"Wintun v%d.%d.%d.%d loaded",
|
||||||
|
(Version >> 48) & 0xff,
|
||||||
|
(Version >> 32) & 0xff,
|
||||||
|
(Version >> 16) & 0xff,
|
||||||
|
(Version >> 0) & 0xff,
|
||||||
|
WINTUN_VERSION_MIN);
|
||||||
|
|
||||||
|
MIB_UNICASTIPADDRESS_ROW AddressRow;
|
||||||
|
InitializeUnicastIpAddressEntry(&AddressRow);
|
||||||
|
WintunGetAdapterLUID(Adapter, &AddressRow.InterfaceLuid);
|
||||||
|
AddressRow.Address.Ipv4.sin_family = AF_INET;
|
||||||
|
AddressRow.Address.Ipv4.sin_addr.S_un.S_addr = htonl((10 << 24) | (6 << 16) | (7 << 8) | (7 << 0)); /* 10.6.7.7 */
|
||||||
|
AddressRow.OnLinkPrefixLength = 24; /* This is a /24 network */
|
||||||
|
Result = CreateUnicastIpAddressEntry(&AddressRow);
|
||||||
|
if (Result != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
LogError(L"Failed to set IP address", Result);
|
||||||
|
goto cleanupAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
WINTUN_SESSION_HANDLE Session;
|
||||||
|
Result = WintunStartSession(Adapter, 0x40000, &Session);
|
||||||
|
if (Result != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
LogError(L"Failed to create adapter", Result);
|
||||||
|
goto cleanupAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(WINTUN_LOG_INFO, L"Launching threads and mangling packets...");
|
||||||
|
|
||||||
|
HANDLE Workers[] = { CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceivePackets, (LPVOID)Session, 0, NULL),
|
||||||
|
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendPackets, (LPVOID)Session, 0, NULL) };
|
||||||
|
if (!Workers[0] || !Workers[1])
|
||||||
|
{
|
||||||
|
Result = LogError(L"Failed to create threads", GetLastError());
|
||||||
|
goto cleanupWorkers;
|
||||||
}
|
}
|
||||||
WaitForMultipleObjectsEx(_countof(Workers), Workers, TRUE, INFINITE, TRUE);
|
WaitForMultipleObjectsEx(_countof(Workers), Workers, TRUE, INFINITE, TRUE);
|
||||||
Result = ERROR_SUCCESS;
|
Result = ERROR_SUCCESS;
|
||||||
|
|
||||||
cleanupWorkers:
|
cleanupWorkers:
|
||||||
HaveQuit = TRUE;
|
HaveQuit = TRUE;
|
||||||
SetEvent(QuitEvent);
|
SetEvent(QuitEvent);
|
||||||
for (size_t i = 0; i < _countof(Workers); ++i)
|
for (size_t i = 0; i < _countof(Workers); ++i)
|
||||||
|
{
|
||||||
if (Workers[i])
|
if (Workers[i])
|
||||||
{
|
{
|
||||||
WaitForSingleObject(Workers[i], INFINITE);
|
WaitForSingleObject(Workers[i], INFINITE);
|
||||||
CloseHandle(Workers[i]);
|
CloseHandle(Workers[i]);
|
||||||
}
|
}
|
||||||
SetConsoleCtrlHandler(CtrlHandler, FALSE);
|
}
|
||||||
|
WintunEndSession(Session);
|
||||||
|
cleanupAdapter:
|
||||||
|
WintunDeleteAdapter(Adapter, FALSE, NULL);
|
||||||
|
WintunFreeAdapter(Adapter);
|
||||||
cleanupQuit:
|
cleanupQuit:
|
||||||
|
SetConsoleCtrlHandler(CtrlHandler, FALSE);
|
||||||
CloseHandle(QuitEvent);
|
CloseHandle(QuitEvent);
|
||||||
cleanupWintun:
|
cleanupWintun:
|
||||||
FreeLibrary(Wintun);
|
FreeLibrary(Wintun);
|
||||||
|
@ -52,17 +52,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
<SpectreMitigation>false</SpectreMitigation>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<SpectreMitigation>false</SpectreMitigation>
|
<SpectreMitigation>false</SpectreMitigation>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -107,6 +104,14 @@
|
|||||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>iphlpapi.lib;kernel32.lib;ntdll.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>..\api</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
@ -5,14 +5,6 @@
|
|||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="example.c">
|
<ClCompile Include="example.c">
|
||||||
|
@ -64,13 +64,21 @@ Global
|
|||||||
{F7679B65-2FEC-469A-8BAC-B07BF4439422}.Release|x86.ActiveCfg = Release|Win32
|
{F7679B65-2FEC-469A-8BAC-B07BF4439422}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{F7679B65-2FEC-469A-8BAC-B07BF4439422}.Release|x86.Build.0 = Release|Win32
|
{F7679B65-2FEC-469A-8BAC-B07BF4439422}.Release|x86.Build.0 = Release|Win32
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|amd64.ActiveCfg = Debug|x64
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|amd64.ActiveCfg = Debug|x64
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|amd64.Build.0 = Debug|x64
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm.ActiveCfg = Debug|ARM
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm.ActiveCfg = Debug|ARM
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm.Build.0 = Debug|ARM
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm64.ActiveCfg = Debug|ARM64
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm64.ActiveCfg = Debug|ARM64
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|arm64.Build.0 = Debug|ARM64
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|x86.ActiveCfg = Debug|Win32
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Debug|x86.Build.0 = Debug|Win32
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|amd64.ActiveCfg = Release|x64
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|amd64.ActiveCfg = Release|x64
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|amd64.Build.0 = Release|x64
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm.ActiveCfg = Release|ARM
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm.ActiveCfg = Release|ARM
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm.Build.0 = Release|ARM
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm64.ActiveCfg = Release|ARM64
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm64.ActiveCfg = Release|ARM64
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|arm64.Build.0 = Release|ARM64
|
||||||
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|x86.ActiveCfg = Release|Win32
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{2ABAC503-245D-4F53-85C5-0F844DEF738B}.Release|x86.Build.0 = Release|Win32
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user