dll注入技术

news2024/12/28 5:02:58

一、dll注入的概念

当一个进程运行时,它会加载并使用一些动态链接库(DLL)来提供额外的功能和资源。这些DLL可以被多个进程共享,使得代码重用和资源共享变得更加高效。DLL注入技术利用了这种共享机制。它允许向正在运行的进程中注入一个动态链接库(DLL),被注入的DLL可以利用它所在的进程的权限执行一些特殊的任务,比如修改进程内存中的数据、劫持进程的执行流程、监控进程的行为等等。

dll注入的本质就是把一个不属于某个进程的dll文件加载到该进程当中。

dll注入技术设计的初衷是为了给第三方应用程序提供功能扩展的一种方式。比如当一款软件的作者因为各种原因停止对软件的更新维护,这样我们就可以通过把软件的扩展功能写入dll文件当中去,然后把dll注入到目标软件的进程当中,就可以和软件自带的dll一样正常使用了

二、dll技术的应用

DLL注入技术可以有多种应用,包括但不限于以下几个方面:

  • 软件调试:开发人员可以使用DLL注入技术来调试应用程序。它可以在目标进程中注入一个调试DLL,用于监视和分析应用程序的行为,以帮助发现和修复错误。因为我们的软件编译好发布之后,由于平台不一样,所以我们完全可以通过别的一般的电脑来模拟用户,测试软件的功能是否一切正常。

  • 病毒编写:恶意软件开发者可以利用DLL注入来实现恶意功能,如窃取用户信息、远程控制目标计算机等。这是一种常见的攻击技术,因此安全软件通常会检测和阻止恶意DLL注入。

  • 反病毒软件:一些反病毒软件利用DLL注入技术来监视和防止恶意软件的运行。它们在目标进程中注入一个监控DLL,以便检测和拦截恶意行为。

  • 游戏修改:一些玩家可能使用DLL注入技术来修改游戏的行为或增加额外的功能。这可能包括修改游戏内存中的数值、实现外挂功能等。然而,这种行为通常被游戏开发者视为违规行为,可能导致封号或其他惩罚。

三、dll相关的API

DLL注入相关API

OpenProcess

打开远程进程

VirtualAllocEx

在远程进程中申请内存空间

WriteProcessMemory

写入数据到远程进程

CreateRemoteThread

创建远程线程

Loadlibrary

加载模块

WaitForSingleObject

等待信号

VirtualFreeEx

释放远程进程内存空间

CloseHandle

关闭句柄

四、编写代码前的准备工作:

1、OpenProcess函数

OpenProcess是Windows操作系统提供的一个函数,它用于打开一个现有的进程,并返回一个与该进程关联的进程句柄。该函数的原型如下:

HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL bInheritHandle,
  DWORD dwProcessId
);

参数说明:

  • dwDesiredAccess:指定进程的访问权限。这可以是一些常量值的组合,如PROCESS_ALL_ACCESS、PROCESS_CREATE_PROCESS、PROCESS_QUERY_INFORMATION等。
  • bInheritHandle:指定新句柄是否可以被继承。如果为TRUE,则新句柄可以被继承;如果为FALSE,则新句柄不可以被继承。
  • dwProcessId:指定要打开的进程的进程ID号。

OpenProcess函数可以用于获取一个现有进程的句柄,以便在该进程中执行一些操作,如读取或写入进程内存、修改进程的安全属性、发送消息等。需要注意的是,不同的进程拥有不同的访问权限,因此在使用OpenProcess函数时需要正确设置进程的访问权限,以避免权限问题导致的操作失败。

OpenProcess函数常用于编写一些系统工具或调试程序,以及一些需要与其他进程交互的应用程序。在编写一些恶意软件或攻击程序时,OpenProcess函数也可以被用于获取另一个进程的句柄,并对其进行一些未授权的操作。因此,在使用OpenProcess函数时需要谨慎,以避免对系统造成不良影响。

2、VirtualAllocEx函数

VirtualAllocEx是Windows操作系统提供的一个函数,用于在指定进程的虚拟地址空间中分配内存。函数原型如下:

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

