MD5消息摘要算法学习

news2024/10/13 2:02:52

  MD5(Message Digest Algorithm 5)是一种广泛使用的哈希函数,它用于生成128位的哈希值(也称为消息摘要)。MD5主要用于确保信息的完整性,即可以通过对数据生成的哈希值来验证数据是否被篡改。尽管MD5在过去被广泛应用,但由于它在安全性上的漏洞,现代加密中已逐渐被更安全的算法(如SHA-256)所取代。
在这里插入图片描述
  MD5是一种哈希函数算法,哈希函数是一种用于消息认证的一种方式。消息认证是一种确定完整性并进行认证的技术。

消息认证

  为了防止消息背篡改,发布消息的部门会在发布消息的同时发布该消息的哈希值,哈希值就是通过哈希函数计算计算出来的值。

哈希函数

  • 函数的输入是任意长。
  • 函数的输出是固定长。
  • 单向不可逆。

整体流程

  算法输入为任意长的消息,然后将这段任意长的消息分为512比特(64字节)长度的分组,给定一个128比特的初始化变量,然后每个被分成512比特的分组数据会分别经过一个HMD5压缩函数的运算,最终结果会生成128比特(16字节)的消息摘要(哈希值)。

算法处理过程

补位

信息计算前先要进行位补位,设补位后信息的长度为LEN(bit),则LEN%512 = 448(bit),即数据扩展至 K * 512 + 448(bit)。即K * 64+56(byte),K为整数。补位操作始终要执行,即使补位前信息的长度对512求余的结果是448。具体补位操作:补一个1,然后补0至满足上述要求。总共最少要补1bit,最多补512bit。

尾部追加信息长度

将输入信息的原始长度b(bit)表示成一个64-bit的数字,把它添加到上一步的结果后面(在32位的机器上,这64位将用2个字来表示并且低位在前)。当遇到b大于2^64这种极少的情况时,b的高位被截去,仅使用b的低64位。经过上面两步,数据就被填补成长度为512(bit)的倍数。也就是说,此时的数据长度是16个字(32byte)的整数倍。此时的数据表示为: M[0 … N-1] 其中的N是16的倍数。

初始化缓冲区

用一个四个字的缓冲器(A,B,C,D)来计算报文摘要,A,B,C,D分别是32位的寄存器,初始化使用的是十六进制表示的数字,注意低字节在前: word A: 01 23 45 67 word B: 89 ab cd ef word C: fe dc ba 98 word D: 76 54 32 10

以分组为单位迭代处理

对每个512位的数据块进行4轮加密处理,每轮使用不同的非线性函数和常数表。

非线性函数

MD5的每一轮处理使用了不同的非线性函数,这些函数的输入是B、C、D三个变量。以下是每轮使用的非线性函数(x, y, z是32位数):

  • 第一轮:F(B, C, D) = (B & C) | (~B & D)
    逻辑操作:选择B和C相同的位,或者选择B为0时的D位。
  • 第二轮:G(B, C, D) = (B & D) | (C & ~D)
    逻辑操作:选择B和D相同的位,或者选择C的位,D为0时。
  • 第三轮:H(B, C, D) = B ⊕ C ⊕ D
    逻辑操作:对B、C、D执行按位异或操作。
  • 第四轮:I(B, C, D) = C ⊕ (B | ~D)
    逻辑操作:对C执行按位异或,B或非D。

常数表

每轮中使用的常数表(称为T表)是由正弦函数的整数部分生成的64个常数 ,这些常数确保每一轮的操作是独立且混淆原始输入的。

每轮的操作

每轮都会对A、B、C、D进行64次操作,这些操作包括:

选择一个子块M:将512位的数据块分成16个32位的子块M0,M1,M2,,...,M15

计算临时变量K:使用当前轮的非线性函数,结合当前的A、B、C、D值、消息块和一个常量表中的值(这些常数是MD5的设计者精心选取的,来源于正弦函数的值)。

循环左移S:将计算出的临时结果进行循环左移,然后与B相加。
在这里插入图片描述

输出更新

每次64次操作完成后,A、B、C、D的值会更新,并累加到上一轮的结果中。最终,这些值组合在一起,形成最后的128位哈希值。

代码实现

md5.h

