WPF开发-防火墙入站规则设置

前言

网络位置类型

区别:防火墙的域、专用、公用的网络位置类型不同。

  • 域:Windows可以验证对计算机所联接域的域控制器的访问。

  • 公共:除域网络之外,其他所有网络最初都归为公共网络一类。直接连到Internet的网络或者位于公共场所(如机场和咖啡店)的网络应保留为公共网络。

  • 专用:由用户或应用程序标识为专用的网络。只应将可信网络标识为专用网络。用户很可能希望将家庭网络或小型企业网络标识为专用网络。

操作系统使用防火墙配置文件按照连接性、连接数和类别来识别并记住与它们连接的每个网络。

在高级安全Windows防火墙中有三种网络位置类型:域、公共、专用。

出入站规则

Windows 系统默认的规则
默认阻止入站连接,默认允许出站连接。也就是说,凡是入站连接,任何程序和端口都要在防火墙上配置入站规则,否则都会被禁止。

入站规则
入站规则是用来限制远程主机访问本机的服务的,本机接收的请求中如果被请求的程序或具体端口是被限制的,则该请求会被拦截。

出站规则
出站规则是用来限制对外访问的,从本机发出的请求中,如果请求的对象是被禁止的,该请求会被拦截,表现方式就是断网。

两种方式

  • 端口方式
  • 程序方式(推荐)

注意

端口方式,开发过程和最终安装时都是一样的规则。推荐使用。

入站规则不生效

HttpListener启动的用户是由操作系统决定的。

如果您在Windows上使用HttpListener,则默认情况下,它将使用系统用户启动。

但是:

系统用户启动的服务,设置的防火墙入站规则程序方式不生效。

只能使用端口方式

推荐方式

运行Bat方式添加端口规则

查看端口信息

1
2
3
netstat -ano

netstat -ano | findstr 10088

查看入站规则

键盘点击Ctrl+R打开运行,输入Firewall.cpl回车打开防火墙页面。

点击高级设置=>入站规则

里面就可以看到添加的配置,就代表成功了。

代码添加

添加COM引用

引用=>添加引用=>选择COM 页=> 找到NetFwTypeLib, 确定即可

添加命名空间

1
using NetFwTypeLib;

程序方式添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;

using NetFwTypeLib;

