TLS指纹跟踪网络安全实践(C/C++代码实现)

news2024/11/16 3:21:28

TLS指纹识别是网络安全领域的重要技术,它涉及通过分析TLS握手过程中的信息来识别和验证通信实体的技术手段。TLS(传输层安全)协议是用于保护网络数据传输的一种加密协议,而TLS指纹则是该协议在实际应用中产生的独特标识,它包含了诸如密码套件、协议版本和加密算法等信息。

TLS指纹的原理

TLS指纹是通过检查TLS握手过程中使用的密码套件、协议版本和加密算法等信息来确定的。由于不同的TLS实现在这些参数的选择上有所差异,因此可以通过比较TLS指纹来判断通信是否来自预期的源或目标。

TLS握手过程详细解释

TLS指纹识别技术是一种网络安全技术,它通过分析TLS握手过程中的信息来识别和验证通信实体。这种技术手段主要依赖于对TLS协议中的特定数据进行提取和分析,以此来确定通信双方的身份和确保通信的完整性与安全性。以下是对该技术实施方式的详细解释:

  1. TLS握手过程:TLS握手是TLS协议中建立安全连接的关键步骤。在这一过程中,客户端和服务器协商加密算法、交换密钥并验证对方的身份。此过程涉及多个消息的传递,如ClientHello、ServerHello、Certificate、ServerKeyExchange、ClientKeyExchange等。

  2. 信息提取:在TLS指纹识别中,核心步骤是从TLS握手过程中提取关键信息。这些信息可能包括:

    • 密码套件(Cipher Suite):客户端和服务器协商使用的加密算法和密钥交换算法的组合。
    • 协议版本(Protocol Version):TLS协议的版本,不同的实现可能支持不同版本的TLS。
    • 扩展(Extensions):TLS协议中允许的额外特性或选项,如支持的椭圆曲线、应用层协议协商等。
    • 随机数(Random Numbers):TLS握手双方各自生成的随机值,用于后续的密钥生成。
  3. 特征分析:通过分析上述信息,可以构建一个唯一的或较为特定的TLS指纹,该指纹可以代表一个特定的设备、应用程序或组织。例如,某些服务器可能总是使用特定的密码套件或协议版本,这些信息可以用来区分不同的服务器。

  4. 指纹匹配与验证:将提取的TLS指纹与已知的指纹数据库进行匹配,以验证通信实体的身份。如果发现匹配的指纹,则可以较高信心确认通信实体的身份。反之,如果指纹不匹配,则可能表明存在中间人攻击或其他安全威胁。

  5. 应用场景:TLS指纹识别技术广泛应用于网络安全领域,包括但不限于:

    • 入侵检测系统(IDS):用于识别并警告异常的TLS通信模式,可能指示着潜在的攻击活动。
    • 网络流量分析:帮助网络管理员理解并管理流经网络的设备和应用程序类型。
    • 威胁情报:在网络安全领域,TLS指纹可以作为跟踪特定威胁行为体的关键指标。

TLS指纹的作用

TLS指纹主要用于检测网络欺骗、中间人攻击、间谍活动等安全威胁。此外,TLS指纹还可以用于识别和管理设备和应用程序,提高网络安全性。

TLS指纹识别是一种网络安全技术,它的作用主要包括以下几点:

  1. 服务识别:通过分析TLS握手过程中的数据,可以识别出使用的特定服务或应用程序,即使它们运行在常见的端口上,如HTTP(S)。

  2. 客户端和服务器指纹识别:可以区分不同的浏览器、操作系统或特定版本的TLS库,这有助于识别潜在的安全漏洞或配置问题。

  3. 安全漏洞检测:某些TLS实现可能包含特定的安全漏洞。通过指纹识别,可以识别出易受攻击的系统,并采取相应的安全措施。

  4. 恶意软件识别:恶意软件经常使用特定的TLS配置来进行通信。通过分析TLS指纹,可以识别出恶意流量。

  5. 合规性检查:组织可以使用TLS指纹识别来确保其系统遵守安全协议和最佳实践,例如禁用已知不安全的TLS版本或密码套件。

  6. 网络监控与入侵检测:在网络安全监控中,TLS指纹识别可以帮助识别异常通信模式,这可能是入侵或其他安全事件的迹象。

  7. 性能优化:了解客户端和服务器的TLS实现可以帮助优化TLS握手过程,提高通信效率。

  8. 协议分析与研究:研究人员可以使用TLS指纹识别来分析TLS协议的使用情况,以及不同实现之间的差异。

  9. 匿名性分析:在某些情况下,通过分析TLS指纹,可以评估通信方的匿名性水平,例如,某些客户端或服务器的TLS实现可能更容易被识别。

