【我的 Anti-AV 学习手札】DLL注入

无敌舍友s神免杀学了一个阶段,达者为师,向s师傅学习!!
ps:我的基础实在薄弱,WIN编程甚至都没做过,所以笔记翔实些

在这里插入图片描述


一、注入思路

1.在进程中开辟一段空间
2.存入dll绝对路径地址的字符串
3.使用RemoteCreateThread函数带起LoadLibrary函数带起dll

二、具体实现

(一)获取进程ID

CreateToolhelp32SnapshotProcess32FirstProcess32Next函数。这些函数可以让你遍历系统中所有运行的进程,并获取每个进程的名称和ID。

  1. 创建进程快照: 使用CreateToolhelp32Snapshot函数创建一个系统快照,包含所有进程的信息。
  2. 遍历进程: 使用Process32FirstProcess32Next函数遍历快照中的所有进程。
  3. 匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
  4. 返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。

1、CreateToolhelp32Snapshot

CreateToolhelp32Snapshot是Windows API中的一个函数,用于创建系统快照(snapshot),包含指定类型的系统信息。这个快照可以包含进程、线程、堆、模块等信息。创建快照后,你可以使用其他工具帮助API函数(如Process32FirstProcess32Next)来遍历这些信息。

HANDLE WINAPI CreateToolhelp32Snapshot(
  DWORD dwFlags,
  DWORD th32ProcessID
);
  1. dwFlags: 指定快照中包含的信息类型,可以是以下值的组合:

    • TH32CS_SNAPHEAPLIST: 包含指定进程的堆列表。
    • TH32CS_SNAPMODULE: 包含指定进程的模块列表。
    • TH32CS_SNAPPROCESS: 包含系统中所有进程的列表。
    • TH32CS_SNAPTHREAD: 包含系统中所有线程的列表。
    • TH32CS_SNAPALL: 包含所有上述信息(等同于TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD)。
    • TH32CS_INHERIT: 指定快照句柄是可继承的。
  2. th32ProcessID: 指定进程ID,如果dwFlags包含TH32CS_SNAPHEAPLISTTH32CS_SNAPMODULETH32CS_SNAPALL,则该参数指定要包含在其快照中的进程ID。如果dwFlags包含TH32CS_SNAPPROCESSTH32CS_SNAPTHREAD,则该参数可以为0,表示包含所有进程或线程。

  • 如果函数成功,返回一个快照句柄(HANDLE)。
  • 如果函数失败,返回INVALID_HANDLE_VALUE(通常为-1)。
    //创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
    HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        //差错控制
    if (snapShot == INVALID_HANDLE_VALUE) {
        printf("[-] SnapShot ERROR");
        getchar();
        exit(-1);
    }

2、Process32First和Process32Next

Process32FirstProcess32Next 是 Windows API 中的两个函数,用于遍历通过 CreateToolhelp32Snapshot 创建的进程快照中的所有进程。这两个函数通常一起使用,以遍历系统中的所有进程。

BOOL WINAPI Process32First(
  HANDLE hSnapshot,
  LPPROCESSENTRY32 lppe
);
BOOL WINAPI Process32Next(
  HANDLE hSnapshot,
  LPPROCESSENTRY32 lppe
);
  • hSnapshot: 通过 CreateToolhelp32Snapshot 创建的快照句柄。
  • lppe: 指向 PROCESSENTRY32 结构的指针,用于接收进程信息。
typedef struct tagPROCESSENTRY32 {
    DWORD   dwSize;
    DWORD   cntUsage;
    DWORD   th32ProcessID;      // 进程ID
    ULONG_PTR th32DefaultHeapID;
    DWORD   th32ModuleID;
    DWORD   cntThreads;
    DWORD   th32ParentProcessID; // 父进程ID
    LONG    pcPriClassBase;
    DWORD   dwFlags;
    CHAR    szExeFile[MAX_PATH]; // 进程可执行文件名
} PROCESSENTRY32, *PPROCESSENTRY32;
  //遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
    PROCESSENTRY32 p32;
    p32.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(hSnapShot, &p32)) {
        do {
            if (wcscmp(p32.szExeFile, processName)){
                TargetID = p32.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapShot, &p32));
    }
    else {
        printf("[-] Get first process ERROR");
        getchar();
        exit(-1);
    }
    return TargetID;

3、GetProcessIDByName函数实现

#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <string>
#include <cstdio>