#ifndef MD5_H
#define MD5_H

typedef struct
{
	unsigned int count[2];
	unsigned int state[4];
	unsigned char buffer[64];
}MD5_CTX;

void MD5Init(MD5_CTX* context);
void MD5Update(MD5_CTX* context, unsigned char* input, unsigned int inputlen);
void MD5Final(MD5_CTX* context, unsigned char digest[16]);

#endif

md5.c

#include <string.h>
#include "md5.h"

#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
	{ \
		a += F(b,c,d) + x + ac; \
		a = ROTATE_LEFT(a,s); \
		a += b; \
	}
#define GG(a,b,c,d,x,s,ac) \
	{ \
		a += G(b,c,d) + x + ac; \
		a = ROTATE_LEFT(a,s); \
		a += b; \
	}
#define HH(a,b,c,d,x,s,ac) \
	{ \
		a += H(b,c,d) + x + ac; \
		a = ROTATE_LEFT(a,s); \
		a += b; \
	}
#define II(a,b,c,d,x,s,ac) \
	{ \
		a += I(b,c,d) + x + ac; \
		a = ROTATE_LEFT(a,s); \
		a += b; \
	}

void MD5Transform(unsigned int state[4], unsigned char block[64]);
void MD5Encode(unsigned char* output, unsigned int* input, unsigned int len);
void MD5Decode(unsigned int* output, unsigned char* input, unsigned int len);

