libbpf-bootstrap开发指南:socket 监测与过滤 - sockfilter

目录

代码分析

comm 数据部分

BPF 代码部分

功能说明

rb 结构分析

ip_is_fragment 函数分析

bpf_skb_load_bytes函数分析

GRE协议说明

用户代码部分

功能说明

open_raw_sock& 原始套接字

setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))

执行效果

分片与不分片的处理难度说明


代码分析

comm 数据部分
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2022 Jacky Yin */
#ifndef __SOCKFILTER_H
#define __SOCKFILTER_H

struct so_event {
	__be32 src_addr;
	__be32 dst_addr;
	union {
		__be32 ports;
		__be16 port16[2];
	};
	__u32 ip_proto;
	__u32 pkt_type;
	__u32 ifindex;
};

#endif /* __SOCKFILTER_H */
BPF 代码部分
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2022 Jacky Yin */
#include <stddef.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "sockfilter.h"

#define IP_MF	  0x2000
#define IP_OFFSET 0x1FFF

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct {
	__uint(type, BPF_MAP_TYPE_RINGBUF);
	__uint(max_entries, 256 * 1024);
} rb SEC(".maps");

static inline int ip_is_fragment(struct __sk_buff *skb, __u32 nhoff)
{
	__u16 frag_off;

	bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 2);
	frag_off = __bpf_ntohs(frag_off);
	return frag_off & (IP_MF | IP_OFFSET);
}

SEC("socket")
int socket_handler(struct __sk_buff *skb)
{
	struct so_event *e;
	__u8 verlen;
	__u16 proto;
	__u32 nhoff = ETH_HLEN;

	bpf_skb_load_bytes(skb, 12, &proto, 2);
	proto = __bpf_ntohs(proto);
	if (proto != ETH_P_IP)
		return 0;

	if (ip_is_fragment(skb, nhoff))
		return 0;

	/* reserve sample from BPF ringbuf */
	e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
	if (!e)
		return 0;

	bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), &e->ip_proto, 1);

	if (e->ip_proto != IPPROTO_GRE) {
		bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), &(e->src_addr), 4);
		bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), &(e->dst_addr), 4);
	}

	bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1);
	bpf_skb_load_bytes(skb, nhoff + ((verlen & 0xF) << 2), &(e->ports), 4);
	e->pkt_type = skb->pkt_type;
	e->ifindex = skb->ifindex;
	bpf_ringbuf_submit(e, 0);

	return skb->len;
}
功能说明

从接收到的数据包中提取一些元数据,并将这些元数据保存到一个ring buffer中。具体来说,它提取的元数据包括:

  • IP协议类型 (protocol)
  • 如果IP协议不是GRE (Generic Routing Encapsulation),则提取源IP地址 (saddr) 和目标IP地址 (daddr)
  • IP头中的版本和头长度字段 (verlen)
  • TCP或UDP的源端口和目标端口 (ports)
  • 数据包类型 (pkt_type)
  • 接收数据包的网络接口索引号 (ifindex)

这个BPF程序只处理非分片的IP数据包,并且只处理以太网协议类型为IP的数据包。

rb 结构分析

rb是一个ring buffer类型的map,最大容量为256 * 1024项。ring buffer是一种先进先出(FIFO)的数据结构,用于保存从数据包中提取的元数据。

在之前的案例中也提到过这个结构,就是用于和用户空间之间通信的

ip_is_fragment 函数分析

ip_is_fragment函数的主要作用是检查给定的IP数据包是否是一个分片。在IP协议中,如果一个数据包过大,超过了最大传输单元(MTU),那么这个数据包会被分片(fragmented)成多个更小的数据包以进行传输。这个过程可以在发送端进行,也可以在路由过程中由路由器进行。每个分片都是一个完整的IP数据包,包含了完整的IP头,但只包含了原始数据包的一部分数据。

ip_is_fragment函数通过检查IP头中的"fragment offset"字段来判断一个数据包是否是分片。这个字段的结构如下:

  • 最高位是保留位,总是0。
  • 下一个位是"More Fragments"(MF)位,如果这个位是1,那么表示后面还有更多的分片。
  • 剩下的13位是"Fragment Offset",表示这个分片在原始数据包中的偏移量。

