第16节 实战:文件转shellcode

news2024/11/17 13:54:24

我最近做了一个关于shellcode入门和开发的专题课👩🏻‍💻,主要面向对网络安全技术感兴趣的小伙伴。这是视频版内容对应的文字版材料,内容里面的每一个环境我都亲自测试实操过的记录,有需要的小伙伴可以参考。

我的个人主页:https://imbyter.com

对于一些功能简单的shellcode编写,我们可以用前面介绍的shellcode框架直接编写生成,但对于功能比较复杂的需求,我们也可以选择先以常规C/C++开发方式,编写好dll和exe文件后,再将其转换为shellcode文件,或者对于已有的dll或exe文件,不需要重写代码,直接将其转换为shellcode文件。

这节主要介绍如何实现对指定的PE文件(dll和exe文件)转shellcode的方法。dll和exe两种类型的文件,两者在shellcode转换方面的主要区别是:

dll文件exe文件
重定位表的处理一定存在重定位表,需要对其地址进行重定位处理。不一定存在重定位表,存在处理重定位和不处理两种情况。
入口函数调用可能有DllMain函数或自定义导出函数。main函数或自定义入口函数。

不管是dll还是exe,将其转为shellcode的关键点有以下几个:

  1. PE文件在shellcode中的存放;
  2. shellcode运行时PE文件的定位;
  3. PE文件在shellcode中的内存加载执行;

现在我们来逐一解决以上问题:

一、生成shellcode时PE的封装​

在我们前面所说的shellcode编写方式中,shellcode总大小就是我们实际编写shellcode的所有代码,但对于PE转成的shellcode而言,最后生成的shellcode文件总大小为我们编写的shellcode与PE文件的总和。

我们可以根据自己的需求来设计PE文件在整个shellcode中的位置,可以放在前面、中间,或者后面。将PE文件放在整个shellcode的最后面实现起来相对容易,如图所示:

在代码上的实现如下:

0.createshellcode.cpp:

#include "a.start.h"
#include "z.end.h"

#pragma comment(linker,"/entry:EntryMain")

// 该函数用于生成shellcode文件。函数本身与生成的shellcode功能没有任何关系,
// 并没包含在生成的shellcode里面,所以该函数编写不受shellcode编写规范限制。
int EntryMain()
{
    DWORD   dwWriten = 0;
    DWORD   dwRead = 0;
    HANDLE  hShellcodeFile = NULL;
    HANDLE  hPEFile = NULL;

    // 创建文件,用于保存最后的shellcode
    hShellcodeFile = CreateFileA("shellcode.bin", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if (hShellcodeFile == INVALID_HANDLE_VALUE)
    {
        return -1;
    }

    // 计算shellcode头大小
    SIZE_T dwShellcodeHeaderSize = (SIZE_T)ShellCodeEnd - (SIZE_T)ShellCodeStart;

    // 将shellcode写入到文件
    WriteFile(hShellcodeFile, ShellCodeStart, (DWORD)dwShellcodeHeaderSize, &dwWriten, NULL);

    // 打开目标PE文件(EXE或者DLL)
    hPEFile = CreateFileA("TestDll.dll", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hPEFile == INVALID_HANDLE_VALUE)
    {
        CloseHandle(hShellcodeFile);
        return -1;
    }

    // 获取目标PE大小
    DWORD dwPESize = GetFileSize(hPEFile, NULL);
    LPVOID lpPEData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwPESize);

    // 读取目标文件内容
    ReadFile(hPEFile, lpPEData, dwPESize, &dwRead, NULL);

    // 将PE文件内容写入shellcode文件
    WriteFile(hShellcodeFile, lpPEData, dwPESize, &dwWriten, NULL);

    HeapFree(GetProcessHeap(), 0, lpPEData);

    CloseHandle(hShellcodeFile);
    CloseHandle(hPEFile);

    return 0;
}

以上代码:

  1. 创建shellcode.bin文件;
  2. 将shellcode头写入到文件中;
  3. 读取目标PE文件,此处为动态链接库TestDll.dll文件(dll或exe文件,可根据实际需求修改);
  4. 将PE文件内容继续写入shellcode.bin中。

二、运行shellcode时PE的定位​