namespace ZUtils
{
public class ZNetFwManger
{
/// <summary>
/// 删除防火墙例外中应用程序
/// </summary>
/// <param name="ruleName"></param>
private static void NetFwDelApps(string ruleName)
{
//根据规则名称移除规则
INetFwPolicy2 policy2 =
(INetFwPolicy2)Activator
.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
try
{
//根据规则名称移除规则
policy2.Rules.Remove(ruleName);
}
catch (Exception)
{
// ignored
}
}

/// <summary>
/// 添加入站规则
/// </summary>
/// <param name="name"></param>
/// <param name="executablePath"></param>
public static void NetFwAllowApps(string name, string executablePath)
{
try
{
//先移除已存在的
NetFwDelApps(name);
INetFwRule rule = (INetFwRule)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FWRule"));
rule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
rule.Description = "Allow all for " + name;
rule.ApplicationName = executablePath;
rule.Enabled = true;
rule.InterfaceTypes = "All";
rule.Name = name;
INetFwPolicy2 policy2 =
(INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
policy2.Rules.Add(rule);
}
catch (Exception)
{
// ignored
}
}
}
}

调用

1
2
3
4
5
var processModule = Process.GetCurrentProcess().MainModule;
if (processModule != null)
{
ZNetFwManger.NetFwAllowApps("Z_星火云鸽智慧课堂", processModule.FileName);
}

名称不要用@开头,会导致规则无法删除。

端口方式添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using NetFwTypeLib;
using System;

namespace ZUtils
{
public class ZFirewallManager
{
private static string GetName(int port, string protocol)
{
return "z-allow-port-" + protocol + "-" + port;
}

public static void AllowPort(int port, string protocol)
{
DelPort(port, protocol);
//创建一个INetFwRule对象
Type type = Type.GetTypeFromProgID("HNetCfg.FwRule");
INetFwRule rule = (INetFwRule)Activator.CreateInstance(type);

//设置规则的属性
rule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW; //允许连接
rule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN; //入站规则
rule.Enabled = true; //启用规则
rule.InterfaceTypes = "All"; //适用于所有网络接口
rule.Name = GetName(port, protocol); //规则名称
if (protocol.ToLower() == "tcp")
{
rule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; //TCP协议
}
else
{
rule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP; //UDP协议
}

rule.LocalPorts = "" + port; //本地端口号

//获取FirewallPolicy对象
Type policyType = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
INetFwPolicy2 policy = (INetFwPolicy2)Activator.CreateInstance(policyType);

//将规则添加到防火墙策略中
policy.Rules.Add(rule);
}

public static void DelPort(int port, string protocol)
{
//获取FirewallPolicy对象
Type policyType = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
INetFwPolicy2 policy = (INetFwPolicy2)Activator.CreateInstance(policyType);

//获取现有的规则集合
INetFwRules rules = policy.Rules;

//查找名称的规则并删除它
foreach (INetFwRule rule in rules)
{
if (rule.Name == GetName(port, protocol))
{
rules.Remove(rule.Name);
Console.WriteLine(@"Firewall rule deleted successfully.");
break;
}
}
}
}
}

调用

1
2
3
4
// 添加防火墙入站规则
ZFirewallManager.AllowPort(10077, "tcp");
ZFirewallManager.AllowPort(10088, "tcp");
ZFirewallManager.AllowPort(55666, "udp");

命令行添加(推荐)

添加程序规则

添加规则

1
netsh advfirewall firewall add rule name="MyApp" dir=in action=allow program="C:\MyApp.exe"

删除规则

1
netsh advfirewall firewall delete rule name="MyApp" program="C:\MyApp.exe"

使用批处理将当前目录下的*.exe都加入到白名单中:

addfirewall.bat

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off

set INPUT_RULE_NAME=Z-XHSCHOOL-IN
for %%a in (*.exe) do (
netsh advfirewall firewall show rule name=%INPUT_RULE_NAME% >nul
if not ERRORLEVEL 1 (
echo del %INPUT_RULE_NAME%
netsh advfirewall firewall delete rule name=%INPUT_RULE_NAME% program="%cd%\%%a" >null
)
echo add %INPUT_RULE_NAME%
netsh advfirewall firewall add rule name=%INPUT_RULE_NAME% dir=in action=allow program="%cd%\%%a" >null
)
pause

根据系统版本添加防火墙规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@echo off
cd "%CD%"
for /f "tokens=4,5 delims=. " %%a in ('ver') do if %%a%%b geq 60 goto new

:old
cmd /c netsh firewall delete allowedprogram program="%CD%\ZClient.exe" profile=ALL
cmd /c netsh firewall add allowedprogram program="%CD%\ZClient.exe" name="ZClient" ENABLE
cmd /c netsh firewall add allowedprogram program="%CD%\ZClient.exe" name="ZClient" ENABLE profile=ALL
goto end
:new
cmd /c netsh advfirewall firewall delete rule name="ZClient"
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=public
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=public
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=public
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=public
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=domain
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=domain
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=domain
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=domain
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=private
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=in action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=private
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=tcp enable=yes profile=private
cmd /c netsh advfirewall firewall add rule name="ZClient" dir=out action=allow program="%CD%\ZClient.exe" protocol=udp enable=yes profile=private
:end

添加端口规则

注意

添加这个文件的时候文件编码不能是UTF-8,会导致命令执行失败,要设置为ANSI编码。

addfirewall.bat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@echo off

rem 设置指定端口变量和出入站规则名称
set INPUT_RULE_NAME=Z-XHSCHOOL-IN
set OUT_RULE_NAME=Z-XHSCHOOL-OUT
set PORT=10066,10077,10088,55666

rem 创建入站规则
echo Input Rule
netsh advfirewall firewall show rule name=%INPUT_RULE_NAME% >nul
rem 如果已经存在则先删除
if not ERRORLEVEL 1 (
netsh advfirewall firewall delete rule name=%INPUT_RULE_NAME% >nul
echo %INPUT_RULE_NAME% Del Successed!
)
netsh advfirewall firewall add rule name=%INPUT_RULE_NAME% dir=in action=allow protocol=TCP localport=%PORT% >nul
netsh advfirewall firewall add rule name=%INPUT_RULE_NAME% dir=in action=allow protocol=UDP localport=%PORT% >nul
echo %INPUT_RULE_NAME% Create Successed!

rem 创建出站规则
echo --------------------------------
echo Output Rule
netsh advfirewall firewall show rule name=%OUT_RULE_NAME% >nul
rem 如果已经存在则先删除
if not ERRORLEVEL 1 (
netsh advfirewall firewall delete rule name=%OUT_RULE_NAME% >nul
echo %OUT_RULE_NAME% Del Successed!
)
netsh advfirewall firewall add rule name=%OUT_RULE_NAME% dir=out action=allow protocol=TCP localport=%PORT% >nul
netsh advfirewall firewall add rule name=%OUT_RULE_NAME% dir=out action=allow protocol=UDP localport=%PORT% >nul
echo %OUT_RULE_NAME% Create Successed!

运行Bat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void AddFirewallRule()
{
try
{
// 添加防火墙入站规则
Process process = new Process();
string appPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
process.StartInfo.FileName = $@"{appPath}\addfirewall.bat";
process.StartInfo.UseShellExecute = false; //运行时隐藏dos窗口
process.StartInfo.CreateNoWindow = true; //运行时隐藏dos窗口
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.Verb = "runas"; //设置该启动动作,会以管理员权限运行进程
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// 打印结果
Console.WriteLine(@"================================");
Console.WriteLine(@"添加防火墙规则:");
Console.WriteLine(output);
Console.ReadLine();
Console.WriteLine(@"================================");
}
catch (Exception)
{
// ignored
}
}

系统版本判断

系统版本判断

1
2
3
4
5
6
7
8
9
10
@echo off
cd "%CD%"
for /f "tokens=4,5 delims=. " %%a in ('ver') do if %%a%%b geq 60 goto new
:old
echo "old"
goto end
:new
echo "new"
:end
pause

其中:

  1. for /f "tokens=4,5 delims=. " %%a in ('ver') :使用for命令读取ver命令的输出,并将版本信息按照.和空格分隔,分别存储在变量%%a%%b中。
  2. if %%a%%b geq 60 goto new :判断版本号是否大于等于60(即Windows Vista及以上版本),如果是则跳转到标签new执行相关操作。

整体解析:

  1. 使用ver命令获取Windows版本号,格式为主版本号.次版本号。例如10.0.17134
  2. 使用for语句解析版本号,提取主版本号到%%a,次版本号到%%b
  3. 判断如果主版本号%%a加次版本号%%b大于等于60,则跳转到:new标签。否则跳转到:old标签。
  4. :old标签处打印"old",然后跳转到:end结束。
  5. :new标签处打印"new",然后跳转到:end结束。
  6. :end处暂停等待用户输入。

总结起来,

这个bat脚本的作用是判断Windows版本号是否>= 6.0(也就是Windows Vista及以上版本),如果是则打印"new",否则打印"old"

举个例子:

在Windows 10下运行这个bat,会输出:

1
new

在Windows XP下运行,会输出:

1
old