所以,如果一个数据包是分片,那么它的"More Fragments"位是1,或者它的"Fragment Offset"不是0。在ip_is_fragment函数中,这两个条件被合并在一起,所以只要这个字段的值和IP_MF或IP_OFFSET进行位与操作的结果不是0,那么这个函数就会返回1,表示这个数据包是分片。否则,这个函数就会返回0,表示这个数据包不是分片。

注意,这个函数只能判断一个数据包是否是分片,但不能判断这个分片是原始数据包的哪一部分。为了完整地重组一个分片的数据包,需要收集所有的分片,然后按照它们的"Fragment Offset"字段的值将它们按正确的顺序组合在一起。

	if (ip_is_fragment(skb, nhoff))
		return 0;

如果ip_is_fragment函数返回true(或者非零),那么这段代码就会执行return 0;,意味着该函数在这个点上结束,且返回值为0。这是因为该BPF程序只处理非分片的IP数据包。

bpf_skb_load_bytes函数分析
static inline void bpf_skb_load_bytes(const struct __sk_buff *skb, u32 off, void *to, u32 len);

这个函数的参数包括:

  • skb:这个参数是一个指向数据包的指针。在BPF程序中,数据包通常被表示为struct __sk_buff类型的对象。
  • off:这个参数是要读取的数据在数据包中的偏移量,以字节为单位。
  • to:这个参数是一个指向缓冲区的指针,这个缓冲区用于保存从数据包中读取的数据。
  • len:这个参数是要读取的数据的长度,以字节为单位。

bpf_skb_load_bytes函数的功能是从数据包的指定偏移量开始,读取指定长度的数据,并将这些数据复制到指定的缓冲区。这个函数可以用于读取数据包的任何部分,包括数据包头(例如IP头,TCP头等)和数据包的数据部分。

注意,bpf_skb_load_bytes函数只能在BPF程序中使用,不能在普通的C程序中使用。这是因为这个函数在运行时将被BPF虚拟机转换为对底层数据结构的直接操作。

bpf_skb_load_bytes(skb, 12, &proto, 2);

这行代码是在使用bpf_skb_load_bytes函数从数据包中读取两个字节的数据,这两个字节位于数据包的偏移量为12的位置。这两个字节被解释为网络层协议类型(proto),并且它们被复制到变量proto中。

在以太网帧的标准结构中,偏移量为12的位置是帧类型字段的开始位置。该字段用于表示以太网帧的有效载荷(Payload)中的协议类型。例如,如果这个字段的值是0x0800,那么表示有效载荷中的数据是一个IP数据包;如果这个字段的值是0x86DD,那么表示有效载荷中的数据是一个IPv6数据包。

因此,这行代码的目的是读取以太网帧的类型字段,然后检查这个字段的值。如果这个字段的值表示有效载荷中的数据是一个IP数据包,那么socket_handler函数将继续处理这个数据包;否则,socket_handler函数将忽略这个数据包。

GRE协议说明

在这个代码中是不处理GRE协议的

GRE,全称为Generic Routing Encapsulation(通用路由封装),是一个网络层协议,用于在两个网络节点之间封装任何网络层协议的数据包,使其能够经过不同的网络传输。

GRE协议可用于在两个网络之间建立直接的、点对点的连接,即使这两个网络在地理上相隔很远。这使得GRE协议成为建立VPN(虚拟专用网络)的一种常用技术。

GRE协议的一大优点是它能够封装几乎所有的网络层协议,包括IPv4、IPv6、IPX等。这使得GRE协议非常灵活,能够应用于各种网络环境中。

然而,GRE协议也有一些缺点。例如,GRE协议没有内置的加密功能,因此,如果需要保证数据的安全性,那么就需要额外实现加密功能。此外,由于GRE协议需要额外的头部信息来封装原始的数据包,因此,它会增加网络的带宽使用和处理开销。

GRE是由IETF(互联网工程任务组)在RFC 2784中定义的。

但是因为如下原因,一般不会处理GRE协议。

  1. 复杂性:GRE协议是一种封装协议,它可以封装各种不同的网络层协议。处理GRE协议的数据包需要解封装操作,这增加了处理的复杂性。可能为了简化BPF程序,开发者决定忽略GRE协议的数据包。
  2. 特定用途:这个BPF程序可能是为了处理特定协议的数据包,例如TCP或UDP,而不是GRE。因此,它只关心非GRE协议的数据包。
  3. 性能:解封装GRE数据包会消耗更多的CPU资源,如果这个BPF程序需要处理大量的数据包,那么处理GRE数据包可能会影响性能。
  4. 安全性:由于GRE协议可以封装任意的网络层协议,因此它可能会被用来进行某些类型的网络攻击。例如,攻击者可以使用GRE协议来封装恶意的数据包,以绕过防火墙或IDS系统。因此,一些安全设备或系统可能会选择忽略GRE协议的数据包。
