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

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

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-12 04:48:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-12 04:48:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-12 04:48:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-12 04:48:01       18 阅读

热门阅读

  1. Python学习笔记5:入门知识(五)

    2024-06-12 04:48:01       7 阅读
  2. c++【入门】买文具2

    2024-06-12 04:48:01       8 阅读
  3. 实验4-原地逆转链表(梅开二度)

    2024-06-12 04:48:01       7 阅读
  4. 反射...

    反射...

    2024-06-12 04:48:01      6 阅读
  5. Room数据库使用

    2024-06-12 04:48:01       8 阅读
  6. 【WPF】中的ListBox的ScrollIntoView方法使用

    2024-06-12 04:48:01       9 阅读