Win32远线程注入
远线程注入
远线程(RemoteThread)注入是指一个进程在另一个进程中创建线程的技术,这是一种很经典的DLL注入技术。
虽然比较古老,但是很实用。通过远线程注入,再配合api函数的hook技术,可以实现很多有意思的功能。
实现远线程注入的关键函数
OpenProcess
打开现有的本地进程,函数声明如下:
1 WINAPI 2 OpenProcess( 3 _In_ DWORD dwDesiredAccess, 4 _In_ BOOL bInheritHandle, 5 _In_ DWORD dwProcessId 6 );
参数:
dwDesiredAccess:
访问进程对象。此访问权限为针对进程的安全描述符进行检查,此参数可以是一个或多个进程访问权限。如果调用该函数的进程启用了SeDebugPrivilege权限,则无论安全描述符的内容是什么,它都会授予所请求的访问权限。
bInheritHandle:
若此值为TRUE,则此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。
dwProcessId:
要打开的本地进程的PID
返回值:
成功,返回进程的句柄
失败,返回NULL,要获取错误信息,调用GetLastError
VirtualAllocEx
在指定进程的虚拟地址空间内保留、提交或更改内存的状态。声明如下:
1 WINAPI 2 VirtualAllocEx( 3 _In_ HANDLE hProcess, 4 _In_opt_ LPVOID lpAddress, 5 _In_ SIZE_T dwSize, 6 _In_ DWORD flAllocationType, 7 _In_ DWORD flProtect 8 );
参数:
hProcess:
进程的句柄。VirtualAllocEx函数在该进程的虚拟地址空间内分配内存,句柄必有具有PROCESS_VM_OPERATION权限。
lpAddress:
指定要分配页面所需起始地址的指针。如果lpAddress为NULL,则该函数自动分配内存。
dwSize:
要分配的内存大小,以字节为单位。
flAllocationType:
内存分配类型。此参数必须为以下值之一
值 | 含义 |
---|---|
MEM_COMMIT 0x00001000 | 从指定保留内存页的磁盘) 的总内存大小和分页文件 (分配内存费用。 函数还保证当调用方稍后最初访问内存时,内容将为零。 除非实际访问虚拟地址,否则不会分配实际物理页面。 若要在一个步骤中保留和提交页面,请使用 尝试通过指定 MEM_COMMIT 而不指定 MEM_RESERVE 和非 NULLlpAddress 来提交特定地址范围,除非已保留整个范围。 生成的错误代码ERROR_INVALID_ADDRESS。 尝试提交已提交的页面不会导致函数失败。 这意味着,无需先确定每个页面的当前承诺状态即可提交页面。 如果 lpAddress 指定 enclave 中的地址,则必须MEM_COMMITflAllocationType。 |
MEM_RESERVE 0x00002000 | 保留进程的虚拟地址空间范围,而无需在内存或磁盘上的分页文件中分配任何实际物理存储。 使用 MEM_COMMIT 再次调用 VirtualAllocEx 来提交保留页。 若要在一个步骤中保留和提交页面,请使用 其他内存分配函数(如 malloc 和 LocalAlloc)在释放内存之前不能使用预留内存。 |
MEM_RESET 0x00080000 | 指示 lpAddress 和 dwSize 指定的内存范围中的数据不再感兴趣。 不应从分页文件读取或写入页面。 但是,内存块稍后将再次使用,因此不应取消提交。 此值不能与任何其他值一起使用。 使用此值并不能保证使用 MEM_RESET 操作的范围将包含零。 如果希望范围包含零,请取消提交内存,然后重新提交。 使用 MEM_RESET 时, VirtualAllocEx 函数会忽略 fProtect 的值。 但是,仍必须将 fProtect 设置为有效的保护值,例如 PAGE_NOACCESS。 如果使用 MEM_RESET并且内存范围映射到文件,VirtualAllocEx 将返回错误。 仅当共享视图映射到分页文件时,才可接受该视图。 |
MEM_RESET_UNDO 0x1000000 | 应 仅对之前成功应用MEM_RESET的地址范围调用 MEM_RESET_UNDO 。 它指示调用方对 lpAddress 和 dwSize 指定的指定内存范围中的数据感兴趣,并尝试反转 MEM_RESET的影响。 如果该函数成功,则表示指定地址范围中的所有数据都保持不变。 如果函数失败,则至少将地址范围中的某些数据替换为零。 此值不能与任何其他值一起使用。 如果在之前未MEM_RESET的地址范围上调用MEM_RESET_UNDO,则行为未定义。 指定 MEM_RESET时, VirtualAllocEx 函数将忽略 flProtect 的值。 但是,仍必须将 flProtect 设置为有效的保护值,例如 PAGE_NOACCESS。 Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP: 在 Windows 8 和 Windows Server 2012 之前,不支持 MEM_RESET_UNDO 标志。 |
此参数还可以按指示指定以下值。
MEM_LARGE_PAGES 0x20000000 | 使用 大页支持分配内存。 大小和对齐方式必须是大页最小值的倍数。 若要获取此值,请使用 GetLargePageMinimum 函数。 如果指定此值,还必须指定 MEM_RESERVE 和 MEM_COMMIT。 |
MEM_PHYSICAL 0x00400000 | 保留可用于将 地址窗口扩展 (AWE) 页映射的地址范围。 此值必须与 MEM_RESERVE 一起使用,不能与其他值一起使用。 |
MEM_TOP_DOWN 0x00100000 | 在可能的最高地址分配内存。 这可能比常规分配慢,尤其是在有许多分配时。 |
flProtect:
要分配的页面区域的内存保护。如果页面已提交,则可以指定任何一个如下内存保护常量。
常量/值 | 说明 |
---|---|
PAGE_EXECUTE 0x10 | 启用对页面已提交区域的执行访问。 尝试写入已提交区域会导致访问冲突。 CreateFileMapping 函数不支持此标志。 |
PAGE_EXECUTE_READ 0x20 | 启用对页面已提交区域的执行或只读访问。 尝试写入已提交区域会导致访问冲突。 Windows Server 2003 和 Windows XP: 在 Windows XP SP2 和 Windows Server 2003 SP1 之前, CreateFileMapping 函数不支持此属性。 |
PAGE_EXECUTE_READWRITE 0x40 | 启用对已提交页面区域的执行、只读或读/写访问权限。 Windows Server 2003 和 Windows XP: 在 Windows XP SP2 和 Windows Server 2003 SP1 之前, CreateFileMapping 函数不支持此属性。 |
PAGE_EXECUTE_WRITECOPY 0x80 | 启用对文件映射对象的映射视图的执行、只读或写入时复制访问权限。 尝试写入已提交的写入时复制页会导致为进程创建页面的专用副本。 专用页面标记为 PAGE_EXECUTE_READWRITE,更改将写入新页面。 VirtualAlloc 或 VirtualAllocEx 函数不支持此标志。 Windows Vista、Windows Server 2003 和 Windows XP: 在具有 SP1 和 Windows Server 2008 的 Windows Vista 之前, CreateFileMapping 函数不支持此属性。 |
PAGE_NOACCESS 0x01 | 禁用对已提交页面区域的所有访问。 尝试从提交区域读取、写入或执行区域会导致访问冲突。 CreateFileMapping 函数不支持此标志。 |
PAGE_READONLY 0x02 | 启用对已提交页面区域的只读访问。 尝试写入已提交区域会导致访问冲突。 如果启用了 数据执行防护 ,则尝试在已提交的区域中执行代码会导致访问冲突。 |
PAGE_READWRITE 0x04 | 启用对已提交页面区域的只读或读/写访问。 如果启用了 数据执行保护 ,则尝试在提交的区域中执行代码会导致访问冲突。 |
PAGE_WRITECOPY 0x08 | 启用对文件映射对象的映射视图的只读或写入时复制访问权限。 尝试写入已提交的写入时复制页会导致为进程创建页面的专用副本。 专用页面标记为 PAGE_READWRITE,更改将写入新页面。 如果启用了 数据执行保护 ,则尝试在提交的区域中执行代码会导致访问冲突。 VirtualAlloc 或 VirtualAllocEx 函数不支持此标志。 |
PAGE_TARGETS_INVALID 0x40000000 | 将页面中的所有位置设置为 CFG 的无效目标。 与任何执行页保护(如 PAGE_EXECUTE、 PAGE_EXECUTE_READ、 PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY)一起使用。 对这些页面中的位置的任何间接调用都将失败 CFG 检查,并且进程将终止。 分配的可执行页面的默认行为是标记为 CFG 的有效调用目标。 VirtualProtect 或 CreateFileMapping 函数不支持此标志。 |
PAGE_TARGETS_NO_UPDATE 0x40000000 | 当 VirtualProtect 的保护发生更改时,区域中的页面将不会更新其 CFG 信息。 例如,如果区域中的页面是使用 PAGE_TARGETS_INVALID 分配的,则在页面保护更改时将保留无效信息。 仅当保护更改为可执行类型(如 PAGE_EXECUTE、PAGE_EXECUTE_READ、PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY)时,此标志才有效。 VirtualProtect 保护更改为可执行文件的默认行为是将所有位置标记为 CFG 的有效调用目标。 |
如果lpAddress指定了一个地址,则flProtect不能是以下值之一:
PAGE_NOACCESS
PAGE_GUARD
PAGE_NOCACHE
PAGE_WRITECOMBINE
返回值:
成功,返回分配页面的基址
失败,返回NULL,要获得更多的错误信息,请调用 GetLastError。
WriteProcessMemory
在指定的进程中将数据写入内存区域,要写入的整个区域必须可访问,否则操作失败。函数声明如下:
1 BOOL 2 WINAPI 3 WriteProcessMemory( 4 _In_ HANDLE hProcess, 5 _In_ LPVOID lpBaseAddress, 6 _In_reads_bytes_(nSize) LPCVOID lpBuffer, 7 _In_ SIZE_T nSize, 8 _Out_opt_ SIZE_T* lpNumberOfBytesWritten 9 );
参数:
hProcess:
要修改的进程内存的句柄,句柄必有具有 PROCESS_VM_WRITE和PROCESS_VM_OPERATION访问权限
lpBaseAddress:
指向指定进程中写入数据的基地址指针。在数据传输发生之前,系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问,如果不可以访问,则该函数将失败。
lpBuffer:
指向缓冲区的指针,其中包含要写入指定进程的地址空间中的数据。
nSize:
要写入指定进程的字节数。
lpNumberOfBytesWritten:
指向变量的指针,该变量接收传输到指定进程的字节数。如果lpNumberOfBytes Written为NULL,则忽略该参数。
返回值:
成功,返回值不为0,
失败,返回值为0
CreateRemoteThread
在另一个进程的虚拟地址空间中创建运行的线程。声明如下:
1 HANDLE2 WINAPI3 CreateRemoteThread(4 _In_ HANDLE hProcess,5 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,6 _In_ SIZE_T dwStackSize,7 _In_ LPTHREAD_START_ROUTINE lpStartAddress,8 _In_opt_ LPVOID lpParameter,9 _In_ DWORD dwCreationFlags, 10 _Out_opt_ LPDWORD lpThreadId 11 );
参数:
hProcess:
要创建线程的进程的句柄。句柄必须具有
PROCESS_CREATE_THREAD、
PROCESS_QUERY_INFORMATION、
PROCESS_VM_OPERATION、
PROCESS_VM_WRITE
PROCESS_VM_READ
访问权限
lpThreadAttributes:
指向SECURITY_ATTRIBUTES结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes为NULL,则线程将获得默认的安全描述符,并且不能继承该句柄。
dwStackSize:
堆栈的初始大小,以字节为单位。如果此参数为o,则新线程使用可执行文件的默认大小。
lpStartAddress:
指向由线程执﹐行类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针,并表示远程进程中线程的起始地址,该函数必须存在于远程进程中。
lpParameter:
指向要传递给线程函数的变量的指针。
dwCreationFlags:
控制线程创建的标志。若是0,则表示线程在创建后立即运行。
lpThreadId:
指向接收线程标识符的变量的指针。如果此参数为NULL,则不返回线程标识符。
返回值:
成功,返回新线程的句柄,
失败,返回NULL,如果要获取错误信息,请调用GetLastError
远线程注入实现原理
程序在加载DLL时,通常调用LoadLibrary函数来实现DLL的动态加载。LoadLibrary函数的声明如下:
1 HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
LoadLibrary函数只有一个参数,传递的是要加载的DLL路径字符串。
而CreateRemoteThread需要传递的是目标进程空间中的多线程函数地址,以及多线程参数。
如果程序能够获取目标进程LoadLibrary函数的地址,而且还能够获取目标进程空间中某个DLL路径字符串的地址,
那么,可将LoadLibrary函数的地址作为多线程函数的地址,某个DLL路径字符串作为多线程函数的参数,并传递给CreateRemoteThread函数在目标进程空间中创建一个多线程,这样能不能成功呢?
答案是可以的。这样就可以在目标进程空间中创建一个多线程,这个多线程就是LoadLibrary函数加载DLL。
远线程注入的原理就大致清晰了。那么要想实现远线程注入DLL,还需要解决以下两个问题:
1、是目标进程空间中LoadLibrary函数的地址是多少
2、是如何向目标进程空间中写入DLL路径字符串函数
对于第一个问题,由于Windows引入了基址随机化ASLR ( Address Space Layout Randomization )安全机制,所以导致每次开机时系统DLL的加载基址都不一样,从而导致了DLL导出函数的地址也都不一样。
有些系统DLL(例如kernel32.dll、ntdll.dll )的加载基地址,要求系统启动之后必须固定,如果系统重新启动,则其地址可以不同。
也就是说,虽然进程不同,但是开机后,kernel32.dll的加载基址在各个进程中都是相同的,因此导出函数的地址也相同。所以,自己程序空间的LoadLibrary函数地址和其它进程空间的LoadLibrary函数地址相同。
对于第二个问题,我们可以调用VirtualAllocEx函数在目标进程空间中申请一块内存,然后再调用WriteProcessMemory函数将指定的DLL路径写入到目标进程空间中。
总结起来就是:
在目标进程中创建一个线程,线程会在创建后执行。
因为线程里的函数地址是LoadLibrary,所以等于调用了LoadLibrary函数。
需要申请内存的原因是传递DLL的路径。
远线程注入实现步骤
1、调用OpenProcess函数打开进程,获取进程句柄
2、调用VirtualAllocEx函数在需要注入的进程中申请内存空间
3、在申请的内存空间中写入DLL路径
4、获取LoadLibrary函数地址
5、调用CreateRemoteThread函数创建远线程,函数基地址传LoadLibrary函数地址,参数传申请的内存空间
完整实现代码:
我们先创建一个dll工程
1 // dllmain.cpp : 定义 DLL 应用程序的入口点。2 #include "framework.h"3 #include <Windows.h>4 #include <TlHelp32.h>5 6 BOOL APIENTRY DllMain(HMODULE hModule,7 DWORD ul_reason_for_call,8 LPVOID lpReserved9 ) 10 { 11 switch (ul_reason_for_call) 12 { 13 case DLL_PROCESS_ATTACH: 14 { 15 //TCHAR szBuffer[MAX_PATH]{}; 16 //GetModuleFileName(hModule, szBuffer, 260); 17 MessageBox(NULL, L"Hello World", L"Inject Success", MB_OK); 18 } 19 break; 20 case DLL_THREAD_ATTACH: 21 case DLL_THREAD_DETACH: 22 case DLL_PROCESS_DETACH: 23 break; 24 } 25 return TRUE; 26 }
创建远线程注入代码如下:
1 BOOL CreateRemoteThreadInject(DWORD dwPid, LPCTSTR pszDllFileName)2 {3 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);4 5 if (NULL == hProcess)6 return FALSE;7 8 SIZE_T dwSize = (1 + lstrlen(pszDllFileName)) * sizeof(TCHAR);9 LPVOID lpDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 10 11 if (NULL == lpDllAddr) 12 { 13 CloseHandle(hProcess); 14 return FALSE; 15 } 16 17 BOOL bRet = WriteProcessMemory(hProcess, lpDllAddr, pszDllFileName, dwSize, NULL); 18 19 if (!bRet) 20 { 21 CloseHandle(hProcess); 22 return FALSE; 23 } 24 25 LPVOID lpLoadLibraryFunc = LoadLibraryW; 26 27 HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibraryFunc, (LPVOID)lpDllAddr, 0, NULL); 28 29 if (NULL == hRemoteThread) 30 { 31 CloseHandle(hProcess); 32 return FALSE; 33 } 34 35 //WaitForSingleObject(hRemoteThread, INFINITE); 36 37 CloseHandle(hProcess); 38 CloseHandle(hRemoteThread); 39 return TRUE; 40 }
我们打开记事本,用任务管理器找到进程ID,增加测试代码如下:
将xxxx.dll路径改成前面dll工程输出dll的路径
1 int main() 2 { 3 CreateRemoteThreadInject(13764, LR"(xxxxxx.dll)"); 4 }
运行效果:
通过ProcessHacker工具,可以看到notepad加载的模块里有RemoteThread_lib.dll
示例代码
参考资料:
c++ - CreateRemoteThread() succeeds yet doesn't do anything - Stack Overflow
相关文章:

Win32远线程注入
远线程注入 远线程(RemoteThread)注入是指一个进程在另一个进程中创建线程的技术,这是一种很经典的DLL注入技术。 虽然比较古老,但是很实用。通过远线程注入,再配合api函数的hook技术,可以实现很多有意思的功能。 实现远线程注入…...

CTF 竞赛密码学方向学习路径规划
目录 计算机科学基础计算机科学概念的引入、兴趣的引导开发环境的配置与常用工具的安装Watt Toolkit(Steam)、机场代理Scoop(Windows 用户可选)常用 Python 库SageMathLinux小工具 yafuOpenSSL Markdown编程基础Python其他编程语言…...
2024数学建模国赛B题代码
B题已经完成模型代码!详情查看文末名片 问题1:可以考虑使用统计学中的“样本量估算”方法,使用二项分布或正态近似来决定最少的样本量,并通过假设检验(如单侧检验)在95%和90%置信度下进行判断。 import n…...

PyTorch 卷积层详解
PyTorch 卷积层详解 卷积层(Convolutional Layers)是深度学习中用于提取输入数据特征的重要组件,特别适用于处理图像和序列数据。PyTorch 提供了多种卷积层,分别适用于不同维度的数据。本文将详细介绍这些卷积层,特别…...
【Kubernetes知识点问答题】kubernetes 控制器
目录 1. 说明 K8s 控制器的作用? 2. 什么是 ReplicaSet,说明它的主要用途。 3. Deployment 控制器是如何工作的,举例说明其常见用途。 4. 解释 DaemonSet,列举其使用场景。 5. 什么是 StatefulSet,其主要作用是什么…...

