> For the complete documentation index, see [llms.txt](https://wbglil.gitbook.io/cobalt-strike/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wbglil.gitbook.io/cobalt-strike/cobalt-strike-yuan-li-jie-shao/untitled-5.md).

# 内存加载分析（cs模块）

关于这个内存加载其实它分别代表cs里的两个功能

1. .NET程序集内存加载
2. CS反射dll模块内存加载

第一个.NET程序集内存加载功能应该用过cs的人都知道，但是cs里其实还有一个内存加载就是反射dll模块内存加载，这个主要实现了cs里的一些功能比如mimikatz，screenshot，sshagent，hashdump，等等这些功能全部是由反射dll实现的，Cobalt Strike作者将这些功能拆分成一个个的反射dll在使用时才加载执行（msf也是这样）

我们以截屏命令为例来看看cs是如何实现的

先通过抓包获取screenshot命令发送的数据包和元数据

![](/files/-MK9lMhwkkU25WO8Qn79)

然后解密元数据获取key随后使用此key解密screenshot命令data

**解密元数据**

![](/files/-MK9kSi0eNfT4NqVVDfh)

**解密screenshot命令data（因为包含一个screenshot.dll很大所以我只截图了头部和尾部）**

![头部](/files/-MK9mGg9smLg0uHYz3rz)

![尾部](/files/-MK9mgBRRJ6IozSdZyif)

可以很清楚的看到4D5A明显是一个PE文件（这个就是screenshot反射dll，而且已经被修补过了）

接下来我们具体看看在Cobalt Strike是如何处理screenshot.dll并生成**screenshot命令data**的

![BeaconConsole.class](/files/-MKADxsfxFwJNnfCzsRn)

![TaskBeacon.class](/files/-MKAKRCNHB4cTf3lr8p0)

![Job.class](/files/-MKAKudBtYwNhdjPUPtL)

上面的代码具体我就不说了基本上就是打包数据具体数据结构如下图

![](/files/-MKAMTa8zXMcjXbDcfNR)

现在我们根据任务数据大小0x027A00，把后面的反射dll保存到文件然后对比原始的screenshot.dll看看主要修补了哪里

<div align="center"><img src="/files/-MKFW3MjreRP559Z01Ak" alt="修补后的PE头"></div>

![PE头反汇编](/files/-MKFWwo0HegsFW890sOa)

这个反射dll修补就不多说，前面都讲过了

![命名管道名字修补](/files/-MKFWIa4kZHWxarWDgVp)

这个命名管道的名字其实前面的数据中也有一个（主要就是通过这个命名管道向Beacon传输截屏信息）

现在我们来分析一下这个dll主要逻辑

![main函数](/files/-MKJsseCPovJ3ix30Ea9)

![](/files/-MKJt4g9Qybr7Zv0P96V)

![](/files/-MKJu0tYmB2foAXY8S8a)

具体过程基本就是这样，至于我框出的那几个函数我就不详细截图了无非就是调API

根据以上过程我们就可以写一个程序连接到命名管道读取截图然后保存下来代码如下

```
#include <iostream>
#include <windows.h>
using namespace std;

int main(int argc, const char **argv)
{
	wcout << "Connecting to pipe..." << endl;
	HANDLE pipe = CreateFile(
		"\\\\.\\pipe\\screenshot",
		GENERIC_READ,
		0,
		NULL,
		OPEN_EXISTING,
		0,
		NULL
	);

	if (pipe == INVALID_HANDLE_VALUE) {
		wcout << "Failed to connect to pipe." << endl;
		system("pause");
		return 1;
	}

	wcout << "Reading data from pipe..." << endl;

	DWORD size;
	DWORD numBytesRead = 0;
	BOOL result = ReadFile(
		pipe,
		&size,
		4,
		&numBytesRead,
		NULL
	);
	Sleep(5000);
	char* lpbuf = (char*)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);

	if (result) {
		ReadFile(
			pipe,
			lpbuf,
			size,
			&numBytesRead,
			NULL
		);
		CloseHandle(pipe);
		DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
		DWORD dwShareMode = 0;
		LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
		DWORD dwCreationDisposition = OPEN_ALWAYS;
		DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
		HANDLE hTemplateFile = NULL;
		HANDLE handle = CreateFile("test.jpg", dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
		WriteFile(handle, lpbuf, size, &numBytesRead, NULL);
		CloseHandle(handle);
		
	}

	else {
		wcout << "Failed to read data from the pipe." << endl;
	}
	return 0;
}
```

替换管道名编译成exe后先加载运行screenshot.dll进入阻塞状态，然后运行exe连接管道读取截图并写入文件。load dll的代码直接LoadLibrary即可或者直接rundll32

![](/files/-MKKT52E7eCq2LOnlzYL)

本篇分析截图功能仅仅是作为一个例子至于cs里面的其他功能基本上也是大同小异实现都差不多，需要注意的是Beacon在接收到修补后的反射dll并不是作为dll load使用而是当成shellcode(废话要不然干嘛修补反射dll)注入到其他进程运行然后使用命名管道通信