函数参数说明:

  • hProcess:指定要在哪个进程中分配内存,该参数需要指定要分配内存的进程句柄。
  • lpAddress:指定欲分配的虚拟内存起始地址,如果为NULL,表示由系统自动选择一个合适的地址。
  • dwSize:指定要分配的内存大小,以字节为单位。
  • flAllocationType:指定内存分配的类型,可以使用一些常量值如MEM_COMMIT、MEM_RESERVE等。
  • flProtect:指定内存的保护属性,可以使用一些常量值如PAGE_READWRITE、PAGE_EXECUTE_READ等。

VirtualAllocEx函数的返回值是一个LPVOID类型的指针,指向在指定进程的虚拟地址空间中分配的内存的起始地址。如果函数调用失败,则返回NULL。

使用VirtualAllocEx函数可以实现在指定进程的虚拟地址空间中分配内存,从而使得其他进程可以访问和使用该内存。需要注意的是,使用VirtualAllocEx函数分配的内存需要在使用完毕后通过VirtualFreeEx函数释放,以避免内存泄漏和资源浪费。

3、WriteProcessMemory函数:

WriteProcessMemory函数是Windows API中的一个函数,它可以将数据写入到指定进程的内存中。该函数可以用于在一个进程中注入代码或数据,实现进程间通信等功能。声明如下:

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

参数说明:

  • hProcess:目标进程的句柄。
  • lpBaseAddress:要写入数据的目标进程内存的起始地址。
  • lpBuffer:要写入的数据缓冲区的指针。
  • nSize:要写入的数据的大小,以字节为单位。
  • lpNumberOfBytesWritten:一个指向变量的指针,用于接收实际写入的字节数。

WriteProcessMemory函数返回的是一个布尔类型的值,表示有没有写内存成功。

4、CreateRemoteThread函数

CreateRemoteThread是Windows操作系统提供的一个函数,它可以在指定的进程空间中创建一个远程线程,以便在该进程中执行指定的函数。函数原型如下:

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

函数参数说明:

  • hProcess:指定要在哪个进程中创建线程,该参数需要指定要创建线程的进程句柄。
  • lpThreadAttributes:指定线程的安全属性,如果不需要设置,则传入NULL即可。
  • dwStackSize:指定线程的堆栈大小,如果不需要设置,则传入0即可。
  • lpStartAddress:指定要在远程线程中执行的函数地址。
  • lpParameter:指定要传递给远程线程的参数。
  • dwCreationFlags:指定线程的创建标志,可以使用一些常量值如0、CREATE_SUSPENDED等。
  • lpThreadId:指向一个变量,用于返回线程ID号。

使用CreateRemoteThread函数可以实现在一个进程空间中注入一个线程,并且可以传递一些参数给该线程。该函数常用于编写一些系统工具或调试程序,以及一些需要与其他进程交互的应用程序。

以下是一个使用CreateRemoteThread函数实现DLL注入的示例代码:

#include <windows.h>

int main()
{
    // 获取目标进程句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1234);
    if (hProcess == NULL) {
        return 0;
    }

    // 获取LoadLibrary函数地址
    LPVOID lpLoadLibrary = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
    if (lpLoadLibrary == NULL) {
        CloseHandle(hProcess);
        return 0;
    }

    // 分配内存空间
    LPVOID lpDllPath = VirtualAllocEx(hProcess, NULL, strlen("C:\\test.dll") + 1, MEM_COMMIT, PAGE_READWRITE);
    if (lpDllPath == NULL) {
        CloseHandle(hProcess);
        return 0;
    }

    // 将DLL路径写入目标进程内存
    WriteProcessMemory(hProcess, lpDllPath, "C:\\test.dll", strlen("C:\\test.dll") + 1, NULL);

    // 在目标进程中创建远程线程
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibrary, lpDllPath, 0, NULL);
    if (hThread == NULL) {
        VirtualFreeEx(hProcess, lpDllPath, strlen("C:\\test.dll") + 1, MEM_RELEASE);
        CloseHandle(hProcess);
        return 0;
    }

    // 等待线程结束
    WaitForSingleObject(hThread, INFINITE);

    // 关闭句柄,释放内存
    CloseHandle(hThread);
    VirtualFreeEx(hProcess, lpDllPath, strlen("C:\\test.dll") + 1, MEM_RELEASE);
    CloseHandle(hProcess);

5、LoadLibrary函数

