简介这是一款基于 gh0st 的被控端单 exe 修改版远程控制源码面向安全研究人员、免杀学习者和恶意代码分析人员用于理解远控木马的工作原理与对抗主动防御的实现方式。与常见的 dll 版、服务启动版不同该版本将客户端功能压缩进单个 exe并在源码中集成了动态函数调用等常用免杀方法方便读者观察 API 获取、敏感调用下沉与特征规避的落地写法。整个资源包约 114KB共 54 个文件以 28 个头文件、21 个 C 源文件为主另外包含工程配置文件和 lib 依赖模块覆盖音频采集、视频捕获、键盘记录、文件管理、屏幕监控、注册表编辑、系统信息获取等远控核心功能可直接在 Visual Studio 中加载编译注意编译前需安装对应 SDK。已有 1247 人学习下载。阅读源码后能较完整地掌握 gh0st 被控端的框架结构、各类远程管理模块的调用关系以及免杀设计中动态函数解析和模块拆分等典型技巧适合作为二次开发、行为检测或应急分析的研究样本。 我最早接触远程控制这个方向就是因为一台放在老家机房里的文件服务器。每次出了点小问题都要折腾几十公里跑过去实在顶不住才想到自己折腾一套单 exe 的远程控制工具。断断续续搞了大半年踩了不少坑也沉淀了一套比较完整的方案。这两天重新梳理了一遍源码干脆把整套设计和实现思路写出来给有同样需求的朋友一个参考。先说清楚这个东西到底是什么。所谓“RAT”全称是 Remote Administration Tool远程管理工具。很多人听到 Remote Access Tool 或者 RAT 就下意识觉得是黑客工具但真正在企业运维、个人设备管理、远程协助这些场景里一套好用的远程控制工具就是刚需。比如帮远在老家的爸妈修电脑、管理办公室几台无人值守的机器、远程调试家中的 NAS这些都是典型的使用场景。我做的这套工具定位很明确便携、免安装、单文件、开箱即用双击 exe 就能跑起来不需要目标机器上有任何额外环境。这篇文章我会把整套源码的设计思路、模块划分、关键技术点和打包发布流程都讲透全程基于我实际用下来的经验。内容会偏向工程落地适合有一定编程基础、想自己做一套轻量远控工具的人也适合开发者想了解 RAT 单 exe 方案是怎么一回事的朋友。1. 项目整体设计思路与技术选型1.1 单 exe 远程控制的本质拆解远程控制工具从架构上看绕不开几个核心模块被控端Server/Agent、控制端Client、通信通道以及支撑二者的网络穿透和认证机制。我拿到一个远程控制需求时第一件事不是写代码而是先问自己一个问题这套工具的“边界”在哪里是只在局域网内用还是需要跨互联网是临时用一次还是长期驻留这两个问题直接决定整个技术方案的走向也决定了后续所有代码的复杂度。单 exe 版本的“单文件”特性本质上是把程序运行时需要的所有依赖资源动态库、配置、图标、可能需要的附加数据在编译阶段就整合进了一个可执行文件。这样在设计上带来两个明显的好处部署简单拷走即用内存和文件占用更可控。但代价是程序体积可能膨胀某些杀毒软件会对自解压或捆绑型单文件产生误报这个问题后面我会专门讲。1.2 语言选型从 C 到 C# 再到 Go我为什么最终定在 C#远程控制工具的开发语言选择非常关键。我最早是用 C 写的用的是 Win32 API 加 GDI 抓屏那时候的思路是尽量依赖系统原生接口把体积压到最小。C 确实够底层、可控性强但开发效率比较低尤其是在处理界面、线程同步和网络异常场景时动不动就崩调试成本太高。后来我在一个开源项目里看到有人用 C# 做了一套轻量远程控制工具借助 .NET Framework 自带的 WinForms 和 Socket 异步编程代码量大大减少稳定性和可维护性也上来了。我从那时开始转向 C#主力使用 .NET Framework 4.5 作为目标框架。选择这个版本主要是因为 Windows 7 以上的系统基本都内置了对应运行时不需要额外安装 .NET 环境非常契合“单 exe、双击就能跑”的定位。还有一个值得提的备选方案是 Go。Go 的交叉编译能力确实很吸引人可以轻松产出 Windows、Linux、macOS 三平台的可执行文件且单文件体积小得惊人。但 Go 在 GUI 这块生态太弱做远程控制里的桌面预览、键盘鼠标事件模拟、窗口枚举这些操作时需要绕不少弯路。所以在 2024 年左右我评估过一阵子之后最终还是把主力方案定在了 C# 上把 Go 保留为日后做跨平台被控端的一个研究选项。1.3 为什么“单 exe”如此重要很多人可能不理解程序做成单 exe 和做成安装包到底有多大差别。就拿我自己的使用场景来说我在远程维护客户机器的时候不希望在他们电脑上留下一个安装程序列表、一堆动态库和注册表残留项。单 exe 版能真正做到“拷贝即运行、删除即消失”对临时性远程协助来说体验好非常多。另外单 exe 在对抗“环境依赖”上有天然优势。我遇到过很多被控端机器是精简版 Windows连 VC 运行库都不全如果你做的是依赖一堆运行时组件的小程序过去根本跑不起来。把源代码静态编译或者做好依赖嵌入后这个问题就直接消失了。当然单 exe 也不是没有代价。首先杀毒软件的查杀率会变高其次单文件在内存中运行时一般都会先释放临时文件到磁盘再真正执行逻辑这个过程如果没处理好反而更容易留下痕迹。所以我在源码设计里专门做了内存加载和临时文件清理机制这两点在第五部分会展开聊。2. 模块架构、通信协议与会话管理2.1 主控端与被控端的连接方式设计远程控制的通信模式主流方案有两种正向连接和反向连接。正向连接就是被控端开启监听端口控制端主动去连接适合被控端有公网 IP 或者可以做端口映射的场景。但现实中被控端常常处于各种复杂的局域网环境里没有公网 IP这时候正向连接基本不可用。反向连接则反过来被控端主动向外发起连接控制端监听在公网服务器上两者的角色换了一下。我的源码里同时支持这两种连接模式通过配置切换。默认使用的还是反向连接 心跳保活机制。这么做的主要理由是在大多数远程协助场景中被控端网络环境都比控制端更“内敛”由内向外发起连接被防火墙拦截的概率更低穿透成功率更高。2.2 通信协议自定义二进制协议不做 HTTP远程控制这类场景实时性要求高数据流种类多命令、文件、屏幕帧、键盘鼠标事件、剪贴板同步如果直接套一层 HTTP协议头冗余大解析也啰嗦。所以我在双方通信上设计了一套轻量二进制协议核心结构并不复杂4 字节魔数用来快速校验是不是自家协议的包1 字节版本号方便以后做协议升级2 字节消息类型标记是控制命令、文件传输还是屏幕数据4 字节数据长度限制单包最大长度防止恶意超大包拖垮程序若干字节校验位确保数据传输完整实际有效载荷这样的设计在数据解析时的性能比 JSON 或 XML 高出一截而且可以很方便地对每个消息类型做权限控制。所有命令通道都走同一个 TCP 连接但通过消息类型字段区分处理的优先级非常干净。2.3 会话建立和断线重连机制网络控制里最怕的就是控制到一半网络波动一下整个控制会话直接死掉。我这里实现的“心跳 断线重连”机制大概思路如下被控端每隔 3 秒向控制端发送一次心跳包控制端若在 10 秒内没有收到任何数据就判定连接已失效主动关闭连接。如果连接断开被控端会根据配置的服务器地址列表每隔 5 秒尝试重新连接一次。如果是临时断网也加了指数退避逻辑避免双方同时疯狂重连导致服务器崩溃。这套机制实际跑下来稳定性比我想象中好很多。有一次客户办公室路由器因为电压不稳重启了我这边的控制端显示断线但我没有做任何操作大约 1 分钟后控制界面自动恢复了整个过程不会丢失已经传了一半的文件任务。2.4 多客户端管理ID 标识与权限等级被控端在启动后会生成一个唯一 ID基于机器名 CPU ID 磁盘序列号混合计算这个 ID 会显示在主控端的设备列表里。同时主控端可以通过配置给不同的被控端分配不同的“权限等级”。为什么要做权限等级因为在实际团队协作场景中不可能所有人都对被控端有完全控制权。我给自己团队里的每个成员分配的唯一密钥就绑定了对应权限管理员可以被控端所有功能普通运维人员只能执行命令、看屏幕和传文件不能重启或注销系统给外部供应商的临时授权甚至只能看屏幕不能操作鼠标键盘。在源码实现里权限校验是在控制端发指令前执行的同时也写入了被控端的授权逻辑防止有人直接绕过主控端、伪造指令来控制被控端。3. 核心功能模块是怎么实现的3.1 屏幕捕获与传输从“逐帧 BitBlt”到“区域差分对比”这是整个远程控制里技术含量最高的一块。第一版我用的最原始的办法定时截取整个屏幕保存成 JPEG直接传过去。在局域网环境下分辨率一高画面就会变成幻灯片。后来我做了两个大的优化。第一个优化是区域差分。屏幕内容大部分时间是静态的只有鼠标在动或者局部窗口变化。我先把屏幕按 64×64 像素的方块切块计算每一块的哈希值只有内容发生变化的块才编码并传输。主控端把新块覆盖到对应位置其余部分自然保持原样。实测下来静态桌面的带宽占用能从每秒 2MB 以上降到几十 KB效果非常显著。第二个优化是编码格式。Windows 平台下用自带的 GDI 把区域转成 JPEG压缩率还是差一点后来换成了在 GPU 上做硬编码主控端接受到的数据量更少了延迟也更低从原来的 400 多毫秒降到了 100 毫秒左右。实际实现时还有一个被很多人忽略的坑多显示器环境下如果只抓主屏幕用户会把目标窗口拖到副屏上导致控制端完全看不到画面。所以代码里要遍历所有显示器并分别抓取然后把各个屏幕的画面拼成一张大图传过去后续再在主控端做屏幕切换或拼接显示。3.2 键盘鼠标事件注入两种常见的实现方式远程控制要做的第二件事就是把控制端本地的鼠标键盘操作“搬”到被控端电脑上。Windows 下可以用SendInput和PostMessage两条路线。SendInput 是模拟系统级输入事件几乎所有程序都能收到但如果在没有管理员权限时去操作一些高权限窗口可能会被 UIPI用户接口特权隔离拦截。PostMessage 是直接给目标窗口发消息优点是可以绕过焦点限制但如果程序对消息的响应处理不标准容易丢失操作。我的方案是两者结合默认用 SendInput只有在权限不足时自动降级到 PostMessage。另外我发现一个细节如果控制端本地正在全屏玩游戏输入钩子会干扰远程操作这时候最好让控制端自动禁用本地鼠标键盘的钩子只把操作传输到远端不然你会看到控制端本地被你的操作”带走“非常混乱。3.3 文件管理和远程终端文件管理模块我设计成两栏式类似一个小型 FTP 工具。左侧是本机目录右侧是被控端目录支持上传下载、重命名、删除等基本操作。传输走独立的数据通道而核心的进度展示我用的是异步回调避免 UI 卡死。远程终端则是用管道named pipe创建了一个隐藏的cmd.exe进程把它的标准输入、输出和错误输出都重定向到网络上。这样一来控制端就能像坐在那台机器前一样执行命令并实时看到输出。有一点要特别提醒创建隐藏终端的时候务必要在 STARTUPINFO 里指定wShowWindow为隐藏否则用户会看到一个黑色命令行窗口一闪而过暴露了远程控制的存在。3.4 摄像头画面调取与语音对讲远程协助时经常需要确认对方周围的环境所以我加了一个摄像头模块。通过调用系统摄像头 API把采集到的画面编码成视频流控制端可以通过独立窗口查看。语音对讲模块用的是压缩编码8kHz 采样率16bit 位深单声道在带宽较差的情况下也可以保持基本可用的质量。这里注意麦克风权限一定要在被控端启动时主动申请并保存状态不然在某些系统版本上静默调用麦克风会被直接拒绝。4. 单文件打包与免安装的实现细节4.1 依赖资源怎么塞进一个 exeC# 项目在编译后默认会产生一个 exe 和一堆 dll。要把所有东西打包成一个文件常规做法是使用 ILMerge 或者通过 NuGet 包管理器集成Costura.Fody这个库。Costura.Fody 的原理是在编译时把那些程序集作为嵌入资源写入主 exe然后在运行时动态解析和加载。配置非常简单装好包后只需在FodyWeavers.xml里写上Costura /即可。不过实际项目中除了托管程序集外有时候还会用到非托管 DLL比如 SQLite、OpenCV 等这种 Costura 默认是不能直接处理的。需要额外把它嵌入到资源文件里然后在程序启动时写出到临时目录再 LoadLibrary。这部分是我在源码里单独封装了NativeDllLoader类来处理的P/Invoke 到这个加载器上避免在系统的程序集加载路径中找不到依赖。4.2 该不该加壳和压缩很多人做完单文件后习惯再套一层 UPX 压缩想把体积压到更小。但我自己的经验是能不加壳就不加壳。UPX 是基于文件解压后运行的机制很多杀软对 UPX 类壳的敏感度非常高本来安全的东西套了个壳反而被标记成病毒。而且壳一旦在处理程序入口时出错整个程序直接崩连修复的机会都没有。我实际使用的方案是生成的单文件用dnSpy大概看一眼有没有明显异常不额外加壳用编译期的Optimize选项和裁剪未使用的程序集来控体积。最终的 exe 体积维持在 2.8MB 左右平衡了兼容性和便携性。4.3 杀毒软件误报的几种规避思路做远程控制工具最痛的就是误报。我实测过同样的代码用 C# 打包成单文件比用 Python 打包的误报率低得多。在检查了各种在线查杀平台后我总结出几个要点程序不做任何无意义的行为不要有隐藏进程、隐藏窗口、修改注册表启动项之外的动作。那些额外的敏感行为对合法工具来说完全没意义只会增加误报风险。不要带 UPX 壳、不要带商业壳。前面已经说了原因。尽量不动态生成可执行代码C# 的开源协议解析、表达式树这类特性虽然方便但杀软对动态代码的模型检测非常敏感。如果有条件做数字签名。没有签名的情况下 Windows 下拉框会提示未知发布者而且杀软会更严格。如果因为某些原因被误报最简单有效的办法就是向对应厂商提交申诉把自己的源码摘要和编译环境说明清楚。这套策略执行下来最近几个版本的误报率已经比最初少了很多至少在我经常用的 360 和火绒上已经能稳定通过。5. 踩坑实录与排查经验5.1 主控界面卡死原因居然是同步网络调用第一个版本的控制端界面写得很糟糕点击连接后UI 线程直接阻塞在TcpClient.Connect()上等三五秒钟没连上窗口就假死。后来全部改成async/await的异步模式用ConfigureAwait(false)避免线程上下文切换带来的额外开销顺便在窗口关闭时加了任务取消机制彻底解决了卡死问题。5.2 被控端在锁屏状态下无法注入键盘事件有一次测试时我把被控端桌面锁定后再通过远程输入密码登录发现SendInput完全没有效果。查了很久才确认这是系统会话隔离机制导致的。解决方式是让被控端以系统服务方式运行或者使用安全的桌面切换 API。实际产品中我们推荐被控端设置为“锁屏后保持正常运行”并加上独立密码认证这样既保证安全性也方便远程维护。5.3 网络传输中的粘包问题TCP 是流式协议没有消息边界。如果直接读一个固定长度的字节数组数据少的时候会读不全数据多的时候会把两包合并成一包。我最初的写法是直接按固定长度读导致屏幕图像经常错位、花屏。后来在代码里实现了完整的“分包重组”逻辑先读 4 字节消息头拿到完整消息长度再循环读取直到把所有字节都读完才交给上层处理。要注意分包重组里的循环必须处理好读取超时否则某一包数据丢了一半整个连接都会卡死在那里。5.4 遇到一个怪事断线重连后画面是花的这个问题排查到最后发现是被控端在断线期间屏幕分辨率发生了变化。比如远程桌面进去的时候是 1920×1080远程桌面退出后屏幕变成了 1024×768。而传输层还按原来的分辨率计算块偏移画面自然就乱了。后来我在每次会话重连握手时强制重新发送一次分辨率信息并在主控端检测到分辨率变化时清空所有显示缓存完整刷新一次主画面这个问题就彻底消失了。6. 合法使用与安全边界远程控制工具是一把双刃剑这一点我心里非常清楚。所以整套源码里我从一开始就做了两个强制性的安全设计第一所有被控端在首次启动时控制端必须输入被控端显示的验证码才能建立会话。这个验证码是一次性的有效时间只有 60 秒过期自动失效不是默认密码或者硬编码口令。第二被控端可以设置安全模式。在安全模式下被控端右下角会常驻一个小图标任何远程操作的瞬间用户都会看到屏幕上出现提示光晕并且可以一键断开所有未经验证的连接。这个设计对维护人员来说可能在操作上多了一步但对被控方的知情权和安全感来说是非常必要的保障。我不建议任何人把这套源码用在未获授权的设备上。远程控制的价值在于提高效率而不是破坏信任。如果你用这套源码做了什么违法或者违背道德的事那是使用者本身的问题技术不会背这个锅。对绝大多数朋友来说把控制端和被控端都部署在自己的设备、公司的办公机器或者经过对方明确授权的电脑上就足够发挥它的价值了。7. 后续扩展方向与个人心得目前这套单 exe 远控项目在我手里已经迭代了几个大版本。从最初的 C 版到 C# 重构再到补充文件管理、屏幕差分、语音对讲等模块每一轮重构我都会明显感觉程序的稳定性和可维护性上一个台阶。我个人的心得是远程控制这类工具稳定性和安全性永远优先于炫技和功能堆砌功能再全、画面再流畅如果连接三天两头断线或者存在被滥用的风险那一切都白搭。后续我还想做的事包括支持 UDP 直连打洞模式来应对那些没有公网服务器的内网环境做一个简单的 WebRTC 信令服务器让控制端能够从浏览器直接发起连接以及在控制端加入连接审计日志方便企业合规管理环境记录所有会话。如果你正在考虑自己写一套远程控制工具我建议不要一上来就去搜各种现成源码直接抄。先画一张功能清单想清楚自己的核心场景和目标平台多看看成熟开源项目的协议设计和模块边界再动手写代码。这样一个项目下来能学到的东西远不止“会调用几个 API”这么简单。本文还有配套的精品资源点击获取