用户代码部分
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2022 Jacky Yin */
#include <argp.h>
#include <arpa/inet.h>
#include <assert.h>
#include <bpf/libbpf.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <net/if.h>
#include <signal.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <unistd.h>
#include "sockfilter.h"
#include "sockfilter.skel.h"

static struct env {
	const char *interface;
} env;

const char argp_program_doc[] =
	"BPF socket filter demo application.\n"
	"\n"
	"This program watch network packet of specified interface and print out src/dst\n"
	"information.\n"
	"\n"
	"Currently only IPv4 is supported.\n"
	"\n"
	"USAGE: ./sockfilter [-i <interface>]\n";

static const struct argp_option opts[] = {
	{ "interface", 'i', "INTERFACE", 0, "Network interface to attach" },
	{},
};

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
	switch (key) {
	case 'i':
		env.interface = arg;
		break;
	case ARGP_KEY_ARG:
		argp_usage(state);
		break;
	default:
		return ARGP_ERR_UNKNOWN;
	}
	return 0;
}

static const struct argp argp = {
	.options = opts,
	.parser = parse_arg,
	.doc = argp_program_doc,
};

static const char *ipproto_mapping[IPPROTO_MAX] = {
	[IPPROTO_IP] = "IP",	   [IPPROTO_ICMP] = "ICMP",	  [IPPROTO_IGMP] = "IGMP",
	[IPPROTO_IPIP] = "IPIP",   [IPPROTO_TCP] = "TCP",	  [IPPROTO_EGP] = "EGP",
	[IPPROTO_PUP] = "PUP",	   [IPPROTO_UDP] = "UDP",	  [IPPROTO_IDP] = "IDP",
	[IPPROTO_TP] = "TP",	   [IPPROTO_DCCP] = "DCCP",	  [IPPROTO_IPV6] = "IPV6",
	[IPPROTO_RSVP] = "RSVP",   [IPPROTO_GRE] = "GRE",	  [IPPROTO_ESP] = "ESP",
	[IPPROTO_AH] = "AH",	   [IPPROTO_MTP] = "MTP",	  [IPPROTO_BEETPH] = "BEETPH",
	[IPPROTO_ENCAP] = "ENCAP", [IPPROTO_PIM] = "PIM",	  [IPPROTO_COMP] = "COMP",
	[IPPROTO_SCTP] = "SCTP",   [IPPROTO_UDPLITE] = "UDPLITE", [IPPROTO_MPLS] = "MPLS",
	[IPPROTO_RAW] = "RAW"
};

static int open_raw_sock(const char *name)
{
	struct sockaddr_ll sll;
	int sock;

	sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
	if (sock < 0) {
		fprintf(stderr, "Failed to create raw socket\n");
		return -1;
	}

	memset(&sll, 0, sizeof(sll));
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = if_nametoindex(name);
	sll.sll_protocol = htons(ETH_P_ALL);
	if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
		fprintf(stderr, "Failed to bind to %s: %s\n", name, strerror(errno));
		close(sock);
		return -1;
	}

	return sock;
}

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
	return vfprintf(stderr, format, args);
}

static inline void ltoa(uint32_t addr, char *dst)
{
	snprintf(dst, 16, "%u.%u.%u.%u", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
		 (addr >> 8) & 0xFF, (addr & 0xFF));
}

static int handle_event(void *ctx, void *data, size_t data_sz)
{
	const struct so_event *e = data;
	char ifname[IF_NAMESIZE];
	char sstr[16] = {}, dstr[16] = {};

	if (e->pkt_type != PACKET_HOST)
		return 0;

	if (e->ip_proto < 0 || e->ip_proto >= IPPROTO_MAX)
		return 0;

	if (!if_indextoname(e->ifindex, ifname))
		return 0;

	ltoa(ntohl(e->src_addr), sstr);
	ltoa(ntohl(e->dst_addr), dstr);

	printf("interface: %s\tprotocol: %s\t%s:%d(src) -> %s:%d(dst)\n", ifname,
	       ipproto_mapping[e->ip_proto], sstr, ntohs(e->port16[0]), dstr, ntohs(e->port16[1]));

	return 0;
}