将整个Shellcode拷贝到内存中运行时,我们首先需要知道PE文件在shellcode中的位置,才能将其内存加载运行。在内存中,PE文件是紧跟在shellcode头后面的,所以要想确定PE文件的位置,可以用shellcode所在内存的基址+shellcode头的长度得到。

  • shellcode内存的基址的获取:我们可以在加载器中,将申请的shellcode执行地址作为参数,传递给shellcode的ShellCodeStart函数,这样就能轻松得到这个基址;
  • shellcode头长度:即 ShellCodeEnd 与 ShellCodeStart 的差值;

a.start.cpp:

#include "a.start.h"
#include "api.h"
#include "loadpe.h"
#include "z.end.h"

// lpAddress:shellcode所在内存基址
void ShellCodeStart(LPVOID lpAddress)
{
    CAPI api;
    // 初始化所有用到的函数
    api.InitFunctions();

    // 定位到PE文件内容
    // 完整的shellcode内容 = shellcode头 + PE文件内容
    SIZE_T dwShellcodeHeaderLen = (SIZE_T)ShellCodeEnd - (SIZE_T)ShellCodeStart;
    unsigned char* lpPEDataStart = (unsigned char*)lpAddress + dwShellcodeHeaderLen;

    // 加载PE文件
    CLoadPE PE(&api, lpPEDataStart);
    PE.MemoryLoad();
}

三、运行shellcode时PE的执行​

正常执行PE文件(dll或exe)时,PE文件是由系统加载运行的,在shellcode中,我们需要自己手动实现PE文件在内存中的“展开”和执行。 我们创建一个CLoadPE类,专门用于PE文件的内存加载。

  1. 构造函数CLoadPE(CAPI* api, PVOID lpFileData)接受两个参数,一个CAPI类型用于使用动态调用的函数,一个PVOID表示PE数据的地址:
  2. 函数IsValidPE()通过检查PE文件的"MZ"头和"PE"头,来确定PE文件是否有效;
  3. 函数AllocImageBaseAddress()用于给PE文件分配执行内存;
  4. 函数CopySections()用于将PE文件的个节“展开”到执行内存中;
  5. 函数RepairIAT()用于修复导入表;
  6. 函数RepairReloc()用于修复重定位表;
  7. 函数CallEntryPoint()用于执行入口函数功能;
  8. 为方便执行,整个流程封装在函数MemoryLoad()中执行;

类CLoadPE(loadpe.h和loadpe.cpp):

loadpe.h:

#pragma once

#include "api.h"

class CLoadPE
{
public:
    CLoadPE(CAPI* api, PVOID lpFileData);

public:
    BOOL MemoryLoad();              // 内存加载PE

private:
    BOOL IsValidPE();               // 判断PE格式是否正确
    BOOL IsDllFile();               // 判断PE是DLL还是EXE
    BOOL AllocImageBaseAddress();   // 申请内存空间(基址)
    VOID CopySections();            // 复制节到申请的内存中
    BOOL RepairIAT();               // 修复导入表
    BOOL RepairReloc();             // 修复重定位表

    PVOID MemGetProcAddress(PCSTR FunctionName);  // 获取内存中dll指定函数名的导出函数地址
    VOID CallEntryPoint(CLoadPE* Object);         // 执行入口函数

    PVOID m_ImageBase;              // 内存中PE文件基址

    ULONG_PTR m_EntryPointer;       // 内存中PE文件入口点地址

    // 文件中的PE结构变量
    PIMAGE_DOS_HEADER m_DosHeader;
    PIMAGE_NT_HEADERS m_PeHeader;
    PIMAGE_SECTION_HEADER m_SectionHeader;

    // 加载到内存后的PE结构变量
    PIMAGE_DOS_HEADER m_MemDosHeader;
    PIMAGE_NT_HEADERS m_MemPeHeader;

private:
    CAPI* m_api;

public:
    PVOID m_FileData;               // PE文件内容
};

loadpe.cpp:

#include "loadpe.h"

CLoadPE::CLoadPE(CAPI* api, PVOID lpFileData)
{
    m_api           = api;
    m_FileData      = lpFileData;

    m_ImageBase     = NULL;
    m_EntryPointer  = NULL;

    m_DosHeader     = NULL;
    m_PeHeader      = NULL;
    m_SectionHeader = NULL;
    m_MemDosHeader  = NULL;
    m_MemPeHeader   = NULL;
}

