├── windows └── src │ ├── TcpInterception │ ├── pch.cpp │ ├── NetFilter.h │ ├── pch.h │ ├── Main.cpp │ ├── TcpInterception.inf │ ├── TcpInterception.vcxproj.filters │ ├── TcpInterception.vcxproj │ └── NetFilter.cpp │ └── TcpInterception.sln ├── linux ├── user_mode │ ├── Makefile │ └── nfqueue_tcp_interception.cpp └── kenel_mode │ ├── Makefile │ └── netfilter_tcp_parser.c └── .gitignore /windows/src/TcpInterception/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" -------------------------------------------------------------------------------- /windows/src/TcpInterception/NetFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | NTSTATUS InitializeFilter(PDEVICE_OBJECT deviceObject); 4 | 5 | void DeinitializeFilter(); -------------------------------------------------------------------------------- /linux/user_mode/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | g++ -o nfqueue_tcp_interception nfqueue_tcp_interception.cpp -lnetfilter_queue 3 | 4 | clean: 5 | rm nfqueue_tcp_interception 6 | -------------------------------------------------------------------------------- /windows/src/TcpInterception/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma warning(push) 9 | #pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union 10 | #include 11 | #pragma warning(pop) -------------------------------------------------------------------------------- /linux/kenel_mode/Makefile: -------------------------------------------------------------------------------- 1 | #If KERNELRELEASE is defined, we've been invoked from the 2 | #kernel build system and use its language 3 | ifneq ($(KERNELRELEASE),) 4 | obj-m := netfilter_tcp_parser.o 5 | 6 | #Otherwise we were called directly from the command 7 | #line; invoke the kernel build system. 8 | else 9 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 10 | PWD := $(shell pwd) 11 | 12 | default: 13 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 14 | clean: 15 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 16 | 17 | endif 18 | -------------------------------------------------------------------------------- /windows/src/TcpInterception/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "NetFilter.h" 3 | 4 | static PDEVICE_OBJECT g_deviceObject = nullptr; 5 | 6 | static void DriverUnload(PDRIVER_OBJECT /*driverObject*/) 7 | { 8 | DeinitializeFilter(); 9 | 10 | if (g_deviceObject) 11 | { 12 | IoDeleteDevice(g_deviceObject); 13 | g_deviceObject = nullptr; 14 | } 15 | } 16 | 17 | EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING /*registryPath*/) 18 | { 19 | NTSTATUS status = IoCreateDevice(driverObject, 0, nullptr, FILE_DEVICE_UNKNOWN, 0, false, &g_deviceObject); 20 | if (!NT_SUCCESS(status)) 21 | { 22 | return status; 23 | } 24 | 25 | driverObject->DriverUnload = DriverUnload; 26 | 27 | status = InitializeFilter(g_deviceObject); 28 | if (!NT_SUCCESS(status)) 29 | { 30 | DriverUnload(driverObject); 31 | } 32 | 33 | return status; 34 | } -------------------------------------------------------------------------------- /windows/src/TcpInterception/TcpInterception.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; TcpInterception INF file 3 | ; 4 | 5 | [Version] 6 | Signature = "$Windows NT$" 7 | Class = WFPCALLOUTS 8 | ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} 9 | Provider = %ManufacturerName% 10 | CatalogFile = "TcpInterception.cat" 11 | PnpLockdown = 1 12 | DriverVer = 13 | 14 | [SourceDisksNames] 15 | 1 = %DiskName% 16 | 17 | [SourceDisksFiles] 18 | TcpInterception.sys = 1 19 | 20 | [DestinationDirs] 21 | DefaultDestDir = 12 ; %windir%\system32\drivers 22 | TcpInterception.DriverFiles = 12 ; %windir%\system32\drivers 23 | 24 | [DefaultInstall.NT] 25 | OptionDesc = %Description% 26 | CopyFiles = TcpInterception.CopyDriverFiles 27 | 28 | [DefaultInstall.NT.Services] 29 | AddService = %ServiceName%,,TcpInterception.Service 30 | 31 | [DefaultUninstall.NT] 32 | DelFiles = TcpInterception.DeleteDriverFiles 33 | 34 | [DefaultUninstall.NT.Services] 35 | DelService = TcpInterception,0x200 ; SPSVCINST_STOPSERVICE 36 | 37 | [TcpInterception.CopyDriverFiles] 38 | TcpInterception.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY 39 | 40 | [TcpInterception.DeleteDriverFiles] 41 | TcpInterception.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY 42 | 43 | [TcpInterception.Service] 44 | DisplayName = %ServiceName% 45 | Description = %ServiceDesc% 46 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 47 | StartType = 3 ; SERVICE_DEMAND_START 48 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 49 | ServiceBinary = %12%\TcpInterception.sys 50 | 51 | [Strings] 52 | ManufacturerName="Apriorit" 53 | ClassName="" 54 | DiskName="TcpInterception Source Disk" 55 | Description = "TCP Interception Driver" 56 | ServiceName = "TcpInterception" 57 | ServiceDesc = "TcpInterception" -------------------------------------------------------------------------------- /windows/src/TcpInterception.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.1758 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TcpIntecption", "TcpInterception\TcpInterception.vcxproj", "{FAAF4891-2DDC-406E-8AB0-985886405D80}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x64.ActiveCfg = Debug|x64 17 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x64.Build.0 = Debug|x64 18 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x64.Deploy.0 = Debug|x64 19 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x86.ActiveCfg = Debug|Win32 20 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x86.Build.0 = Debug|Win32 21 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Debug|x86.Deploy.0 = Debug|Win32 22 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x64.ActiveCfg = Release|x64 23 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x64.Build.0 = Release|x64 24 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x64.Deploy.0 = Release|x64 25 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x86.ActiveCfg = Release|Win32 26 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x86.Build.0 = Release|Win32 27 | {FAAF4891-2DDC-406E-8AB0-985886405D80}.Release|x86.Deploy.0 = Release|Win32 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {5EBBA0FA-E470-489D-8553-F76B3617775B} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /windows/src/TcpInterception/TcpInterception.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 11 | 12 | 13 | {8E41214B-6785-4CFE-B992-037D68949A14} 14 | inf;inv;inx;mof;mc; 15 | 16 | 17 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 18 | h;hpp;hxx;hm;inl;inc;xsd 19 | 20 | 21 | {bf80aac0-b415-4351-8f9b-eb83ae0d4f89} 22 | 23 | 24 | 25 | 26 | Driver Files 27 | 28 | 29 | 30 | 31 | Source Files 32 | 33 | 34 | PrecompiledHeaders 35 | 36 | 37 | NetFilter 38 | 39 | 40 | 41 | 42 | PrecompiledHeaders 43 | 44 | 45 | NetFilter 46 | 47 | 48 | -------------------------------------------------------------------------------- /linux/user_mode/nfqueue_tcp_interception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include 14 | #include 15 | #include 16 | #include 17 | } 18 | 19 | #define THROW_IF_TRUE(x, m) do { if((x)) { throw std::runtime_error(m); }} while(false) 20 | 21 | #define CONCAT_0(pre, post) pre ## post 22 | #define CONCAT_1(pre, post) CONCAT_0(pre, post) 23 | #define GENERATE_IDENTIFICATOR(pre) CONCAT_1(pre, __LINE__) 24 | 25 | using ScopedGuard = std::unique_ptr>; 26 | #define SCOPED_GUARD_NAMED(name, code) ScopedGuard name(reinterpret_cast(-1), [&](void *) -> void {code}); (void)name 27 | #define SCOPED_GUARD(code) SCOPED_GUARD_NAMED(GENERATE_IDENTIFICATOR(genScopedGuard), code) 28 | 29 | static int netfilterCallback(struct nfq_q_handle *queue, struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *data) 30 | { 31 | nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfad); 32 | THROW_IF_TRUE(ph == nullptr, "Failed to get packet header"); 33 | 34 | unsigned char *rawData = nullptr; 35 | int len = nfq_get_payload(nfad, &rawData); 36 | THROW_IF_TRUE(len < 0, "Can't get payload data"); 37 | 38 | // TCP header size should be aligned in the 32-bit words 39 | // Need to extend packet by new options size and padding bytes 40 | // In our case it is 1 byte 41 | const int extraOptionSize = 3; 42 | pkt_buff * pkBuff = pktb_alloc(AF_INET, rawData, len + extraOptionSize + 1, 0); 43 | THROW_IF_TRUE(pkBuff == nullptr, "Failed to allocate new pft_buff"); 44 | SCOPED_GUARD( pktb_free(pkBuff); ); 45 | 46 | iphdr *ip = nfq_ip_get_hdr(pkBuff); 47 | THROW_IF_TRUE(ip == nullptr, "Failed to get IP header"); 48 | 49 | // Need to update the total length of the IP header with size of new TCP option with padding 50 | // and update IP header checksum 51 | ip->tot_len = htons(ntohs(ip->tot_len) + extraOptionSize + 1); 52 | nfq_ip_set_checksum(ip); 53 | 54 | THROW_IF_TRUE(nfq_ip_set_transport_header(pkBuff, ip) < 0, "Can't set transport header."); 55 | 56 | if (ip->protocol != IPPROTO_TCP) 57 | { 58 | return nfq_set_verdict(queue, ntohl(ph->packet_id), NF_ACCEPT, 0, nullptr); 59 | } 60 | 61 | tcphdr *tcp = nfq_tcp_get_hdr(pkBuff); 62 | THROW_IF_TRUE(tcp == nullptr, "Failed to get TCP header."); 63 | 64 | if (!tcp->syn) 65 | { 66 | return nfq_set_verdict(queue, ntohl(ph->packet_id), NF_ACCEPT, 0, nullptr); 67 | } 68 | 69 | // TCP offset is specified in 32-bit words so need to multiply its value by 4 70 | char* extraOptions = reinterpret_cast(tcp) + tcp->doff * 4; 71 | // TCP options from 79-252 reserved so we can use value from this range 72 | extraOptions[0] = 100; 73 | // Size in bytes of TCP option including Kind and Size fields 74 | extraOptions[1] = 3; 75 | // Set option value 2 for Linux 76 | extraOptions[2] = 2; 77 | // Need to set padding byte to 0 78 | extraOptions[3] = 0; 79 | 80 | // Need to update data offset for TCP header 81 | tcp->doff += 1; 82 | 83 | // Need to update TCP header checksum 84 | nfq_tcp_compute_checksum_ipv4(tcp, ip); 85 | 86 | return nfq_set_verdict(queue, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff)); 87 | } 88 | 89 | int main() 90 | { 91 | try 92 | { 93 | nfq_handle* handler = nfq_open(); 94 | THROW_IF_TRUE(handler == nullptr, "Can't open hfqueue handler."); 95 | SCOPED_GUARD(nfq_close(handler); ); 96 | 97 | nfq_q_handle* queue = nfq_create_queue(handler, 0, netfilterCallback, nullptr); 98 | THROW_IF_TRUE(queue == nullptr, "Can't create queue handler."); 99 | SCOPED_GUARD(nfq_destroy_queue(queue); ); 100 | 101 | THROW_IF_TRUE(nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0, "Can\'t set queue copy mode."); 102 | 103 | std::cout << "Start processing Netlink socket data" << std::endl; 104 | 105 | int fd = nfq_fd(handler); 106 | std::array buffer; 107 | for (;;) 108 | { 109 | int len = read(fd, buffer.data(), buffer.size()); 110 | THROW_IF_TRUE(len < 0, "Issue while read"); 111 | nfq_handle_packet(handler, buffer.data(), len); 112 | } 113 | 114 | return 0; 115 | } 116 | catch (const std::exception& ex) 117 | { 118 | std::cerr << ex.what(); 119 | } 120 | 121 | return -1; 122 | } 123 | -------------------------------------------------------------------------------- /linux/kenel_mode/netfilter_tcp_parser.c: -------------------------------------------------------------------------------- 1 | /***************************************************** 2 | * This code was compiled and tested on Ubuntu 18.04.1 3 | * with kernel version 4.15.0 4 | *****************************************************/ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static uint16_t checksum(uint32_t sum, uint16_t *buf, int size) 17 | { 18 | while (size > 1) { 19 | sum += *buf++; 20 | size -= sizeof(uint16_t); 21 | } 22 | if (size) { 23 | sum += (uint16_t)*(uint8_t *)buf; 24 | } 25 | 26 | sum = (sum >> 16) + (sum & 0xffff); 27 | sum += (sum >>16); 28 | 29 | return (uint16_t)(~sum); 30 | } 31 | 32 | static uint16_t tcp_checksum(struct iphdr *iph, struct tcphdr *tcph) 33 | { 34 | uint32_t sum = 0; 35 | uint32_t len = tcph->doff * 4; 36 | uint8_t *payload = (uint8_t *)tcph; 37 | 38 | sum += (iph->saddr >> 16) & 0xFFFF; 39 | sum += (iph->saddr) & 0xFFFF; 40 | sum += (iph->daddr >> 16) & 0xFFFF; 41 | sum += (iph->daddr) & 0xFFFF; 42 | sum += htons(IPPROTO_TCP); 43 | sum += htons(len); 44 | 45 | return checksum(sum, (uint16_t *)payload, len); 46 | } 47 | 48 | static struct nf_hook_ops *nfho = NULL; 49 | 50 | static unsigned int callback(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) 51 | { 52 | struct iphdr *iph; 53 | struct tcphdr *tcph; 54 | struct sk_buff *new_skb; 55 | char* extra_options; 56 | const int extra_option_size = 4; 57 | const int multiplicity = 4; 58 | const int option_kind = 100; 59 | 60 | if (!skb) 61 | return NF_ACCEPT; 62 | 63 | iph = ip_hdr(skb); 64 | if (!iph || iph->protocol != IPPROTO_TCP) 65 | return NF_ACCEPT; 66 | 67 | tcph = tcp_hdr(skb); 68 | if (!tcph || !tcph->syn) 69 | return NF_ACCEPT; 70 | 71 | if (ntohs(tcph->dest) != 6044) 72 | return NF_ACCEPT; 73 | 74 | printk(KERN_ALERT "\n Got SYN packet for Dest port %d\n", ntohs(tcph->dest)); 75 | 76 | extra_options = (char*)tcph + tcph->doff * multiplicity - extra_option_size; 77 | if (extra_options[0] == option_kind) { 78 | printk(KERN_ALERT "\n Skip already processed packet that has option\n"); 79 | return NF_ACCEPT; 80 | } 81 | 82 | new_skb = skb_copy_expand(skb, skb_headroom(skb), skb->tail + extra_option_size, GFP_ATOMIC); 83 | if (!new_skb) 84 | return NF_ACCEPT; 85 | 86 | iph = ip_hdr(new_skb); 87 | if (!iph) { 88 | kfree_skb(new_skb); 89 | return NF_ACCEPT; 90 | } 91 | 92 | iph->tot_len = htons(ntohs(iph->tot_len) + extra_option_size); 93 | iph->check = 0; 94 | iph->check = checksum(0, (uint16_t *)iph, iph->ihl * multiplicity); 95 | 96 | tcph = tcp_hdr(new_skb); 97 | if (!tcph) { 98 | kfree_skb(new_skb); 99 | return NF_ACCEPT; 100 | } 101 | 102 | // TCP offset is specified in 32-bit words so need to multiply its value by 4 103 | extra_options = (char*)tcph + tcph->doff * multiplicity; 104 | // TCP options from 79-252 reserved so we can use value from this range 105 | extra_options[0] = option_kind; 106 | // Size in bytes of TCP option including Kind and Size fields 107 | extra_options[1] = 3; 108 | // Set option value 2 for Linux 109 | extra_options[2] = 2; 110 | // Need to set padding byte to 0 111 | extra_options[3] = 0; 112 | 113 | // Need to update data offset for TCP header 114 | tcph->doff += 1; 115 | 116 | // Need to update TCP header checksum 117 | tcph->check = 0; 118 | tcph->check = tcp_checksum(iph, tcph); 119 | 120 | skb_put(new_skb, multiplicity); 121 | 122 | if (ip_local_out(state->net, state->sk, new_skb)) { 123 | kfree_skb(new_skb); 124 | return NF_ACCEPT; 125 | } 126 | 127 | printk(KERN_ALERT "\n Added TCP option for SYN packet for Dest port %d\n", ntohs(tcph->dest)); 128 | 129 | return NF_DROP; 130 | } 131 | 132 | static int __init tcp_parser_init(void) 133 | { 134 | nfho = (struct nf_hook_ops*)kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL); 135 | 136 | /* Initialize netfilter hook */ 137 | nfho->hook = (nf_hookfn*)callback; /* hook function */ 138 | nfho->hooknum = NF_INET_LOCAL_OUT; /* sent packets */ 139 | nfho->pf = PF_INET; /* IPv4 */ 140 | nfho->priority = NF_IP_PRI_FIRST; /* max hook priority */ 141 | 142 | return nf_register_net_hook(&init_net, nfho); 143 | } 144 | 145 | static void __exit tcp_parser_exit(void) 146 | { 147 | nf_unregister_net_hook(&init_net, nfho); 148 | kfree(nfho); 149 | } 150 | 151 | MODULE_LICENSE("GPL"); 152 | 153 | module_init(tcp_parser_init); 154 | module_exit(tcp_parser_exit); 155 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | [Ll]ib/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # .NET Core 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | **/Properties/launchSettings.json 51 | 52 | *_i.c 53 | *_p.c 54 | *_i.h 55 | *.ilk 56 | *.meta 57 | *.obj 58 | *.pch 59 | *.pdb 60 | *.pgc 61 | *.pgd 62 | *.rsp 63 | *.sbr 64 | *.tlb 65 | *.tli 66 | *.tlh 67 | *.tmp 68 | *.tmp_proj 69 | *.log 70 | *.vspscc 71 | *.vssscc 72 | .builds 73 | *.pidb 74 | *.svclog 75 | *.scc 76 | 77 | # Chutzpah Test files 78 | _Chutzpah* 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opendb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | *.VC.db 89 | *.VC.VC.opendb 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | *.sap 96 | 97 | # TFS 2012 Local Workspace 98 | $tf/ 99 | 100 | # Guidance Automation Toolkit 101 | *.gpState 102 | 103 | # ReSharper is a .NET coding add-in 104 | _ReSharper*/ 105 | *.[Rr]e[Ss]harper 106 | *.DotSettings.user 107 | 108 | # JustCode is a .NET coding add-in 109 | .JustCode 110 | 111 | # TeamCity is a build add-in 112 | _TeamCity* 113 | 114 | # DotCover is a Code Coverage Tool 115 | *.dotCover 116 | 117 | # Visual Studio code coverage results 118 | *.coverage 119 | *.coveragexml 120 | 121 | # NCrunch 122 | _NCrunch_* 123 | .*crunch*.local.xml 124 | nCrunchTemp_* 125 | 126 | # MightyMoose 127 | *.mm.* 128 | AutoTest.Net/ 129 | 130 | # Web workbench (sass) 131 | .sass-cache/ 132 | 133 | # Installshield output folder 134 | [Ee]xpress/ 135 | 136 | # DocProject is a documentation generator add-in 137 | DocProject/buildhelp/ 138 | DocProject/Help/*.HxT 139 | DocProject/Help/*.HxC 140 | DocProject/Help/*.hhc 141 | DocProject/Help/*.hhk 142 | DocProject/Help/*.hhp 143 | DocProject/Help/Html2 144 | DocProject/Help/html 145 | 146 | # Click-Once directory 147 | publish/ 148 | 149 | # Publish Web Output 150 | *.[Pp]ublish.xml 151 | *.azurePubxml 152 | # TODO: Comment the next line if you want to checkin your web deploy settings 153 | # but database connection strings (with potential passwords) will be unencrypted 154 | *.pubxml 155 | *.publishproj 156 | 157 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 158 | # checkin your Azure Web App publish settings, but sensitive information contained 159 | # in these scripts will be unencrypted 160 | PublishScripts/ 161 | 162 | # NuGet Packages 163 | *.nupkg 164 | # The packages folder can be ignored because of Package Restore 165 | **/packages/* 166 | # except build/, which is used as an MSBuild target. 167 | !**/packages/build/ 168 | # Uncomment if necessary however generally it will be regenerated when needed 169 | #!**/packages/repositories.config 170 | # NuGet v3's project.json files produces more ignorable files 171 | *.nuget.props 172 | *.nuget.targets 173 | 174 | # Microsoft Azure Build Output 175 | csx/ 176 | *.build.csdef 177 | 178 | # Microsoft Azure Emulator 179 | ecf/ 180 | rcf/ 181 | 182 | # Windows Store app package directories and files 183 | AppPackages/ 184 | BundleArtifacts/ 185 | Package.StoreAssociation.xml 186 | _pkginfo.txt 187 | *.appx 188 | 189 | # Visual Studio cache files 190 | # files ending in .cache can be ignored 191 | *.[Cc]ache 192 | # but keep track of directories ending in .cache 193 | !*.[Cc]ache/ 194 | 195 | # Others 196 | ClientBin/ 197 | ~$* 198 | *~ 199 | *.dbmdl 200 | *.dbproj.schemaview 201 | *.jfm 202 | *.pfx 203 | *.publishsettings 204 | orleans.codegen.cs 205 | 206 | # Since there are multiple workflows, uncomment next line to ignore bower_components 207 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 208 | #bower_components/ 209 | 210 | # RIA/Silverlight projects 211 | Generated_Code/ 212 | 213 | # Backup & report files from converting an old project file 214 | # to a newer Visual Studio version. Backup files are not needed, 215 | # because we have git ;-) 216 | _UpgradeReport_Files/ 217 | Backup*/ 218 | UpgradeLog*.XML 219 | UpgradeLog*.htm 220 | 221 | # SQL Server files 222 | *.mdf 223 | *.ldf 224 | *.ndf 225 | 226 | # Business Intelligence projects 227 | *.rdl.data 228 | *.bim.layout 229 | *.bim_*.settings 230 | 231 | # Microsoft Fakes 232 | FakesAssemblies/ 233 | 234 | # GhostDoc plugin setting file 235 | *.GhostDoc.xml 236 | 237 | # Node.js Tools for Visual Studio 238 | .ntvs_analysis.dat 239 | node_modules/ 240 | 241 | # Typescript v1 declaration files 242 | typings/ 243 | 244 | # Visual Studio 6 build log 245 | *.plg 246 | 247 | # Visual Studio 6 workspace options file 248 | *.opt 249 | 250 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 251 | *.vbw 252 | 253 | # Visual Studio LightSwitch build output 254 | **/*.HTMLClient/GeneratedArtifacts 255 | **/*.DesktopClient/GeneratedArtifacts 256 | **/*.DesktopClient/ModelManifest.xml 257 | **/*.Server/GeneratedArtifacts 258 | **/*.Server/ModelManifest.xml 259 | _Pvt_Extensions 260 | 261 | # Paket dependency manager 262 | .paket/paket.exe 263 | paket-files/ 264 | 265 | # FAKE - F# Make 266 | .fake/ 267 | 268 | # JetBrains Rider 269 | .idea/ 270 | *.sln.iml 271 | 272 | # CodeRush 273 | .cr/ 274 | 275 | # Python Tools for Visual Studio (PTVS) 276 | __pycache__/ 277 | *.pyc 278 | 279 | # Cake - Uncomment if you are using it 280 | # tools/** 281 | # !tools/packages.config 282 | 283 | # Telerik's JustMock configuration file 284 | *.jmconfig 285 | 286 | # BizTalk build output 287 | *.btp.cs 288 | *.btm.cs 289 | *.odx.cs 290 | *.xsd.cs 291 | -------------------------------------------------------------------------------- /windows/src/TcpInterception/TcpInterception.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FAAF4891-2DDC-406E-8AB0-985886405D80} 23 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 24 | v4.5 25 | 12.0 26 | Debug 27 | Win32 28 | TcpInterception 29 | TcpInterception 30 | $(LatestTargetPlatformVersion) 31 | 32 | 33 | 34 | Windows10 35 | true 36 | WindowsKernelModeDriver10.0 37 | Driver 38 | WDM 39 | Desktop 40 | 41 | 42 | Windows10 43 | false 44 | WindowsKernelModeDriver10.0 45 | Driver 46 | WDM 47 | Desktop 48 | 49 | 50 | Windows10 51 | true 52 | WindowsKernelModeDriver10.0 53 | Driver 54 | WDM 55 | Desktop 56 | 57 | 58 | Windows10 59 | false 60 | WindowsKernelModeDriver10.0 61 | Driver 62 | WDM 63 | Desktop 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | DbgengKernelDebugger 75 | $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ 76 | $(SolutionDir)..\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 77 | 78 | 79 | DbgengKernelDebugger 80 | $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ 81 | $(SolutionDir)..\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 82 | 83 | 84 | DbgengKernelDebugger 85 | $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ 86 | $(SolutionDir)..\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 87 | true 88 | 89 | 90 | DbgengKernelDebugger 91 | $(SolutionDir)..\bin\$(Platform)\$(Configuration)\ 92 | $(SolutionDir)..\obj\$(Platform)\$(Configuration)\$(ProjectName)\ 93 | 94 | 95 | 96 | Use 97 | 98 | 99 | 100 | 101 | pch.h 102 | NDIS_SUPPORT_NDIS61=1;%(PreprocessorDefinitions) 103 | 104 | 105 | fwpkclnt.lib;Ndis.lib;%(AdditionalDependencies) 106 | 107 | 108 | 109 | 110 | Use 111 | 112 | 113 | 114 | 115 | pch.h 116 | NDIS_SUPPORT_NDIS61=1;%(PreprocessorDefinitions) 117 | 118 | 119 | fwpkclnt.lib;Ndis.lib;%(AdditionalDependencies) 120 | 121 | 122 | 123 | 124 | Use 125 | 126 | 127 | 128 | 129 | pch.h 130 | NDIS_SUPPORT_NDIS61=1;%(PreprocessorDefinitions) 131 | 132 | 133 | fwpkclnt.lib;Ndis.lib;%(AdditionalDependencies) 134 | 135 | 136 | 137 | 138 | Use 139 | 140 | 141 | 142 | 143 | pch.h 144 | NDIS_SUPPORT_NDIS61=1;%(PreprocessorDefinitions) 145 | 146 | 147 | fwpkclnt.lib;Ndis.lib;%(AdditionalDependencies) 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | Create 161 | pch.h 162 | Create 163 | pch.h 164 | Create 165 | pch.h 166 | Create 167 | pch.h 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /windows/src/TcpInterception/NetFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "NetFilter.h" 3 | 4 | #ifndef FlagOn 5 | #define FlagOn(_F,_SF) ((_F) & (_SF)) 6 | #endif 7 | 8 | #ifndef ClearFlag 9 | #define ClearFlag(_F,_SF) ((_F) &= ~(_SF)) 10 | #endif 11 | 12 | #ifndef SetFlag 13 | #define SetFlag(_F,_SF) ((_F) |= (_SF)) 14 | #endif 15 | 16 | #define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) 17 | 18 | // {B8F58E09-BA43-4837-9723-AD80258E8C0A} 19 | DEFINE_GUID(TCP_INTERCEPTION_SUBLAYER, 0xb8f58e09, 0xba43, 0x4837, 0x97, 0x23, 0xad, 0x80, 0x25, 0x8e, 0x8c, 0xa); 20 | 21 | // {47896A45-35FB-4EFF-92D5-5BC8A91343C3} 22 | DEFINE_GUID(TCP_INTERCEPTION_OUTBOUND_TRANSPORT_V4_CALLOUT, 0x47896a45, 0x35fb, 0x4eff, 0x92, 0xd5, 0x5b, 0xc8, 0xa9, 0x13, 0x43, 0xc3); 23 | 24 | #define NET_BUFFER_LIST_STORAGE_TAG 'gTlB' 25 | #define NET_BUFFER_LIST_PTR_TAG 'gTtP' 26 | #define SEND_PARAMETERS_TAG 'gTpS' 27 | 28 | const wchar_t g_displayName[] = L"TCP Interception Filter"; 29 | 30 | #pragma pack(push, 1) 31 | struct TcpHeader 32 | { 33 | UINT16 source; 34 | UINT16 dest; 35 | UINT32 seq; 36 | UINT32 ackSeq; 37 | 38 | UINT8 ns : 1; 39 | UINT8 reserved : 3; 40 | UINT8 offset : 4; 41 | 42 | UINT8 fin : 1; 43 | UINT8 syn : 1; 44 | UINT8 rst : 1; 45 | UINT8 psh : 1; 46 | UINT8 ack : 1; 47 | UINT8 urg : 1; 48 | UINT8 ece : 1; 49 | UINT8 cwr : 1; 50 | 51 | UINT16 window; 52 | UINT16 check; 53 | UINT16 urgPointer; 54 | }; 55 | #pragma pack(pop) 56 | 57 | struct SendParameters 58 | { 59 | IN_ADDR addr; 60 | FWPS_TRANSPORT_SEND_PARAMS params; 61 | }; 62 | 63 | struct NetworkBufferListStorage 64 | { 65 | NDIS_HANDLE handle; 66 | PVOID ptr; 67 | PMDL mdl; 68 | PNET_BUFFER_LIST list; 69 | SendParameters* params; 70 | }; 71 | 72 | static HANDLE g_engineHandle = nullptr; 73 | static NDIS_HANDLE g_netBufferListHandle = nullptr; 74 | static HANDLE g_injectionHandle = nullptr; 75 | static UINT32 g_id = 0; 76 | 77 | static ULONG GetTcpHeaderSize(const TcpHeader& tcpHeader) 78 | { 79 | // TCP offset is specified in 32-bit words so need to multiply its value by 4 80 | return tcpHeader.offset * 4; 81 | } 82 | 83 | static void FreeNetworkBufferListStorage(NetworkBufferListStorage* storage) 84 | { 85 | if (!storage) 86 | { 87 | return; 88 | } 89 | 90 | if (storage->ptr) 91 | { 92 | ExFreePoolWithTag(storage->ptr, NET_BUFFER_LIST_PTR_TAG); 93 | } 94 | 95 | if (storage->mdl) 96 | { 97 | IoFreeMdl(storage->mdl); 98 | } 99 | 100 | if (storage->list) 101 | { 102 | FwpsFreeNetBufferList(storage->list); 103 | } 104 | 105 | if (storage->params) 106 | { 107 | ExFreePoolWithTag(storage->params, SEND_PARAMETERS_TAG); 108 | } 109 | 110 | ExFreePoolWithTag(storage, NET_BUFFER_LIST_STORAGE_TAG); 111 | } 112 | 113 | static void CompleteCallback(PVOID context, 114 | PNET_BUFFER_LIST /*netBufferList*/, 115 | BOOLEAN /*dispatchLevel*/ 116 | ) 117 | { 118 | FreeNetworkBufferListStorage(reinterpret_cast(context)); 119 | } 120 | 121 | static NTSTATUS InitializeNetworkBufferListStorage(NetworkBufferListStorage& storage, 122 | SendParameters* params, 123 | ULONG size) 124 | { 125 | storage.ptr = ExAllocatePoolWithTag(NonPagedPoolNx, 126 | size, 127 | NET_BUFFER_LIST_PTR_TAG); 128 | if (!storage.ptr) 129 | { 130 | return STATUS_INSUFFICIENT_RESOURCES; 131 | } 132 | 133 | storage.mdl = IoAllocateMdl(storage.ptr, size, false, false, nullptr); 134 | if (!storage.mdl) 135 | { 136 | return STATUS_INSUFFICIENT_RESOURCES; 137 | } 138 | 139 | MmBuildMdlForNonPagedPool(storage.mdl); 140 | 141 | NTSTATUS status = FwpsAllocateNetBufferAndNetBufferList(g_netBufferListHandle, 142 | 0, 143 | 0, 144 | storage.mdl, 145 | 0, 146 | size, 147 | &storage.list); 148 | if (NT_SUCCESS(status)) 149 | { 150 | storage.params = params; 151 | } 152 | 153 | return status; 154 | } 155 | 156 | static NetworkBufferListStorage* CreateNetworkBufferListStorage(ULONG size, SendParameters* params) 157 | { 158 | auto netBufferStorage = reinterpret_cast(ExAllocatePoolWithTag( 159 | NonPagedPoolNx, 160 | sizeof(NetworkBufferListStorage), 161 | NET_BUFFER_LIST_STORAGE_TAG)); 162 | if (!netBufferStorage) 163 | { 164 | return nullptr; 165 | } 166 | 167 | NTSTATUS status = InitializeNetworkBufferListStorage(*netBufferStorage, params, size); 168 | if (!NT_SUCCESS(status)) 169 | { 170 | FreeNetworkBufferListStorage(netBufferStorage); 171 | netBufferStorage = nullptr; 172 | } 173 | 174 | return netBufferStorage; 175 | } 176 | 177 | static SendParameters* CreateSendParameters(const SCOPE_ID& scopeId, UINT32 address) 178 | { 179 | auto params = reinterpret_cast(ExAllocatePoolWithTag(NonPagedPoolNx, 180 | sizeof(SendParameters), 181 | SEND_PARAMETERS_TAG)); 182 | 183 | if (!params) 184 | { 185 | return nullptr; 186 | } 187 | 188 | RtlZeroMemory(params, sizeof(SendParameters)); 189 | 190 | params->params.remoteAddress = reinterpret_cast(¶ms->addr); 191 | params->params.remoteScopeId = scopeId; 192 | params->addr.S_un.S_addr = _byteswap_ulong(address); 193 | 194 | return params; 195 | } 196 | 197 | static NTSTATUS InsertUpdatedTcpPacket(NetworkBufferListStorage& storage, 198 | const TcpHeader& origTcpHeader, 199 | ULONG newTcpHeaderSize, 200 | UINT64 endpointHandle, 201 | COMPARTMENT_ID compId) 202 | { 203 | auto newTcpHeader = reinterpret_cast(NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(storage.list), 204 | newTcpHeaderSize, 205 | nullptr, 206 | 1, 207 | 0)); 208 | if (!newTcpHeader) 209 | { 210 | return STATUS_UNSUCCESSFUL; 211 | } 212 | 213 | const auto origTcpHeaderSize = GetTcpHeaderSize(origTcpHeader); 214 | RtlCopyMemory(newTcpHeader, &origTcpHeader, origTcpHeaderSize); 215 | 216 | // TCP offset is specified in 32-bit words so need to multiply its value by 4 217 | char* extraOptions = reinterpret_cast(Add2Ptr(newTcpHeader, origTcpHeaderSize)); 218 | // TCP options from 79-252 reserved so we can use value from this range 219 | extraOptions[0] = 100; 220 | // Size in bytes of TCP option including Kind and Size fields 221 | extraOptions[1] = 3; 222 | // Set option value 1 for Windows 223 | extraOptions[2] = 1; 224 | // Need to set padding byte to 0 225 | extraOptions[3] = 0; 226 | 227 | // DataSize specifies the size of the TCP header in 32 - bit words 228 | newTcpHeader->offset = newTcpHeaderSize / 4; 229 | newTcpHeader->check = 0; 230 | 231 | return FwpsInjectTransportSendAsync(g_injectionHandle, 232 | nullptr, 233 | endpointHandle, 234 | 0, 235 | &storage.params->params, 236 | AF_INET, 237 | compId, 238 | storage.list, 239 | CompleteCallback, 240 | &storage); 241 | } 242 | 243 | static void ProcessTransportData(const FWPS_INCOMING_VALUES0* inFixedValues, 244 | const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, 245 | void* layerData, 246 | FWPS_CLASSIFY_OUT0* classifyOut) 247 | { 248 | // Skip packet not connected to our target port 249 | if (inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16 != 6044) 250 | { 251 | return; 252 | } 253 | 254 | const auto injectionState = FwpsQueryPacketInjectionState0(g_injectionHandle, 255 | static_cast(layerData), 256 | nullptr); 257 | 258 | // Skip packets injected by our driver 259 | if (FWPS_PACKET_INJECTED_BY_SELF == injectionState || 260 | FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF == injectionState) 261 | { 262 | return; 263 | } 264 | 265 | NET_BUFFER_LIST* netBufferList = static_cast(layerData); 266 | // TCP header always stored in the first NET_BUFFER_LIST 267 | ASSERT(NET_BUFFER_LIST_NEXT_NBL(netBufferList) == nullptr); 268 | 269 | // For the SYN packet NET_BUFFER_LIST will contain only one net buffer 270 | NET_BUFFER* netBuffer = NET_BUFFER_LIST_FIRST_NB(netBufferList); 271 | 272 | // TCP header size without options equals 20 bytes 273 | static const ULONG tcpHeaderSize = 20; 274 | const auto origTcpHeader = reinterpret_cast(NdisGetDataBuffer(netBuffer, tcpHeaderSize, nullptr, 1, 0)); 275 | // Skip packets that doesn't have SYN flag set 276 | if (!origTcpHeader || !origTcpHeader->syn) 277 | { 278 | return; 279 | } 280 | 281 | // Need to block and absorb current packet to inject new packet with options instead of it 282 | classifyOut->actionType = FWP_ACTION_BLOCK; 283 | SetFlag(classifyOut->flags, FWPS_CLASSIFY_OUT_FLAG_ABSORB); 284 | 285 | const ULONG originalTcpHeaderSize = GetTcpHeaderSize(*origTcpHeader); 286 | 287 | // Get the size of the TCP header with options 288 | const auto origTcpHeaderWithOptions = reinterpret_cast(NdisGetDataBuffer(netBuffer, originalTcpHeaderSize, nullptr, 1, 0)); 289 | if (!origTcpHeaderWithOptions) 290 | { 291 | DbgPrint("Failed to get TCP header with options\n"); 292 | return; 293 | } 294 | 295 | auto sendParams = CreateSendParameters(inMetaValues->remoteScopeId, 296 | inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32); 297 | if (!sendParams) 298 | { 299 | return; 300 | } 301 | 302 | // TCP header size should be aligned in the 32-bit words 303 | // Need to extend packet by new options size and padding bytes 304 | // In our case it is 1 byte 305 | static const int extraOptionSize = 3; 306 | const ULONG newTcpHeaderSize = extraOptionSize + originalTcpHeaderSize + 1; 307 | 308 | // Need to allocate new NET_BUFFER_LIST for new packet with new TCP header 309 | auto netBufferListStorage = CreateNetworkBufferListStorage(newTcpHeaderSize, sendParams); 310 | if (!netBufferListStorage) 311 | { 312 | ExFreePoolWithTag(sendParams, SEND_PARAMETERS_TAG); 313 | return; 314 | } 315 | sendParams = nullptr; 316 | 317 | NTSTATUS status = InsertUpdatedTcpPacket(*netBufferListStorage, 318 | *origTcpHeader, 319 | newTcpHeaderSize, 320 | inMetaValues->transportEndpointHandle, 321 | static_cast(inMetaValues->compartmentId)); 322 | if (!NT_SUCCESS(status)) 323 | { 324 | FreeNetworkBufferListStorage(netBufferListStorage); 325 | netBufferListStorage = nullptr; 326 | } 327 | } 328 | 329 | static void CalloutConnectClassifyFn( 330 | const FWPS_INCOMING_VALUES0* inFixedValues, 331 | const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, 332 | void* layerData, 333 | const FWPS_FILTER0* filter, 334 | UINT64 /*flowContext*/, 335 | FWPS_CLASSIFY_OUT0* classifyOut) 336 | { 337 | // Allowing the traffic for another filter to make a final decision. 338 | if (FlagOn(classifyOut->rights, FWPS_RIGHT_ACTION_WRITE)) 339 | { 340 | classifyOut->actionType = FWP_ACTION_CONTINUE; 341 | } 342 | 343 | if (layerData) 344 | { 345 | ProcessTransportData(inFixedValues, inMetaValues, layerData, classifyOut); 346 | } 347 | 348 | // Callout function should clear the FWPS_RIGHT_ACTION_WRITE flag when it returns FWP_ACTION_BLOCK for the suggested action 349 | // and if FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT flag is set 350 | if (FWP_ACTION_BLOCK == classifyOut->actionType || FlagOn(filter->flags, FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)) 351 | { 352 | ClearFlag(classifyOut->rights, FWPS_RIGHT_ACTION_WRITE); 353 | } 354 | } 355 | 356 | static NTSTATUS CalloutNotifyFn( 357 | FWPS_CALLOUT_NOTIFY_TYPE /*notifyType*/, 358 | const GUID* /*filterKey*/, 359 | FWPS_FILTER0* /*filter*/) 360 | { 361 | return STATUS_SUCCESS; 362 | } 363 | 364 | static NTSTATUS InitializeCallout(PDEVICE_OBJECT deviceObject) 365 | { 366 | FWPM_SUBLAYER subLayer = {}; 367 | subLayer.displayData.name = const_cast(L"TcpInterception Sub-Layer"); 368 | subLayer.subLayerKey = TCP_INTERCEPTION_SUBLAYER; 369 | 370 | NTSTATUS status = FwpmSubLayerAdd(g_engineHandle, &subLayer, nullptr); 371 | if (!NT_SUCCESS(status)) 372 | { 373 | return status; 374 | } 375 | 376 | FWPS_CALLOUT0 sCallout = 377 | { 378 | TCP_INTERCEPTION_OUTBOUND_TRANSPORT_V4_CALLOUT, 379 | 0, 380 | CalloutConnectClassifyFn, 381 | CalloutNotifyFn, 382 | nullptr 383 | }; 384 | 385 | status = FwpsCalloutRegister0(deviceObject, &sCallout, &g_id); 386 | if (!NT_SUCCESS(status)) 387 | { 388 | return status; 389 | } 390 | 391 | FWPM_CALLOUT mCallout = {}; 392 | mCallout.calloutKey = TCP_INTERCEPTION_OUTBOUND_TRANSPORT_V4_CALLOUT; 393 | mCallout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4; 394 | mCallout.displayData.name = const_cast(g_displayName); 395 | 396 | status = FwpmCalloutAdd(g_engineHandle, &mCallout, nullptr, nullptr); 397 | if (!NT_SUCCESS(status)) 398 | { 399 | return status; 400 | } 401 | 402 | // Add Filter for TCP only 403 | FWPM_FILTER_CONDITION filterCondition = {}; 404 | filterCondition.fieldKey = FWPM_CONDITION_IP_PROTOCOL; 405 | filterCondition.matchType = FWP_MATCH_EQUAL; 406 | filterCondition.conditionValue.type = FWP_UINT8; 407 | filterCondition.conditionValue.uint16 = IPPROTO_TCP; 408 | 409 | FWPM_FILTER filter = {}; 410 | filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4; 411 | filter.displayData.name = const_cast(g_displayName);; 412 | filter.displayData.description = filter.displayData.name; 413 | 414 | filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; 415 | filter.action.calloutKey = TCP_INTERCEPTION_OUTBOUND_TRANSPORT_V4_CALLOUT; 416 | filter.filterCondition = &filterCondition; 417 | filter.numFilterConditions = 1; 418 | filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; 419 | filter.weight.type = FWP_EMPTY; 420 | 421 | status = FwpmFilterAdd(g_engineHandle, &filter, NULL, NULL); 422 | if (!NT_SUCCESS(status)) 423 | { 424 | return status; 425 | } 426 | 427 | status = FwpsInjectionHandleCreate(AF_UNSPEC, 0, &g_injectionHandle); 428 | if (!NT_SUCCESS(status)) 429 | { 430 | return status; 431 | } 432 | 433 | return status; 434 | } 435 | 436 | NTSTATUS InitializeFilter(PDEVICE_OBJECT deviceObject) 437 | { 438 | FWPM_SESSION session = {}; 439 | session.flags = FWPM_SESSION_FLAG_DYNAMIC; 440 | 441 | NTSTATUS status = FwpmEngineOpen(nullptr, RPC_C_AUTHN_WINNT, nullptr, &session, &g_engineHandle); 442 | if (!NT_SUCCESS(status)) 443 | { 444 | return status; 445 | } 446 | 447 | // Need to allocate NET_BUFFER_LIST pool to create NET_BUFFER_LISTs for new packets 448 | NET_BUFFER_LIST_POOL_PARAMETERS netBufferListParameters = {}; 449 | netBufferListParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; 450 | netBufferListParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; 451 | netBufferListParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; 452 | netBufferListParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; 453 | netBufferListParameters.PoolTag = 'IpcT'; 454 | // NdisAllocateNetBufferAndNetBufferList can be called only if fAllocateNetBuffer is TRUE and DataSize is zero. 455 | netBufferListParameters.DataSize = 0; 456 | netBufferListParameters.fAllocateNetBuffer = TRUE; 457 | 458 | g_netBufferListHandle = NdisAllocateNetBufferListPool(nullptr, &netBufferListParameters); 459 | if (!g_netBufferListHandle) 460 | { 461 | return STATUS_INSUFFICIENT_RESOURCES; 462 | } 463 | 464 | status = FwpmTransactionBegin(g_engineHandle, 0); 465 | if (!NT_SUCCESS(status)) 466 | { 467 | return status; 468 | } 469 | 470 | status = InitializeCallout(deviceObject); 471 | if (NT_SUCCESS(status)) 472 | { 473 | FwpmTransactionCommit(g_engineHandle); 474 | } 475 | else 476 | { 477 | FwpmTransactionAbort(g_engineHandle); 478 | } 479 | 480 | return status; 481 | } 482 | 483 | void DeinitializeFilter() 484 | { 485 | if (g_id) 486 | { 487 | FwpsCalloutUnregisterById(g_id); 488 | } 489 | 490 | if (g_injectionHandle) 491 | { 492 | FwpsInjectionHandleDestroy(g_injectionHandle); 493 | } 494 | 495 | if (g_netBufferListHandle) 496 | { 497 | NdisFreeNetBufferListPool(g_netBufferListHandle); 498 | } 499 | 500 | if (g_engineHandle) 501 | { 502 | FwpmEngineClose(g_engineHandle); 503 | g_engineHandle = nullptr; 504 | } 505 | } --------------------------------------------------------------------------------