static volatile bool exiting = false;

static void sig_handler(int sig)
{
	exiting = true;
}

int main(int argc, char **argv)
{
	struct ring_buffer *rb = NULL;
	struct sockfilter_bpf *skel;
	int err, prog_fd, sock;

	env.interface = "lo";
	/* Parse command line arguments */
	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
	if (err)
		return -err;

	/* Set up libbpf errors and debug info callback */
	libbpf_set_print(libbpf_print_fn);

	/* Cleaner handling of Ctrl-C */
	signal(SIGINT, sig_handler);
	signal(SIGTERM, sig_handler);

	/* Load and verify BPF programs*/
	skel = sockfilter_bpf__open_and_load();
	if (!skel) {
		fprintf(stderr, "Failed to open and load BPF skeleton\n");
		return 1;
	}

	/* Set up ring buffer polling */
	rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
	if (!rb) {
		err = -1;
		fprintf(stderr, "Failed to create ring buffer\n");
		goto cleanup;
	}

	/* Create raw socket for localhost interface */
	sock = open_raw_sock(env.interface);
	if (sock < 0) {
		err = -2;
		fprintf(stderr, "Failed to open raw socket\n");
		goto cleanup;
	}

	/* Attach BPF program to raw socket */
	prog_fd = bpf_program__fd(skel->progs.socket_handler);
	if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) {
		err = -3;
		fprintf(stderr, "Failed to attach to raw socket\n");
		goto cleanup;
	}

	/* Process events */
	while (!exiting) {
		err = ring_buffer__poll(rb, 100 /* timeout, ms */);
		/* Ctrl-C will cause -EINTR */
		if (err == -EINTR) {
			err = 0;
			break;
		}
		if (err < 0) {
			fprintf(stderr, "Error polling perf buffer: %d\n", err);
			break;
		}
		sleep(1);
	}

cleanup:
	ring_buffer__free(rb);
	sockfilter_bpf__destroy(skel);
	return -err;
}
功能说明

用于分析网络数据包的 BPF (Berkeley Packet Filter) 程序。程序会侦听指定的网络接口,并打印出源和目标的信息。目前,只支持处理 IPv4 协议。

open_raw_sock& 原始套接字

open_raw_sock() 函数的主要功能是创建一个原始套接字,并将其绑定到指定的网络接口上。这个函数的主要步骤如下:

  1. 通过 socket() 系统调用创建一个原始套接字。其中,PF_PACKET 表示底层的包接口,SOCK_RAW 表示原始套接字,SOCK_NONBLOCK 和 SOCK_CLOEXEC 分别表示非阻塞和在 exec() 调用后关闭套接字,htons(ETH_P_ALL) 表示接收所有类型的数据包。
  2. 使用 if_nametoindex() 函数获取网络接口的索引。
  3. 使用 bind() 函数将套接字绑定到指定的网络接口上。bind() 调用需要一个 sockaddr_ll 结构体,其中包含了套接字、网络接口和协议的信息。

原始套接字(Raw Socket)允许在更低的网络层次(如 IP 或 Ethernet 层次)上进行通信。使用原始套接字,应用程序可以自行处理或生成协议头部,例如,IP 头或 TCP 头,而不是由操作系统的网络栈来处理。这使得原始套接字非常适合于实现网络监控工具,或者自定义协议。

相比之下,普通的套接字(如 TCP 或 UDP 套接字)是在更高的网络层次(如传输层)上进行通信。这些套接字由操作系统的网络栈自动处理协议头部,并为应用程序提供了一个简单的读写接口。

需要注意的是,由于原始套接字能够直接访问底层网络协议,使用原始套接字通常需要相应的权限(如 root 权限)。

setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))

这段代码使用 setsockopt() 函数将 BPF(Berkeley Packet Filter)程序附加到原始套接字上。

下面是每个参数的详细解释:

  • sock:这是你要修改属性的 socket 文件描述符。
  • SOL_SOCKET:这是选项定义级别的参数,SOL_SOCKET 表示这个选项是 socket 层面的。
  • SO_ATTACH_BPF:这是你要修改的选项。SO_ATTACH_BPF 选项用于将 BPF 程序附加到 socket 上。
  • &prog_fd:这是你要设置的选项值。在这个例子中,它是一个指向 BPF 程序的文件描述符的指针。
  • sizeof(prog_fd):这是选项值的大小。

