diff --git a/binaries/win64/winws.exe b/binaries/win64/winws.exe index 0485f61..a90a4f3 100644 Binary files a/binaries/win64/winws.exe and b/binaries/win64/winws.exe differ diff --git a/binaries/win64/zapret-winws/winws.exe b/binaries/win64/zapret-winws/winws.exe index 0485f61..a90a4f3 100644 Binary files a/binaries/win64/zapret-winws/winws.exe and b/binaries/win64/zapret-winws/winws.exe differ diff --git a/docs/windows.eng.md b/docs/windows.eng.md index 51358f5..e954412 100644 --- a/docs/windows.eng.md +++ b/docs/windows.eng.md @@ -50,6 +50,8 @@ Task of `iptables` is done inside `winws` through `windivert` filters. `Windiver --wf-raw=|@ ; raw windivert filter string or filename --wf-save= ; save windivert filter string to a file and exit --ssid-filter=ssid1[,ssid2,ssid3,...] ; enable winws only if any of specified wifi SSIDs connected + --nlm-filter=net1[,net2,net3,...] ; enable winws only if any of specified NLM network is connected. names and GUIDs are accepted. + --nlm-list[=all] ; list Network List Manager (NLM) networks. connected only or all. ``` `--wf-l3`, `--wf-tcp`, `--wf-udp` can take multiple comma separated arguments. @@ -64,6 +66,10 @@ Multiple `winws` processes are allowed. However, it's discouraged to intersect t SSID names must be written in the same case as the system sees them. This option does not analyze routing and does not detect where traffic actually goes. If multiple connections are available, the only thing that triggers `winws` operation is wifi connection presence. That's why it's a good idea to add also `--wf-iface` filter to not break ethernet, for example. +`--nlm-filter` is like `--ssid-filter` but works with names or GUIDs from Network List Manager. NLM names are those you see in Control Panel "Network and Sharing Center". +NLM networks are adapter independent. Usually MAC address of the default router is used to distinugish networks. NLM works with any type of adapters : ethernet, wifi, vpn and others. +That's why NLM is more universal than `ssid-filter`. + `Cygwin` shell does not run binaries if their directory has it's own copy of `cygwin1.dll`. That's why exists separate standalone version in `binaries/win64/zapret-tpws`. `Cygwin` is required for `blockcheck.sh` support but `winws` itself can be run standalone without cygwin. diff --git a/docs/windows.txt b/docs/windows.txt index 2985a7c..fe5864f 100644 --- a/docs/windows.txt +++ b/docs/windows.txt @@ -56,7 +56,8 @@ https://learn.microsoft.com/en-us/security-updates/SecurityAdvisories/2015/30339 --wf-raw=|@ ; задать напрямую фильтр windivert из параметра или из файла. имени файла предшествует символ @. --wf-save= ; сохранить сконструированный фильтр windivert в файл для последующей правки вручную --ssid-filter=ssid1[,ssid2,ssid3,...] ; включать winws только когда подключена любая из указанных wifi сетей - + --nlm-filter=net1[,net2,net3,...] ; включать winws только когда подключена любая из указанных сетей NLM + --nlm-list[=all] ; вывести список сетей NLM. по умолчанию только подключенных, all - всех. Параметры --wf-l3, --wf-tcp, --wf-udp могут брать несколько значений через запятую. Номера интерфейсов можно узнать так : netsh int ip show int. @@ -80,6 +81,15 @@ winws включается. Это нужно, чтобы можно было п И это может сломать дурение на ethernet. Поэтому полезно так же будет добавить фильтр --wf-iface на индекс интерфейса wifi адаптера, чтобы не трогать другой трафик. +--nlm-filter аналогичен --ssid-filter, но работает с именами или GUIDами сетей Network List Manager (NLM). +Это те сети, которые вы видите в панели управления в разделе "Центр управления сетями и общим доступом". +Под сетью подразумевается не конкретный адаптер, а именно сетевое окружение конкретного подключения. +Обычно проверяется mac адрес шлюза. К сети можно подключиться через любой адаптер, и она останется той же самой. +Если подключиться, допустим, к разными роутерам по кабелю, то будут разные сети. +А если к одному роутеру через 2 разных сетевых карточки на том же компе - будет одна сеть. +NLM абстрагирует типы сетевых адаптеров. Он работает как с wifi, так и с ethernet и любыми другими. +Поэтому это более универсальный метод, чем ssid фильтр. + Если в путях присутствуют национальные символы, то при вызове winws из cmd или bat кодировку нужно использовать OEM. Для русского языка это 866. Пути с пробелами нужно брать в кавычки. diff --git a/nfq/Makefile b/nfq/Makefile index b334934..5439bdd 100644 --- a/nfq/Makefile +++ b/nfq/Makefile @@ -5,7 +5,7 @@ CFLAGS_MAC = -mmacosx-version-min=10.8 CFLAGS_CYGWIN = -Wno-address-of-packed-member -static LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz LIBS_BSD = -lz -LIBS_CYGWIN = -lz -Lwindivert -lwindivert -lwlanapi +LIBS_CYGWIN = -lz -Lwindivert -lwindivert -lwlanapi -lole32 -loleaut32 -luuid SRC_FILES = *.c crypto/*.c all: nfqws diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index 6873197..98df47d 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -16,6 +16,7 @@ #ifdef __CYGWIN__ #include +#include #endif uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment) @@ -963,10 +964,204 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac static HANDLE w_filter = NULL; static OVERLAPPED ovl = { .hEvent = NULL }; static const struct str_list_head *wlan_filter_ssid = NULL; -static DWORD wlan_filter_tick=0; +static const struct str_list_head *nlm_filter_net = NULL; +static DWORD logical_net_filter_tick=0; uint32_t w_win32_error=0; +INetworkListManager* pNetworkListManager=NULL; -bool wlan_filter_match(const struct str_list_head *ssid_list) +static void guid2str(const GUID *guid, char *str) +{ + snprintf(str,37, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); +} +static bool str2guid(const char* str, GUID *guid) +{ + unsigned int u[11],k; + + if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", u+0, u+1, u+2, u+3, u+4, u+5, u+6, u+7, u+8, u+9, u+10)) + return false; + guid->Data1 = u[0]; + if ((u[1] & 0xFFFF0000) || (u[2] & 0xFFFF0000)) return false; + guid->Data2 = (USHORT)u[1]; + guid->Data3 = (USHORT)u[2]; + for (k = 0; k < 8; k++) + { + if (u[k+3] & 0xFFFFFF00) return false; + guid->Data4[k] = (UCHAR)u[k+3]; + } + return true; +} + +bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter) +{ + win_dark_deinit(); + wlan_filter_ssid = LIST_EMPTY(ssid_filter) ? NULL : ssid_filter; + nlm_filter_net = LIST_EMPTY(nlm_filter) ? NULL : nlm_filter; + if (nlm_filter_net) + { + if (SUCCEEDED(w_win32_error = CoInitialize(NULL))) + { + if (FAILED(w_win32_error = CoCreateInstance(&CLSID_NetworkListManager, NULL, CLSCTX_ALL, &IID_INetworkListManager, (LPVOID*)&pNetworkListManager))) + { + win_dark_deinit(); + return false; + } + } + } + return true; +} +bool win_dark_deinit(void) +{ + if (pNetworkListManager) + { + pNetworkListManager->lpVtbl->Release(pNetworkListManager); + pNetworkListManager = NULL; + } + if (nlm_filter_net) CoUninitialize(); + wlan_filter_ssid = nlm_filter_net = NULL; +} + + +bool nlm_list(bool bAll) +{ + bool bRet = true; + + if (SUCCEEDED(w_win32_error = CoInitialize(NULL))) + { + INetworkListManager* pNetworkListManager; + if (SUCCEEDED(w_win32_error = CoCreateInstance(&CLSID_NetworkListManager, NULL, CLSCTX_ALL, &IID_INetworkListManager, (LPVOID*)&pNetworkListManager))) + { + IEnumNetworks* pEnum; + if (SUCCEEDED(w_win32_error = pNetworkListManager->lpVtbl->GetNetworks(pNetworkListManager, NLM_ENUM_NETWORK_ALL, &pEnum))) + { + INetwork* pNet; + VARIANT_BOOL bIsConnected, bIsConnectedInet; + GUID idNet; + BSTR bstrName; + char Name[128]; + int connected; + for (connected = 1; connected >= !bAll; connected--) + { + for (;;) + { + if (FAILED(w_win32_error = pEnum->lpVtbl->Next(pEnum, 1, &pNet, NULL))) + { + bRet = false; + break; + } + if (!pNet) break; + if (SUCCEEDED(w_win32_error = pNet->lpVtbl->get_IsConnected(pNet, &bIsConnected)) && + SUCCEEDED(w_win32_error = pNet->lpVtbl->get_IsConnectedToInternet(pNet, &bIsConnectedInet)) && + SUCCEEDED(w_win32_error = pNet->lpVtbl->GetNetworkId(pNet, &idNet)) && + SUCCEEDED(w_win32_error = pNet->lpVtbl->GetName(pNet, &bstrName))) + { + if (!!bIsConnected == connected) + { + if (WideCharToMultiByte(CP_UTF8, 0, bstrName, -1, Name, sizeof(Name), NULL, NULL)) + { + printf("Name : %s", Name); + if (bIsConnected) printf(" (connected)"); + if (bIsConnectedInet) printf(" (inet)"); + printf("\n"); + guid2str(&idNet, Name); + printf("NetID : %s\n", Name); + printf("\n"); + } + else + { + w_win32_error = HRESULT_FROM_WIN32(GetLastError()); + bRet = false; + } + } + SysFreeString(bstrName); + } + else + bRet = false; + pNet->lpVtbl->Release(pNet); + if (!bRet) break; + } + if (!bRet) break; + pEnum->lpVtbl->Reset(pEnum); + } + pEnum->lpVtbl->Release(pEnum); + } + else + bRet = false; + pNetworkListManager->lpVtbl->Release(pNetworkListManager); + } + else + bRet = false; + } + else + bRet = false; + + CoUninitialize(); + return bRet; +} + +static bool nlm_filter_match(const struct str_list_head *nlm_list) +{ + // no filter given. always matches. + if (!nlm_list || LIST_EMPTY(nlm_list)) + { + w_win32_error = 0; + return true; + } + + bool bRet = true, bMatch = false; + IEnumNetworks* pEnum; + + if (SUCCEEDED(w_win32_error = pNetworkListManager->lpVtbl->GetNetworks(pNetworkListManager, NLM_ENUM_NETWORK_ALL, &pEnum))) + { + INetwork* pNet; + VARIANT_BOOL bIsConnected; + GUID idNet,g; + BSTR bstrName; + char Name[128]; + struct str_list *nlm; + for (;;) + { + if (FAILED(w_win32_error = pEnum->lpVtbl->Next(pEnum, 1, &pNet, NULL))) + { + bRet = false; + break; + } + if (!pNet) break; + if (SUCCEEDED(w_win32_error = pNet->lpVtbl->get_IsConnected(pNet, &bIsConnected)) && + SUCCEEDED(w_win32_error = pNet->lpVtbl->GetNetworkId(pNet, &idNet)) && + SUCCEEDED(w_win32_error = pNet->lpVtbl->GetName(pNet, &bstrName))) + { + if (bIsConnected) + { + if (WideCharToMultiByte(CP_UTF8, 0, bstrName, -1, Name, sizeof(Name), NULL, NULL)) + { + LIST_FOREACH(nlm, nlm_list, next) + { + bMatch = !strcmp(Name,nlm->str) || str2guid(nlm->str,&g) && !memcmp(&idNet,&g,sizeof(GUID)); + if (bMatch) break; + } + } + else + { + w_win32_error = HRESULT_FROM_WIN32(GetLastError()); + bRet = false; + } + + } + SysFreeString(bstrName); + } + else + bRet = false; + pNet->lpVtbl->Release(pNet); + if (!bRet || bMatch) break; + } + pEnum->lpVtbl->Release(pEnum); + } + else + bRet = false; + return bRet && bMatch; +} + +static bool wlan_filter_match(const struct str_list_head *ssid_list) { DWORD dwCurVersion; HANDLE hClient = NULL; @@ -1031,12 +1226,17 @@ found: goto ex; } -static bool wlan_filter_match_rate_limited(void) +bool logical_net_filter_match(void) +{ + return wlan_filter_match(wlan_filter_ssid) && nlm_filter_match(nlm_filter_net); +} + +static bool logical_net_filter_match_rate_limited(void) { DWORD dwTick = GetTickCount() / 1000; - if (wlan_filter_tick == dwTick) return true; - wlan_filter_tick = dwTick; - return wlan_filter_match(wlan_filter_ssid); + if (logical_net_filter_tick == dwTick) return true; + logical_net_filter_tick = dwTick; + return logical_net_filter_match(); } static HANDLE windivert_init_filter(const char *filter, UINT64 flags) @@ -1084,9 +1284,8 @@ void rawsend_cleanup(void) CloseHandle(ovl.hEvent); ovl.hEvent=NULL; } - wlan_filter_ssid = NULL; } -bool windivert_init(const char *filter, const struct str_list_head *ssid_filter) +bool windivert_init(const char *filter) { rawsend_cleanup(); w_filter = windivert_init_filter(filter, 0); @@ -1099,7 +1298,6 @@ bool windivert_init(const char *filter, const struct str_list_head *ssid_filter) rawsend_cleanup(); return false; } - wlan_filter_ssid = ssid_filter; return true; } return false; @@ -1117,7 +1315,7 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, errno=EINTR; return false; } - if (!wlan_filter_match_rate_limited()) + if (!logical_net_filter_match_rate_limited()) { errno=ENODEV; return false; @@ -1142,7 +1340,7 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, errno=EINTR; return false; } - if (!wlan_filter_match_rate_limited()) + if (!logical_net_filter_match_rate_limited()) { errno=ENODEV; return false; diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index eb7ef97..d2ba973 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -152,10 +152,13 @@ bool tcp_has_fastopen(const struct tcphdr *tcp); #ifdef __CYGWIN__ extern uint32_t w_win32_error; -bool windivert_init(const char *filter, const struct str_list_head *ssid_filter); +bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter); +bool win_dark_deinit(void); +bool logical_net_filter_match(void); +bool nlm_list(bool bAll); +bool windivert_init(const char *filter); bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa); bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa); -bool wlan_filter_match(const struct str_list_head *ssid_list); #else // should pre-do it if dropping privileges. otherwise its not necessary bool rawsend_preinit(bool bind_fix4, bool bind_fix6); diff --git a/nfq/desync.c b/nfq/desync.c index ab4e2d6..5522914 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -7,7 +7,6 @@ #include "hostlist.h" #include "conntrack.h" -#include #include diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 7998b5b..212ed2f 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -412,9 +412,15 @@ static int win_main(const char *windivert_filter) pre_desync(); + if (!win_dark_init(¶ms.ssid_filter, ¶ms.nlm_filter)) + { + fprintf(stderr, "win_dark_init failed. win32 error %u (0x%08X)\n", w_win32_error, w_win32_error); + return w_win32_error; + } + for(;;) { - if (!wlan_filter_match(¶ms.ssid_filter)) + if (!logical_net_filter_match()) { printf("logical network is not present. waiting it to appear.\n"); fflush(stdout); @@ -423,17 +429,21 @@ static int win_main(const char *windivert_filter) if (bQuit) { DLOG("QUIT requested\n") + win_dark_deinit(); return 0; } usleep(500000); } - while (!wlan_filter_match(¶ms.ssid_filter)); + while (!logical_net_filter_match()); printf("logical network now present\n"); fflush(stdout); } - if (!windivert_init(windivert_filter, ¶ms.ssid_filter)) + if (!windivert_init(windivert_filter)) + { + win_dark_deinit(); return w_win32_error; + } printf("windivert initialized. capture is started.\n"); @@ -460,9 +470,11 @@ static int win_main(const char *windivert_filter) else if (errno==EINTR) { DLOG("QUIT requested\n") + win_dark_deinit(); return 0; } fprintf(stderr, "windivert: recv failed. errno %d\n", errno); + win_dark_deinit(); return w_win32_error; } *ifout=0; @@ -504,6 +516,7 @@ static int win_main(const char *windivert_filter) fflush(stderr); } } + win_dark_deinit(); return 0; } @@ -550,6 +563,7 @@ static void cleanup_params(void) HostFailPoolDestroy(¶ms.hostlist_auto_fail_counters); #ifdef __CYGWIN__ strlist_destroy(¶ms.ssid_filter); + strlist_destroy(¶ms.nlm_filter); #endif } static void exit_clean(int code) @@ -799,6 +813,8 @@ static void exithelp(void) " --wf-save=\t\t\t\t; save windivert filter string to a file and exit\n" "\nLOGICAL NETWORK FILTER:\n" " --ssid-filter=ssid1[,ssid2,ssid3,...]\t\t; enable winws only if any of specified wifi SSIDs connected\n" + " --nlm-filter=net1[,net2,net3,...]\t\t; enable winws only if any of specified NLM network is connected. names and GUIDs are accepted.\n" + " --nlm-list[=all]\t\t\t\t; list Network List Manager (NLM) networks. connected only or all.\n" #endif "\nHOSTLIST FILTER:\n" " --hostlist=\t\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n" @@ -910,7 +926,7 @@ int main(int argc, char **argv) char windivert_filter[8192], wf_pf_tcp_src[256], wf_pf_tcp_dst[256], wf_pf_udp_src[256], wf_pf_udp_dst[256], wf_save_file[256]; bool wf_ipv4=true, wf_ipv6=true; unsigned int IfIdx=0, SubIfIdx=0; - unsigned int hash_wf_tcp=0,hash_wf_udp=0,hash_wf_raw=0,hash_ssid_filter=0; + unsigned int hash_wf_tcp=0,hash_wf_udp=0,hash_wf_raw=0,hash_ssid_filter=0,hash_nlm_filter=0; *windivert_filter = *wf_pf_tcp_src = *wf_pf_tcp_dst = *wf_pf_udp_src = *wf_pf_udp_dst = *wf_save_file = 0; #endif @@ -959,6 +975,7 @@ int main(int argc, char **argv) #ifdef __CYGWIN__ LIST_INIT(¶ms.ssid_filter); + LIST_INIT(¶ms.nlm_filter); #else if (can_drop_root()) // are we root ? { @@ -1049,6 +1066,8 @@ int main(int argc, char **argv) {"wf-raw",required_argument,0,0}, // optidx=56 {"wf-save",required_argument,0,0}, // optidx=57 {"ssid-filter",required_argument,0,0}, // optidx=58 + {"nlm-filter",required_argument,0,0}, // optidx=59 + {"nlm-list",optional_argument,0,0}, // optidx=60 #endif {NULL,0,NULL,0} }; @@ -1582,6 +1601,31 @@ int main(int argc, char **argv) } } break; + case 59: /* nlm-filter */ + hash_nlm_filter=hash_jen(optarg,strlen(optarg)); + { + char *e,*p = optarg; + while (p) + { + e = strchr(p,','); + if (e) *e++=0; + if (*p && !strlist_add(¶ms.nlm_filter, p)) + { + fprintf(stderr, "strlist_add failed\n"); + exit_clean(1); + } + p = e; + + } + } + break; + case 60: /* nlm-list */ + if (!nlm_list(optarg && !strcmp(optarg,"all"))) + { + fprintf(stderr, "could not get list of NLM networks\n"); + exit_clean(1); + } + exit_clean(0); #endif } @@ -1630,7 +1674,7 @@ int main(int argc, char **argv) HANDLE hMutexArg; { char mutex_name[128]; - snprintf(mutex_name,sizeof(mutex_name),"Global\\winws_arg_%u_%u_%u_%u_%u_%u_%u_%u",hash_wf_tcp,hash_wf_udp,hash_wf_raw,hash_ssid_filter,IfIdx,SubIfIdx,wf_ipv4,wf_ipv6); + snprintf(mutex_name,sizeof(mutex_name),"Global\\winws_arg_%u_%u_%u_%u_%u_%u_%u_%u_%u",hash_wf_tcp,hash_wf_udp,hash_wf_raw,hash_ssid_filter,hash_nlm_filter,IfIdx,SubIfIdx,wf_ipv4,wf_ipv6); hMutexArg = CreateMutexA(NULL,TRUE,mutex_name); if (hMutexArg && GetLastError()==ERROR_ALREADY_EXISTS) diff --git a/nfq/params.h b/nfq/params.h index 7404473..773eb19 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -66,7 +66,7 @@ struct params_s int udplen_increment; #ifdef __CYGWIN__ - struct str_list_head ssid_filter; + struct str_list_head ssid_filter,nlm_filter; #else bool droproot; uid_t uid;