unsigned char PADDING[] = {
	0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

void MD5Init(MD5_CTX* context) {
	context->count[0] = 0;
	context->count[1] = 0;
	context->state[0] = 0x67452301;
	context->state[1] = 0xEFCDAB89;
	context->state[2] = 0x98BADCFE;
	context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX* context, unsigned char* input, unsigned int inputlen)
{
	unsigned int i = 0, index = 0, partlen = 0;
	index = (context->count[0] >> 3) & 0x3F;
	partlen = 64 - index;
	context->count[0] += inputlen << 3;
	if (context->count[0] < (inputlen << 3)) {
		context->count[1]++;
	}
	context->count[1] += inputlen >> 29;

	if (inputlen >= partlen) {
		memcpy(&context->buffer[index], input, partlen);
		MD5Transform(context->state, context->buffer);
		for (i = partlen; i + 64 <= inputlen; i += 64) {
			MD5Transform(context->state, &input[i]);
		}
		index = 0;
	}
	else {
		i = 0;
	}

	memcpy(&context->buffer[index], &input[i], inputlen - i);
}

void MD5Final(MD5_CTX* context, unsigned char digest[16]) {
	unsigned int index = 0, padlen = 0;
	unsigned char bits[8];
	index = (context->count[0] >> 3) & 0x3F;
	padlen = (index < 56) ? (56 - index) : (120 - index);
	MD5Encode(bits, context->count, 8);
	MD5Update(context, PADDING, padlen);
	MD5Update(context, bits, 8);
	MD5Encode(digest, context->state, 16);
}
void MD5Encode(unsigned char* output, unsigned int* input, unsigned int len) {
	unsigned int i = 0, j = 0;
	while (j < len) {
		output[j] = input[i] & 0xFF;
		output[j + 1] = (input[i] >> 8) & 0xFF;
		output[j + 2] = (input[i] >> 16) & 0xFF;
		output[j + 3] = (input[i] >> 24) & 0xFF;
		i++;
		j += 4;
	}
}
void MD5Decode(unsigned int* output, unsigned char* input, unsigned int len) {
	unsigned int i = 0, j = 0;
	while (j < len) {
		output[i] = (input[j]) | (input[j + 1] << 8) | (input[j + 2] << 16) | (input[j + 3] << 24);
		i++;
		j += 4;
	}
}
void MD5Transform(unsigned int state[4], unsigned char block[64]) {
	unsigned int a = state[0];
	unsigned int b = state[1];
	unsigned int c = state[2];
	unsigned int d = state[3];
	unsigned int x[64];

	MD5Decode(x, block, 64);

	FF(a, b, c, d, x[0], 7, 0xd76aa478);
	FF(d, a, b, c, x[1], 12, 0xe8c7b756);
	FF(c, d, a, b, x[2], 17, 0x242070db);
	FF(b, c, d, a, x[3], 22, 0xc1bdceee);
	FF(a, b, c, d, x[4], 7, 0xf57c0faf);
	FF(d, a, b, c, x[5], 12, 0x4787c62a);
	FF(c, d, a, b, x[6], 17, 0xa8304613);
	FF(b, c, d, a, x[7], 22, 0xfd469501);
	FF(a, b, c, d, x[8], 7, 0x698098d8);
	FF(d, a, b, c, x[9], 12, 0x8b44f7af);
	FF(c, d, a, b, x[10], 17, 0xffff5bb1);
	FF(b, c, d, a, x[11], 22, 0x895cd7be);
	FF(a, b, c, d, x[12], 7, 0x6b901122);
	FF(d, a, b, c, x[13], 12, 0xfd987193);
	FF(c, d, a, b, x[14], 17, 0xa679438e);
	FF(b, c, d, a, x[15], 22, 0x49b40821);

	GG(a, b, c, d, x[1], 5, 0xf61e2562);
	GG(d, a, b, c, x[6], 9, 0xc040b340);
	GG(c, d, a, b, x[11], 14, 0x265e5a51);
	GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
	GG(a, b, c, d, x[5], 5, 0xd62f105d);
	GG(d, a, b, c, x[10], 9, 0x2441453);
	GG(c, d, a, b, x[15], 14, 0xd8a1e681);
	GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
	GG(a, b, c, d, x[9], 5, 0x21e1cde6);
	GG(d, a, b, c, x[14], 9, 0xc33707d6);
	GG(c, d, a, b, x[3], 14, 0xf4d50d87);
	GG(b, c, d, a, x[8], 20, 0x455a14ed);
	GG(a, b, c, d, x[13], 5, 0xa9e3e905);
	GG(d, a, b, c, x[2], 9, 0xfcefa3f8);
	GG(c, d, a, b, x[7], 14, 0x676f02d9);
	GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);


	HH(a, b, c, d, x[5], 4, 0xfffa3942);
	HH(d, a, b, c, x[8], 11, 0x8771f681);
	HH(c, d, a, b, x[11], 16, 0x6d9d6122);
	HH(b, c, d, a, x[14], 23, 0xfde5380c);
	HH(a, b, c, d, x[1], 4, 0xa4beea44);
	HH(d, a, b, c, x[4], 11, 0x4bdecfa9);
	HH(c, d, a, b, x[7], 16, 0xf6bb4b60);
	HH(b, c, d, a, x[10], 23, 0xbebfbc70);
	HH(a, b, c, d, x[13], 4, 0x289b7ec6);
	HH(d, a, b, c, x[0], 11, 0xeaa127fa);
	HH(c, d, a, b, x[3], 16, 0xd4ef3085);
	HH(b, c, d, a, x[6], 23, 0x4881d05);
	HH(a, b, c, d, x[9], 4, 0xd9d4d039);
	HH(d, a, b, c, x[12], 11, 0xe6db99e5);
	HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
	HH(b, c, d, a, x[2], 23, 0xc4ac5665);


	II(a, b, c, d, x[0], 6, 0xf4292244);
	II(d, a, b, c, x[7], 10, 0x432aff97);
	II(c, d, a, b, x[14], 15, 0xab9423a7);
	II(b, c, d, a, x[5], 21, 0xfc93a039);
	II(a, b, c, d, x[12], 6, 0x655b59c3);
	II(d, a, b, c, x[3], 10, 0x8f0ccc92);
	II(c, d, a, b, x[10], 15, 0xffeff47d);
	II(b, c, d, a, x[1], 21, 0x85845dd1);
	II(a, b, c, d, x[8], 6, 0x6fa87e4f);
	II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
	II(c, d, a, b, x[6], 15, 0xa3014314);
	II(b, c, d, a, x[13], 21, 0x4e0811a1);
	II(a, b, c, d, x[4], 6, 0xf7537e82);
	II(d, a, b, c, x[11], 10, 0xbd3af235);
	II(c, d, a, b, x[2], 15, 0x2ad7d2bb);
	II(b, c, d, a, x[9], 21, 0xeb86d391);
	state[0] += a;
	state[1] += b;
	state[2] += c;
	state[3] += d;
}



main.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "md5.h"  // 包含你的 MD5 实现