DWORD GetProcessIDByName(LPCTSTR processName) {
    DWORD TargetID = -1;
    //创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        //差错控制
    if (hSnapShot == INVALID_HANDLE_VALUE) {
        printf("[-] SnapShot ERROR");
        getchar();
        return -1;
    }
    //遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
    PROCESSENTRY32 p32;
    p32.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(hSnapShot, &p32)) {
        do {
    //匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
            if (!wcscmp(p32.szExeFile, processName)){
                TargetID = p32.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapShot, &p32));
    }
    else {
        printf("[-] Get first process ERROR");
        getchar();
        return -1;
    }
    //返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。
    return TargetID;
}

int main() {
    DWORD id=GetProcessIDByName(L"notepad.exe");
    printf("[+] Find target process id: %d", id);
    return 0;
}

(二)DLL注入

1、OpenProcess

OpenProcess 是 Windows API 中的一个函数,用于获取一个已经存在的进程的句柄。这个句柄可以用来对进程进行各种操作,如读取或写入进程内存、终止进程、查询进程信息等。

HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL  bInheritHandle,
  DWORD dwProcessId
);
  1. dwDesiredAccess
  • 指定对进程的访问权限。可以是一组标志的组合,常见的标志包括:
    • PROCESS_ALL_ACCESS:所有权限。
    • PROCESS_CREATE_THREAD:创建线程。
    • PROCESS_VM_OPERATION:操作进程内存。
    • PROCESS_VM_READ:读取进程内存。
    • PROCESS_VM_WRITE:写入进程内存。
    • PROCESS_DUP_HANDLE:复制句柄。
    • PROCESS_TERMINATE:终止进程。
    • PROCESS_QUERY_INFORMATION:查询进程信息。
    • PROCESS_SET_INFORMATION:设置进程信息。
  1. bInheritHandle

    • 指定返回的句柄是否可以被新创建的子进程继承。如果为 TRUE,则子进程可以继承该句柄;如果为 FALSE,则不能继承。
  2. dwProcessId

    • 指定要打开的进程的标识符(Process ID)。
  • 如果函数成功,返回值是一个指向指定进程的句柄。
  • 如果函数失败,返回值为 NULL。可以使用 GetLastError 函数获取扩展错误信息。
  //1. 获取目标进程句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProcess == NULL) {
        printf("[-] OpenProcess Error!");
        getchar();
        return -1;
    }

2、VirtualAllocEx

VirtualAllocEx 是 Windows API 中的一个函数,用于在指定进程的地址空间内分配内存。这个函数主要用于进程间通信、代码注入等操作。