当这段代码执行后,所有通过 sock socket 接收的数据包将首先通过 prog_fd 指向的 BPF 程序进行处理。也就是说,BPF 程序能够在数据包被 socket 读取之前对其进行过滤或修改。

执行效果

interface: lo	protocol: TCP	127.0.0.1:56738(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:57856(dst)
interface: lo	protocol: TCP	127.0.0.1:57856(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:57856(dst)
interface: lo	protocol: TCP	127.0.0.1:57856(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:57856(dst)
interface: lo	protocol: TCP	127.0.0.1:57856(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:56738(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:56738(dst)
interface: lo	protocol: TCP	127.0.0.1:56738(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:56738(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:52778(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:52762(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:56738(dst)
interface: lo	protocol: TCP	127.0.0.1:56738(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52778(dst)
interface: lo	protocol: TCP	127.0.0.1:52778(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52778(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52778(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52762(dst)
interface: lo	protocol: TCP	127.0.0.1:52762(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52762(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:52762(dst)
interface: lo	protocol: TCP	127.0.0.1:52778(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:52762(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:57856(dst)
interface: lo	protocol: TCP	127.0.0.1:57856(src) -> 127.0.0.1:37351(dst)
interface: lo	protocol: TCP	127.0.0.1:37351(src) -> 127.0.0.1:57856(dst)
interface: lo	protocol: TCP	127.0.0.1:57856(src) -> 127.0.0.1:37351(dst)

分片与不分片的处理难度说明

  1. 完整性问题:在处理分片数据包时,你需要保证所有的分片都已经收到,并且能正确地组合在一起以恢复原始的数据包。如果某个分片丢失,那么整个数据包就无法正确地恢复。这需要在应用层实现额外的逻辑来处理分片的接收和重组。
  2. 资源使用:处理分片数据包通常需要更多的资源。例如,你需要在内存中保存已经收到的分片,等待剩下的分片到达。如果收到的分片数量很大,或者某个分片长时间无法到达,那么这可能会占用大量的内存和CPU资源。
  3. 安全问题:分片数据包可以被用来进行一些网络攻击。例如,攻击者可以故意发送大量的不完整的分片,导致你的应用消耗大量的资源去处理这些分片,从而实现拒绝服务(DoS)攻击。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
双向的TCP流量过滤软件,它允许您添加自定义正则表达式(正则表达式)过滤器。预置过滤包括:HTTP头信息,POST和GET数据,域名或即使*过滤*在任何连接传递的数据。 为了对付新的威胁,TCP过滤器包括一个强大的威胁检测引擎,用于检测和阻止黑洞,网络攻击,恶意URL和其他基于Web的威胁。 本软件会经常更新数据库,为了您的安全。 [10-01-2012] v1.4.0.0 Added "Password Protect Websites" Password is saved encrypted Added "Status" TAB Added "Execute Action after X minutes of idle activity" (RegEx Rules) Added "Domain:" info in alert dialog (RegEx Rules) Added "URL:" info in alert dialog (RegEx Rules) Improved "Threats Detection Engine (TDE)" Optimized UI Added "Actions" links in Status TAB Added right-click option "Set Password" on Domains->Protected TAB Added right-click option "Options" on RegEx Rules TAB Block download of executable files Block download of PDF files with JavaScript code Block download of PDF files Block download of Java (JAR) files Block download of Wordpad (RTF) files Block download of Video (AVI, FLV, MPG, MOV) files Block download of Flash (SWF) files Block download of ZIP and RAR files Block download of Microsoft Word and Excel files Block a website by TLD Disable task manager when in stealth mode Added "Menu"->"Disable Task Manager" Added "Menu"->"Enable Task Manager" Disable cmd dos prompt when in stealth mode Lock all cdroms when in stealth mode Added "Menu"->"Disable CMD Dos Prompt" Added "Menu"->"Enable CMD Dos Prompt" Added "Menu"->"Lock CD-ROMs" Added "Menu"->"UnLock CD-ROMs" Block download of JavaScript (JS) files Block IRC traffic Block FTP traffic Added "Rules"->"ADS" TAB to manage regexes to block ADS links Added "Threats"->Process Behavioral Analysis (Block connections of suspicious processes) Added "Block all unknown websites" (allow only whitelisted domains) Block IMs traffic (MSN Messenger, Y! Messenger) Updated "Reset Settings" Disabled "Threats Detection Engine (TDE)" (will be available in final version) Minor fixes and optimizations

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ym影子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值