int main() {
    MD5_CTX* context = (MD5_CTX*)malloc(sizeof(MD5_CTX));
    memset(context, 0, sizeof(context));
    if (context == NULL) {
        printf("Memory allocation failed.\n");
        return 1; // 分配失败,返回错误码
    }
    unsigned char digest[16];
    char input[] = "Bileton";
    int i;

    // 初始化 MD5 上下文
    MD5Init(context);

    // 更新 MD5 上下文,处理输入数据
    MD5Update(context, (unsigned char*)input, strlen(input));

    // 生成最终的 MD5 哈希值
    MD5Final(context, digest);

    // 打印 MD5 哈希值,格式化为 16 进制字符串
    printf("MD5(\"%s\") = ", input);
    for (i = 0; i < 16; i++) {
        printf("%02x", digest[i]);  //MD5("Bileton") = 1483ab1f77ea828faa5f78514d2765c1
    }
    printf("\n");
    free(context);
    return 0;
}

github项目地址:https://github.com/talent518/md5/blob/master/md5.c

代码分析

MD5Update()

void MD5Update(MD5_CTX* context, unsigned char* input, unsigned int inputlen)
{
	unsigned int i = 0, index = 0, partlen = 0;
	
	// 计算当前 context->buffer 中已有数据的字节偏移量,即输入数据应该存储到缓冲区中的位置。
	// 右移三位相当于除以8,得到字节数,然后 & 0x3F 取模确保index处于0-63的范围。
	index = (context->count[0] >> 3) & 0x3F;
	
	// 当前块剩余空间大小
	partlen = 64 - index;
	
	// 将输入数据长度转换为比特,并累加到 context->count[0] 中。
	context->count[0] += inputlen << 3;

	// 如果 context->count[0] 发生了溢出(即新值变小了),说明输入的数据长度超过了 2^32 比特。
	if (context->count[0] < (inputlen << 3)) {
		// 高位部分记录溢出
		context->count[1]++;
	}
	
	// 将输入数据长度(字节数)的高位部分加到 count[1] 中。
	context->count[1] += inputlen >> 29;
	
	// 数据输入长度大于或等于分组长度
	if (inputlen >= partlen) {
		// 把数据copy到缓冲区
		memcpy(&context->buffer[index], input, partlen);
		
		// 进行轮加密
		MD5Transform(context->state, context->buffer);
		for (i = partlen; i + 64 <= inputlen; i += 64) {
			MD5Transform(context->state, &input[i]);
		}
		index = 0;
	}
	else {
		i = 0;
	}
	// 最后,将输入中剩余的部分(不足 64 字节)复制到 buffer 中,以便在下一次调用时继续处理。
	memcpy(&context->buffer[index], &input[i], inputlen - i);
}

总结

场景:数据长度小于 448 位(56 字节)。

假设输入数据长度为 N 字节,并且 N < 56。

第一步:调用 MD5Update

  • 数据输入:假设输入的数据长度是 N 字节,N < 56。例如,输入数据是 20 字节。
  • MD5 缓存处理:MD5Update 会把这 N 字节的数据填充到 context->buffer 中。由于 N 小于 56 字节,所以这次调用并不会触发 MD5Transform 计算,只是把数据暂存到 buffer 中。
  • 效果:此时,buffer 中的内容为 20 字节的数据,buffer 的剩余空间是 64 - 20 = 44 字节。

第二步:调用 MD5Final 进行填充和处理

MD5Final 负责在数据的末尾进行填充,使数据长度变为 512 位(64 字节)的整数倍,以满足 MD5 的分块运算要求。
填充过程(这里要经过两次MD5Update)

  • 添加 1 位:在数据末尾加上一个 1 位(0x80 表示)。这会占用 1 字节,并用零填充剩余的位。
  • 补零到 448 位:在 1 位之后继续填充 0 直到达到 448 位(56 字节)。对于我们假设的 20 字节输入,填充后 buffer 里有 20(原始数据) + 1(0x80) + 35(0 值填充) = 56 字节。
  • 附加原始数据长度:在 buffer 的末尾添加 8 字节,表示原始数据长度的二进制表示(单位是比特)。如果原始数据是 N 字节,则这里附加 N * 8 位的表示。这个长度信息用于表示数据的实际大小。