绕过TLS指纹护盾的方法

在某些情况下,为了测试网络通信安全性、调试网络问题或访问被阻止的网站,可能需要绕过TLS指纹护盾。这可以通过使用代理服务器改变客户端的TLS指纹,或者使用自定义的TLS库来实现自定义的TLS握手过程来完成。以下是一些常见的方法:

  1. 更改默认配置:修改TLS库或应用程序的默认配置,使用非标准的密码套件、TLS版本或扩展,以减少被识别的可能性。

  2. 使用自定义TLS实现:开发或使用自定义的TLS实现,这些实现可能不遵循常见的指纹模式。

  3. 动态TLS参数:在TLS握手过程中动态选择参数,如密码套件或扩展,以避免产生一致的指纹。

  4. 使用TLS前导数据:在TLS握手之前发送一些随机数据,以混淆指纹识别工具对TLS数据的分析。

  5. 应用层加密:在TLS层之上应用额外的加密层,使得TLS指纹分析更加困难。

  6. 使用代理或VPN:通过代理服务器或VPN连接来隐藏真实的TLS指纹,因为这些中间节点可能会使用不同的TLS配置。

  7. 混淆TLS扩展:使用不常用或自定义的TLS扩展,使得指纹识别工具难以匹配已知的指纹模式。

  8. 更改客户端或服务器软件版本:定期更新或更改使用的软件版本,以避免因使用已知易受攻击或具有特定指纹的版本而被识别。

  9. 使用多证书策略:为不同的客户端或服务使用不同的TLS证书,以减少被指纹识别的可能性。

  10. 利用TLS 1.3的特性:TLS 1.3引入了一些新特性,如会话恢复和0-RTT,这些特性可以减少TLS握手过程中的可识别信息。

  11. 使用隐私保护技术:例如使用TLS的匿名DH或ECDH密钥交换算法,以减少泄露客户端或服务器的识别信息。

  12. 混淆或随机化SNI(Server Name Indication):SNI可以被用于指纹识别,通过随机化或混淆SNI值可以降低被识别的风险。

TLS指纹的应用

TLS指纹技术在网络安全领域的应用广泛,它不仅能够帮助识别和管理设备与应用程序,还能够检测网络欺骗和中间人攻击等安全威胁。同时,TLS指纹也是揭示恶意软件通信流量的关键工具。以下是TLS指纹技术的主要应用场景:

  1. Bot流量检测:通过分析TLS握手过程中的信息,TLS指纹可以用于识别和验证通信实体,从而检测和识别Bot流量。每个客户端(如浏览器、计算机软件、程序)所支持的协议版本、加密套件、扩展、加密算法等都是不同的,因此可以通过比较TLS指纹来判断通信是否来自预期的源或目标。在Bot对抗场景下,JA4指纹主要有两种应用方式:唯一性检测和一致性检测。唯一性检测是针对某些客户端程序设计的特殊性,使得这些客户端具备独一无二的JA4指纹,并且这些客户端的指纹变化周期较长,通过唯一性检测可有效识别此类异常客户端。一致性检测则是对客户端声明的设备信息(操作系统、浏览器类型、版本号)与其JA4指纹进行对比,检测是否与指纹对应的真实设备信息一致。
  2. 安全分析:TLS指纹能够指示客户端应用程序通过TLS通信的方式,而服务器端的TLS指纹能够指示服务器响应。如果两者结合起来,实质上就生成了客户端和服务器之间的加密协商的指纹[4]。
  3. 主动扫描:随着攻击者使用TLS的情况增长,通过主动探测发现同类攻击基础设施被认为是一步先手棋,有效的TLS指纹可以在消耗资源可控的情况下提供更好的效果。
  4. 恶意软件通信揭示:由于许多恶意软件会利用TLS协议来隐藏其通信流量,TLS指纹技术可以通过分析TLS数据包的特征来揭示这些恶意通信。
  5. 网络安全监控:TLS指纹技术为网络安全提供了一种有效的监控手段,帮助网络安全专业人员识别并防范各种网络威胁。
  6. 构建完整指纹库:为了成功识别Bot流量,构建一个完整的JA4指纹库是关键,这涉及到收集和更新大量的TLS指纹信息。
  7. 模块化网络指纹设计:JA4提供了一套易于使用和共享的模块化网络指纹,取代2017年起的JA3 TLS指纹标准,有助于更有效地进行威胁搜寻和分析。
  8. 绕过TLS指纹护盾:在某些特殊情况下,绕过TLS指纹护盾可能是必要的,例如进行网络安全测试或调试网络问题时。但这需要谨慎操作,并确保合法性和安全性]。

