/*
* windivertctl.c
* (C) 2019, all rights reserved,
*
* This file is part of WinDivert.
*
* WinDivert is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* WinDivert is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* DESCRIPTION:
*
* usage: windivertctl.exe list
*/
#include
#include
#include
#include
#include
#include
#include "windivert.h"
#define MAX_PACKET 0xFFFF
#define MAX_FILTER_LEN 30000
/*
* Modes.
*/
typedef enum
{
LIST,
WATCH,
KILL,
UNINSTALL
} MODE;
/*
* Entry.
*/
int __cdecl main(int argc, char **argv)
{
HANDLE handle, process, console, mutex;
INT16 priority = -333; // Arbitrary.
UINT packet_len;
static UINT8 packet[MAX_PACKET];
static char path[MAX_PATH+1];
static char filter_str[MAX_FILTER_LEN];
DWORD path_len;
BOOL or;
WINDIVERT_ADDRESS addr;
ULONGLONG freq, start_count;
LARGE_INTEGER li;
MODE mode;
SC_HANDLE manager = NULL, service = NULL;
SERVICE_STATUS status;
const char *filter = "true";
const char *err_str = NULL;
if (argc != 2 && argc != 3)
{
usage:
fprintf(stderr, "usage: %s (list|watch|kill) [filter]\n", argv[0]);
fprintf(stderr, " %s uninstall\n", argv[0]);
exit(EXIT_FAILURE);
}
if (strcmp(argv[1], "list") == 0)
{
mode = LIST;
}
else if (strcmp(argv[1], "watch") == 0)
{
mode = WATCH;
}
else if (strcmp(argv[1], "kill") == 0)
{
mode = KILL;
}
else if (strcmp(argv[1], "uninstall") == 0)
{
if (argc != 2)
{
goto usage;
}
mode = UNINSTALL;
}
else
{
goto usage;
}
if (argc == 3)
{
filter = argv[2];
}
// Time management
QueryPerformanceFrequency(&li);
freq = li.QuadPart;
QueryPerformanceCounter(&li);
start_count = li.QuadPart;
// Open WinDivert REFLECT handle:
handle = WinDivertOpen(filter, WINDIVERT_LAYER_REFLECT, priority,
WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_RECV_ONLY |
(mode == WATCH? 0: WINDIVERT_FLAG_NO_INSTALL));
if (handle == INVALID_HANDLE_VALUE)
{
if (mode != WATCH && GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
{
// WinDivert driver is not running, so no open handles.
return 0;
}
if (GetLastError() == ERROR_INVALID_PARAMETER &&
!WinDivertHelperCompileFilter(filter, WINDIVERT_LAYER_REFLECT,
NULL, 0, &err_str, NULL))
{
fprintf(stderr, "error: invalid filter \"%s\"\n", err_str);
exit(EXIT_FAILURE);
}
fprintf(stderr, "error: failed to open the WinDivert device (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
if (mode != WATCH && !WinDivertShutdown(handle, WINDIVERT_SHUTDOWN_BOTH))
{
fprintf(stderr, "error: failed to shutdown WinDivert handle (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
if (!WinDivertSetParam(handle, WINDIVERT_PARAM_QUEUE_LENGTH,
WINDIVERT_PARAM_QUEUE_LENGTH_MAX) ||
!WinDivertSetParam(handle, WINDIVERT_PARAM_QUEUE_SIZE,
WINDIVERT_PARAM_QUEUE_SIZE_MAX) ||
!WinDivertSetParam(handle, WINDIVERT_PARAM_QUEUE_TIME,
WINDIVERT_PARAM_QUEUE_TIME_MAX))
{
fprintf(stderr, "error: failed to set WinDivert handle params (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
// Main loop:
console = GetStdHandle(STD_OUTPUT_HANDLE);
while (TRUE)
{
if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
if (mode != WATCH && GetLastError() == ERROR_NO_DATA)
{
break;
}
fprintf(stderr, "failed to receive event (%d)\n", GetLastError());
continue;
}
switch (addr.Event)
{
case WINDIVERT_EVENT_REFLECT_OPEN:
// Open handle:
if (mode == KILL || mode == UNINSTALL)
{
SetConsoleTextAttribute(console, FOREGROUND_RED);
fputs("KILL", stdout);
}
else
{
SetConsoleTextAttribute(console, FOREGROUND_GREEN);
fputs("OPEN", stdout);
}
break;
case WINDIVERT_EVENT_REFLECT_CLOSE:
// Close handle:
if (mode != WATCH)
{
continue;
}
SetConsoleTextAttribute(console, FOREGROUND_RED);
fputs("CLOSE", stdout);
break;
default:
fputs("???", stdout);
break;
}
process = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE,
FALSE, addr.Reflect.ProcessId);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" time=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
printf("%.3fs", (double)(addr.Reflect.Timestamp - (INT64)start_count) /
(double)freq);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" pid=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
printf("%u", addr.Reflect.ProcessId);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" exe=", stdout);
path_len = 0;
if (process != NULL)
{
path_len = GetProcessImageFileName(process, path, sizeof(path));
if (mode == KILL || mode == UNINSTALL)
{
TerminateProcess(process, 0);
}
CloseHandle(process);
}
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
printf("%s", (path_len != 0? path: "???"));
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" layer=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
switch (addr.Reflect.Layer)
{
case WINDIVERT_LAYER_NETWORK:
fputs("NETWORK", stdout);
break;
case WINDIVERT_LAYER_NETWORK_FORWARD:
fputs("NETWORK_FORWARD", stdout);
break;
case WINDIVERT_LAYER_FLOW:
fputs("FLOW", stdout);
break;
case WINDIVERT_LAYER_SOCKET:
fputs("SOCKET", stdout);
break;
case WINDIVERT_LAYER_REFLECT:
fputs("REFLECT", stdout);
break;
default:
fputs("???", stdout);
break;
}
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" flags=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
if (addr.Reflect.Flags == 0)
{
fputs("0", stdout);
}
else
{
or = FALSE;
if ((addr.Reflect.Flags & WINDIVERT_FLAG_SNIFF) != 0)
{
fputs("SNIFF", stdout);
or = TRUE;
}
if ((addr.Reflect.Flags & WINDIVERT_FLAG_DROP) != 0)
{
printf("%sDROP", (or? "|": ""));
or = TRUE;
}
if ((addr.Reflect.Flags & WINDIVERT_FLAG_RECV_ONLY) != 0)
{
printf("%sRECV_ONLY", (or? "|": ""));
or = TRUE;
}
if ((addr.Reflect.Flags & WINDIVERT_FLAG_SEND_ONLY) != 0)
{
printf("%sSEND_ONLY", (or? "|": ""));
or = TRUE;
}
if ((addr.Reflect.Flags & WINDIVERT_FLAG_NO_INSTALL) != 0)
{
printf("%sNO_INSTALL", (or? "|": ""));
or = TRUE;
}
}
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" priority=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
printf("%d", addr.Reflect.Priority);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
fputs(" filter=", stdout);
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN);
if (WinDivertHelperFormatFilter((char *)packet, addr.Reflect.Layer,
filter_str, sizeof(filter_str)))
{
printf("\"%s\"", filter_str);
}
else
{
printf("\"%s\"", (char *)packet);
}
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
putchar('\n');
}
if (!WinDivertClose(handle))
{
fprintf(stderr, "error: failed to close WinDivert handle (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
if (mode == UNINSTALL)
{
// Stop & delete the WinDivert service:
mutex = CreateMutex(NULL, FALSE, "WinDivertDriverInstallMutex");
if (mutex == NULL)
{
fprintf(stderr, "error: failed to create WinDivert driver "
"install mutex (%d)\n", GetLastError());
return EXIT_FAILURE;
}
switch (WaitForSingleObject(mutex, INFINITE))
{
case WAIT_OBJECT_0: case WAIT_ABANDONED:
break;
default:
fprintf(stderr, "error: failed to acquire WinDivert driver "
"install mutex (%d)\n", GetLastError());
return EXIT_FAILURE;
}
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (manager == NULL)
{
fprintf(stderr, "error: failed to open service manager (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
service = OpenService(manager, "WinDivert", SERVICE_ALL_ACCESS);
if (service == NULL)
{
fprintf(stderr, "error: failed to open WinDivert service (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
{
fprintf(stderr, "error: failed to stop WinDivert service (%d)\n",
GetLastError());
return EXIT_FAILURE;
}
if (status.dwCurrentState != SERVICE_STOPPED)
{
fprintf(stderr, "error: failed to stop WinDivert service");
return EXIT_FAILURE;
}
CloseServiceHandle(service);
CloseServiceHandle(manager);
SetConsoleTextAttribute(console, FOREGROUND_GREEN);
fputs("UNINSTALL", stdout);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
puts(" WinDivert");
ReleaseMutex(mutex);
CloseHandle(mutex);
}
return 0;
}