MD5Transform:
在填充和附加长度信息后,buffer 的总长度变为 64 字节(512 位),刚好满足 MD5Transform 的要求。
调用 MD5Transform 对填充后的 64 字节块进行 MD5 计算,更新 context->state,即内部的 MD5 状态。

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

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

相关文章

60. 排列序列【回溯】

文章目录 60. 排列序列解题思路Go代码 60. 排列序列 60. 排列序列 给出集合 [1,2,3,...,n]&#xff0c;其所有元素共有 n! 种排列。 按大小顺序列出所有排列情况&#xff0c;并一一标记&#xff0c;当 n 3 时, 所有排列如下&#xff1a; “123”“132”“213”“231”“31…

SpringBoot 集成GPT实战,超简单详细

Spring AI 介绍 在当前的AI应用开发中&#xff0c;像OpenAI这样的GPT服务提供商主要通过HTTP接口提供服务&#xff0c;这导致大部分Java开发者缺乏一种标准化的方式来接入这些强大的语言模型。Spring AI Alibaba应运而生&#xff0c;它作为Spring团队提供的一个解决方案&…

Spring Boot 3 文件管理:上传、下载、预览、查询与删除(全网最全面教程)

在现代Web应用中&#xff0c;文件管理是一个非常重要的功能。Spring Boot作为Java开发领域的热门框架&#xff0c;提供了丰富的工具和API来简化文件管理的操作。本文将详细介绍如何使用Spring Boot 3进行文件的上传、下载、预览、查询与删除操作&#xff0c;并提供一个完整的示…

OpenCV 环境配置

首先下载opencv&#xff0c;在opencv官网进行下载。 按照上面的步骤&#xff0c;点击进去 滑至底部&#xff0c;不切换至不同页&#xff0c;选择合适的版本进行下载(Window系统选择Windows版本进行下载)。 接下来以4.1.2版本为例&#xff1a; 点击之后会进入这个页面&#xff…