此外,在使用TLS指纹技术时,应当充分考虑安全性和隐私保护的需求,谨慎使用相关技术。正确地应用这一技术需要深入理解TLS协议的细节,以及网络安全的广泛知识。

TLS指纹的校验工具

JA3和JA3S是用于识别客户端和服务器之间的TLS协商的指纹识别方法。这种组合的指纹识别可以帮助对特定客户端与其服务器之间的加密通信产生更高保真度的识别。

TLS指纹的查看方式

TLS指纹的查看方式主要包括通过专业的网络抓包和分析工具来获取TLS握手过程中的信息。以下是具体介绍:

  • 网络抓包工具:使用如Wireshark这样的网络抓包工具可以捕获TLS握手过程中的数据包,然后通过分析这些数据包来提取TLS指纹信息。这种方法适用于网络安全专业人员进行深入分析。
  • 在线服务工具:有一些在线服务和工具提供了TLS指纹的检测功能,用户可以通过访问这些网站来查看自己或他人服务器的TLS指纹。例如,可以使用https://tls.browserleaks.com/json 这样的网站来观察不同配置下的TLS指纹变化情况。
  • 计算TLS指纹:在TLS连接开始时,客户端会发送一个TLS Client Hello数据包,该数据包由客户端应用程序生成,通知服务器它支持哪些密码以及其首选的通信方法。根据这个数据包计算得到的一串哈希值,即为TLS指纹。例如,JA3方法用于收集客户端Client Hello数据包中以下字段的字节十进制值:TLS版本、密码套件、扩展列表、椭圆曲线和椭圆曲线格式。然后,它将这些值按出现顺序连接在一起,使用“,”分隔每个字段,使用“-”分隔每个字段中的每个值[3]。
  • 模块化网络指纹设计:JA4提供了一套易于使用和共享的模块化网络指纹,取代2017年起的JA3 TLS指纹标准。JA4检测方法增加了可读性,有助于更有效地进行威胁搜寻和分析。所有JA4+指纹都具有a_b_c格式,用于分隔构成指纹的不同部分。这允许仅利用ab或ac或c进行搜索和检测。

此外,在使用这些工具和方法时,需要注意合法性和安全性的问题。确保你的行为不违反相关法律法规,并且不会对自己的网络安全造成威胁。正确地应用这一技术需要深入理解TLS协议的细节,以及网络安全的广泛知识。

...
struct teredo_header {
  u_int16_t  part_a; 
  u_int16_t  part_b;  
  u_int16_t  length;   
  u_int8_t   nxt_header;
  u_int8_t   hop_length;
  struct     in6_addr ip6_src; 
  struct     in6_addr ip6_dst;
};

#define SSL_MIN_GOOD_VERSION	0x002
#define SSL_MAX_GOOD_VERSION	0x304

#define OFFSET_HELLO_VERSION	9
#define OFFSET_SESSION_LENGTH	43
#define OFFSET_CIPHER_LIST	44

#define SSLV2_OFFSET_HELLO_VERSION	3
#define SSLV2_OFFSET_SESSION_LENGTH	6
#define SSLV2_OFFSET_CIPHER_LIST	44


char* ssl_version(u_short version) {
	static char hex[7];
	switch (version) {
		case 0x002: return "SSLv2";
		case 0x300: return "SSLv3";
		case 0x301: return "TLSv1.0";
		case 0x302: return "TLSv1.1";
		case 0x303: return "TLSv1.2";
		case 0x304: return "TLSv1.3";
	}
	snprintf(hex, sizeof(hex), "0x%04hx", version);
	return hex;
}