Patlibc———更快捷的更换libc
起初是为了简化做pwn题目时,来回更换libc的麻烦,为了简化命令,弄了一个小脚本,可以加入到/usr/local/bin中,当作一个快捷指令🔢 这个写在了tools库(git clone https://github.com/CH13hh/tools…...

基于飞腾平台的Hive的安装配置
【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力,聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域,包含了应用使能套件、软件仓库、软件支持、软件适…...
c# json使用
安装包 用NuGet安装包:Newtonsoft.Json 对象转为Json字符串 public class Person {public string Name { get; set; }public int Age { get; set; } }Person person new Person { Name "John Doe", Age 30 }; string json2 JsonConvert.SerializeO…...

单点登录:cas单点登录实现原理浅析
cas单点登录实现原理浅析 一晃几个月没写博客了,今年多灾多难的一年。 安能摧眉折腰事权贵,使我不得开心颜! 财富是对认知的补偿,不是对勤奋的嘉奖。勤奋只能解决温饱,要挣到钱就得预知风口,或者有独到见解…...

java报错
java.lang.RuntimeException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.tengzhi.base.model.E_xt_xmda...

uniapp动态页面API
目录 uni.setNavigationBarTitle动态设置标题 uni.showNavigationBarLoading为标题添加加载动画与uni.hideNavigationBarLoading停止加载动画 编辑 uni.setNavigationBarColor用于设置导航栏的颜色,包括背景颜色和文字颜色。这对于自定义应用的主题和风格非常有…...
最优化方法Python计算:求解约束优化问题的拉格朗日乘子算法
从仅有等式约束的问题入手。设优化问题(7.8) { minimize f ( x ) s.t. h ( x ) o ( 1 ) \begin{cases} \text{minimize}\quad\quad f(\boldsymbol{x})\\ \text{s.t.}\quad\quad\quad \boldsymbol{h}(\boldsymbol{x})\boldsymbol{o} \end{cases}\quad\quad(1) {minimizef(x)s.…...

每日OJ_牛客_骆驼命名法(递归深搜)
目录 牛客_骆驼命名法(简单模拟) 解析代码 牛客_骆驼命名法(简单模拟) 骆驼命名法__牛客网 解析代码 首先一个字符一个字符的读取内容: 遇到 _ 就直接跳过。如果上一个字符是 _ 则下一个字符转大写字母。 #inclu…...
MySQL 数据库管理与操作指南
文章目录 MySQL 数据库管理与操作指南1. 忘记 MySQL 密码的处理方法2. MySQL 数据库备份与恢复2.1 数据库备份2.2 数据库恢复 3. MySQL 用户与权限管理3.1 创建用户与授权3.2 查看所有用户3.3 删除用户 4. 关闭 GTID 复制模式5. 查看数据表的存储引擎5.1 查看 MySQL 支持的存储…...
Android Manifest 权限描述大全对照表
115工具网(115工具网-一个提供高效、实用、方便的在线工具集合网站)提供Android Manifest 权限描述大全对照表,可以方便andriod开发者查看安卓权限描述功能 权限名称描述android.permission.ACCESS_CHECKIN_PROPERTIES访问登记属性读取或写入…...

Ollama Qwen2 支持 Function Calling
默认 Ollama 中的 Qwen2 模型不支持 Function Calling,使用默认 Qwen2,Ollama 会报错。本文将根据官方模板对 ChatTemplate 进行改进,使得Qwen2 支持 Tools,支持函数调用。 Ollama 会检查对话模板中是否存在 Tools,如…...
APP测试工程师岗位面试题
一、你们公司研发团队采用敏捷开发模式的原因? 由于版本节奏比较快,开发与测试几乎并行,一个版本周期内会有两版在推动,也就是波次发布,波次发布用于尝试新加入的功能,做小范围快速的开发,验证…...

如何进行 AWS 云监控
什么是 AWS? Amazon Web Services(AWS)是 Amazon 提供的一个全面、广泛使用的云计算平台。它提供广泛的云服务,包括计算能力、存储选项、网络功能、数据库、分析、机器学习、人工智能、物联网和安全。 使用 AWS 有哪些好处&…...

第十六篇:走入计算机网络的传输层--传输层概述
1. 传输层的功能 ① 分割与重组数据 一次数据传输有大小限制,传输层需要做数据分割,所以在数据送达后必然也需要做数据重组。 ② 按端口号寻址 IP只能定位数据哪台主机,无法判断数据报文应该交给哪个应用,传输层给每个应用都设…...

提升效率!ArcGIS中创建脚本工具
在我们日常使用的ArcGIS中已经自带了很多功能强大的工具,但有时候遇到个人的特殊情况还是无法满足,这时就可以试着创建自定义脚本工具。 一、编写代码 此处的代码就是一个很简单的给图层更改别名的代码。 1. import arcpy 2. input_fc arcpy.GetParam…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...