简介红狼官方发布的 Gh0st 3.6 远程控制源码包是一份可编译的完整实现面向系统管理员、网络编程学习者和安全研究人员用于剖析主控端与被控端的通信机制也为合法远程管理工具的二次开发提供参考。压缩包约 909KB内容以源码为核心预计包含项目源文件、构建脚本与使用说明等组成部分。目前已吸引 939 人学习。深入研读源码可掌握 TCP/IP 通信、Socket 编程、数据加密传输、客户端与服务端模块划分等关键技术理解远程控制软件从编译到运行的完整链路并在此基础上修改定制或者用于漏洞分析与安全防护研究。需特别留意此类源码应严格限定在学习研究与合规测试环境中使用避免任何未经授权的控制行为。1. 远程控制源码 gh0st3.6拆的是什么能用来做什么说到远程控制源码Gh0st 是老牌项目里绕不开的名字。gh0st3.6_src 是这套源码的打包名称网上流传的 3.6 版本多数标注“红狼官方”它属于十多年前传下来的经典 C/S 架构远程控制框架一个带界面的服务端负责管理一个轻量客户端负责被控中间走自定义 TCP 协议。这套代码放在今天最值得做的事有三类学习 Socket 通信、命令分帧和 GUI 联动把它的客户端改造成内网远程协助工具以及理解老一代远控软件为什么能同时做到“功能全”和“体积小”。适合的人群不是零基础小白而是已经写过一点网络编程、想拆开一个完整 C/S 项目看内部结构的开发者。2. Gh0st 源码结构通信协议、功能模块与可改点2.1 一条指令从控制端走到被控端的过程拆源码第一步不是看界面而是顺着“一条命令”从服务端发出到客户端执行返回的完整链路走一遍。Gh0st 3.6 的通信模型是典型的反向连接服务端监听端口等待客户端上线客户端启动后主动向服务端地址发起连接连接建立之后才是命令交互。这和早期很多正向连接工具完全不同也是这套代码最值得读的部分——大部分新手工程写 Socket 都是“客户端连服务端”而反向连接意味着服务端不需要知道客户端的公网地址只要客户端能访问到服务端即可。整条链路的本质是“请求-响应”。服务端下发一条命令本质上是在发送一个自定义结构体客户端收到后根据命令号进入对应处理分支执行完再回传结果。数据分帧不是用简单的换行符而是用一个带长度字段的包头加负载数据代码如下typedef struct _GHOST_PACKET_HEAD { DWORD dwMark; // 固定标识用于快速校验 DWORD dwVersion; // 协议版本号 DWORD dwCommand; // 命令号决定后续处理逻辑 DWORD dwDataLen; // 负载数据长度 } GHOST_PACKET_HEAD, *PGHOST_PACKET_HEAD;这段结构体是整个协议的核心前 16 个字节是头部告诉接收方“这条数据是什么版本、什么命令、后面跟多少字节”之后的负载才是具体内容比如屏幕截图的压缩数据、文件下载的文件块或命令行的输出文本。接收方先收满头部再按dwDataLen收负载这样即使单包拆成多次到达也能正确拼回完整消息。实际源码里对头部做了封装还带内存池但分帧思路就是这样。参数说明里最关键的是dwVersion。服务端和客户端如果版本号不一致连接建立后第一轮握手就会被丢弃表现为“客户端显示已连接但列表里看不到主机”。我见过不少人改命令号时忘了同步版本号排查半天最后发现两边常量不一样。2.2 目录划分哪部分决定“能控什么”整套源码按职责可以分成三块服务端、客户端和公共模块。服务端就是那个带窗口的管理程序负责主机列表、在线状态、屏幕查看、文件传输和远程终端客户端是运行在被控机器上的轻量程序负责监听服务端下发的命令并执行公共模块则被两边共用里面是压缩解压、网络传输、内存分配这些工具函数。目录结构拆开看大概是这样的对应关系目录实际职责修改后会影响什么Server主界面、主机管理、命令封装界面交互、功能入口Client连接建立、命令分发、动作执行客户端行为、上线逻辑Common网络收发、压缩、公共数据结构协议兼容性、稳定性如果只想控制客户端的“行为范围”重点看 Client 目录里的命令分发函数如果嫌界面丑想重新做管理端界面重点看 Server 目录下的对话框和列表控件代码如果两边交互出了问题Common 里网络收发相关文件才是首先需要查的地方。这套代码一个典型特点是功能命令都集中在一张大 switch 分支里。客户端收到命令号后跳进对应分支屏幕查看就是截屏后分块回传文件管理就是列出磁盘、切换目录、上传下载远程终端就是创建管道执行命令并把输出回传。看清楚这些分支以后删功能或者加功能都容易定位。2.3 可改参数与二次开发入口拿到源码先别急着编译建议全局搜索几个关键常量先看一遍。首先是监听端口服务端和客户端必须写成同一个值其次是超时时间和心跳间隔这个控制的是“多长时间没消息判定掉线”再就是前面提到的协议版本号。这几个数值在源码里都是宏定义或全局常量集中在公共模块和网络初始化代码里。常见做法是先把默认端口改掉再编译避免和你自己局域网里别的服务冲突。我一般会顺便把dwMark固定标识改掉这样就算别人用同版本客户端连上来也会因为标识不匹配而被拒。注意只改端口不改标识协议间仍然兼容标识一旦改动服务端和客户端必须同步改否则双方互相不认识。二次开发的入口也在这几个常量附近。比如想加一个自定义命令流程是先在公共头文件里加一个命令号宏然后在服务端界面加一个按钮并封装发送函数最后在客户端命令分发里加一个 case 分支处理并回传结果。这套流程和写普通网络程序没有区别但好处是整个传输、分帧、回传的轮子都已经造好你只需要填业务逻辑。3. 从源码到可执行文件编译环境与三步构建3.1 选择编译环境老工程在 VS 里的两个大坑这套代码年代比较久用新版 Visual Studio 打开时通常会遇到两个问题工程文件格式太老打不开或者编译器工具集不兼容报一堆宏错误。我在实际拆解时试过两条路一条是用 VS 直接打开 .sln 并转换另一条是手动把源码放进新建工程重新组织两条路都能走通但各有条件。VS 直接转换最省事但要注意安装“使用 C 的桌面开发”工作负载里面包含 MFC 组件。老工程大量依赖 MFC如果只装了基础 C 工具链转换后会卡在cannot open include file afxwin.h这类错误上。另外老项目默认用多字节字符集写的界面字符串处理新版 VS 默认 Unicode编译时会出现一堆LPCWSTR与LPCSTR类型不匹配的报错解决办法是在项目属性里把字符集改回“使用多字节字符集”或者全局搜字符串类型统一改掉。如果不想折腾工程转换也可以手动重建工程。把源码按 Server、Client、Common 三个逻辑分组拉进新工程再手动添加头文件目录和库依赖同样能编译出来而且后面改代码时更可控。两条路线我建议优先走工程转换因为老代码里有些文件路径和资源文件 ID 是绑定的手动重建容易漏资源。3.2 命令行编译与 GUI 编译的完整流程命令行编译适合想自动化构建的情况。先确认 VS 的开发者命令行环境能正常找到cl.exe然后在源码根目录下执行编译。这里我以最常见的“先编公共模块再编服务端和客户端”的顺序为例# 先进入开发者命令行环境或手动执行 vcvarsall.bat # 编译公共模块生成公共库文件 cl /c /EHsc /MD Common\\*.cpp /ICommon # 编译服务端链接公共库和 MFC 库 cl /EHsc /MD /D_UNICODE Server\\*.cpp Common\\*.obj /ICommon /link /SUBSYSTEM:WINDOWS # 编译客户端输出轻量级可执行文件 cl /EHsc /MD /D_UNICODE Client\\*.cpp Common\\*.obj /ICommon /link /SUBSYSTEM:WINDOWS参数说明一下/EHsc是启用标准 C 异常处理/MD表示动态链接运行时库/ICommon指定公共头文件搜索路径/SUBSYSTEM:WINDOWS告诉链接器这是一个窗口程序而不是控制台程序。这套命令会依次生成公共库对象、服务端程序和客户端程序。不过实际项目里直接编译全部文件很容易遇到头文件互相包含的问题因为部分旧代码对头文件的依赖顺序很敏感。所以我在实际操作时更推荐另一个路线打开工程文件先在解决方案资源管理器里设置好“公共模块先编译”的项目依赖关系再在菜单里选 Release 配置分别生成服务端和客户端两个可执行文件。GUI 编译的坑更少实时报错也更友好适合第一次上手。3.3 编译产物与运行依赖编译完成后服务端和客户端是两个独立的 exe不需要额外安装运行库以外的东西。但因为客户端要尽可能小代码里很多地方用了静态链接或动态加载系统 API所以运行时对系统的依赖比普通程序要多一点——主要体现在某些老 API 只在特定系统版本上可用。实测局域网环境里Windows 10 专业版 x64 能正常运行但要注意如果把客户端复制到 x86 架构的旧系统上先确认你编的产物是 x86 版本。这条代码本身是 32 位设计用 64 位工具链编出来的默认产物也是 32 位不要强行编 x64否则指针截断问题会让你怀疑人生。编译成功后验证产物是否正常最简单的方式不是直接跑起来而是先放到任务管理器里看进程映像名称是否正常再用命令行工具查进程是否有监听端口。如果客户端和服务端都已启动但看不到监听端口基本可以断定是网络初始化失败或端口被占这类问题的详细排查我放在第 5 章。4. 部署与使用服务端监听、客户端连接与参数配置4.1 服务端端口、监听与主机注册服务端启动后做三件事读取配置参数、创建监听 Socket、启动界面轮询。配置参数集中在服务端程序目录下一个文本配置文件里包括监听端口、最大连接数、心跳超时和是否自动弹出新上线主机。第一次部署时建议把配置精简到最小只留端口和超时两项验证通了再慢慢放开。服务端登记一台新主机的过程在代码里对应一个“先验证后入库”的逻辑。客户端连上来时服务端先收头部并校验标识和版本号校验通过才把这条 Socket 加入在线列表并刷新界面。我把这个过程还原成伪代码// 服务端收到新连接后的处理框架 SOCKET sClient accept(server_socket, ...); // 第一步收取并解析协议头 GHOST_PACKET_HEAD header recv_header(sClient); // 第二步校验标识和版本不通过直接关闭连接 if (header.dwMark ! MARK_VALUE || header.dwVersion ! PROTOCOL_VERSION) { closesocket(sClient); return; } // 第三步登记主机加入列表并刷新界面 register_host(sClient, client_ip, client_port);这里有三点值得注意。第一accept返回后立即设置 Socket 超时时间避免客户端连接后不发送任何数据导致服务端线程卡死第二标识校验一定要在登记之前做否则恶意或协议不匹配的连接也会占用列表位置第三注册主机前建议先读一个对端 IP 的字符串用整数保存 IP 在界面显示时还要再转一次不如直接存文本省事。服务端的界面主机列表是按“分组-主机”两层管理的分组信息同样存在配置里。新上线的客户端默认进“默认分组”你可以右键手动移动到业务分组里。这套管理机制在实际运维中很好用比如机房几十台机器按楼层分组后出问题直接锁定分组内单台机器做屏幕查看。4.2 客户端连接参数、上线与在线状态客户端的启动流程比服务端简单得多读配置、取服务端地址和端口、建立连接、进入消息循环。客户端配置里最核心的三个参数是服务端 IP、服务端端口、上线间隔。上线间隔指的是客户端连接断开后重新尝试连接的等待时间在弱网环境或服务端重启场景下特别关键。实际部署时我一般会先手动启动一次客户端确认它能出现在服务端主机列表里再把它设为开机启动。这里要注意写清楚服务端 IP 是内网 IP 还是公网 IP——如果服务端部署在公网机器上客户端配置公网 IP如果只是局域网联调配置内网 IP不要写主机名因为有些老代码对主机名解析支持得并不好。连接成功后的在线状态判断依赖心跳包。客户端和服务器之间不交流业务数据时会定时发送一个极短的心跳消息服务端收到后刷新该连接的最后活跃时间。如果超过设定超时时间没收到心跳服务端就把这条主机从在线列表踢掉并释放 Socket。这个机制直接决定了“掉线后多久能反映到界面上”心跳间隔越短越灵敏但越耗费流量间隔太长则客户端断了服务端半天不知道。局域网里我建议心跳间隔设 10 到 15 秒公网环境可以放宽到 30 秒。4.3 一次小规模局域网联调在本地跑通整个链路有一个固定步骤。先在一台 Windows 10 机器上启动服务端并开放指定端口再在另一台同网段机器上配置客户端指向服务端 IP启动客户端观察主机列表变化。最理想的测试是一台实机加一台虚拟机虚拟机里跑客户端实机跑服务端中间不经过路由器 NAT。联调时按这套顺序来做操作步骤作用验证方式服务端启动建立监听端口netstat -ano查监听状态客户端启动发起反向连接服务端列表出现新主机右键打开屏幕查看下发屏幕截图命令窗口显示远程画面断开客户端进程模拟断线服务端超时后移除主机重新启动客户端验证重连机制主机重新出现在列表实测时最常用的验证方式是“屏幕查看”。右键一台在线主机选择屏幕查看后如果能看到对方桌面且延迟在可接受范围说明整条链路——连接建立、命令分帧、截图压缩回传、数据显示——全部正常。如果卡在这一步基本可以按第 5 章的网络相关条目排查。5. 上手常见问题排查编译失败、连不上、秒掉线5.1 编译时报错无法打开afxwin.h现象用 VS 直接打开解决方案文件编译到一半提示fatal error C1083: Cannot open include file: afxwin.h。 原因当前 VS 环境没有安装 MFC 组件。老工程服务端依赖 MFC 的窗口和消息机制缺少头文件等于整个 GUI 框架不可用。 解决在 Visual Studio Installer 中勾选“使用 C 的桌面开发”右侧安装细节里额外勾选“适用于最新 v143 生成工具的 C MFC(x86 和 x64)”。装完后重启 VS重新编译即可。另外老工程默认的字符集是多字节如果后续报字符串类型不匹配再进项目属性把字符集改成“使用多字节字符集”。5.2 客户端提示已连接但服务端列表不显示现象客户端启动后不报错但服务端主机列表始终没有新主机出现。 原因最常见的是协议版本号不一致或固定标识不一致。比如你改了客户端的dwMark常量但服务端没改服务端在校验标识阶段直接丢弃连接。另一种情况是服务端端口和客户端配置端口不一致连接根本没到服务端。 解决先查两边配置里的端口是否一致再用抓包工具看服务端端口上有没有 TCP 握手流量。如果握手成功但很快被断开就去公共头文件里核对dwVersion和dwMark两个常量。我见过 A 同学在这里踩坑最后用文本对比工具逐一比对两个工程里的头文件才发现是旧版本备份残留。5.3 屏幕查看黑屏但连接没断现象主机在线文件管理正常唯独屏幕查看返回全黑画面。 原因屏幕捕获功能依赖系统桌面会话。如果客户端是作为服务启动的运行在 Session 0 下无法访问用户桌面截图自然是黑的。另外屏幕分辨率过高、颜色深度设置不合适也可能导致编码后数据异常。 解决把客户端改成普通用户进程启动不要通过系统服务方式拉起。屏幕查看前把远程机器分辨率调成 1024x768 或 1366x768 这类常见规格能减少兼容性问题。如果确认不是会话问题打开源码看截图分支是否限制了颜色位数把位数放宽到 16 位以上再编译。5.4 连接后几十秒就掉线现象客户端一上线就掉反复重连反复掉间隔很有规律。 原因心跳超时设置太短加上局域网或本机防火墙丢包导致服务端误判客户端失联。另一个常见原因是服务端在线线程清理逻辑有 bug新连接被旧连接的残留资源干扰。 解决把心跳间隔和超时时间同时调大比如心跳间隔 10 秒、超时 30 秒再观察是否掉线。如果仍然掉线看服务端日志里有没有线程异常的记录有就直接在异常处打点检查是不是 Socket 对象未置空导致二次关闭。5.5 服务端端口被系统防火墙拦截现象客户端启动后netstat能看到连接但服务端始终收不到或者服务端完全无法启动监听。 原因Windows 防火墙默认拦截未经授权的入站连接。服务端监听端口没有入站规则时客户端发来的 TCP 握手包直接在内核层被丢弃。 解决在防火墙高级设置里添加“入站规则”允许 TCP 端口号等于服务端配置的那个端口作用域限制为指定子网即可。如果只是联调测试临时关掉防火墙最快但记得联调完立刻恢复系统防火墙策略。这个问题被很多人当成“源码有问题”实际上只是系统防护策略没放行。6. 二次开发方向把 gh0st3.6 变成自己的内网协助工具源码跑通只是第一步真正值钱的是怎么改成适合自己场景的工具。我这里给出三个亲测可行的改动方向都不需要动协议核心框架。第一个方向是加连接口令。默认协议只要标识和版本对上就能连这在多人环境里乱得不行。在协议头后面追加一个固定长度的口令字段服务端校验通过后才登记主机能挡掉一批乱连的客户端。实现时注意口令校验放在标识校验之后即可失败直接关闭不占用后续资源。第二个方向是给文件传输加白名单目录。默认文件管理可以浏览整个磁盘改成只允许访问指定根目录下的文件适合做机房受控文件分发。第三个方向是补操作日志。服务端每次下发命令时把命令号、目标 IP、时间写进文件排查问题时有据可查。口令校验的伪代码我给一份最小实现方便直接抄进连接处理分支// 在标识和版本校验通过之后追加口令校验 char recv_pass[32] {0}; recv(sClient, recv_pass, sizeof(recv_pass), 0); // 和本地配置的口令比对 if (strcmp(recv_pass, g_authPassword) ! 0) { // 口令错误发送拒绝通知后断开 send(sClient, refused, 7, 0); closesocket(sClient); return; } // 口令正确才进入主机注册流程 register_host(sClient, client_ip, client_port);这段代码的关键参数是g_authPassword必须同时写进服务端和客户端配置里长度不要超过 32 字节。同组的客户端可以被统一分发同一个配置文件不同组用不同口令这样内网里多套测试环境互不干扰。改造完成后做一次完整的验证重启服务端、清掉旧客户端进程、用新配置重新启动客户端确认主机列表出现、屏幕查看可用、口令错误时客户端连接显示被拒。我自己的习惯是每加一个功能就重新编一套 Release 产物并保存好构建日志因为老工程反复修改容易埋雷有日志能快速定位是哪轮改动引入的问题。A 同学有次改坏了命令分发分支客户端一连接就崩溃最后就是靠构建日志对比找到多写了一个break导致的问题。从那以后我每次拿到远古 C/S 源码都强制走一遍“改标识、改端口、统一字符集、加口令”这四步再做功能开发。希望帮到你。本文还有配套的精品资源点击获取