struct fingerprint_new {
  uint16_t  fingerprint_id;
  uint16_t  desc_length;
  char      *desc;
  uint16_t  record_tls_version;
  uint16_t  tls_version;
  uint16_t  ciphersuite_length;
  uint8_t   *ciphersuite;
  uint8_t   compression_length; 
  uint8_t   *compression;
  uint16_t  extensions_length;
  uint8_t   *extensions;
  uint16_t  curves_length;
  uint8_t   *curves;
  uint16_t  sig_alg_length;
  uint8_t   *sig_alg;
  uint16_t  ec_point_fmt_length;
  uint8_t   *ec_point_fmt;
  struct    fingerprint_new  *next;
};

...
void print_usage(char *bin_name) {
	fprintf(stderr, "Usage: %s <options>\n\n", bin_name);
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "    -h                This message\n");
	fprintf(stderr, "    -i <interface>    Sniff packets from specified interface\n");
	fprintf(stderr, "    -p <pcap file>    Read packets from specified pcap file\n");
	fprintf(stderr, "    -P <pcap file>    Save packets to specified pcap file for unknown fingerprints\n");
	fprintf(stderr, "    -j <json file>    Output JSON fingerprints\n");
	fprintf(stderr, "    -l <log file>     Output logfile (JSON format)\n");
	fprintf(stderr, "    -d                Show reasons for discarded packets (post BPF)\n");
	fprintf(stderr, "    -f <fpdb>         Load the (binary) FingerPrint Database\n");
	fprintf(stderr, "    -u <uid>          Drop privileges to specified username\n");
	fprintf(stderr, "    -D                Do not discard padding (don't do without understanding what this does)\n");
	fprintf(stderr, "\n");
	return;
}

/* 测试另一种搜索内存中数据库的方法 */
uint shard_fp (struct fingerprint_new *fp_lookup, uint16_t maxshard) {
				return (((fp_lookup->ciphersuite_length) + (fp_lookup->tls_version)) & (maxshard -1));
}