LPVOID VirtualAllocEx(
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flAllocationType,
  DWORD  flProtect
);
  1. hProcess

    • 目标进程的句柄。这个句柄通常通过 OpenProcess 函数获得。
  2. lpAddress

    • 指定希望分配内存的起始地址。如果传入 NULL,系统会自动选择一个合适的起始地址。
  3. dwSize

    • 要分配的内存区域的大小(以字节为单位)。
  4. flAllocationType

    • 指定内存分配的类型。常见的类型包括:
      • MEM_COMMIT:提交物理存储,为内存区域分配物理内存。
      • MEM_RESERVE:保留地址空间,但不分配物理内存。
      • MEM_RESET:标记指定内存区域的内容为未初始化状态。
      • MEM_RESET_UNDO:取消 MEM_RESET 的影响。
      • MEM_LARGE_PAGES:使用大页内存(需要特殊的权限)。
  5. flProtect

    • 指定内存保护类型。常见的类型包括:
      • PAGE_READONLY:只读。
      • PAGE_READWRITE:读写。
      • PAGE_EXECUTE:可执行。
      • PAGE_EXECUTE_READ:可执行、只读。
      • PAGE_EXECUTE_READWRITE:可执行、读写。
      • PAGE_GUARD:标记为“守卫页”,访问时触发异常。
  • 如果函数成功,返回值是指向分配内存区域起始地址的指针。
  • 如果函数失败,返回值为 NULL。可以使用 GetLastError 函数获取扩展错误信息。
  //2. 在目标进程申请分配一块空间
    DWORD size = (wcslen(dllName)+1)* sizeof(TCHAR);
    LPVOID allocAddr = VirtualAllocEx(hProcess,NULL,size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (allocAddr == NULL) {
        printf("[-] VirtualAllocEx Error!");
        getchar();
        return -1;
    }

3、WriteProcessMemory

WriteProcessMemory 是 Windows API 中的一个函数,它允许你将数据写入另一个进程的内存空间。这个函数通常用于进程间通信、调试、代码注入等场景。

BOOL WriteProcessMemory(
  HANDLE  hProcess,
  LPVOID  lpBaseAddress,
  LPCVOID lpBuffer,
  SIZE_T  nSize,
  SIZE_T  *lpNumberOfBytesWritten
);
  1. hProcess

    • 目标进程的句柄。这个句柄通常通过 OpenProcess 函数获得。
  2. lpBaseAddress

    • 目标进程中要写入数据的目标地址。
  3. lpBuffer

    • 指向要写入目标进程的数据缓冲区的指针。
  4. nSize

    • 要写入的数据的大小(以字节为单位)。
  5. lpNumberOfBytesWritten

    • 指向一个变量的指针,用于接收实际写入的字节数。如果不需要这个信息,可以传入 NULL
  • 如果函数成功,返回值为 TRUE
  • 如果函数失败,返回值为 FALSE。可以使用 GetLastError 函数获取扩展错误信息。
    //3. 往目标进程的目标空间写数据
    if (!WriteProcessMemory(hProcess,addr,dllName,size,NULL)){
        printf("[-] WriteProcessMemory Error!");
        getchar();
        return -1;
    }

4、GetModuleHandle和GetProcAddress

获取 kernel32.dllLoadLibraryW 函数的地址,并将其转换为 LPTHREAD_START_ROUTINE 类型的函数指针。这个操作通常用于创建远程线程或在目标进程中加载 DLL 文件。

  1. 获取模块句柄

    HMODULE hModule = GetModuleHandle(L"kernel32.dll");
    
    • GetModuleHandle 函数返回指定模块的句柄。这里获取的是 kernel32.dll 模块的句柄。
  2. 获取函数地址

    FARPROC pThread = GetProcAddress(hModule, "LoadLibraryW");
    
    • GetProcAddress 函数返回指定模块中指定函数的地址。这里获取的是 LoadLibraryW 函数的地址。
  3. 转换为线程函数指针类型

    LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
    
    • LPTHREAD_START_ROUTINE 是一个指向线程函数的指针类型。这里将 LoadLibraryW 函数的地址转换为 LPTHREAD_START_ROUTINE 类型。

5、CreateRemoteThread

CreateRemoteThread 是 Windows API 中的一个函数,它允许你在另一个进程中创建一个线程。这个函数通常用于进程间通信、代码注入等高级操作。

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);
  1. hProcess

    • 目标进程的句柄。这个句柄通常通过 OpenProcess 函数获得。
  2. lpThreadAttributes

    • 指向 SECURITY_ATTRIBUTES 结构的指针,用于指定线程的安全属性。如果为 NULL,则使用默认安全属性。
  3. dwStackSize

    • 新线程的初始堆栈大小,以字节为单位。如果为 0,则使用与系统相关的默认值。
  4. lpStartAddress

    • 指向新线程的起始地址(函数指针)。这个地址必须在目标进程的地址空间中有效。
  5. lpParameter

    • 传递给新线程的参数。这个参数可以是任何类型,但必须是目标进程的地址空间中的有效地址。
  6. dwCreationFlags

    • 控制线程创建的标志。常见的标志包括:
      • 0:线程在创建后立即运行。
      • CREATE_SUSPENDED:线程在创建后处于挂起状态,可以通过 ResumeThread 函数恢复运行。
  7. lpThreadId

    • 指向一个变量的指针,用于接收新线程的 ID。如果不需要这个信息,可以传入 NULL
  • 如果函数成功,返回值为新线程的句柄。
  • 如果函数失败,返回值为 NULL。可以使用 GetLastError 函数获取扩展错误信息。
    //5. CreateRemoteThread创建线程
    HANDLE remoteThread = CreateRemoteThread(hProcess,NULL,0, addr, allocAddr, 0,NULL);
    if (remoteThread == NULL) {
        printf("[-] CreateRemoteThread Error!");
        getchar();
        return -1;
    }

6、WaitForSingleObject

WaitForSingleObject 是 Windows API 中的一个函数,用于等待指定的内核对象进入信号状态。这个函数常用于多线程编程中,以实现线程同步。

