利用ReflectiveDLL来武装你的Cobalt Strike
字数 1129 2025-08-20 18:16:55
利用ReflectiveDLL武装Cobalt Strike的详细指南
前言
Cobalt Strike作为当前渗透测试工作中常用的工具,可以通过插件扩展其功能。本文详细讲解如何利用反射DLL技术(ReflectiveDLL)来增强Cobalt Strike的能力,包括C++和Golang两种语言的实现方式。
C++ ReflectiveDLL实现
基本原理
Cobalt Strike的bdllspawn命令基于Stephen Fewer的ReflectiveDLLInjection项目实现,允许不落地加载DLL文件。
关键实现步骤
- 参数转换处理:
- 在
ReflectiveDll.c中通过DLLMain函数的lpReserved参数传递数据 - 需要进行参数类型转换:
- 在
#include "ReflectiveLoader.h"
#include <string>
#include <shellapi.h>
#pragma comment(lib, "Shell32.lib")
std::string szargs;
std::wstring wszargs;
std::wstring wsHostFile;
int argc = 0;
LPWSTR* argv = NULL;
// 参数转换代码
szargs = (PCHAR)lpReserved;
wszargs = StringToWString(szargs);
argv = CommandLineToArgvW(wszargs.data(), &argc);
- 功能编写示例:
hAppInstance = hinstDLL;
printf("C++ ReflectiveDLL\n");
if (lpReserved != NULL) {
szargs = (PCHAR)lpReserved;
wszargs = StringToWString(szargs);
argv = CommandLineToArgvW(wszargs.data(), &argc);
}
if (argv == NULL) {
printf("[+] Error Arguments ! \n");
break;
}
printf("[+] Args Count : %d \n", argc);
for (size_t i = 0; i < argc; i++) {
wprintf(TEXT("[%d] %s \n"), i, argv[i]);
}
fflush(stdout);
ExitProcess(0);
- Aggressor Script集成:
alias hello {
$args = substr($0, 6);
bdllspawn($1, script_resource("reflective_dll.dll"),$args, "test dll", 5000, false);
}
Golang ReflectiveDLL实现
基本原理
通过修改Go程序使其能够作为反射DLL加载,参考go-ReflectiveDLL项目。
关键实现步骤
- main.go修改:
package main
import "C"
import (
"fmt"
"os"
gsq "github.com/kballard/go-shellquote"
)
//export test
func test(arg string) {
args, err := gsq.Split(arg)
if err == nil {
fmt.Println("Golang ReflectiveDLL")
os.Args = args
fmt.Printf("Args Count %d\n",len(os.Args))
for i := 0; i < len(os.Args); i++ {
fmt.Printf("[%d] %s\n",i,os.Args[i])
}
}
}
func main() {}
- dllmain.c实现:
#include "dllmain.h"
#include<Windows.h>
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
{
GoString goArgs = {0};
if(lpReserved != NULL){
goArgs.p = (char*)lpReserved;
goArgs.n = strlen(lpReserved);
}else{
goArgs.p = "";
goArgs.n = 0;
}
test(goArgs);
}
break;
// 其他case处理...
}
return TRUE;
}
- 编译方法:
- 使用项目提供的
x64.bat进行编译 - 注意:Golang编译的DLL文件较大(约2MB),可能超过Cobalt Strike的1MB限制
- 使用项目提供的
进程间通信实现
当注入到其他进程时,可通过命名管道实现输出捕获:
- Inject.c修改:
// 创建命名管道
HANDLE hPipe = CreateNamedPipe(
TEXT("\\\\.\\Pipe\\mypipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0,
0,
NMPWAIT_WAIT_FOREVER,
NULL);
// 等待连接并读取数据
while (1) {
if (ReadFile(hPipe, buf, 256, &rlen, NULL)) {
printf("\t%s", buf);
} else {
printf("\n[+] Read Data From Pipe End!\n");
break;
}
}
- DLL端实现:
HANDLE hPipe = CreateFile(
TEXT("\\\\.\\Pipe\\mypipe"),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
char buf[256] = "";
sprintf(buf, "C++ ReflectiveDLL\n");
WriteFile(hPipe, buf, sizeof(buf), &wlen, 0);
注意事项
-
Golang DLL大小限制:
- Cobalt Strike限制反射DLL大小在1MB以内
- Golang编译的简单DLL就有约2MB,不适合直接使用
- 可考虑Cobalt Strike 4.1+的Beacon Object File (BOF)功能替代
-
参数传递:
- 原始ReflectiveDLLInjection项目中的inject无法传参
- 需要修改代码将参数传入
LoadRemoteLibraryR函数
-
进程注入选择:
- 注入当前进程可直接传参
- 注入其他进程需要先将参数写入目标进程内存
替代方案
对于Golang程序,可考虑以下方案:
- 使用Cobalt Strike 4.1+的BOF功能
- 将核心功能拆分为小型DLL
- 使用混合编程(Go调用C/C++)
示例代码
完整示例代码可参考:
https://github.com/uknowsec/ReflectiveDLLInjection-Notes