int main(int argc, char **argv) {
...



	setlinebuf(stdout);

	if (argc == 1) {
		print_usage(argv[0]);
		exit(-1);
	}

	for (i = arg_start; i < argc && argv[i][0] == '-' ; i++) {
		switch (argv[i][1]) {
			case '?':
			case 'h':
				print_usage(argv[0]);
				exit(0);
				break;
			case 'p':
				/* Open the file */
				/* 检查接口是否已设置 */
				if (handle != NULL) {
					printf("-p and -i are mutually exclusive\n");
					exit(-1);
				}
				handle = pcap_open_offline(argv[++i], errbuf);
				printf("Reading from file: %s\n", argv[i]);
				break;
			case 'P':
				/* Open the file */
				output_handle = pcap_dump_open(pcap_open_dead(DLT_EN10MB, 65535), argv[++i]);
				if (output_handle != NULL) {
					printf("Writing samples to file: %s\n", argv[i]);
				} else {
					printf("Could not save samples: %s\n", errbuf);
					exit(-1);
				}
				break;
			case 'i':
				/* Open the interface */
				/* 检查文件是否已成功打开,如果文件名不正确,我们可能无法嗅探 */
				if (handle != NULL) {
					printf("-p and -i are mutually exclusive\n");
					exit(-1);
				}
				handle = pcap_open_live(argv[++i], SNAP_LEN, 1, 1000, errbuf);
				printf("Using interface: \033[1;36m%s\033[1;m\n", argv[i]);
				break;
			case 'j':
				/* JSON output to file */
				if((json_fd = fopen(argv[++i], "a")) == NULL) {
					printf("Cannot open JSON file for output\n");
					exit(-1);
				}
				// Buffering is fine, but linebuf needed for tailers to work properly
				setlinebuf(json_fd);
				break;
			case 'l':
				/* Output to log file */
				if((log_fd = fopen(argv[++i], "a")) == NULL) {
					printf("Cannot open log file for output\n");
					exit(-1);
				}
				// 缓冲很好,但需要linebuf才能让裁缝正常工作
				setlinebuf(log_fd);
				break;
			case 's':
				/* JSON output to stdout */
				if((json_fd = fopen("/dev/stdout", "a")) == NULL) {
					printf("Cannot open JSON file for output\n");
					fprintf(json_fd, "FD TEST\n");
					exit(-1);
				}
				break;
			case 'd':
				/* 显示丢弃的数据包信息 */
				show_drops = 1;
				break;
			case 'D':
				/* 丢弃填充物 */
				discard_pad = 0;
				break;
			case 'u':
				/* 用于将权限丢弃到的用户 */
				priv_passwd = getpwnam(argv[++i]);
				if(priv_passwd == NULL) {
					printf("Cannot find user: %s\n", argv[i]);
					exit(-1);
				}
				unpriv_user = priv_passwd->pw_uid;
				break;
			case 'f':
				/*二进制指纹数据库 */
				/* 将来这将覆盖默认位置,因为这将是默认格式 */
				if((fpdb_fd = fopen(argv[++i], "r")) == NULL) {
					printf("Cannot open fingerprint database file\n");
					exit(-1);
				}

				break;
			default :
				printf("Unknown option '%s'\n", argv[i]);
				exit(-1);
				break;

		}
	}

	if(fpdb_fd == NULL) {
		/* 未设置文件名,正在尝试当前目录 */
		if((fpdb_fd = fopen("tlsfp3.db", "r")) == NULL) {
			printf("Cannot open fingerprint database file (try -f)\n");
			printf("(This is a new feature, tlsfp.db should be in the source code directory)\n");
			exit(-1);
		}

	}

	if (unpriv_user != -1) {
		if (setgroups(0, NULL) == -1) {
			fprintf(stderr, "WARNING: could not set groups to 0 prior to dropping privileges\n");
		} else {
			fprintf(stderr, "Dropped effective group successfully\n");
		}
		if (setgid(getgid()) == -1) {
  			fprintf(stderr, "WARNING: could not drop group privileges\n");
		} else {
			fprintf(stderr, "Dropped effective group successfully\n");
		}
		if (setuid(unpriv_user) == -1) {
			fprintf(stderr, "WARNING: could not drop privileges to specified UID\n");
		} else {
			fprintf(stderr, "Changed UID successfully\n");
		}
	}

	// Register signal Handlers
	if(!(register_signals())) {
		printf("Could not register signal handlers\n");
		exit(0);
	}

	/* 如果没有设置log_fd,我们可以直接打印到stdout */
	if(log_fd == NULL) {
		log_fd = stdout;
	}


	if(fpdb_fd != NULL) {
		/* 查找文件大小 */
		fseek(fpdb_fd, 0L, SEEK_END);
		filesize = ftell(fpdb_fd);
		fseek(fpdb_fd, 0L, SEEK_SET);

		/* 分配内存并将文件存储在fpdb_raw中 */
		fpdb_raw = malloc(filesize);
		if (fread(fpdb_raw, 1, filesize, fpdb_fd) == filesize) {
			// printf("Yay, looks like the FPDB file loaded ok\n");
			fclose(fpdb_fd);
		} else {
			printf("There seems to be a problem reading the FPDB file\n");
			fclose(fpdb_fd);
			exit(-1);
		}
	}

	/* 检查并移过版本标题(如果错误则退出) */
	if (*fpdb_raw == 0) {
		fpdb_raw++;
	} else {
		printf("Unknown version of FPDB file\n");
		exit(-1);
	}

	int x, y;
	struct fingerprint_new *fp_current;
	extern struct fingerprint_new *search[8][5];

	/* 初始化,以便知道何时处于任何一个链中的第一个 */
	for (x = 0 ; x < 8 ; x++) {
		for (y = 0 ; y < 5 ; y++) {
			search[x][y] = NULL;
		}
	}


	for (x = 0 ; x < (filesize-1) ; fp_count++) {


		/* 为一个签名分配内存 */
		fp_current = malloc(sizeof(struct fingerprint_new));


		fp_current->fingerprint_id = (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));
		x += 2;
		fp_current->desc_length =  (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));
		fp_current->desc = (char *)fpdb_raw+x+2;

		x += (uint16_t) ((*(fpdb_raw+x) >> 16) + (*(fpdb_raw+x+1)) + 1); // Skip the description

		fp_current->record_tls_version = (uint16_t) ((uint16_t)*(fpdb_raw+x+1) << 8) + ((uint8_t)*(fpdb_raw+x+2));
		fp_current->tls_version = (uint16_t) ((uint16_t)*(fpdb_raw+x+3) << 8) + ((uint8_t)*(fpdb_raw+x+4));
		fp_current->ciphersuite_length = (uint16_t) ((uint16_t)*(fpdb_raw+x+5) << 8) + ((uint8_t)*(fpdb_raw+x+6));
		fp_current->ciphersuite = fpdb_raw+x+7;

		x += (uint16_t) ((*(fpdb_raw+x+5) >> 16) + (*(fpdb_raw+x+6)))+7; // Skip the ciphersuites

		fp_current->compression_length = *(fpdb_raw+x);
		fp_current->compression = fpdb_raw+x+1;

		x += (*(fpdb_raw+x))+1; // Skip over compression algo's

		fp_current->extensions_length = (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));
		fp_current->extensions = fpdb_raw+x+2;

		/* If discarding padding, strip out here.  In future, if this becomes default I will remove it at the database creation time */
		if(discard_pad == 1) {
			int counter;
			int debug_counter;
			for (counter = 0; counter < fp_current->extensions_length; counter += 2) {
				/* This is the two byte value for the padding extension */
				if(fp_current->extensions[counter] == 0 && fp_current->extensions[counter+1] == 21) {

					memmove(fp_current->extensions+(counter), fp_current->extensions+(counter+2), (fp_current->extensions_length - (counter + 2)));
					fp_current->extensions_length -= 2;


		x += (uint16_t)((*(fpdb_raw+x) >> 16) + *(fpdb_raw+x+1))+2;



		fp_current->curves_length = (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));

		if(fp_current->curves_length == 0) {
			fp_current->curves = NULL;
		} else {
			fp_current->curves = fpdb_raw+x+2;
		}

		x += (uint16_t)((*(fpdb_raw+x) >> 16) + *(fpdb_raw+x+1))+2;  // 跳过曲线

		fp_current->sig_alg_length = (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));

		if(fp_current->sig_alg_length == 0) {
			fp_current->sig_alg = NULL;
		} else {
			fp_current->sig_alg = fpdb_raw+x+2;
		}

		x += (uint16_t)((*(fpdb_raw+x) >> 16) + *(fpdb_raw+x+1))+2;  // 跳过过去的签名算法

		fp_current->ec_point_fmt_length = (uint16_t) ((uint16_t)*(fpdb_raw+x) << 8) + ((uint8_t)*(fpdb_raw+x+1));

		if(fp_current->ec_point_fmt_length == 0) {
			fp_current->ec_point_fmt = NULL;
		} else {
			fp_current->ec_point_fmt = fpdb_raw+x+2;
		}
		x += (uint16_t)((*(fpdb_raw+x) >> 16) + *(fpdb_raw+x+1))+2;

		/* 指向适当(较小)列表的多个指针数组 */
		fp_current->next = search[((fp_current->ciphersuite_length & 0x000F) >> 1 )][((fp_current->tls_version) & 0x00FF)];
		search[((fp_current->ciphersuite_length & 0x000F) >> 1 )][((fp_current->tls_version) & 0x00FF)] = fp_current;
	}

	printf("Loaded %i signatures\n", fp_count);


	if(json_fd == NULL) {
		if((json_fd = fopen("/dev/null", "a")) == NULL) {
			printf("Cannot open JSON file (/dev/null) for output\n");
			exit(-1);
		}
	}

	if (handle == NULL) {
		fprintf(stderr, "Couldn't open source %s: %s\n", dev, errbuf);
		exit(EXIT_FAILURE);
	}

	/* 确保我们在以太网设备上进行捕获[2] */
	if (pcap_datalink(handle) != DLT_EN10MB) {
		fprintf(stderr, "%s is not an Ethernet\n", dev);
		exit(EXIT_FAILURE);
	}


	if (pcap_compile(handle, &fp, default_filter, 0, 0) == -1) {
		fprintf(stderr, "Couldn't parse filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		exit(EXIT_FAILURE);
	}

	if (pcap_setfilter(handle, &fp) == -1) {
		fprintf(stderr, "Couldn't install filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		exit(EXIT_FAILURE);
	}
	/* 用于日志的etup主机名变量(在多个主机的情况下) */
	if(gethostname(hostname, HOST_NAME_MAX) != 0) {
		snprintf(hostname, sizeof("unknown"), "unknown");
	}

	/* 设置回调函数 */
	pcap_loop(handle, -1, got_packet, NULL);

	fprintf(stderr, "Reached end of pcap\n");

	/* cleanup */
	pcap_freecode(&fp);
	pcap_close(handle);

	return 0;
}

在命令行中运行编译后的程序,捕获网络流量。
这里的 -i ens33 是一个示例参数,表示从 ens33 网络接口捕获数据包。根据需要替换为其他参数。
可以尝试不同的命令行参数来测试程序的不同功能。例如:

-i <interface> 从指定的网络接口捕获数据包。
-p <pcap file> 从指定的pcap文件中读取数据包。
-j <json file> 将TLS指纹以JSON格式输出到文件。
...


用curl产生tls流量,想要活动pcap文件,可以使用tcpdump抓取tls流量;

If you need the complete source code, please add the WeChat number (c17865354792)

总结

TLS指纹识别是一项重要的网络安全技术,它通过对TLS握手过程中的信息进行分析,帮助识别和验证通信实体。

参考:RFC 5246、RFC 2246、RFC 6176、RFC 4346、RFC 7525

We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1812852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Desoutter电动拧紧扳手控制器维修看看这里

马头拧紧工具控制器作为现代工业生产中的重要设备&#xff0c;其稳定性和可靠性对于生产线的连续运行至关重要。然而&#xff0c;在使用过程中&#xff0c;由于各种原因&#xff0c;可能会出现马头电动拧紧控制器故障。【选择子锐机器人维修服务&#xff0c;您将享受以下优势和…

湖南大学CS-2023期末考试解析

前言 有幸作为助教参与信息院“周末夜校”讲解2023年试卷第2-3题&#xff08;汇编&#xff09;&#xff0c;借这个契机重新温习了一下这份试卷。 以下各部分的PPT由讲评助教分别完成并讲解。 1,6题 计科21杨助教2,3题 计科21甘晴void4,5题 智能21姚助教 由于2,3题过程较为繁…

HJS-DE1/3断电延时时间继电器 导轨安装 约瑟JOSEF

​HJS-DE断电延时时间继电器 HJS-DE1/3断电延时时间继电器 HJS-DE1/2断电延时时间继电器 HJS-DE断电延时时间继电器 用途 时间继电器HJS-DE1/3作为通电立即动作断电延时返回的元件&#xff0c;用于交流或直流保护和自动控制装置中&#xff0c;使被控元件得到所需延时。本继电…

10_Transformer预热---注意力机制(Attention)

1.1 什么是注意力机制(attention) 注意力机制&#xff08;Attention Mechanism&#xff09;是一种在神经网络中用于增强模型处理特定输入特征的能力的技术。它最早被应用于自然语言处理&#xff08;NLP&#xff09;任务中&#xff0c;特别是在机器翻译中&#xff0c;如Google的…

如何在Java中使用pdfbox,进行生成pdf操作,如文本居中,数据二列显示及图片显示?

pdfbox的基本使用 1. 添加 PDFBox 依赖 首先&#xff0c;确保你的项目中已经添加了 PDFBox 的 Maven 依赖。你可以使用类似以下的 Maven 依赖配置&#xff1a; <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId&g…

【第1章】Vue环境搭建

文章目录 前言一、安装Node1. 下载2. 安装3. 验证3.1 npm版本与Node.js版本3.2 验证环境 4. npm4.1 安装npm4.2 安装包4.3 全局安装包4.4 更新包4.5 删除包4.6 查看已安装的包4.7 初始化package.json 5. 国内源 二、安装Visual Studio Code1.下载2.安装3.安装Vue - Official 三…

Nuxt3 实战 (八):优雅的实现暗黑主题模式

前言 在 Nuxt3 中要实现暗黑模式&#xff0c;需要用到一个库&#xff1a;color-mode&#xff0c;它可以帮助我们很轻易地实现暗黑模式切换。 具体使用 安装 nuxtjs/color-mode 依赖&#xff1a; pnpm add nuxtjs/color-mode -D打开 nuxt.config.ts 配置文件注入依赖&#x…

【Linux】shell脚本变量——系统变量、环境变量和用户自定义变量

系统变量 系统变量是由系统预设的&#xff0c;它们通常在系统启动时被加载&#xff0c;并对所有用户和所有shell实例都有效。这些变量通常控制着系统的行为和配置&#xff0c;例如PATH&#xff08;命令搜索路径&#xff09;、HOME&#xff08;用户主目录&#xff09;等。系统变…

美团强势领涨恒指,港股即将迎来触底反弹?

恒指早间低开低走&#xff0c;持续低位徘徊&#xff0c;一度试探万八关口&#xff0c;最低见17994点&#xff0c;市场情绪表现疲弱&#xff0c;大型科技股普遍走低&#xff0c;但主要指数午后回升&#xff0c;恒生科技指数率先转涨&#xff0c;美团(3690.HK)涨超4%领涨成分股&a…

Java毕业设计 基于springboot vue大学生助学贷款管理系统

Java毕业设计 基于springboot vue大学生助学贷款管理系统 SpringBoot 大学生助学贷款管理系统 功能介绍 学生 登录 注册 个人中心 修改密码 个人信息 助学贷款 申请贷款 放贷信息 还贷信息 公告资讯 学校 登录 注册 个人中心 修改密码 个人信息 助学贷款管理 申请贷款管理 公…

TCP四次挥手全过程详解

TCP四次挥手全过程 有几点需要澄清&#xff1a; 1.首先&#xff0c;tcp四次挥手只有主动和被动方之分&#xff0c;没有客户端和服务端的概念 2.其次&#xff0c;发送报文段是tcp协议栈的行为&#xff0c;用户态调用close会陷入到内核态 3.再者&#xff0c;图中的情况前提是双…

LeetCode:419. 甲板上的战舰(遍历 Java)

目录 419. 甲板上的战舰 题目描述&#xff1a; 实现代码与解析&#xff1a; 遍历 原理思路&#xff1a; 419. 甲板上的战舰 题目描述&#xff1a; 给你一个大小为 m x n 的矩阵 board 表示甲板&#xff0c;其中&#xff0c;每个单元格可以是一艘战舰 X 或者是一个空位 . &…

【机器学习300问】111、解释目标检测的基本概念?

一、目标检测基本概念 &#xff08;1&#xff09;目标检测的定义 目标检测是计算机视觉领域的一项关键任务&#xff0c;它旨在识别图像或视频帧中出现的所有感兴趣目标&#xff08;物体&#xff09;的位置和类别。简而言之&#xff0c;目标检测不仅需要判断图像中存在哪些类型…

LVS – NAT 模式集群构建

目录 1 环境准备 1.1 准备四台服务器 1.2 IP与网关均按照下图配置 1.3 网卡配置 1.4 real server 安装 web服务 1.5 安装ipvsadm 管理工具 2 使用ipvsadm管理LVS 2.1 创建集群指定使用的算法 2.2 添加真实服务器指定工作原理 2.3 查看是否正确创建集群 3 测试 3.1 使用tcpdump…

6.Hugging Face Transformers 快速入门

Hugging Face Transformers 库独特价值 丰富的预训练模型:提供广泛的预训练模型,如BERT、GPT、T5等,适用于各种NLP任务。易于使用:设计注重易用性,使得即使没有深厚机器学习背景的开发者也能快速上手。最新研究成果的快速集成:经常更新,包含最新的研究成果和模型。强大的…

SQL进阶day12——高级条件语句

1筛选限定昵称成就值活跃日期的用户 我的代码&#xff1a;答案不对 select uid,u.nick_name,u.achievement from exam_record er join practice_record pr using(uid) join user_info u using(uid) where u.nick_name like "牛客%号" and u.achievement between …

冯喜运:6.11晚间黄金原油走势如何及独家交易操作策略

【黄金消息面分析】&#xff1a;随着全球经济的波动和地缘政治的紧张&#xff0c;黄金作为传统的避险资产&#xff0c;其市场表现一直备受投资者关注。本文将深入分析黄金市场的即时反应&#xff0c;并结合技术分析&#xff0c;对黄金的未来走势进行预测&#xff0c;同时探讨可…

35.四方定理

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动&#xff0c;旨在激发青少年对学习人工智能与算法设计的热情与兴趣&#xff0c;提升青少年科学素养&#xff0c;引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/477 题目背…

CyberDAO:引领Web3时代的DAO社区文化

致力于Web3研究和孵化 CyberDAO自成立以来&#xff0c;致力于推动Web3研究和孵化&#xff0c;吸引了来自技术、资本、商业、应用与流量等领域的上千名热忱成员。我们为社区提供多元的Web3产品和商业机会&#xff0c;触达行业核心&#xff0c;助力成员捕获Web3.0时代的红利。 目…

82. 删除排序链表中的重复元素 and II

链接直达&#xff1a; 保留重复元素 不保留重复元素 题目&#xff1a; 1: 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。示例 1&#xff1a;输入&#xff1a;head [1,1,2] 输出&#xff1a;[1…