BOOL CLoadPE::MemoryLoad()
{
    if (m_FileData == NULL)
    {
        return FALSE;
    }
    
    m_DosHeader = (PIMAGE_DOS_HEADER)m_FileData;
    m_PeHeader = (PIMAGE_NT_HEADERS)(m_DosHeader->e_lfanew + (ULONG_PTR)m_DosHeader);

    // 检查PE格式是否有效
    if (!IsValidPE())
    {
        return FALSE;
    }

    // 申请内存空间
    if (!AllocImageBaseAddress())
    {
        return FALSE;
    }
    // 分配数据节块 
    CopySections();

    // 修复导入表
    if (!RepairIAT())
    {
        return FALSE;
    }

    // 修复重定位表
    if (!RepairReloc())
    {
        return FALSE;
    }

    // 执行入口函数功能
    CallEntryPoint(this);

    return TRUE;
}

BOOL CLoadPE::IsValidPE()
{
    if (m_DosHeader->e_magic != IMAGE_DOS_SIGNATURE || m_PeHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        // 不是有效的PE文件
        return FALSE;
    }

#ifdef _WIN64
    if (m_PeHeader->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
    {
        return FALSE;
    }
#else
    if (m_PeHeader->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
    {
        return FALSE;
    }
#endif

    return TRUE;
}

BOOL CLoadPE::IsDllFile()
{
    return m_PeHeader->FileHeader.Characteristics & IMAGE_FILE_DLL;
}

BOOL CLoadPE::AllocImageBaseAddress()
{
    if (m_PeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size == 0)
    {
        // 尝试在基址处申请空间
        m_ImageBase = m_api->VirtualAlloc((LPVOID)m_PeHeader->OptionalHeader.ImageBase, m_PeHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (m_ImageBase == NULL)
        {
            return FALSE;
        }
    }
    else
    {
        // 尝试申请新内存空间
        m_ImageBase = m_api->VirtualAlloc(NULL, m_PeHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (m_ImageBase == NULL)
        {
            return FALSE;
        }
    }

    m_api->memcpy(m_ImageBase, m_DosHeader, m_PeHeader->OptionalHeader.SizeOfHeaders);

    return TRUE;
}

VOID CLoadPE::CopySections()
{
    m_SectionHeader = IMAGE_FIRST_SECTION(m_PeHeader);
    for (size_t i = 0; i < m_PeHeader->FileHeader.NumberOfSections; i++)
    {
        if (m_SectionHeader->SizeOfRawData != 0 && m_SectionHeader->VirtualAddress != 0)
        {
            m_api->memcpy((PVOID)((ULONG_PTR)m_ImageBase + m_SectionHeader->VirtualAddress), (PVOID)((ULONG_PTR)m_DosHeader + m_SectionHeader->PointerToRawData), m_SectionHeader->SizeOfRawData);
        }
        m_SectionHeader++;
    }
    m_MemDosHeader = (PIMAGE_DOS_HEADER)m_ImageBase;
    m_MemPeHeader  = (PIMAGE_NT_HEADERS)(m_MemDosHeader->e_lfanew + (ULONG_PTR)m_MemDosHeader);

    m_EntryPointer = (ULONG_PTR)m_ImageBase + m_MemPeHeader->OptionalHeader.AddressOfEntryPoint;
}

BOOL CLoadPE::RepairIAT()
{
#ifdef _WIN64
    union
    {
        struct
        {
            ULONGLONG low : 63;
            ULONGLONG high : 1;
        }BitField;
        ULONGLONG Value;
    }Temp = { 0 };
#else
    union
    {
        struct
        {
            ULONG low : 31;
            ULONG high : 1;
        }BitField;
        ULONG Value;
    }Temp = { 0 };
#endif 

    if (m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size > 0)
    {
        PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)m_ImageBase + m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
        while (pIID->Name != NULL)
        {
            PIMAGE_THUNK_DATA pITD = (PIMAGE_THUNK_DATA)((ULONG_PTR)m_ImageBase + pIID->OriginalFirstThunk);
            HMODULE hModule = m_api->LoadLibraryA((char*)((ULONG_PTR)m_ImageBase + pIID->Name));
            if (hModule == NULL)
            {
                // DLL加载失败
                return FALSE;
            }

            PULONG_PTR pFuncAddr = (PULONG_PTR)((ULONG_PTR)m_ImageBase + pIID->FirstThunk);
            while (*pFuncAddr != 0)
            {
                Temp.Value = pITD->u1.AddressOfData;
                if (Temp.BitField.high == 1)
                {
                    // 通过序号取函数地址
                    *pFuncAddr = (ULONG_PTR)m_api->GetProcAddress(hModule, (LPCSTR)Temp.BitField.low);
                }
                else
                {
                    // 通过名称取函数地址
                    PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)m_ImageBase + pITD->u1.AddressOfData);
                    *pFuncAddr = (ULONG_PTR)m_api->GetProcAddress(hModule, pFuncName->Name);
                }
                pITD++;
                pFuncAddr++;
            }
            pIID++;
        }
    } 
    return TRUE;
}

BOOL CLoadPE::RepairReloc()
{
    if (m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0)
    {
        typedef struct BASE_RELOCATION_ENTRY {
            USHORT Offset : 12;
            USHORT Type : 4;
        } BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;

        INT_PTR Offset = (INT_PTR)((ULONG_PTR)m_ImageBase - m_PeHeader->OptionalHeader.ImageBase);
        IMAGE_BASE_RELOCATION* pIBR = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)m_ImageBase + m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

        while (pIBR->VirtualAddress != 0)
        {
            int NumberOfBlocks = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(BASE_RELOCATION_ENTRY);
            BASE_RELOCATION_ENTRY* Block = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)pIBR + sizeof(IMAGE_BASE_RELOCATION));

            for (int i = 0; i < NumberOfBlocks; i++)
            {
                if (Block->Type != IMAGE_REL_BASED_ABSOLUTE)
                {
                    PINT_PTR RepairAddr = (PINT_PTR)((ULONG_PTR)m_ImageBase + pIBR->VirtualAddress + Block->Offset);
                    if (*RepairAddr <= 0) 
                    {
                        return FALSE; 
                    }
                    *RepairAddr += Offset;
                }
                Block++;
            }
            pIBR = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pIBR + pIBR->SizeOfBlock);
        }
        m_MemPeHeader->OptionalHeader.ImageBase = (ULONG_PTR)m_ImageBase;
    }
    return TRUE;
}