LoadLibrary函数是Windows操作系统提供的一个函数,它可以加载一个动态链接库(DLL)文件到进程空间中,并返回该DLL的句柄。

需要注意的是它仅仅是把dll文件加载到进程的空间,但是想要调用这个dll还需要别的操作,比如在这个dll的DllMain里面添加附加到线程的时候执行等等。

6、如何在VS2019创建一个dll文件?

在 Visual Studio 2019 中创建 DLL 项目的步骤如下:

  1. 打开 Visual Studio 2019,点击 "创建新项目",选择 Visual C++ -> 动态链接库 (DLL) 项目。

  2. 在 "创建新项目" 对话框中,为项目命名并选择保存位置,然后单击 "下一步"。

  3. 在 "应用程序类型" 选项卡下,选择 "DLL" 选项,并将 "附加选项" 选项卡下的 "空项目" 复选框选中。

  4. 单击 "创建",Visual Studio 将生成一个名为 "Dll1" 的默认 DLL 项目。

  5. 在 "解决方案资源管理器" 中,展开 "源文件" 节点并双击 "Dll1.cpp" 文件。

  6. 在 "Dll1.cpp" 文件中编写 DLL 的代码,例如:

#include "pch.h"
#include "framework.h"

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:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) void HelloWorld()
{
    MessageBox(NULL, "Hello, World!", "Dll1.dll", MB_OK);
}

DllMain 是 Windows DLL 中的一个特殊函数,用于处理 DLL 的加载、卸载以及其他状态的通知。

该函数的签名为:

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved);

参数说明:

  • hModule: DLL 的模块句柄。该参数可以用来获取 DLL 中的其他资源,例如资源文件、函数地址等。
  • ul_reason_for_call: 表示 DLL 被加载、卸载或者其他状态的通知。可能的值包括:
    • DLL_PROCESS_ATTACH: DLL 被进程加载时调用。
    • DLL_PROCESS_DETACH: DLL 被进程卸载时调用。
    • DLL_THREAD_ATTACH: 进程创建新线程时调用。
    • DLL_THREAD_DETACH: 进程中线程退出时调用。
  • lpReserved: 保留参数。在实际开发中,一般不使用该参数。

在实际使用中,DllMain 函数常用于执行一些初始化或清理操作,例如:

  • 在 DLL 加载时,进行资源的初始化,例如初始化共享内存、建立临界区等。
  • 在 DLL 卸载时,进行资源的释放,例如清除共享内存、释放临界区等。

需要注意的是,在 DllMain 函数中,有一些操作是不安全的,例如:

  • 调用其他 DLL 函数。这可能会导致死锁或死循环,因为其他 DLL 函数也可能调用 DllMain 函数。
  • 创建新线程。在 DllMain 函数中创建新线程可能会导致死锁,因为该函数会在 DLL 加载之前被调用,此时可能没有完成初始化操作。
  • 调用某些系统函数。某些系统函数可能会使用其他 DLL,这可能会导致死锁或死循环。

因此,在编写 DllMain 函数时需要小心谨慎,避免出现安全问题。

五、dll注入实例编写

我们通过dll注入实现一个小功能:比如我有一个朋友叫浮沉,他最近痴迷于天天尬跑小游戏,但是他妈不想让他天天打电动,于是让我编写一个小软件提示他:人在做,妈再看。从而达到一定的惊醒目的,并且在他吃惊恐惧之余,趁机关掉这个小游戏。话不多说,让我们来一起愉快的编码吧!

1、首先是注入dll文件到目标进程的代码

还是先讲一下整体的思路:

1)首先我们需要对别的进程进行任何的操作,首先一步,也是最重要的一步就是要拿到目标进程的句柄,即通过OpneProcess()函数获取目标进程的句柄,通过句柄来执行后面的一切操作!

2)通过VirtualAllocEx函数在目标进程申请一块内存空间,在这块内存空间当中存放我们需要传递的参数,包括用到的变量等等,甚至还可以在这里写入函数的硬编码来直接调用执行

3)通过WriteProcessMemory函数进行跨进程的读写,主要就是把我们需要用到的数据,包括参数,变量等等写入到刚刚VirtualAllocEx申请的这段内存空间当中,在本文当中就是把LoadLibrary需要加载的dll文件的名称写道这里面来,方便后面给LoadLibrary传参