DWORD WaitForSingleObject(
    HANDLE hHandle,      // 要等待的内核对象的句柄
    DWORD dwMilliseconds // 等待的时间(以毫秒为单位)
);
  1. hHandle:要等待的内核对象的句柄。可以是以下类型的句柄:

    • 线程(CreateThread 返回的句柄)
    • 进程(CreateProcess 返回的句柄)
    • 事件(CreateEvent 返回的句柄)
    • 信号量(CreateSemaphore 返回的句柄)
    • 互斥体(CreateMutex 返回的句柄)
    • 定时器(CreateWaitableTimer 返回的句柄)
    • 其他的可等待对象
  2. dwMilliseconds:等待的时间(以毫秒为单位)。如果这个值为 INFINITE,函数将无限期地等待下去,直到对象变为有信号状态。如果这个值为 0,函数将立即返回,不等待。

WaitForSingleObject 返回以下几种可能的值:

  • WAIT_OBJECT_0:指定的对象变为有信号状态。
  • WAIT_ABANDONED:当等待的对象是互斥体时,互斥体被放弃,通常是因为持有互斥体的线程在退出时没有释放它。
  • WAIT_TIMEOUT:在指定的时间内,对象没有变为有信号状态。
  • WAIT_FAILED:函数调用失败,可以使用 GetLastError 获取错误码。
    //6. 等待线程结束
    WaitForSingleObject(remoteThread, INFINITE);

7、VirtualFreeEx

VirtualFreeEx 是 Windows API 中的一个函数,用于在指定的进程的地址空间中释放之前通过 VirtualAllocEx 分配的虚拟内存区域。这个函数允许你释放内存页,并可以选择将这些页面中的内存标记为可用(即解除分配)。

BOOL VirtualFreeEx(
    HANDLE hProcess,      // 目标进程的句柄
    LPVOID lpAddress,     // 要释放的内存地址
    SIZE_T dwSize,        // 要释放的内存大小
    DWORD dwFreeType      // 释放类型
);
  1. hProcess:目标进程的句柄。这个句柄必须具有 PROCESS_VM_OPERATION 访问权限。
  2. lpAddress:指向要释放的内存区域的起始地址的指针。这个地址必须是之前通过 VirtualAllocEx 分配的内存区域的起始地址。
  3. dwSize:要释放的内存区域的大小(以字节为单位)。如果 dwSize 为 0 并且 dwFreeTypeMEM_RELEASE,则整个区域将被释放。
  4. dwFreeType:指定释放操作的类型。可以是以下值之一:
    • MEM_DECOMMIT:解除分配指定页范围内的内存,但不释放这些页面。这些页面可以稍后重新提交。
    • MEM_RELEASE:释放指定页范围内的内存,并将其标记为可用。释放后,这些页面不能再被访问。
  • 如果函数成功,返回值为非零值。
  • 如果函数失败,返回值为 0。可以使用 GetLastError 获取错误代码。
    //7. 释放dll空间
    VirtualFreeEx(hProcess, allocAddr, size, MEM_RELEASE);

三、完整代码

(一)弹窗demo.dll

#include <windows.h>
#include <iostream>

// 导出函数:显示消息框
extern "C" __declspec(dllexport) void ShowMessage() {
    MessageBox(NULL, TEXT("Hello from MyDLL!"), TEXT("Message"), MB_OK);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        ShowMessage();
    }
    return TRUE;
}

(二)DLL注入

#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <string>
#include <cstdio>

DWORD GetProcessIDByName(LPCTSTR processName) {
	DWORD TargetID = -1;
	//创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	//差错控制
	if (hSnapShot == INVALID_HANDLE_VALUE) {
		printf("[-] SnapShot ERROR");
		getchar();
		return -1;
	}
	//遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
	PROCESSENTRY32 p32;
	p32.dwSize = sizeof(PROCESSENTRY32);
	if (Process32First(hSnapShot, &p32)) {
		do {
			//匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
			if (!wcscmp(p32.szExeFile, processName)) {
				TargetID = p32.th32ProcessID;
				break;
			}
		} while (Process32Next(hSnapShot, &p32));
	}
	else {
		printf("[-] Get first process ERROR");
		getchar();
		return -1;
	}
	//返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。
	return TargetID;
}