PVOID CLoadPE::MemGetProcAddress(PCSTR FunctionName)
{
    // 从到处目录获取指定函数
    if (m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0)
    {
        PIMAGE_EXPORT_DIRECTORY pIED = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)m_ImageBase + m_MemPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

        PDWORD FuncNameTable = (PDWORD)((ULONG_PTR)m_ImageBase + pIED->AddressOfNames);
        PDWORD FuncAddrTable = (PDWORD)((ULONG_PTR)m_ImageBase + pIED->AddressOfFunctions);

        for (DWORD i = 0; i < pIED->NumberOfNames; i++)
        {
            if (m_api->strcmp(FunctionName, (PCSTR)((ULONG_PTR)m_ImageBase + *FuncNameTable)) == 0)
            {
                return (PVOID)((ULONG_PTR)m_ImageBase + *FuncAddrTable);
            }
            FuncNameTable++;
            FuncAddrTable++;
        }
    }
    return NULL;
}

VOID CLoadPE::CallEntryPoint(CLoadPE* Object)
{
    if (IsDllFile())
    {
        // DllMain入口函数
        typedef BOOL(WINAPI* DllEntryProc)(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved);
        ((DllEntryProc)Object->m_EntryPointer)((HINSTANCE)Object->m_ImageBase, DLL_PROCESS_ATTACH, NULL);

        // 导出函数(根据实际函数定义修改)
        typedef void(*ExportFun)();
        char szExpFun[] = {'t','e','s','t','f','u','n',0};
        ExportFun lpproc = (ExportFun)MemGetProcAddress(szExpFun);
        if (lpproc != NULL)
        {
            lpproc();
        }
    }
    else
    {
        // main函数
        ((void(*)())(Object->m_EntryPointer))();
    }
}

