简介:本资源为gh0st3.6远程控制软件的完整VC++源代码工程,面向网络安全研究者、逆向分析初学者及Windows底层开发学习者,用于深入理解RAT类工具的通信机制、系统级控制原理与攻防对抗技术。压缩包共298个文件,以145个头文件(h)和87个实现文件(cpp)构成核心逻辑,辅以10个静态库(lib)、6个工程配置文件(dsp/dsw)、4个资源脚本(rc/rc2)及若干位图、光标、图标等UI资源,完整支撑VC6.0环境下的编译与调试;包体大小1.33MB,结构紧凑,便于逐模块研读。已有314人学习下载,适合开展网络编程、多线程控制、Windows API调用、基础加解密与反调试技术的实操分析。源码中涵盖socket通信建模、服务端/客户端双模设计、DLL动态加载、注册表持久化及隐蔽信道雏形等典型模块,是理解传统Windows远控架构不可多得的实践样本。
1. 这不是远控工具,而是一份被反复验证的 Windows 桌面级远程控制通信协议实现样本
你搜“gh0st3.6 源代码”,大概率是刚接手一个遗留系统维护任务,或是正在做网络协议逆向分析的课程设计,又或者在复现某高校《恶意软件行为建模》实验课的配套材料。别急着编译——这份 VC++ 工程不是开箱即用的远控客户端,它本质是一套高度模块化、带完整服务端/客户端双端逻辑、基于 Windows API 和 Winsock 的远程控制通信协议教学级实现。它不依赖第三方框架,所有 socket 封装、内存注入钩子(仅限本地调试模式)、屏幕捕获(GDI+)、键盘鼠标模拟(SendInput)、文件传输分块校验逻辑,全在源码里摊开写。适合想真正搞懂“远程桌面类程序底层怎么握手、怎么传帧、怎么保持心跳”的人,而不是找现成黑盒工具。如果你正卡在“为什么我的自研远控服务端收不到客户端上线包”“为什么截屏数据在 TCP 流里总粘包”“如何让命令执行结果可靠回传”这类问题上,这份代码就是你该拆的第一份“协议解剖标本”。它不新,但足够干净;它不炫技,但每行 Win32 调用都有明确上下文。
2. 从工程结构到核心通信流程:先看懂它怎么组织,再动手改
2.1 工程目录与关键模块职责划分(VC++ 6.0 兼容结构)
整个gh0st3.6源码包解压后呈现典型的单体 Windows 应用结构,主目录下含Client、Server、Common三个核心文件夹,外加Resource(图标/对话框资源)和Doc(简易说明)。这不是现代 CMake 工程,没有CMakeLists.txt,所有.dsp(Project)和.dsw(Workspace)文件均指向 VC++ 6.0 环境。重点模块如下:
| 目录 | 关键文件 | 核心职责 | 是否可独立复用 |
|---|---|---|---|
Common/ | NetPacket.h/cpp | 定义全部通信协议包结构(PACKET_TYPE_CMD,PACKET_TYPE_SCREEN,PACKET_TYPE_FILE),含序列化/反序列化函数 | ✅ 是,协议头可直接移植 |
Util.h/cpp | 提供跨平台基础工具:字符串编码转换(ANSI/Unicode)、CRC32 校验、内存池管理(CMemPool) | ✅ 是,无 Win32 依赖 | |
Client/ | GhostClient.cpp | 主客户端入口,初始化 socket、启动心跳线程、注册窗口消息钩子(仅 Debug 模式启用) | ⚠️ 需剥离钩子逻辑才安全 |
ScreenCap.cpp | GDI 截屏实现:CreateDC→BitBlt→GetDIBits,输出 RGB24 原始数据流 | ✅ 是,可封装为独立 DLL | |
Server/ | GhostServer.cpp | 服务端主循环:select()多路复用监听,AcceptEx接收连接,WSARecv异步收包 | ✅ 是,网络层逻辑清晰 |
提示:不要试图用 VS2019+ 直接打开
.dsp文件——VC++ 6.0 的项目格式已废弃。正确做法是新建空 Win32 控制台工程,手动添加上述.cpp/.h文件,并在项目属性中关闭预编译头(/Yu→/Y-),否则stdafx.h缺失会报错。
2.2 协议包结构解析:为什么它能稳定传屏和命令
gh0st3.6的健壮性源于其精简但完备的二进制协议设计。所有数据包以固定 12 字节头部起始,定义在Common/NetPacket.h中:
#pragma pack(1) struct PACKET_HEADER { DWORD dwMagic; // 固定 0x47483053 ('GH0S') WORD wVersion; // 协议版本,当前 0x0306 (3.6) WORD wType; // 包类型,如 0x0001=CMD, 0x0002=SCREEN DWORD dwLength; // 后续数据长度(不含 header) DWORD dwCRC32; // 整个包(header+data)的 CRC32 校验值 }; #pragma pack()关键点在于:
- Magic 字段防误解析:服务端收到数据时,先检查前 4 字节是否为
0x47483053,非此值直接丢弃,避免 TCP 粘包导致的协议错位; - CRC32 校验覆盖全包:
dwCRC32在发送前由CalcCRC32((BYTE*)&header, sizeof(header) + dwLength)计算,接收端校验失败则整包丢弃,杜绝静默数据损坏; - wType 严格分域:
SCREEN包后续紧跟SCREEN_INFO结构(宽/高/位深),CMD包后续是CMD_INFO(命令字符串长度+内容),无歧义解析。
2.3 客户端上线流程:三次握手之外的“心跳注册”
客户端启动后并非简单 connect 就完事,而是执行四阶段注册:
- TCP 连接建立:调用
socket(AF_INET, SOCK_STREAM, 0)→connect()到服务端 IP:Port; - 身份认证包:发送
PACKET_TYPE_AUTH包,其中dwLength = 32,数据区填入 32 字节随机 SessionKey(由CryptGenRandom生成); - 服务端挑战响应:服务端收到后,用相同 SessionKey 加密一段 16 字节 Challenge(如时间戳+随机数),返回
PACKET_TYPE_CHALLENGE; - 客户端完成认证:客户端解密 Challenge,若匹配则发送
PACKET_TYPE_REGISTER,携带主机名、用户名、IP 等信息,服务端存入在线列表。
注意:这个流程在
Client/GhostClient.cpp的ConnectToServer()函数中实现,Server/GhostServer.cpp的ProcessAuthPacket()处理认证逻辑。若你只需通信框架,可注释掉PACKET_TYPE_AUTH相关分支,改为明文PACKET_TYPE_REGISTER直连,降低调试复杂度。
2.4 屏幕捕获与传输:GDI 截图 + 分块压缩的落地细节
ScreenCap.cpp的CaptureScreen()函数是性能关键路径。它不使用PrintWindow(兼容性差)或Desktop Duplication API(Win8+ 专属),而是经典 GDI 方案:
// ScreenCap.cpp BOOL CaptureScreen(BYTE** ppBuffer, DWORD* pdwSize, int* pWidth, int* pHeight) { HDC hScreenDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL); // 获取屏幕 DC HDC hMemDC = CreateCompatibleDC(hScreenDC); HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); SelectObject(hMemDC, hBitmap); BitBlt(hMemDC, 0, 0, *pWidth, *pHeight, hScreenDC, 0, 0, SRCCOPY); // 拷贝到内存位图 BITMAPINFO bmi = {0}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = *pWidth; bmi.bmiHeader.biHeight = -*pHeight; // 负值表示 top-down DIB bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; DWORD dwBmpSize = ((*pWidth * 24 + 31) / 32) * 4 * (*pHeight); // 计算 24bpp 行对齐大小 *ppBuffer = new BYTE[dwBmpSize]; GetDIBits(hMemDC, hBitmap, 0, *pHeight, *ppBuffer, &bmi, DIB_RGB_COLORS); *pdwSize = dwBmpSize; DeleteObject(hBitmap); DeleteDC(hMemDC); DeleteDC(hScreenDC); return TRUE; }参数说明:
*pWidth/*pHeight:由GetSystemMetrics()获取,确保适配多显示器主屏;dwBmpSize计算必须按 4 字节对齐(Windows DIB 行要求),否则GetDIBits返回黑图;biHeight设为负值,强制GetDIBits输出 top-down 数据(RGB 顺序),避免后续转 JPEG 时需翻转。
截得原始 RGB24 数据后,客户端调用CompressImage()(内部用 zlib 压缩)减小体积,再按MAX_PACKET_SIZE=4096分块,每块封装为独立PACKET_TYPE_SCREEN包发送。服务端收到后按序重组,再交由DecodeAndDisplay()渲染。
3. 编译与调试:VC++ 6.0 兼容性修复与运行时依赖
3.1 VC++ 6.0 环境搭建:为什么必须用原生环境
尽管可用 VS2022 创建空工程导入源码,但gh0st3.6存在三处强绑定 VC++ 6.0 的特性:
- CRT 版本硬依赖:
msvcrt.dll导出表与 VC6 CRT 完全一致,VS2015+ 默认链接vcruntime140.dll,运行时LoadLibrary("msvcrt.dll")失败; - MFC 6.0 特有宏:
#ifdef _AFXDLL分支调用AfxGetApp()->m_hInstance,VS 新版 MFC 已移除此接口; - 异常处理模型:VC6 使用
SEH(结构化异常),而 VS 默认/EHsc(C++ 异常),混用导致catch(...)无法捕获 Win32 异常。
解决方案:
下载官方 VC++ 6.0 企业版 ISO(微软已归档为“Visual Studio 6.0 Service Pack 6”),安装后打 SP6 补丁。无需配置 SDK,VC6 自带 Win98/2000 Platform SDK。
3.2 关键编译错误修复清单(实测 12 处)
| 错误号 | 原始代码位置 | 现象 | 修复方式 | 原因 |
|---|---|---|---|---|
| C2065 | Common/Util.cppline 87 | 'snprintf' : undeclared identifier | 替换为_snprintf | VC6 不支持 C99snprintf,用_snprintf替代 |
| C2440 | Client/GhostClient.cppline 215 | cannot convert from 'const char [4]' to 'LPCWSTR' | 在字符串前加L,如L"Ghost" | VC6 默认 ANSI 编译,SetWindowText需LPCWSTR,加L强制宽字符 |
| C2664 | Server/GhostServer.cppline 482 | cannot convert parameter 2 from 'char *' to 'const unsigned char *' | CalcCRC32((const BYTE*)buf, len)显式类型转换 | CalcCRC32声明参数为const BYTE*,传char*需强制转换 |
| C4700 | Common/NetPacket.cppline 121 | local variable 'crc' used without having been initialized | 初始化DWORD crc = 0; | VC6 严格检查未初始化变量,VS 较宽松 |
| C2039 | Client/ScreenCap.cppline 56 | 'GetTickCount64' : is not a member of 'global namespace' | 替换为GetTickCount() | GetTickCount64为 Win7+ API,VC6 SDK 无声明 |
血泪经验:修复完所有 C2xxx 错误后,务必在 Project Settings → C/C++ → Code Generation 中将Use run-time library设为
Multithreaded DLL (/MD),否则链接时LIBCD.LIB与MSVCRT.LIB冲突。
3.3 运行时依赖与调试技巧
编译成功后生成GhostClient.exe和GhostServer.exe,但直接双击会闪退。原因及解决:
缺失
msvcrt.dll:VC6 程序默认动态链接msvcrt.dll,而 Win10/11 已移除该 DLL。
→ 解决:将 VC6 安装目录下的msvcrt.dll(版本 6.00.8797.0)复制到 EXE 同目录,或改用静态链接(Project Settings → C/C++ → Code Generation → Use run-time library →Multithreaded (/MT))。服务端无法监听端口:
bind()返回WSAEACCES。
→ 解决:以管理员权限运行GhostServer.exe,或修改Server/GhostServer.cpp中PORT宏为1024以上(如8080),避开特权端口限制。客户端连接后无响应:Wireshark 抓包显示只发 SYN,无 ACK。
→ 解决:检查防火墙是否拦截GhostClient.exe,临时关闭防火墙测试;或确认服务端 IP 地址填写正确(勿用127.0.0.1测试跨机,应填局域网真实 IP)。
4. 避坑:五个让你编译通过却功能失效的隐蔽陷阱
4.1 现象:客户端成功连接服务端,但服务端日志显示 “Invalid packet header”,立即断开连接
原因:客户端发送的PACKET_HEADER.dwMagic字节序错误。VC6 在 x86 平台默认小端序,但若你在代码中手动拼接dwMagic = 'G' | 'H'<<8 | '0'<<16 | 'S'<<24,而'G'是 ASCII 值 0x47,实际写入内存为0x47 00 00 00,而非协议要求的0x47 48 30 53。
解决:严格使用宏定义#define MAGIC_GH0S 0x47483053,禁止运行时计算。检查NetPacket.cpp中PackHeader()函数是否直接赋值header.dwMagic = MAGIC_GH0S。
4.2 现象:截屏功能在服务端显示为全黑或绿色噪点
原因:GetDIBits()获取的 RGB 数据未按 Windows DIB 规范进行行对齐。*pWidth=1366时,每行字节数应为(1366*3 + 3) & ~3 = 4104,但代码中误算为1366*3=4098,导致后续行数据错位。
解决:在ScreenCap.cpp的CaptureScreen()中,将dwBmpSize计算改为:
int nPitch = ((*pWidth * 24 + 31) / 32) * 4; // 正确行对齐 dwBmpSize = nPitch * (*pHeight);4.3 现象:执行 CMD 命令(如dir c:\)后,服务端收不到任何输出,或只收到乱码
原因:CreateProcess()启动cmd.exe时,未正确设置STARTUPINFO.hStdOutput和hStdError为匿名管道句柄,且未调用SetHandleInformation()设置HANDLE_FLAG_INHERIT。
解决:在Client/Command.cpp的ExecuteCommand()中,创建管道后必须:
SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); // 关键! si.hStdOutput = hWritePipe; si.hStdError = hWritePipe; si.dwFlags |= STARTF_USESTDHANDLES;4.4 现象:服务端多客户端连接时,第二个客户端上线后,第一个客户端的屏幕停止刷新
原因:GhostServer.cpp中g_ClientList使用std::vector存储客户端 socket,但遍历发送屏幕数据时,采用for(int i=0; i<g_ClientList.size(); i++),而g_ClientList在另一线程中被erase()修改,导致迭代器失效。
解决:改用线程安全的std::list,或在发送循环前lock全局互斥量:
EnterCriticalSection(&g_csClientList); for(auto it = g_ClientList.begin(); it != g_ClientList.end(); ++it) { send(it->sock, screenBuf, size, 0); } LeaveCriticalSection(&g_csClientList);4.5 现象:在高 DPI 显示器(如 200% 缩放)下,客户端截屏区域偏移,只捕获左上角 1/4 屏幕
原因:GetSystemMetrics(SM_CXSCREEN)返回的是逻辑像素(DPI 缩放后),而BitBlt()操作的是物理像素。SM_CXSCREEN在 200% 下返回 1920,但物理宽度实为 3840。
解决:改用GetDeviceCaps(hScreenDC, HORZRES)获取物理宽度,GetDeviceCaps(hScreenDC, VERTRES)获取物理高度:
HDC hScreenDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL); int phyWidth = GetDeviceCaps(hScreenDC, HORZRES); int phyHeight = GetDeviceCaps(hScreenDC, VERTRES); // 后续 BitBlt 使用 phyWidth/phyHeight5. 协议扩展与安全加固:把教学样本变成可用的开发基座
5.1 增加 TLS 加密通道:替换裸 TCP 为 OpenSSL 封装
gh0st3.6原生使用明文 TCP,生产环境必须加密。最轻量方案是用 OpenSSL 的SSL_connect()/SSL_read()替换connect()/recv()。步骤如下:
- 引入 OpenSSL 1.1.1t 静态库:下载 Windows 预编译版(
Win32\openssl-1.1.1t-win32-mingw.zip),解压后将libcrypto.lib、libssl.lib加入 VC6 工程 Linker 输入; - 修改
Common/NetPacket.h:增加#define USE_SSL宏开关; - 重写 socket 初始化:在
Client/GhostClient.cpp的ConnectToServer()中:#ifdef USE_SSL SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); const SSL_METHOD* method = SSLv23_client_method(); SSL_CTX* ctx = SSL_CTX_new(method); m_ssl = SSL_new(ctx); SSL_set_fd(m_ssl, m_socket); if(SSL_connect(m_ssl) <= 0) { /* 处理 SSL 握手失败 */ } #endif - 替换收发函数:
recv()→SSL_read(m_ssl, buf, len),send()→SSL_write(m_ssl, buf, len)。
注意:OpenSSL 1.1.1 不再支持 SSLv2/v3,
SSLv23_client_method()实际协商 TLSv1.2+,符合现代安全要求。证书验证可暂跳过(SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL)),生产环境需加载 CA 证书链。
5.2 屏幕传输优化:从 RGB24 到 MJPEG 流式编码
原始 RGB24 截图体积大(1920×1080×3 ≈ 6MB/帧),网络传输卡顿。升级为 MJPEG 可压缩至 200KB/帧。方案:调用jpeg_mem_dest()将libjpeg编码结果直接写入内存缓冲区。
// 新增 EncodeToJpeg() 函数(需链接 libjpeg.lib) int EncodeToJpeg(BYTE* pRGB, int width, int height, BYTE** ppJpeg, DWORD* pdwJpegSize) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); // 设置输出目标为内存 jpeg_mem_dest(&cinfo, ppJpeg, pdwJpegSize); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, 75, TRUE); // 压缩质量 75 jpeg_start_compress(&cinfo, TRUE); JSAMPROW row_pointer[1]; int row_stride = width * 3; while(cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &pRGB[cinfo.next_scanline * row_stride]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); return 0; }调用后,*ppJpeg指向 JPEG 二进制流,可直接封装进PACKET_TYPE_SCREEN发送。服务端用libjpeg解码即可渲染。
5.3 命令执行沙箱化:用 Job Object 限制进程资源
原生CreateProcess()启动的cmd.exe可无限消耗 CPU/内存。加固方案:创建 Job Object 并关联子进程。
// Client/Command.cpp HANDLE hJob = CreateJobObject(NULL, NULL); JOBOBJECT_BASIC_LIMIT_INFORMATION jli = {0}; jli.PerProcessUserTimeLimit.QuadPart = 30000000; // 3秒 CPU 时间限制 jli.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME; SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &jli, sizeof(jli)); // 创建进程时指定 job AssignProcessToJobObject(hJob, hProcess);这样,dir c:\等耗时命令超时后自动终止,防止 DoS 攻击。
5.4 服务端高并发改造:从 select() 到 I/O Completion Port
原select()模型在 100+ 连接时性能骤降。IOCP 是 Windows 最佳实践。改造要点:
- 将每个客户端 socket 关联到 IOCP:
CreateIoCompletionPort((HANDLE)s, hIOCP, 0, 0); WSARecv()改为重叠 I/O,投递WSABUF;- 单独线程池调用
GetQueuedCompletionStatus()处理完成包; g_ClientList改为哈希表(std::unordered_map<SOCKET, CLIENT_INFO*>)提升查找效率。
**从那以后我每次接手远程控制类项目,都强制走一遍这四步加固:TLS 加密、MJPEG 压缩、Job Object 限频、IOCP 并发。哪怕只是课程设计,也按生产标准跑通——因为漏洞不会区分‘作业’和‘上线’,它只认代码逻辑。希望帮到你。
本文还有配套的精品资源,点击获取