4)使用CreateRemoteThread函数+LoadLibrary在游戏进程当中创建一个远程线程并执行它,LoadLibrary正好满足CreateRemoteThread当中回调函数的格式,而且每个进程它一定会加载Kernel32.dll这个dll文件,所以就一定可以在远程的线程当中调用LoadLibrary这个函数,那么LoadLibrary函数做什么功能呢?》就是把我们写好的dll文件加载到当前的这个进程里面!

#include<iostream>
using namespace std;
#include<Windows.h>
#include <tlhelp32.h>
#include<tchar.h>
#pragma comment(lib, "msvcrt.lib")

DWORD GetProcessID(LPCTSTR szProcessName)
{
    DWORD dwProcessID = 0;
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapShot == INVALID_HANDLE_VALUE) {
        return dwProcessID;
    }
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    if (!Process32First(hSnapShot, &pe32)) {
        CloseHandle(hSnapShot);
        return dwProcessID;
    }
    do {
        if (_tcsicmp(pe32.szExeFile, szProcessName) == 0) {
            dwProcessID = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapShot, &pe32));
    CloseHandle(hSnapShot);
    return dwProcessID;
}

int main() {

    const char* data = "D://Dll1.dll";
    int buffSize = strlen(data) + 1; 
    DWORD dwProcessId = GetProcessID(L"《天天尬跑》V1.0版本.exe");
    HANDLE hGameProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    char* lpRemoteMem = (char*)VirtualAllocEx(hGameProcess, NULL, buffSize, MEM_COMMIT, PAGE_READWRITE);
    SIZE_T nBytesWritten = 0;
    BOOL bRet = WriteProcessMemory(hGameProcess, lpRemoteMem, data, buffSize, &nBytesWritten);
    if (bRet == FALSE || nBytesWritten != buffSize) {
        CloseHandle(hGameProcess);
        VirtualFreeEx(hGameProcess, lpRemoteMem, 0, MEM_RELEASE);
        return 0;
    }
    HANDLE hThread = CreateRemoteThread(hGameProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpRemoteMem, 0, NULL);

    WaitForSingleObject(hThread, -1);
    VirtualFreeEx(hGameProcess, lpRemoteMem, 0, MEM_RELEASE);
    CloseHandle(hThread);
    CloseHandle(hGameProcess);
    cout << "报告!浮沉的小游戏已经被做掉了!圆满完成任务!" << endl;
    system("pause");
    return 0;

}

2、在dll文件当中具体实现的功能

#include"pch.h"
#include<iostream>
using namespace std;
#include<Windows.h>
#include <tlhelp32.h>
#include<tchar.h>
#pragma comment(lib, "msvcrt.lib")

extern "C" __declspec(dllexport)DWORD GetProcessID(LPCTSTR szProcessName)
{
    DWORD dwProcessID = 0;
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapShot == INVALID_HANDLE_VALUE) {
        return dwProcessID;
    }
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    if (!Process32First(hSnapShot, &pe32)) {
        CloseHandle(hSnapShot);
        return dwProcessID;
    }
    do {
        if (_tcsicmp(pe32.szExeFile, szProcessName) == 0) {
            dwProcessID = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapShot, &pe32));
    CloseHandle(hSnapShot);
    return dwProcessID;
}

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: {
        MessageBoxA(NULL, "浮沉又在打电动噢!", "严重警告!!!", MB_OK);
        //必须要拿到句柄,才能对进程进行操作!!!
        int pid = GetProcessID(L"《天天尬跑》V1.0版本.exe");
        
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, pid);
        if (hProcess == NULL) {
            // 处理打开进程失败的情况
            int err = GetLastError();
            MessageBoxA(NULL, (LPCSTR)GetLastError(), "严重警告!!!", MB_OK);
            return -1;
        }
        BOOL bTerminated = TerminateProcess(hProcess, 0);
        if (bTerminated) {
            MessageBoxA(NULL, (LPCSTR)GetLastError(), "严重警告!!!", MB_OK);
        }
        CloseHandle(hProcess);
        break;
    }
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

在编写dll的时候有两点需要注意:

1、Tandy在这里调试代码的时候遇到了问题:明明包含了需要的头文件,但是却报了一堆找不到的错误:

这种情况下我们要考虑到头文件的包含顺序的问题,一般来说要把".h"这种放到最前面!于是通过把#include"pch.h"放在第一行,就成功编译通过了:

2、记得修改dll的属性为动态链接库

3、最终的执行效果图 

 我们可以看到,当我们执行dll注入的代码之后,浮沉玩游戏的窗口会弹出来一个提示框:浮沉又在打电动噢,然后无论是点击×号,还是点击确定,这个游戏进程都会被毫不留情地关闭掉!😂😂😂😂😂

今天的内容就到这里了,那么恭喜你完成了第三天的学习打开任务!!!

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

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

相关文章

厦大凭什么和清北复一起入选首批国家集成电路产教融合创新平台?对台区位优势吗

最近&#xff0c;有网友提出这样一个问题&#xff0c;众所周知&#xff0c;中国集成电路研究主要集中在北京和上海&#xff0c;清北可以和北京合作&#xff0c;复旦可以和上海合作&#xff0c;那么厦大和谁合作&#xff0c;厦大又凭什么和清北复一起入选了首批国家集成电路产教…

RecyclerView 分层级展示(抽屉) TreeView

先看效果 难点: 数据层级的划分;理清楚层级关系, 剩下的就简单明了了; 1. 第一步 new Adapter, and setAdapter; (不需要setLayoutManager, Adapter里有set); mAdapter new TreeViewAdapter(getContext(),mRecyclerView); mRecyclerView.setAdapter(mAdapter); 2. 第二步, …

素雅的登录界面,简单而优雅