DWORD RemoteThreadInject(DWORD pid, LPCUWSTR dllName) {
	//1. 获取目标进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProcess == NULL) {
		printf("[-] OpenProcess Error!");
		getchar();
		return -1;
	}
	//2. 在目标进程申请分配一块空间
	DWORD size = (wcslen(dllName)+1)* sizeof(TCHAR);
	LPVOID allocAddr = VirtualAllocEx(hProcess,NULL,size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (allocAddr == NULL) {
		printf("[-] VirtualAllocEx Error!");
		getchar();
		return -1;
	}
	//3. 往目标进程的目标空间写数据
	if (!WriteProcessMemory(hProcess,allocAddr,dllName,size,NULL)){
		printf("[-] WriteProcessMemory Error!");
		getchar();
		return -1;
	}
	//4. 获取 `kernel32.dll` 中 `LoadLibraryW` 函数的地址,并将其转换为 `LPTHREAD_START_ROUTINE` 类型的函数指针
	HMODULE hModule = GetModuleHandle(L"kernel32.dll");
	FARPROC pThread = GetProcAddress(hModule, "LoadLibraryW");
	LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
	//5. CreateRemoteThread创建线程
	HANDLE remoteThread = CreateRemoteThread(hProcess,NULL,0, addr, allocAddr, 0,NULL);
	if (remoteThread == NULL) {
		printf("[-] CreateRemoteThread Error!");
		getchar();
		return -1;
	}
	//6. 等待线程结束
	WaitForSingleObject(remoteThread, INFINITE);
	//7. 释放dll空间
	VirtualFreeEx(hProcess, allocAddr, size, MEM_RELEASE);
	//8. 关闭句柄
	CloseHandle(hProcess);
	return TRUE;
}
int main() {
	DWORD processID = GetProcessIDByName(L"notepad.exe");
	if (processID < 0) {
		printf("[-] No Target Process");
		getchar();
		exit(-1);
	}
	if (RemoteThreadInject(processID, L"C:\\Users\\user\\Desktop\\WIN-AntiAV\\DLL-popwindow\\x64\\Debug\\DLL-popwindow.dll") < 0) {
		printf("[-] Inject Failed");
		getchar();
		exit(-1);
	}
	return 0;
}

四、效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词&#xff1a;一多、响应式、媒体查询、栅格布局、断点、UI 随着设备形态的逐渐增多&#xff0c;应用界面适配也面临着很大问题&#xff0c;在以往的安卓应用开发过程中&#xff0c;往往需要重新开发一套适用于大屏展示的应用&#xff0c;耗时又耗力&#xff0c;而鸿蒙提供…

linux 安装 mongodb

选择MongoDB版本 https://www.mongodb.com/try/download/community-kubernetes-operator 我的系统是centos7.9 这里只能最高只能选择mongo7 复制下载链接&#xff1a;https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.15.tgz 获取安装教程&#xff1a; h…

《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析

导读&#xff1a;SQL 诞生于 20 世纪 70 年代&#xff0c;至今已有半个世纪。SQL 语言具有语法简单&#xff0c;低学习门槛等特点&#xff0c;诞生之后迅速普及与流行开来。由于 SQL 具有易学易用的特点&#xff0c;使得开发人员容易掌握&#xff0c;企业若能在其计算机软件中支…

PostgreSQL pg-xact(clog)目录文件缺失处理

一、 背景 前些天晚上突然收到业务反馈&#xff0c;查询DB中的一个表报错 Could not open file "pg-xact/005E": No such file or directory. 两眼一黑难道是文件损坏了...登录查看DB日志&#xff0c;还好没有其他报错&#xff0c;业务也反馈只有这一个表在从库查询报…

Cursor的chat与composer的使用体验分享

经过一段时间的试用&#xff0c;下面对 Composer 与 Chat 的使用差别进行总结&#xff1a; 一、长文本及程序文件处理方面 Composer 在处理长文本时表现较为稳定&#xff0c;可以对长文进行更改而不会出现内容丢失的情况。而 Chat 在更改长的程序文件时&#xff0c;有时会删除…

MATLAB课程:AI工具辅助编程——MATLAB+LLMs

给出一些可能有用的方法辅助大家写代码。 方法一&#xff1a;MATLAB软件LLM (不太懂配置的同学们为了省事可以主要用这个方法) 方法一特别针对本门MATLAB教学课程&#xff0c;给出一种辅助ai工具的操作指南。MATLAB中可以安装MatGPT插件&#xff0c;该插件通过调用ChatGPT的API…

2.索引:SQL 性能分析详解

