Win32远线程注入

news2024/9/22 4:07:37

远线程注入

远线程(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 来提交保留页。 若要在一个步骤中保留和提交页面,请使用 MEM_COMMIT | MEM_RESERVE调用 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

使用 大页支持分配内存。

如果指定此值,还必须指定 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 HANDLE
 2 WINAPI
 3 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 lpReserved
 9 )
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

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

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

相关文章

自己封装栈和队列

队列 #include <iostream>using namespace std; class queue { private:int *data;int size;int front;int tail;public://无参构造queue():size(20){datanew int [size];front0;tail0;}//有参构造queue(int s){datanew int [size];sizes;front0;tail0;}~queue(){delete …

MySQL三大日志详解

binlog相关 bin log是什么?作用是什么呢? 答: bin log实际上是一个物理日志,当我们对某个数据页进行修改操作时我们就会将这个操作写到bin log中,当我们数据库需要进行主备、主从复制等操作时,都可以基于bin log保证数据一致性。 那bin log缓冲区了解嘛? 答: 如下图…

硬件产品经理进阶:产品层次划分的3个方法

目录 1、内容简介 2、产品三层次概念 3、产品四层次概念 4、产品五层次概念 作者简介 1、内容简介 产品本身指的是能够满足需求和欲望的一种媒介物。 可以是实体、也可以是虚拟的服务。 在产品竞争白热化的今天&#xff0c; 如果只是考虑把产品做出来、 仅仅在实际产…

【微信小程序入门】3、微信小程序开发基础及微信开发者工具的使用

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

vue2———组件

一个简单的组件 组件进行注册并使用 结果&#xff1a; 在进行对组件的学习时遇见一些问题&#xff1a; 1、组件的命名 解决方法&#xff1a; 组件的命名 Vue.js 组件的命名遵循一些最佳实践&#xff0c;这些实践有助于保持代码的清晰和一致性。 多单词命名&#xff1a;Vue 官…

【网络安全】Collabora在线存储型XSS(CVE-2024-29182)+代码审计

未经许可,不得转载。 文章目录 前言正文代码审计前言 Collabora 是一家专注于开源软件的公司,主要提供与文档协作、办公套件和企业解决方案相关的服务。 Collabora 提供了 Collabora Online,这是一个基于 Web 的在线办公套件,允许用户在浏览器中实时编辑文档。这种服务特…

Two to One——C语言提高题【7 kyu】

一、原题 链接&#xff1a;Training on Two to One | Codewars Take 2 strings s1 and s2 including only letters from a to z. Return a new sorted string (alphabetical ascending), the longest possible, containing distinct letters - each taken only once - coming…

继承:复杂的菱形继承与虚继承

目录 前言 复杂的菱形继承及菱形虚拟继承 继承方式 virtual关键字 虚拟继承的原理 原理&#xff1a; 额外消耗&#xff1a; 构造顺序为什么是ABCD 不允许使用间接非虚拟基类原理 假设只有A B 为什么virtual加在B C中而不是D中&#xff1f; 如何实现一个不能被继承的类…

AtCoder ABC 359 F 题解

本题要看出性质并进行验证&#xff0c;程序难度低。&#xff08;官方 Editorial 似乎没有写证明过程&#xff1f;难道是过于显而易见了吗…&#xff09; 题意 给你一个数组 a a a&#xff0c;对于一棵 n n n 个节点的树 T T T&#xff0c; d i d_i di​ 为每个节点的度&am…

基于Java+SpringBoot+Vue的校园社团信息管理

基于JavaSpringBootVue的校园社团信息管理 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;&公&粽&号 查找《智能编…

C++ 栈和队列的简单封装(9.3)

1.栈的封装 代码 #include <iostream>using namespace std;typedef int datatype; class Stack { private:datatype *data;int max_size; //栈的大小int the_top; //栈顶 public:Stack(){data new int[50];max_size 50;the_top -1;}Stack(int a){data n…

一个好用的Maven依赖冲突解决插件:Maven Helper

在项目开发&#xff0c;或项目Maven需要新增依赖、项目依赖组件升级时&#xff0c;经常会出现添加后&#xff0c;因为各个模块中有相同的依赖、不同的版本而导致依赖冲突&#xff0c;从而导致项目启动不起来&#xff0c;这种冲突非常恶心&#xff0c;因为是传递依赖所以会看不出…

【数据推荐】我国省市县三级的人口受教育状况数据(分年龄\性别\户籍)

人口数据是我们在各项研究中都经常使用的数据。之前我们为大家分享过基于《2020中国人口普查分县资料》整理的全国范围的第七次人口普查人口数据&#xff0c;具体包括如下8个分表&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff1a; 表1&#xff1a;我国省市县三…

大二暑假去龙旗科技(上海)做了两个月软件测试实习生,讲讲我的经历和感受

目录 1.为什么选择软件测试 2.入职&#xff0c;辞职流程以及实习工作内容 3.行业选择和个人感悟 新的学期开始了兄弟们&#xff0c;我也已经断更几个月了&#xff0c;这几个月我并没有摆烂&#xff0c;我选择了备考蓝桥杯&#xff0c;复习期末&#xff0c;暑假出去实习。结果…

SpringDataJPA系列(5)@Query应该怎么用?

SpringDataJPA系列(5)Query应该怎么用&#xff1f; 之前说到过&#xff0c;DMQ查询策略有两种&#xff1a;方法命令和Query注解的方式。为什么需要两种呢&#xff1f;它们分别适用的场景是怎么样的&#xff1f; Query使用 定义一个通过名字查询用户的方法 以下是测试方法&…

将泛型和函数式编程结合,竟然会让代码这么优雅!

但这种方式却太表象了&#xff0c;没有灵魂和深度&#xff0c;过去的那些日子&#xff0c;我感觉自己的编程水平也就限于把重复的代码抽一抽&#xff0c;&#xff08;如下图所示一样&#xff09;&#xff0c;甚至觉得代码优化不就是这样吗&#xff0c;这样的状态一直维持很久。…

Linux多线程——利用C++模板对pthread线程库封装

文章目录 线程封装主要框架线程启动线程等待其他信息 测试函数 线程封装 我们之前介绍过pthread的线程库&#xff0c;这个线程库主要是基于C语言的void*指针来进行传参和返回 我们使用C的模板对其封装可以让他的使用更加方便&#xff0c;并且经过测试可以让我们更加直观的了解…

DPDK基础入门(二):Cache与大页优化

Cache简介 目前Cache主要由三级组成: L1 Cache, L2 Cache和Last Level Cache(LLC)。 L1最快&#xff0c;但容量小&#xff0c;可能只有几十KB。LLC慢&#xff0c;但容量大&#xff0c;可能多达几十MB。 L1和L2 Cache一般集成在CPU内部。另外,&#xff0c;L1和L2 Cache是每个处…

【2024】Datawhale X 李宏毅苹果书 AI夏令营 Task3

本文是关于李宏毅苹果书”第2章 实践方法论“学习内容的记录。 模型在测试集上表现不佳&#xff0c;可能是因为模型没有充分学习训练集。模型不能充分学习训练集的原因&#xff1a; 模型偏差优化问题过拟合不匹配 一、模型偏差 模型偏差是指&#xff1a;由于模型过于简单&a…

网站如何针对不同的DDOS进行防御?

建设网站租用服务器是多数企业及个人的选择&#xff0c;一个安全稳定的服务器对网站的重要性无需再赘述。要保证服务器租用的安全和稳定&#xff0c;除了需要服务器自身有强大的硬、软件基础之外&#xff0c;还需要防范外部的一些因素&#xff0c;常见的就是各种网络攻击&#…