先上效果图&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>*, *::after, *::before {margin: 0;padding: 0;box-sizing: bord…

本地服务器与云服务器哪个好?

本地服务器和云服务器是企业可以使用的两种不同的服务器设置。主要区别在于本地服务器托管&#xff0c;第三方提供商托管云服务器。那么&#xff0c;本地服务器和云服务器哪个更好呢&#xff1f; 接下来&#xff0c;将带大家讨论本地服务器和云服务器的优缺点&#xff0c;并帮…

Golang笔记:使用embed包将静态资源嵌入到程序中

文章目录 目的使用演示//go:embed 指令在WebServer中应用总结 目的 Golang编译程序默认是静态编译&#xff0c;会把相关的库都打包到一起&#xff0c;这在分发部署使用时非常方便。不过如果项目中用到的外部的静态资源文件&#xff0c;通常就需要将这些资源和程序一起拷贝分发…

软件测试面试大全(涵盖了软件测试的全部核心技术点),应对技术面妥妥的

软件测试面试题&#xff1a;项目 1、简单介绍下最近做过的项目 根据自己的项目整理完成&#xff0c;要点&#xff1a; 1&#xff09;项目背景、业务、需求、核心业务的流程 2&#xff09;项目架构&#xff0c;B/S 还是 C/5&#xff0c;数据库用的什么? 中间件用的什么&…

[开源工具] [Unity实战]Jenkins如何配置拉取Git/Jenkins使用Unity一键打包[windows][android]

[开源工具]Jenkins如何配置拉取Git/Jenkins使用Unity一键打包[windows][android] 1.背景介绍1.1Jenkins是什么?1.2用JenkinsUnity的好处? 2.Jenkins安装&使用2.1Java112.2 下载jenkins.war(我用tomcat所以需要,可以用jenkins安装版本)2.3 使用Tomcat92.4将tomcat9解压后,…

1.高级面试-MySQL、Redis、特殊场景、Java

本文目录如下&#xff1a; 高级面试一、MySQLB树有什么优点&#xff1f; image.pngInnoDB 和 MyISAM 的索引结构有什么区别 (聚簇索引-非聚簇索引)&#xff1f; 二、RedisRedis 如何保证存储的都是 热点数据&#xff1f;大量 key 为同一过期时间怎么办&#xff1f;缓存一致性的…

TS构建微信小程序后使用vant weapp框架配置

1.npm 安装 # 通过 npm 安装 npm i vant/weapp -S --production 2.配置app.json 将 app.json 中的 "style": "v2" 去除&#xff0c;小程序的新版基础组件强行加上了许多样式&#xff0c;难以覆盖&#xff0c;不关闭将造成部分组件样式混乱。 3.修改 pr…

ubuntu20 kvm显卡直通实验-a4000

一、环境&#xff1a; 显卡&#xff1a;NVIDIA RTX A4000 系统&#xff1a;Ubuntu20.04 CPU&#xff1a;intel二、安装kvm sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager ovmf添加用户到“libvirt”和“kvm”组 s…

个人简历上的照片有什么要求?这些基本要求要知道

简历对我们来说非常重要&#xff0c;因为它可以帮助我们展示我们的工作经验、技能和教育背景&#xff0c;从而让面试者更好地了解我们的能力和合适程度。简历通常是我们与公司之间的第一次接触&#xff0c;因此它应该清晰、简洁和易于阅读&#xff0c;以便引起面试者的兴趣&…

【机械硬盘重新挂载】

一、背景描述 目前机器上有一个6.5T的机械硬盘&#xff0c;挂在为/home目录&#xff0c;但是目前处于公司业务需要&#xff0c;需要将机械硬盘重新挂载为/data&#xff0c;但是/home与/data中都有业务数据&#xff0c;需要我们谨慎操作。 二、概念解析 挂载源&#xff1a;要…

机器学习项目实战-能源利用率 Part-1(数据清洗)

1. 项目背景 2009年的《当地法案84号》&#xff0c;或纽约市基准法案&#xff0c;要求对能源和用水量进行年度基准测试和披露信息。被覆盖的财产包括单个建筑物的税收地块&#xff0c;其总建筑面积大于50,000平方英尺&#xff08;平方英尺&#xff09;&#xff0c;以及具有超过…

CISP-PTE认证是什么?含金量有多高?

CISP即“注册信息安全专业人员”&#xff0c;由中国信息安全测评中心根据中央编办授权&#xff0c;系国家对信息安全人员资质的最高认可。那么&#xff0c;CISP-PTE认证又是什么呢&#xff1f;它的含金量又有多高&#xff1f;下面&#xff0c;国科科技就为你介绍。 CISP-PTE是…

什么是Redisson分布式锁?有什么作用?

前言&#xff1a; 如果你的简历中写了做过电商项目&#xff0c;那么面试官基本都会从SpringBoot、SpringCloud以及Dubbo这些微服务架构涉及的知识问起&#xff0c;然后深入到问什么是分布事务、分布式锁以及分布式缓存等内容。 这篇文章主要聊聊什么是Redisson分布式锁&#…

【Java EE 初阶】线程安全及死锁解决方案

目录 1.多线程下线程不安全的问题 1.使用多个线程对Array List集合进行添加操作并打印&#xff0c;查看结果 2.如何在多线程环境下使用线程安全的集合类 CopyOnWriteArrayList 3.多线程环境下使用队列 4.多线程环境下使用哈希表 1.HashTable线程安全 2.Concurrent Hash M…

yolov8训练记录

resume: 将model设置为最近一次训练的权重路径 (last.pt) resume设置为True YOLOv8 在 Python 环境中直接使用&#xff0c;不用 ultralytics 库 pip uninstall ultralytics 原因&#xff1a;安装 ultralytics 库&#xff0c;只能在虚拟环境中使用&#xff0c;自己修改代码非常…

ACM 1004 | 母牛的故事

文章目录 0x00 前言 0x01 题目描述 0x02 问题分析 0x03 代码设计 0x04 完整代码 0x05 运行效果 0x06 总结 0x00 前言 C 语言网不仅提供 C 语言&#xff0c;还包括 C 、 java 、算法与数据结构等课程在内的各种入门教程、视频录像、编程经验、编译器教程及软件下载、题解博…

新库上线 | CnOpenData·A股上市公司担保数据

A股上市公司担保数据 一、数据简介 “对外担保”&#xff0c;是指上市公司为他人提供的担保&#xff0c;包括上市公司对控股子公司的担保。“上市公司及其控股子公司的对外担保总额”&#xff0c;是指包括上市公司对控股子公司担保在内的上市公司对外担保总额与上市公司控股子…

初级数据结构——栈和队列

目录 1.栈栈的概念及结构栈的实现栈的结构初始化栈入栈出栈获取栈顶元素获取栈中有效元素个数检测栈是否为空销毁栈Stack.hStack.cTest.c 2.队列队列的概念及结构队列的实现队列的结构初始化队列队尾入队列队头出队列获取队列头部元素获取队列队尾元素获取队列中有效元素个数检…