SQL性能分析是数据库优化中重要的一环。通过分析SQL的执行频率、慢查询日志、PROFILE工具以及EXPLAIN命令&#xff0c;能够帮助我们识别出数据库性能的瓶颈&#xff0c;并做出有效的优化措施。以下将详细讲解这几种常见的SQL性能分析工具和方法。 一、SQL 执行频率 SQL执行频率…

使用Go语言编写一个简单的NTP服务器

NTP服务介绍 NTP服务器【Network Time Protocol&#xff08;NTP&#xff09;】是用来使计算机时间同步化的一种协议。 应用场景说明 为了确保封闭局域网内多个服务器的时间同步&#xff0c;我们计划部署一个网络时间同步服务器&#xff08;NTP服务器&#xff09;。这一角色将…

深度学习经典模型之VGGNet

1 VGGNet 1.1 模型介绍 ​ VGGNet是由牛津大学视觉几何小组&#xff08;Visual Geometry Group, VGG&#xff09;提出的一种深层卷积网络结构&#xff0c;他们以7.32%的错误率赢得了2014年ILSVRC分类任务的亚军&#xff08;冠军由GoogLeNet以6.65%的错误率夺得&#xff09;和…

Android的BroadcastReceiver

1.基本概念&#xff1a;BroadCast用于进程间或者线程间通信 本质上是用Binder方法&#xff0c;以AMS为订阅中心&#xff0c;完成注册&#xff0c;发布&#xff0c;监听的操作。 2.简单实现的例子 package com.android.car.myapplication;import android.content.BroadcastRe…

分布式数据库中间件mycat

MyCat MyCat是一个开源的分布式数据库系统&#xff0c;它实现了MySQL协议&#xff0c;可以作为数据库代理使用。 MyCat(中间件)的核心功能是分库分表&#xff0c;即将一个大表水平分割为多个小表&#xff0c;存储在后端的MySQL服务器或其他数据库中。 它不仅支持MySQL&#xff…

Java多线程编程(四)- 阻塞队列,生产者消费者模型,线程池

目录&#xff1a; 一.阻塞队列 二.线程池 一.阻塞队列 1.阻塞队列是⼀种特殊的队列. 也遵守 "先进先出" 的原则 阻塞队列能是⼀种线程安全的数据结构, 并且具有以下特性&#xff1a; 1.1.当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素 1.…

深度剖析JUC中LongAdder类源码

文章目录 1.诞生背景2.LongAdder核心思想3.底层实现&#xff1a;4.额外补充 1.诞生背景 LongAdder是JDK8新增的一个原子操作类&#xff0c;和AtomicLong扮演者同样的角色&#xff0c;由于采用AtomicLong 保证多线程数据同步&#xff0c;高并发场景下会导致大量线程同时竞争更新…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…

树形结构数据

树形结构数据 树形结构数据是一种基础且强大的数据结构&#xff0c;广泛应用于计算机科学和软件开发的各个领域。它模拟了自然界中树的层级关系&#xff0c;通过节点和它们之间的连接来组织数据。在本文中&#xff0c;我们将深入探讨树形结构数据的概念、特点、类型以及它们在…

dell服务器安装ESXI8

1.下载镜像在官网 2.打开ipmi&#xff08;idrac&#xff09;&#xff0c;将esxi镜像挂载&#xff0c;然后服务器开机 3.进入bios设置cpu虚拟化开启&#xff0c;进入boot设置启动选项为映像方式 4..进入安装引导界面3.加载完配置进入安装 系统提示点击继 5.选择安装磁盘进行…

信息安全数学基础(46)域和Galois理论

域详述 定义&#xff1a; 域是一个包含加法、减法、乘法和除法&#xff08;除数不为零&#xff09;的代数结构&#xff0c;其中加法和乘法满足交换律、结合律&#xff0c;并且乘法对加法满足分配律。同时&#xff0c;域中的元素&#xff08;通常称为数&#xff09;在加法和乘法…

Windows端口占用/Java程序启动失败-进程占用的问题解决

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Python酷库之旅-第三方库Pandas(204)

目录 一、用法精讲 951、pandas.IntervalIndex.values属性 951-1、语法 951-2、参数 951-3、功能 951-4、返回值 951-5、说明 951-6、用法 951-6-1、数据准备 951-6-2、代码示例 951-6-3、结果输出 952、pandas.IntervalIndex.from_arrays类方法 952-1、语法 952…