ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

gh0st 3.6远控源码剖析:编译、协议与加密全解读

gh0st 3.6远控源码剖析:编译、协议与加密全解读 简介Gh0st 3.6 红狼官方发布的远程控制源码包定位为可编译的完整工程面向具备一定编程基础的系统管理员、安全研究人员和网络编程学习者用于深入理解远程管理工具的内部实现。压缩包整体约909KB体积紧凑目前未单独标注文件总数和类型明细从命名与常见结构看内容通常包含 C 工程源码、构建脚本、使用说明及配套资源。该资源已有939人学习/下载。通过源码可系统梳理主控端与客户端通信模型、TCP/IP 与 Socket 编程、数据加密传输、模块划分和界面交互等关键环节既能支撑二次开发也能为安全研究提供分析 Gh0st 行为特征、排查潜在威胁的直观素材。对初学者可从编译成功入手理解远程控制软件的组成对有经验者可进一步定制通信协议、界面或防护策略学习时建议在隔离环境操作并遵守相关法律与软件许可要求。1. gh0st 3.6 源码是什么老古董反而把远控原理讲得最完整gh0st 3.6 源码红狼官方整理版是我这些年见过最典型的 Windows 远控源码控制端、被控端、插件、加密通信全部摊开在工程里一点不藏私。它不像商业远控向日葵那样把能力封装成黑匣子而是在几千行 C 里把“远程控制电脑”这条链路拆成了可以逐行阅读的实现——上线、心跳、屏幕回传、Shell 交互、文件管理每一环都有真实代码对应。对正在搞 src 的师傅来说它是练协议分析的标准样本对蓝队来说它是提取静态特征、写检测规则的活教材对刚入行的新人它比很多教程都更实在。但这篇文章的前提必须写在前头gh0st 3.6 本质是远程控制木马源码。你能编译它、分析它前提是目标机器归你所有或者你在授权的测试环境、隔离虚拟机里做样本研究。未授权拿它做任何形式的远程控制都超出本文讨论范围。后面讲的编译步骤、参数调整、坑点排查全部默认在本地虚拟机和内网靶机里进行先立住这条线再往下看代码。2. 源码目录背后控制端、被控端与协议层的分工2.1 红狼版在 src 社区里的定位gh0st 3.6 在 src 安全社区流传了很多年版本多到让人眼晕。3.6 指的是功能集和通信协议比较完整的一套控制端能管理多个被控端支持屏幕、键盘、文件、Shell、进程等常见远控操作。红狼版通常是在原版基础上做过工程整理的版本修正部分编译错误、把界面按钮汉化、让插件模块在 VS2008/VS2010 下能直接编译。不同整理版的行为细节略有差异但骨架大致相同。我一般拿到这类源码第一件事不是急着编译而是先确认它的模块划分。Gh0st 3.6 最值得学习的一点是插件化屏幕捕捉、键盘记录、文件管理这些功能被拆成独立模块主程序只负责调度和通信。这种设计让新人能按功能点逐块读代码而不需要一上来就啃整个 8000 行的 MainFrame。红狼版的“官方”二字主要强调来源完整性实际价值在源码本身不在发布者名头。2.2 目录结构与关键文件先把骨架认全解压源码后先过一遍目录不要急着双击 .sln。常见的 gh0st 3.6 工程会包含以下部分命名在不同整理版里略有出入但职责非常清晰模块常见目录职责控制端ClientMFC 界面工程管理主机列表、下发指令、展示回显被控端Server常驻进程或服务等待指令并执行远程操作插件集Plugins 或独立目录屏幕、键盘、文件、Shell 等功能的实现单元公共层Common 或 LibSocket 封装、协议包头、加密算法、公共数据结构和宏定义配置模块Config 或 Install被控端上线地址、端口、服务名、互斥体等运行时参数其中我建议新人优先读 Common 和 Server 的入口Common 里的 PacketHeader 定义了协议包怎么切分边界Server 的入口代码决定被控端启动后先做什么、再做什么。屏幕、键盘那些插件本质上都是“收到命令号 → 调对应 API → 把结果塞回协议包”的重复套路读懂一张协议表插件就都通了。文件清单以 Release 包里实际目录为准不同整理版差异主要集中在这里不必纠结谁的文件名更标准。2.3 TCP 长连接与“反弹”上线模型gh0st 3.6 的通信模型是标准的 TCP 长连接而且是被控端主动去连控制端。这个“主动”是远控场景的经典选择被控端大多在 NAT 后面没有公网 IP控制端只需一个可访问的监听端口被控端不断尝试 connect 就能把通道建起来。// 被控端上线连接逻辑示意伪代码 SOCKET s socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family AF_INET; addr.sin_port htons(8000); // 控制端监听端口 addr.sin_addr.s_addr inet_addr(127.0.0.1); // 控制端地址 while (1) { if (connect(s, (SOCKADDR*)addr, sizeof(addr)) 0) { // 连接成功进入心跳和命令处理循环 break; } Sleep(5000); // 连接失败5 秒后重试 }这段代码说明三个关键点。第一控制端必须先处于监听状态否则 connect 直接失败这也是后文“上了线但列表空”最常见的排查方向。第二端口要和控制端配置完全一致htons 负责把主机字节序转成网络字节序写错字节序会导致跨端通信直接黑屏或断连。第三Sleep 间隔是重连节奏常见设置在 3 到 10 秒之间太短会大量无效握手太长会让人感觉“上线迟钝”。理解了这个模型后面调防火墙、改心跳间隔都有据可依。3. 编译 gh0st 3.6 的完整环境从源码到两个能跑的 exe3.1 环境准备与第一个大坑字符集和 SDK编译这种老工程最稳的组合是 Windows 7 x86 虚拟机加 VS2008 或 VS2010。不要一上来就上 Win10/Win11 加 VS2019MFC 工程的老依赖和新的 C 运行时会有各种兼容问题。我在第一次编译时吃过亏直接在 64 位系统上开 VS2010结果链接阶段报一堆 LNK2019后来换成 x86 虚拟机一次通过这说明 gh0st 3.6 的工程默认就是 32 位思维。环境准备按这个顺序来先装系统再装 Visual Studio 及对应 MFC 组件然后把源码解压到纯英文路径比如 D:\src\ghost36。路径里有中文或空格时老工程里写死的绝对路径和资源引用很容易翻车。接下来打开项目属性把字符集从“Unicode”切成“使用多字节字符集”这一步不做大量字符串相关代码会编译不过。最后确认链接器输入里包含 ws2_32.lib它是 Winsock 的库。# 用 vcvarsall.bat 初始化 32 位编译环境 call C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat x86 # 打开源码所在目录 cd /d D:\src\ghost36vcvarsall.bat 是 VS 命令行编译的入口后面的 x86 参数表示生成 32 位目标。如果 VS 安装位置不同把路径改成你自己的实际路径。cd 到源码目录后再执行编译命令而不是直接双击 sln这样能保证环境变量正确注入。3.2 编译解决方案从 sln 到 Release 输出环境就绪后用命令行编译整个解决方案devenv Gh0st.sln /Rebuild Release /UseEnv/UseEnv 会强制 devenv 使用刚才 vcvarsall 注入的环境变量避免它重新加载默认工具链导致 SDK 路径错乱。这条命令的执行结果会在 Release 目录下生成两个核心文件控制端程序和被控端程序。如果编译过程中报“cannot open source input file”基本是 SDK 没有安装完整或路径不对回到上一节检查 Windows SDK 组件而不是怀疑代码本身。编译完成后先不要在任何真实机器上运行这两个文件。我要强调一个习惯把控制端和被控端分别放在两台独立的虚拟机里做测试物理机只做文件中转。老代码的稳定性远没有你想象的那么好屏幕回传模块在特定分辨率下会崩溃Shell 交互也可能把虚拟机搞到卡死隔离环境里随便折腾坏了就还原快照。这一步能省下后面 90% 的麻烦。3.3 被控端配置上线地址、端口、服务名、互斥体gh0st 3.6 的源码里通常带一个配置界面或配置结构体用来指定被控端连哪里、监听哪个端口、以什么名字在系统里驻留。常见做法是把配置写进一个结构体再在运行时读取typedef struct _SERVER_CONFIG { char szHost[128]; // 控制端 IP 或域名可配置多个备选 int nPort; // 控制端监听端口 char szService[64]; // 服务名或进程名 char szMutex[64]; // 互斥体名防止被控端重复启动 int nAutoStart; // 是否注册为自启动服务 } SERVER_CONFIG, *PSERVER_CONFIG;这里的 szHost 是通信关键测试时先写 127.0.0.1被控端和控制端在同一台虚拟机。nPort 要和控制端监听的端口一致常见的默认值是 8000 或 8080具体以源码里的监听参数为准。szMutex 容易被新手忽略两个被控端进程同时运行时互斥体会阻止第二个实例这本身是防误启动机制但排查问题时经常让人误判为“程序没反应”。nAutoStart 在测试阶段先设 0避免被控端进程注册成系统服务后任务管理里都找不到它的窗口增加调试难度。3.4 跑通第一条链路本机回环测试配置写好后最推荐的验证路径是这样的先启动控制端确认它开始监听 8000 端口再启动被控端观察它是否成功 connect。控制端界面里如果出现一个新的在线节点说明链路已经通了。这时不要急着试屏幕或文件先看心跳包能不能稳定维持这是后面一切操作的前提。万一第一步就失败用 netstat 命令查监听状态netstat -ano | findstr 8000看到 LISTENING 说明控制端在监听被控端 connect 才有地方落。如果这里什么都看不到问题九成出现在防火墙或端口没绑对。这个排查顺序我后面会专门展开先记结论链路不通时从监听端往连接端逐层排查比乱改代码有效率得多。4. 通信面拆解gh0st 3.6 的包头、命令号与硬编码加密4.1 一次远控操作完整的数据流gh0st 3.6 的一次远程操作数据流大概是这样的控制端在界面上点击屏幕查看界面线程把命令号和数据塞进协议包走加密函数通过 TCP Socket 发出去。被控端收到字节流后先按包头定义的边界把整包切出来再解密、解析命令号、分发到对应插件模块插件执行完把结果按同样的格式封装回传。整个过程是同步请求响应模式心跳包例外心跳是单向靠定时器驱动的。包头结构是理解这一整套协议的关键。常见实现长这样#pragma pack(push, 1) typedef struct _GH0ST_HEADER { DWORD dwMark; // 固定魔数界定包边界 DWORD dwCommand; // 命令号决定操作类型 DWORD dwLength; // 数据区长度 } GH0ST_HEADER, *PGH0ST_HEADER; #pragma pack(pop)#pragma pack(1) 强制结构体按 1 字节对齐这是网络协议的标准做法结构体成员之间不能有编译器默认填充的空白字节否则两端解析出的长度永远对不上。dwMark 是固定值相当于协议的身份标识用来区分“这是 gh0st 包”和“这是无关流量”很多检测引擎针对远控的规则就是从这 4 字节入手。dwCommand 具体数值在不同版本里不一样必须看源码里的枚举定义千万不要拿网上某篇文章的数值直接套。4.2 命令号到底有哪些先建一张自己的映射表读 gh0st 3.6 源码时我建议你做的第一张表不是架构图而是命令号映射表。控制端每条指令都是一个 DWORD 常量对应一个操作。常见的分组大致是这样命令分组功能范围典型命令登录与会话握手、鉴权、上线注册登录请求、登录响应、心跳屏幕类屏幕快照与分块回传开始屏幕查看、停止屏幕查看文件类远程文件操作枚举目录、下载文件、上传文件Shell 类远程命令行交互启动 Shell、输入命令、退出 Shell系统类进程、窗口、注册表枚举进程、结束进程这张表的价值在于当你抓到一个被控端的通信样本时只需把包头里的 dwCommand 对照这张表就能判断它在下发什么操作。我在做样本分析时一般先把枚举定义抄到一个文本文件里后面抓包看到数值直接查效率比反复翻源码高很多。4.3 加密逻辑与稳定特征为什么说 gh0st 是蓝队好样本gh0st 3.6 的加密实现放在公共模块里早期版本常见做法是逐字节异或或者替换算法密钥以数组形式硬编码在二进制中。这类实现放到今天看漏洞很多但对安全研究却非常友好。加密函数的骨架大概是// DataEncrypt 模块的逐字节变换示意 void Gh0stEncrypt(char *pBuf, int nLen) { // 密钥表编译进二进制长度固定 for (int i 0; i nLen; i) { pBuf[i] ^ g_byKey[i % sizeof(g_byKey)]; } }注意这里的 g_byKey 是静态数组编译后每个被控端二进制里都有相同的一段密钥数据。这意味着两件事第一通信内容虽然被混淆但密钥表本身成为稳定的静态特征杀毒引擎和蓝队特征引擎都会优先匹配它第二密钥一旦泄露历史流量里所有被控端通信都能被还原。所以你在研究这份源码时不要把实际密钥值写进笔记或公开文档里尤其不要在 src 漏洞报告里贴二进制片段这是基本的安全习惯。加密算法的缺陷也值得记一笔异或加密没有扩散性明文里连续相同的字节在密文里也是连续相同模式统计特征非常明显。对检测方来说不需要完整解密只要算一下流量里的字节分布就能判断是不是简单异或。对学习者来说这就是一个绝佳的对照实验把异或换成 CTR 模式的流密码密文分布立刻变成伪随机这比背十篇密码学原理都直观。4.4 心跳与断线重连的参数陷阱心跳机制是 gh0st 3.6 稳定性的命门。被控端每隔一定秒数发送一个心跳包控制端收到后刷新在线时间如果超过某个阈值没收到节点就会被判定掉线并从列表移除。这两个参数在源码里通常以常量形式存在#define HEARTBEAT_INTERVAL 30000 // 心跳间隔单位毫秒 #define HEARTBEAT_TIMEOUT 90000 // 超时阈值单位毫秒间隔 30 秒、阈值 90 秒是比较常见的组合。它的问题在于如果被控端在 NAT 后面NAT 会话老化时间如果小于 60 秒空闲连接可能被中间设备静默断开心跳因为间隔太长来不及保活节点就频繁掉线。我一般会把间隔调到 20 秒阈值调到 90 秒对 NAT 场景更友好。注意这两个参数的数值是强相关的间隔必须显著小于阈值否则控制端还没收到下一条心跳就把节点踢掉了那种“刚上线就消失”的现象多半是这个原因。5. gh0st 3.6 源码上手的 5 个常见坑与排查路径5.1 编译报错LNK2019 和 cannot open source input file现象用 VS2010 打开解决方案后直接按 F7 编译报一堆 LNK2019 未解析外部符号或者干脆提示 cannot open source input file。原因LNK2019 多半是字符集不匹配或者平台工具集太新老代码里的 _T 宏和 MFC 字符映射函数在 Unicode 模式下解析出不同符号名于是找不到入口。cannot open source input file 则是 SDK 路径没配好VS 找不到 Windows.h 或特定头文件。解决先把字符集切到多字节再把工程属性里的目标平台版本改成已安装的 SDK 版本最后确认 ws2_32.lib 在依赖项里。如果还报错用 vcvarsall.bat 初始化干净环境后重新 Rebuild老代码在纯命令行环境下的表现往往比 IDE 里稳定。5.2 被控端进程在跑控制端列表却始终为空现象被控端启动了任务管理器能看到进程但控制端界面上始终没有新节点上线。原因先检查监听端。控制端没有监听端口、防火墙拦了入站连接、或者被控端配置里的 IP 写成了物理机内网地址而不是测试机地址都会让 connect 失败。另一个隐蔽原因是被控端配置的端口和控制端实际监听端口差了 1 位数。解决按“监听 → 连通 → 配置”三级排查。先在控制端所在机器执行 netstat -ano | findstr 端口确认 LISTENING再在被控端机器用 telnet IP 端口测试 TCP 连通性最后回源码确认配置结构体里的 szHost 和 nPort 正确写入。顺序错乱会浪费大量时间我自己的习惯永远是先查监听端。5.3 屏幕回传黑屏或花屏分块与颜色位数现象远程屏幕能连上但画面大面积黑屏或者图像撕裂、只刷新出一部分。原因gh0st 3.6 的屏幕回传是按分块扫描的控制端请求一屏画面被控端把屏幕分成若干块逐块编码回传。分块尺寸设太大时单块编码耗时过长控制端渲染线程等不及就把旧数据清了颜色位数设成 32 位时单包数据量暴涨半路丢包就会花屏。解决把屏幕传输的颜色位数改为 16 位减少单包体积分块尺寸调小比如从 64x64 改成 32x32虽然回传频率高一点但每一块都更稳定。还有一招是把远程桌面分辨率调低到 1024x768 以下源数据量小了整个链路都会顺畅很多这是被很多人忽略的捷径。5.4 心跳断连节点上线后几分钟就消失现象被控端能上线控制端也能看到节点但每隔几分钟节点就消失过一会儿又自动回来。原因NAT 会话老化时间比心跳间隔短中间设备断了空闲连接或者超时阈值设得太紧控制端还没收到下一条心跳就判定超时。前者是网络环境问题后者是配置问题表现形式几乎一样必须先区分再动手。解决先把心跳间隔从 30 秒降到 20 秒观察一个完整周期。如果不再掉线说明是 NAT 老化导致心跳保活生效了。如果还在掉再检查超时阈值是否比心跳间隔大至少三倍。老源码里这两个常量经常写在一起改的时候容易漏掉一个导致新问题。5.5 杀软秒报毒这是预期行为不是编译配置错了现象被控端 exe 刚生成出来Windows Defender 直接隔离甚至控制端程序都会被报风险。原因这本身就是远控木马静态特征里包含敏感 API 调用和硬编码密钥不报毒才奇怪。它不是编译环境的问题也不是代码的问题这是身份问题。解决研究操作必须放在断网的虚拟机里给样本文件添加只读标记记录对应的 SHA256方便后续追踪。不要把样本通过云盘、U 盘或邮件发送给任何人也不要把编译产物留在物理机桌面上。如果你在授权项目里做验证应该在测试环境里申请白名单并注明样本用途测完立刻销毁。这是底线也是血泪经验样本失控一次后续所有工作都会被怀疑合规性。6. 从 gh0st 3.6 里抽三个进阶动作抓包、提特征、改加密6.1 用 Wireshark 验证协议特征跑通链路之后先别急着点功能按钮。在控制端机器上开 Wireshark过滤条件写 tcp.port 8000再让被控端重连一次。你会看到清晰的同步握手序列、周期性的心跳包以及每次操作前后的请求响应。把心跳包间隔和包长记下来对照源码里的常量验证你对心跳机制的理解是否准确。6.2 把硬编码密钥变成检测规则用文本编辑器打开被控端 exe搜索源码里密钥数组的十六进制片段把它提取出来作为特征字符串。我在本地实验时一般会写成一条简单的检测规则# 检测规则示意特征值从样本中提取后再替换 rule gh0st_redwolf { strings: $key { 3C 5A 9E F8 1B 2C 4D 6E } # 示例字节实际以提取为准 $hdr { 00 00 00 00 00 00 00 01 } # 示例命令号 condition: $key and $hdr }规则里的字节值必须从你自己的样本里提取不能用网上现成的因为不同整理版的密钥表可能完全不同。这条规则的意义在于它能在一堆正常软件里快速筛出 gh0st 家族样本这也是蓝队分析这份源码最常见的目标。6.3 重写加密做一次对照实验我对这份源码做过最值得的一次改造是保持包头结构不变把加密函数从逐字节异或换成 CTR 模式的 XOR 流。改完之后用 Wireshark 重新抓包会发现包长的边界规律没变但数据区的字节分布变成了伪随机。这证明你真正理解了协议封装与加密分离的边界。如果改完通信立刻断掉说明还有某个模块绕过了加密层直接收发数据这种边界问题恰恰是源码分析里最有价值的学习点。这个实验留给你的启发是老远控的协议未必精巧但它把所有网络通信该遇到的问题都展示了一遍。边界不齐、密钥硬编码、心跳与超时参数纠缠这些问题在今天的新项目里依然存在。读懂 gh0st 3.6 的代码再看其他远控样本你会觉得很多新花样只是换了外壳内核还是那一套。搞安全研究这几年我最深的教训就是不要因为代码老就轻视它越老的代码越诚实坑都写在明面上希望你也能从这里读出自己的方法论。本文还有配套的精品资源点击获取
返回列表