聚类分析 | NRBO-GMM聚类优化算法

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新)NRBO-GMM聚类优化算法 (NRBO聚类优化&#xff0c;创新&#xff0c;独家) 牛顿-拉夫逊优化算法优化GMM高斯混合聚类优化算法 matlab语言&#xff0c;一键出图&#xff0c;直接运行 1.牛顿-拉夫逊优化算法(New…

STM32—BKP备份寄存器RTC实时时钟

1.BKP简介 BKP(Backup Registers)备份寄存器BKP可用于存储用户应用程序数据。当VDD&#xff08;2.0~3.6V&#xff09;电源被切断&#xff0c;他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒&#xff0c;或系统复位或电源复位时&#xff0c;他们也不会被复位TAMP…

教培机构如何向知识付费转型

在数字化时代&#xff0c;知识付费已成为一股不可忽视的潮流。面对这一趋势&#xff0c;教育培训机构必须积极应对&#xff0c;实现向知识付费的转型&#xff0c;以在新的市场环境中立足。 一、教培机构应明确自身的知识定位。 在知识付费领域&#xff0c;专业性和独特性是关键…

【Python】selenium获取鼠标在网页上的位置,并定位到网页位置模拟点击的方法

在使用Selenium写自动化爬虫时&#xff0c;遇到验证码是常事了。我在写爬取测试的时候&#xff0c;遇到了点击型的验证码&#xff0c;例如下图这种&#xff1a; 这种看似很简单&#xff0c;但是它居然卡爬虫&#xff1f;用简单的点触验证码的方法来做也没法实现 平常的点触的方…

数据迁移:如何保证在不停机的情况下平滑的迁移数据

1. 引言 数据迁移是一个常见的需求&#xff0c;比如以下的场景&#xff0c;我们都需要进行数据迁移。 大表修改表结构单表拆分进行分库分表、扩容系统重构&#xff0c;使用新的表结构来存储数据 2. 迁移准备 2.1 备份工具 2.1.1 mysqldump mysqldump 是 MySQL 自带的用于…

【计网】从零开始认识https协议 --- 保证安全的网络通信

在每个死胡同的尽头&#xff0c; 都有另一个维度的天空&#xff0c; 在无路可走时迫使你腾空而起&#xff0c; 那就是奇迹。 --- 廖一梅 --- 从零开始认识https协议 1 什么是https协议2 https通信方案2.1 只使用对称加密2.2 只使用非对称加密2.3 双方都使用非对称加密2.4 …

Winform和WPF的技术对比

WinForms&#xff08;Windows Forms&#xff09;和WPF&#xff08;Windows Presentation Foundation&#xff09;是用于创建桌面应用程序的两种技术。尽管两者都可以用于开发功能强大的Windows应用程序&#xff0c;但它们的设计理念、功能和开发体验都有显著区别。在本文中&…

(亲测可行)ubuntu下载安装c++版opencv4.7.0和4.5.0 安装opencv4.5.0报错及解决方法

文章目录 &#x1f315;系统配置&#x1f315;打开终端&#xff0c;退出anacoda激活环境(如果有的话)&#x1f315;安装依赖&#x1f319;安装g, cmake, make, wget, unzip&#xff0c;若已安装&#xff0c;此步跳过&#x1f319;安装opencv依赖的库&#x1f319;安装可选依赖 …

Smartfusion2开发环境的搭建

Libero软件安装包括libero安装、bibero补丁安装、bibero的license添加和官方ip库的添加等4部分内容组成。具体内容如下所示&#xff1a; 1 Libero软件安装 1、解压LiberoSoC_v11.8的安装包到当前目录&#xff0c;然后运行Libero中的可执行软件进行安装&#xff1b; 图1 双击l…

Javascript实现Punycode编码/解码

Punycode编码/解码的Javascript实现。 用法 const punycode require(punycode); console.log(punycode.encode(用法)); //nwwn1p console.log(punycode.decode(nwwn1p)) //用法console.log(punycode.toIDN(用法.中国)); //xn--nwwn1p.xn--fiqs8s console.log(punycode.fromI…

【AAOS】Android Automotive 13模拟器源码下载及编译

源码下载 repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r69 repo sync -c --no-tags --no-clone-bundle 源码编译 source build/envsetup.sh lunch sdk_car_x86_64-userdebug make -j8 运行效果 emualtor HomeMapAll appsSettings…

CUDA-X

NVIDIA CUDA-X 文章目录 前言一、CUDA-X 微服务CUDA-X 微服务CUDA-X 库二、CUDA-X 数据处理三、CUDA-X AI四、CUDA-X HPC总结前言 适用于 AI 的采用 GPU 加速的微服务和库。 释放 GPU 在 AI 应用程序中的潜能 探索 NVIDIA CUDA-X AI 正在推动变革的 AI 领域和可在其中使用的 G…

win10 解决Qt编译得到的可执行文件 *.exe 无法启动的问题

问题描述 在Qt 5.12.4 写了一个服务端程序&#xff0c;编译可以通过&#xff0c;但是打开debug目录下的可执行文件&#xff0c;就报以下错误&#xff1a; 解决方案 方法一 复制缺失的dll到TCPServer.exe目录下 方法二 可能是系统环境变量没有配好 将你电脑上的Qt安装目录…

linux入门——“权限”

linux中有权限的概念&#xff0c;最常见的就是安装一些命令的时候需要输入sudo来提权&#xff0c;那么为什么要有这个东西呢&#xff1f; linux是一个多用户操作系统&#xff0c;很多东西看起来是有很多分&#xff0c;但是实际的存储只有一份&#xff08;比如命令&#xff0c;不…

网站在对抗机器人攻击的斗争中失败了

95% 的高级机器人攻击都未被发现&#xff0c;这一发现表明当前的检测和缓解策略存在缺陷。 这表明&#xff0c;虽然一些组织可能拥有基本的防御能力&#xff0c;但他们没有足够的能力应对更复杂的攻击。 例如利用人工智能和机器学习来模仿人类行为的攻击。 这些统计数据强调…

数据结构之顺序表详解:从原理到C语言实现

引言 在上一篇文章中我们讲到了时间复杂度与空间复杂度&#xff0c;今天我们接着讲数据结构中的内容。 数据的存储和组织方式决定了程序的效率。而顺序表&#xff0c;也就是大家熟悉的数组&#xff0c;正是我们编程中的“起步工具”。它简单易懂&#xff0c;却能帮你解决许多…