TLS指纹跟踪网络安全实践(C/C++代码实现)
TLS指纹识别是网络安全领域的重要技术,它涉及通过分析TLS握手过程中的信息来识别和验证通信实体的技术手段。TLS(传输层安全)协议是用于保护网络数据传输的一种加密协议,而TLS指纹则是该协议在实际应用中产生的独特标识,它包含了诸如密码套件、协议版本和加密算法等信息。
TLS指纹的原理
TLS指纹是通过检查TLS握手过程中使用的密码套件、协议版本和加密算法等信息来确定的。由于不同的TLS实现在这些参数的选择上有所差异,因此可以通过比较TLS指纹来判断通信是否来自预期的源或目标。
TLS握手过程详细解释
TLS指纹识别技术是一种网络安全技术,它通过分析TLS握手过程中的信息来识别和验证通信实体。这种技术手段主要依赖于对TLS协议中的特定数据进行提取和分析,以此来确定通信双方的身份和确保通信的完整性与安全性。以下是对该技术实施方式的详细解释:
-
TLS握手过程:TLS握手是TLS协议中建立安全连接的关键步骤。在这一过程中,客户端和服务器协商加密算法、交换密钥并验证对方的身份。此过程涉及多个消息的传递,如ClientHello、ServerHello、Certificate、ServerKeyExchange、ClientKeyExchange等。
-
信息提取:在TLS指纹识别中,核心步骤是从TLS握手过程中提取关键信息。这些信息可能包括:
- 密码套件(Cipher Suite):客户端和服务器协商使用的加密算法和密钥交换算法的组合。
- 协议版本(Protocol Version):TLS协议的版本,不同的实现可能支持不同版本的TLS。
- 扩展(Extensions):TLS协议中允许的额外特性或选项,如支持的椭圆曲线、应用层协议协商等。
- 随机数(Random Numbers):TLS握手双方各自生成的随机值,用于后续的密钥生成。
-
特征分析:通过分析上述信息,可以构建一个唯一的或较为特定的TLS指纹,该指纹可以代表一个特定的设备、应用程序或组织。例如,某些服务器可能总是使用特定的密码套件或协议版本,这些信息可以用来区分不同的服务器。
-
指纹匹配与验证:将提取的TLS指纹与已知的指纹数据库进行匹配,以验证通信实体的身份。如果发现匹配的指纹,则可以较高信心确认通信实体的身份。反之,如果指纹不匹配,则可能表明存在中间人攻击或其他安全威胁。
-
应用场景:TLS指纹识别技术广泛应用于网络安全领域,包括但不限于:
- 入侵检测系统(IDS):用于识别并警告异常的TLS通信模式,可能指示着潜在的攻击活动。
- 网络流量分析:帮助网络管理员理解并管理流经网络的设备和应用程序类型。
- 威胁情报:在网络安全领域,TLS指纹可以作为跟踪特定威胁行为体的关键指标。
TLS指纹的作用
TLS指纹主要用于检测网络欺骗、中间人攻击、间谍活动等安全威胁。此外,TLS指纹还可以用于识别和管理设备和应用程序,提高网络安全性。
TLS指纹识别是一种网络安全技术,它的作用主要包括以下几点:
-
服务识别:通过分析TLS握手过程中的数据,可以识别出使用的特定服务或应用程序,即使它们运行在常见的端口上,如HTTP(S)。
-
客户端和服务器指纹识别:可以区分不同的浏览器、操作系统或特定版本的TLS库,这有助于识别潜在的安全漏洞或配置问题。
-
安全漏洞检测:某些TLS实现可能包含特定的安全漏洞。通过指纹识别,可以识别出易受攻击的系统,并采取相应的安全措施。
-
恶意软件识别:恶意软件经常使用特定的TLS配置来进行通信。通过分析TLS指纹,可以识别出恶意流量。
-
合规性检查:组织可以使用TLS指纹识别来确保其系统遵守安全协议和最佳实践,例如禁用已知不安全的TLS版本或密码套件。
-
网络监控与入侵检测:在网络安全监控中,TLS指纹识别可以帮助识别异常通信模式,这可能是入侵或其他安全事件的迹象。
-
性能优化:了解客户端和服务器的TLS实现可以帮助优化TLS握手过程,提高通信效率。
-
协议分析与研究:研究人员可以使用TLS指纹识别来分析TLS协议的使用情况,以及不同实现之间的差异。
-
匿名性分析:在某些情况下,通过分析TLS指纹,可以评估通信方的匿名性水平,例如,某些客户端或服务器的TLS实现可能更容易被识别。
绕过TLS指纹护盾的方法
在某些情况下,为了测试网络通信安全性、调试网络问题或访问被阻止的网站,可能需要绕过TLS指纹护盾。这可以通过使用代理服务器改变客户端的TLS指纹,或者使用自定义的TLS库来实现自定义的TLS握手过程来完成。以下是一些常见的方法:
-
更改默认配置:修改TLS库或应用程序的默认配置,使用非标准的密码套件、TLS版本或扩展,以减少被识别的可能性。
-
使用自定义TLS实现:开发或使用自定义的TLS实现,这些实现可能不遵循常见的指纹模式。
-
动态TLS参数:在TLS握手过程中动态选择参数,如密码套件或扩展,以避免产生一致的指纹。
-
使用TLS前导数据:在TLS握手之前发送一些随机数据,以混淆指纹识别工具对TLS数据的分析。
-
应用层加密:在TLS层之上应用额外的加密层,使得TLS指纹分析更加困难。
-
使用代理或VPN:通过代理服务器或VPN连接来隐藏真实的TLS指纹,因为这些中间节点可能会使用不同的TLS配置。
-
混淆TLS扩展:使用不常用或自定义的TLS扩展,使得指纹识别工具难以匹配已知的指纹模式。
-
更改客户端或服务器软件版本:定期更新或更改使用的软件版本,以避免因使用已知易受攻击或具有特定指纹的版本而被识别。
-
使用多证书策略:为不同的客户端或服务使用不同的TLS证书,以减少被指纹识别的可能性。
-
利用TLS 1.3的特性:TLS 1.3引入了一些新特性,如会话恢复和0-RTT,这些特性可以减少TLS握手过程中的可识别信息。
-
使用隐私保护技术:例如使用TLS的匿名DH或ECDH密钥交换算法,以减少泄露客户端或服务器的识别信息。
-
混淆或随机化SNI(Server Name Indication):SNI可以被用于指纹识别,通过随机化或混淆SNI值可以降低被识别的风险。
TLS指纹的应用
TLS指纹技术在网络安全领域的应用广泛,它不仅能够帮助识别和管理设备与应用程序,还能够检测网络欺骗和中间人攻击等安全威胁。同时,TLS指纹也是揭示恶意软件通信流量的关键工具。以下是TLS指纹技术的主要应用场景:
- Bot流量检测:通过分析TLS握手过程中的信息,TLS指纹可以用于识别和验证通信实体,从而检测和识别Bot流量。每个客户端(如浏览器、计算机软件、程序)所支持的协议版本、加密套件、扩展、加密算法等都是不同的,因此可以通过比较TLS指纹来判断通信是否来自预期的源或目标。在Bot对抗场景下,JA4指纹主要有两种应用方式:唯一性检测和一致性检测。唯一性检测是针对某些客户端程序设计的特殊性,使得这些客户端具备独一无二的JA4指纹,并且这些客户端的指纹变化周期较长,通过唯一性检测可有效识别此类异常客户端。一致性检测则是对客户端声明的设备信息(操作系统、浏览器类型、版本号)与其JA4指纹进行对比,检测是否与指纹对应的真实设备信息一致。
- 安全分析:TLS指纹能够指示客户端应用程序通过TLS通信的方式,而服务器端的TLS指纹能够指示服务器响应。如果两者结合起来,实质上就生成了客户端和服务器之间的加密协商的指纹[4]。
- 主动扫描:随着攻击者使用TLS的情况增长,通过主动探测发现同类攻击基础设施被认为是一步先手棋,有效的TLS指纹可以在消耗资源可控的情况下提供更好的效果。
- 恶意软件通信揭示:由于许多恶意软件会利用TLS协议来隐藏其通信流量,TLS指纹技术可以通过分析TLS数据包的特征来揭示这些恶意通信。
- 网络安全监控:TLS指纹技术为网络安全提供了一种有效的监控手段,帮助网络安全专业人员识别并防范各种网络威胁。
- 构建完整指纹库:为了成功识别Bot流量,构建一个完整的JA4指纹库是关键,这涉及到收集和更新大量的TLS指纹信息。
- 模块化网络指纹设计:JA4提供了一套易于使用和共享的模块化网络指纹,取代2017年起的JA3 TLS指纹标准,有助于更有效地进行威胁搜寻和分析。
- 绕过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 44char* 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 properlysetlinebuf(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 Handlersif(!(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 descriptionfp_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 ciphersuitesfp_current->compression_length = *(fpdb_raw+x);fp_current->compression = fpdb_raw+x+1;x += (*(fpdb_raw+x))+1; // Skip over compression algo'sfp_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
相关文章:
TLS指纹跟踪网络安全实践(C/C++代码实现)
TLS指纹识别是网络安全领域的重要技术,它涉及通过分析TLS握手过程中的信息来识别和验证通信实体的技术手段。TLS(传输层安全)协议是用于保护网络数据传输的一种加密协议,而TLS指纹则是该协议在实际应用中产生的独特标识࿰…...
小白学RAG:大模型 RAG 技术实践总结
节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 汇总合集…...
Doris Connector 结合 Flink CDC 实现 MySQL 分库分表
1. 概述 在实际业务系统中为了解决单表数据量大带来的各种问题,我们通常采用分库分表的方式对库表进行拆分,以达到提高系统的吞吐量。 但是这样给后面数据分析带来了麻烦,这个时候我们通常试将业务数据库的分库分表同步到数据仓库时&#x…...
ModbusTCP、TCP/IP都走网线,一样吗?
在现代通信技术中,Modbus/TCP和TCP/IP协议是两种广泛应用于工业自动化和网络通信领域的协议。尽管它们都运行在网线上,但它们在设计、结构和应用场景上有着明显的区别。 Modbus/TCP协议是什么 Modbus/TCP是一种基于TCP/IP的应用层协议,它是Mo…...
网络学习(13)|Spring Boot中获取HTTP请求头(Header)内容的详细解析
文章目录 方法一:使用HttpServletRequest实现原理代码示例优点缺点适用场景 方法二:使用RequestContextHolder实现原理代码示例优点缺点适用场景 方法三:使用RequestHeader注解实现原理代码示例优点缺点适用场景 总结 在Spring Boot应用中&am…...
【漏洞复现】宏景eHR pos_dept_post SQL注入漏洞
0x01 产品简介 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。 0x02 漏洞概述 宏景eHR pos_dept_post 接囗处存在SQL注入漏洞,未经过身份认证的远程攻击者利用此漏洞执行任意SQL指令,…...
82. 删除排序链表中的重复元素 and II
链接直达: 保留重复元素 不保留重复元素 题目: 1: 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。示例 1:输入:head [1,1,2] 输出:[1…...
C++ 判断目标文件是否被占用(独占)(附源码)
在IM软件中发起文件发送时,如果要发送的是某word文件,并且该word文件被office打开,则会提示文件正在被占用无法发送,如下所示: 那文件被占用到底是如何判断出来的呢?其实很简单,调用系统API函数CreateFile,打开该文件(OPEN_EXISTING),传入FILE_SHARE_READ共享读标记…...
计划任务 之 一次性的计划任务
计划任务 作用:定时自动完成特定的工作 计划任务的分类: (1)一次性的计划任务 例如下周三对系统的重要文件备份一次 (2)周期性重复计划任务 例如每天晚上12:00备份一次 一次性的任务计划:…...
非比较排序之计数排序
目录 一、什么是计数排序 二、思路 三、代码实现 一、什么是计数排序 计数排序是一种非比较型的排序算法,它通过统计待排序数据中每个元素出现的次数,然后根据这个次数来进行排序。计数排序的具体步骤如下: 首先找出待排序数据中的最大值…...
Django路由与会话深度探索:静态、动态路由分发,以及Cookie与Session的奥秘
系列文章目录 Django入门全攻略:从零搭建你的第一个Web项目Django ORM入门指南:从概念到实践,掌握模型创建、迁移与视图操作Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解Django ORM深度游ÿ…...
第7章 用户输入和 while 循环
第7章 用户输入和 while 循环 7.1 函数 input()的工作原理7.1.1 编写清晰的程序7.1.2 使用 int()来获取数值输入7.1.3 求模运算符 7.2 while 循环简介7.2.1 使用 while 循环7.2.2 让用户选择何时退出7.2.3 使用标志7.2.4 使用 break 退出循环7.2.5 在循环中使用 continue7.2.6 …...
xshell远程无法链接上VM的centos7
1、现象如下, 2.1解决办法:查证后发现这个默认的设置为vmnet0 2.2解决办法:重启win10的虚拟机网卡(先禁用再启用) 3.参考文章:Xshell连接不上虚拟机centos7_centos7的nat模式可以ping通网络,但是用xshell连…...
拥抱AI-图片学习中的卷积神经算法详解
一、定义 卷积神经算法(Convolutional Neural Networks, CNN)是深度学习领域中的一种重要算法,特别适用于处理图像相关的任务。以下是卷积神经算法的详细解释: 1. 基本概念 定义:卷积神经网络是一类包含卷积计算且具…...
超详解——深入详解Python基础语法——基础篇
目录 1 .语句和变量 变量赋值示例: 打印变量的值: 2. 语句折行 反斜杠折行示例: 使用括号自动折行: 3. 缩进规范 缩进示例: 4. 多重赋值(链式赋值) 多重赋值的应用: 5 .多…...
系统架构设计师【论文-2017年 试题2】: 论软件架构风格(包括写作要点和经典范文)
题目:论软件架构风格 (2017年 试题2) 软件体系结构风格是描述某一特定应用领域中系统组织方式的惯用模式。体系结构风格 定义一个系统家族,即一个体系结构定义一个词汇表和一组约束。词汇表中包含一些构件和 连接件类型ÿ…...
Spring Boot 事务传播机制详解
Spring Boot 事务传播机制详解 1. 事务传播机制概述 Spring Boot 中的事务传播机制用于处理多个事务方法之间相互调用时的事务行为,保证数据的完整性和一致性。当务传播机制定义了在调用一个事务方法时,当前事务该如何传播或传递。Spring Boot 中的事务…...
【机器学习】生成对抗网络 (Generative Adversarial Networks | GAN)
生成对抗网络 (Generative Adversarial Networks | GAN) 介绍 生成对抗网络 (Generative Adversarial Networks,简称GAN) 是一种强大的深度学习模型,用于生成具有逼真感的图像、音频和文本等内容。GAN 的核心理念是通过训练两个神经网络,生…...
[ADS信号完整性分析]深入理解IBIS AMI模型设计:从基础到实践
在高速数字设计领域,信号完整性(SI)分析对于确保系统性能至关重要。IBIS AMI(Algorithmic Model Interface)模型作为一种强大的工具,能够帮助设计师在系统层面上评估和优化SERDES(串行器/解串器…...
Plotly : 超好用的Python可视化工具
文章目录 安装:开始你的 Plotly 之旅基本折线图:简单却强大的起点带颜色的散点图:数据的多彩世界三维曲面图:探索数据的深度气泡图:让世界看到你的数据小提琴图:数据分布的优雅展现旭日图:分层数…...
Linux电话本的编写-shell脚本编写
该电话本可以实现以下功能 1.添加用户 2.查询用户 3.删除用户 4.展示用户 5.退出 代码展示: #!/bin/bash PHONEBOOKphonebook.txt function add_contact() { echo "Adding new contact..." read -p "Enter name: " name …...
蓝牙开发 基础知识
零、基础知识 0.1、Android 应用可通过 Bluetooth API 执行以下操作 扫描其他蓝牙设备查询本地蓝牙适配器的配对蓝牙设备建立 RFCOMM 通道通过服务发现连接到其他设备与其他设备进行双向数据传输管理多个连接 0.2、蓝牙进行通信的四大必需任务 设置蓝牙查找局部区域内的配对…...
QNX 7.0.0开发总结
1 QNX编译 1.1 基本概念 QNX可以直接使用Linux Makefile编译库和二进制,在Makefile文件中指定CCaarch64-unknown-nto-qnx7.0.0-g,或者CCx86_64-pc-nto-qnx7.0.0-g,保存退出后,运行source /qnx_sdk_path/qnxsdp-env.sh,…...
Golang使用讯飞星火AI接口
一、API申请 https://www.bilibili.com/video/BV1Yw411m7Rs/?spm_id_from333.337.search-card.all.click&vd_source707ec8983cc32e6e065d5496a7f79ee6 注册申请,需要在此页面获取appid、apisecret、apikey https://www.xfyun.cn/ https://console.xfyun.cn/ser…...
矫正儿童发音好帮手
《言语构音语音训练手册——下颌、唇部、舌部构音运动障碍》教辅书 儿童言语构音语音问题越来越受到家长的关注,大多数家长受到儿童说话晚、口齿不清、发音错误等问题的困扰,国外报道2岁儿童言语构音语音障碍达到17%,3岁达4%~7.5%࿰…...
wordpress主题导航主题v4.16.2哈哈版
1.下载授权接口源码onenav-auth-api-v2.zip ,在宝塔新建一个网站,域名为 auth.iotheme.cn,设置wordpress伪静态,申请ssl证书。将上面源码解压后上传到此网站根目录。 2. 在宝塔根目录etc下 hosts 中添加 127.0.0.1 auth.iotheme.…...
内存分布图
1.基本数据类型和常量存放在常量池中。 2.类的成员存放在堆中,如果成员是其他类对象也存放在堆中 3.数组和数组的内容放在堆中 4.类对象存放在栈中。 5.单独的对象存放在栈中。 6.引用数据类型存放在堆或栈中。 Java中对象到底存在堆中还是栈中_java对象在堆还…...
如何发布自己的NPM插件包?
安装 Node.js : 如果没有安装的,Nodejs下载安装:http://nodejs.cn/download/ 首先确保你已经安装了 Node.js 和 npm。你可以通过运行以下命令来检查是否已经安装: node -v npm -v初始化项目: 创建一个新的项目文件夹…...
计算广告读书杂记-待整理
不知不觉已经在字节干了两年多广告研发,也跳槽去了一家广告公司继续深耕,借着这个劲,重新读一遍《计算广告》这本书,并将一些重点概念进行记录。...
No module named _sqlite3解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
产品网站推广/别做网络推广员
前言:对用户的需求挖掘,其实算是技巧运用心理博弈。以下是对需求挖掘的小小总结,以后再慢慢补充。 一、需求挖掘: 1、概念:从用户需求出发,挖掘用户内心真正的目标,并转换为产品需求的过程。 …...
淄博哪里有网站建设平台/产品怎么做市场推广
1.原因:刚进入这家公司,给同事交接完,直接使用他的电脑,每次提交代码都显示他的用户名,本以为是电脑系统名称呢,可是修改了之后没有效果 2.解决方案: 打开C盘里的 .gitconfig文件 看下git的用户…...
用手机怎么制作app软件/seo网络推广怎么做
一、元素背景是指哪些区域 默认情况下元素的背景是指元素border(包含border)以内的区域。 在CSS3中可用使用background-clip改变元素背景区域。 1.1 background-clip 指定背景在被应用元素上的绘制区域。元素盒模型包含content, padding, border, margi…...
东莞教育建站/php搭建一个简单的网站
将目光转移到RenderingThreadMain()函数,这是个任务系统,各种渲染任务在此执行。 1,通过ENQUEUE_RENDER_COMMAND向队列添加渲染任务 可见,有很多种渲染任务 二,查看其定义, 1&am…...
wordpress腾讯后台账号/国内比较好的软文网站
点击上方蓝字 关注我们1、游戏简介游戏名称:萌宅物语无限爱心版游戏类型:养成游戏游戏平台:安卓整理时间:2020-05-30游戏评分:8.72、游戏介绍心得技巧分享特别说明游戏已修改为无限爱心版,在游戏中完成教程…...
电商网站建设开发的语言有哪些/企业网络营销策略
我们先看规则: 1、所有使用公众号支付方式发起支付请求的链接地址,都必须在支付授权目录之下;2、最多设置5个支付授权目录,且域名必须通过ICP备案;3、头部要包含http或https,须细化到二级或三级目录&#x…...