四、项目属性设置​

  1. 禁用“优化”选项:

  2. Release的“运行库”设为“多线程(/MT)”,禁用“安全检查”:

自己动手,更进一步:

  1. 在生成的shellcode中,将PE文件进行压缩和加密;
  2. 在运行shellcode时,先解密解压然后再内存加载。

如果有任何问题,可以在我们的知识社群中提问和沟通交流:

图片​​

一个人走得再快,不如一群人走得更远!🤜🤛


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

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

相关文章

在微信小程序项目中安装和使用 Vant 组件库

vant Wwapp 小程序开发组件库官网 Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装 Vant 组件库 1.在微信小程序项目文件目录的空白位置右键&#xff0c;选择在外部终端窗口中打开 2在命令行输入如下命令&#xff08;在项目中创建包管理配置文件 package.json&#xff09; …

PHP 自提时间

前端: 后台设置: 代码: public function getBusinessHour(){// 需求单门店$data (new StoreModel())->limit(1)->select()->toArray();$days explode(,, $data[0][shop_hours]);$businessHours $days[1];// 使用 explode 分割字符串&#xff0c;获取开始和结束时…

局域网监控软件有哪些:好用的局域网桌面监控软件神器分享

如何有效地监控和管理内部员工的计算机使用行为&#xff0c;防范潜在的安全风险&#xff0c;提高工作效率&#xff0c;是众多企业管理者关注的焦点。 而一款优秀的局域网桌面监控软件无疑能为企业的IT治理提供有力支撑。 小编在此给大家推荐一款好用的局域网桌面监控软件——域…

地平线X3开发板配置wifi调试

1. 系统镜像制作 系统镜像的制作依赖bsp与补丁包&#xff0c;bsp在天工开物全量包中&#xff1a;https://developer.horizon.ai/resource 补丁下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1YKcOWL0EpboGq-SnqwIGeQ 提取码&#xff1a;b6lf 补丁包中有详细…

TSINGSEE青犀智慧校园视频综合管理系统建设方案及平台功能

一、方案背景 智慧校园视频综合管理系统以物联网、云计算、大数据分析等新技术为核心&#xff0c;将基于计算机网络的信息服务融入学校的各个应用与服务领域&#xff0c;实现互联和协作。通过“云边缘计算”构架&#xff0c;在各应用子系统的基础数据层面上&#xff0c;打通上…

高级查询(一)

解决需求&#xff1a;Excel都可以轻松搞定 分组查询原理&#xff1a; 语法&#xff1a; SELECT …<列名> FROM <表名> [WHERE<条件表达式> ] GROUP BY 参与分组的列 可以与 聚合函数作用的列 一块显示 注意 SELECT列表中只能包含&#xff1a; 1、被分…

i春秋-Test

题目 解题 参考WP https://blog.csdn.net/qq_40654505/article/details/107142533/目录扫描 复现wp payload为&#xff1a; search.php?searchtype5&tid&areaeval($_POST[cmd])使用蚁剑连接 http://eci-2ze4iyhwj7xvb68bsb2t.cloudeci1.ichunqiu.com:80/search.ph…

Springboot整合 Spring Cloud OpenFeign

1.OpenFeign简介 1.相比于Netflix Feign&#xff0c;OpenFeign支持Spring MVC注解&#xff0c;整合了Ribbon(springcloud在Hoxton.M2 RELEASED版本之后舍弃Ribbon。需手动引入spring-cloud-loadbalancer)和Nacos。 2.使得开发人员调用远程接口或者服务之间相互调用就像调用本地…

【智能优化算法】蛛蜂优化算法(Spider Wasp Optimizer,SWO)

蛛蜂优化算法(Spider Wasp Optimizer,SWO)是期刊“ARTIFICIAL INTELLIGENCE REVIEW”&#xff08;中科院二区 IF11.6&#xff09;的2023年智能优化算法 01.引言 蛛蜂优化算法(Spider Wasp Optimizer,SWO)基于对自然界中雌性黄蜂的狩猎、筑巢和交配行为的复制。该算法具有多种…

JavaFX布局-VBox

JavaFX布局-VBox 常用属性alignmentspacingchildrenmarginpaddingvgrow 实现方式Java实现Xml实现 综合案例 VBox按照垂直顺序从上到下排列其子节点改变窗口大小,不会该部整体布局窗口太小会遮住内部元素&#xff0c;不会产生滚动条 常用属性 alignment 对齐方式 参考枚举值&…

C++ 多态性

一 多态性的分类 编译时的多态 函数重载 运算符重载 运行时的多态 虚函数 1 运算符重载的引入 使用C编写程序时&#xff0c;我们不仅要使用基本数据类型&#xff0c;还要设计新的数据类型-------类类型。 一般情况下&#xff0c;基本数据类型的运算都是运算符来表达&#x…

识别公式的网站都有哪些?分享3个专业的工具!

在数字化时代&#xff0c;公式识别已成为一项重要技能。无论是学生、教师还是科研人员&#xff0c;都可能需要借助公式识别网站来快速准确地获取公式信息。那么&#xff0c;市面上到底有哪些值得一试的识别公式网站呢&#xff1f;本文将为您一一揭晓。 FUNAI FUNAI的公式识别功…

2024洗地机爆款榜单,哪个牌子洗地机值得买?助你轻松选对洗地机

随着现代生活节奏的加快&#xff0c;人们对于家庭清洁的需求也越来越高。家用洗地机作为一种高效清洁工具&#xff0c;能够帮助您轻松应对家庭地板的清洁问题&#xff0c;节省时间和精力。然而&#xff0c;在选择洗地机时&#xff0c;究竟哪个牌子的洗地机值得买呢&#xff1f;…

2024年网络安全岗位缺口已达100万,你该不会还不知道如何入门吧?

我发现最近安全是真的火&#xff0c;火到不管男女老少都想入门学一下。但是&#xff0c;要是真的问起他们&#xff0c;“你觉得网络安全是什么&#xff1f;为什么想学&#xff1f;”&#xff0c;十个人里不见得有一个人能逻辑清晰、态度坚定地回答出来。 作为一个时刻关注行业…

暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略

暗黑4可以搬砖吗&#xff1f;暗黑4怎么搬砖 搬砖攻略 暗黑破坏神4属于是暴雪旗下一款经典游戏IP&#xff0c;在全世界有着广泛的玩家群体&#xff0c;更是在今年暴雪国服宣布回归之后&#xff0c;吸引了一大批新玩家加入。今天小编就为大家带来暗黑4的详细搬砖教程。 现在我们…

Leetcode—100296. 两个字符串的排列差【简单】

2024每日刷题&#xff08;135&#xff09; Leetcode—100296. 两个字符串的排列差 实现代码 class Solution { public:int findPermutationDifference(string s, string t) {int maps[26];int mapt[26];for(int i 0; i < s.size(); i) {int idxs s[i] - a;int idxt t[i…

服务器被后台爆破怎么处理

服务器后台遭受密码爆破攻击是网络安全中常见的威胁之一&#xff0c;这种攻击通过不断尝试不同的用户名和密码组合来破解系统登录凭证&#xff0c;一旦成功&#xff0c;攻击者便能非法访问系统资源。 本文将介绍如何识别此类攻击&#xff0c;并采取有效措施进行防御&#xff0c…

《四》系统模块整体功能关联与实现

在上一篇里&#xff0c;我们完成了动作的创建&#xff0c;那么这一次&#xff0c;我们把它加载到界面上&#xff0c;把需要是实现的动作都加上。 MyWord::MyWord(QWidget *parent): QMainWindow(parent) {mdiAreanew QMdiArea;mdiArea->setHorizontalScrollBarPolicy(Qt::S…

微信加粉计数器

1.采用非注入式开发&#xff0c;支持无限多开 2.每个账号都有独立的分组&#xff0c;实时远程网页数据分享 3.后台功能强大&#xff0c;操作简单&#xff0c;自动去重复&#xff0c;准确计数分秒不差

ubuntu使用交叉编译链编译FFTW动态库

一、从官方下载FFTW压缩包 1. 在 /usr/local 新建目录fftw目录&#xff0c; 将压缩包放在 /usr/local/fftw 中解压。 2.打开解压后的文件 导入环境变量&#xff1a; export PATH$PATH:/home/theer/workspace/a64/cqa64_linuxqt5.8_bv3s/buildroot-2017.02.3/output/host/…