ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Star Rat 3.1远控源码深度剖析:通信加密与二次开发实战

Star Rat 3.1远控源码深度剖析:通信加密与二次开发实战

简介:这是一份面向Windows远程控制开发者的Star Rat 3.1升级版完整源码,聚焦远程桌面管理、系统运维与批量设备控制场景,便于中高级开发者在真实工程中研究远控原理并按需定制。源码对Windows XP、Vista、Win7、Win8、2000及2003等系统做了兼容性支持,适合需要扩展服务端/客户端功能、优化线程调度或排查连接稳定性的技术人群。压缩包共4558个文件,大小约22.39MB,主要包含cpp/h源码、rc资源脚本、png界面素材,以及sln/vcxproj工程文件与少量dll/exe辅助工具,可在Visual Studio环境中直接查看、编译和调试。已有450人学习/下载,适合作为远控软件开发的学习样本。深入阅读可掌握多线程连接管理、Socket通信、安全认证与加密机制,也能根据业务需要调整屏幕传输、文件操作和日志策略;附带bat与ini/xml配置,有助于理解部署流程。

1. Star Rat 3.1:这份源码升级版到底动了什么,值不值得下

先说结论:Star Rat 3.1标注“源码升级版”,核心价值不在“新增了多少功能”,而在“作者把一套可编译、可二次开发的远控框架完整摊开来了”。如果你是想找个能直接用、双击就能连的成品工具,那它大概率会让你失望;但如果你是做安全测试、想研究远控通信链路和免杀对抗逻辑的开发者,这套源码能帮你省掉大量从零逆向的时间。

它解决的典型问题有三个:一是远控客户端与服务端的通信协议怎么设计才稳定,二是功能模块怎么拆分才能保证后期加插件不崩,三是升级机制怎么做才能让已有会话不断线。适合的人群也明确——有编程基础的开发者、做红队工具研究的测试人员、以及想理解远控基本原理的学生。纯拿来主义的新手,建议先补完网络编程基础再碰它。

另外提醒一句:远控类源码属于高敏感工具,研究学习可以,但任何未经授权的部署和使用都是违法甚至犯罪的行为。本文只做技术拆解,帮助你理解其实现思路,请务必在法律允许的范围内使用。

2. 从源码结构看懂 Star Rat 3.1:模块划分与编译入口

2.1 拿到手先看目录:六大模块的分工逻辑

解压源码包后,第一件事不是找 exe,而是先看目录结构。Star Rat 3.1 的源码组织方式比较清晰,根目录下会看到 server、client、common、build、plugins、docs 这六个一级目录。这种划分在远控项目里属于标准做法,但 3.1 版在细节上做了调整。

server 目录存放控制端源码,负责监听、会话管理和下发指令;client 目录是被控端,需要编译成单个可执行文件;common 是两者的共享库,包括加解密、压缩、协议结构体定义;build 目录里是编译脚本和配置文件;plugins 是插件接口和示例插件;docs 是作者写的接口说明和更新日志。

这种切分的直接好处是编译环境干净。client 和 server 共用 common 里的协议定义,改协议字段时只需动一处,两边重新编译即可。3.1 版相比更早的版本,把加密模块从 common 里单独抽了一层,这个细节对后期替换加密算法非常关键——你不需要动主逻辑,只替换 crypto 层的实现就行。

2.2 编译环境的真实配置:不是装好 IDE 就能跑

Star Rat 3.1 的编译环境要求比一般项目略苛刻。服务端和客户端的编译工具链是分开的,服务端推荐在 Windows 10/11 上用 Visual Studio 2019 或 2022 编译,客户端则需要你在同一台机器上安装额外的 MinGW-w64 工具链,因为作者用了跨平台编译链来生成目标平台的执行文件。

实际编译前,你需要确认三件事:第一,Windows SDK 版本不低于 10.0.18362,否则 Winsock 相关头文件可能不匹配;第二,MinGW-w64 必须选择 posix 线程模型版本,win32 线程模型的编译产物在后续用到 C++11 线程库时会报错;第三,构建脚本里写死了 Python 3.8+ 的路径依赖,项目使用 Python 脚本做资源打包与配置生成,这一步缺少会导致最后生成的主程序缺失配置节。

有一个很容易踩的坑是路径问题。build 目录下的脚本默认假设源码放在 C:\StarRat31 目录下,你换成别的盘符或深路径后,脚本里的相对路径引用会部分失效。我一般会直接在 C 盘根目录建一个短路径的文件夹来放源码,省去改脚本的时间。

2.3 配置文件的生成机制:第一次编译前必须改的两个参数

Star Rat 3.1 的配置文件不是预置的,而是在编译过程中通过脚本生成的。生成逻辑读取 build/config.json,这个文件里有两个参数直接影响编译产物的可用性。

第一个是监听端口列表。默认配置里写的是 8080、8081 两个端口,生产环境建议改成非常用端口。第二个是服务端地址指向——如果你的服务端跑在固定 IP 上,直接填 IP;如果用动态域名,就把地址填成域名。注意这个字段同时影响 client 的初始连接逻辑,填错会导致被控端上线后立刻闪断。

配置文件里还有一组开关控制编译进 client 的功能组件,例如键盘记录模块、屏幕截取、文件管理、远程 Shell 等。默认全部开启,但如果你只需要其中两三个功能,可以把其他开关置 false,这样生成的客户端体积能小接近一半。3.1 版在文档里明确提到了编译期裁剪的思路,这是升级版相比旧版一个非常实用的改进点。

3. 通信链路与加密机制:连接怎么建立,数据怎么防窃听

3.1 双通道设计:主连接与控制通道的职责区别

Star Rat 3.1 的通信设计没有走复杂的多路复用,而是用了更成熟、更稳妥的双通道方案:一条主连接负责传输命令执行结果、文件列表这类常规数据;另一条通道走加密的控制指令。两条通道共用同一个底层 socket,但应用层做了帧区分,帧头里的 type 字段决定这条数据走哪条逻辑链路。

这个设计有什么实际好处?最直接的一点是,当你在远程执行一个长时间任务(比如下载一个大文件)时,控制指令不会被阻塞。因为控制帧和数据帧在应用层是并发处理的,只要底层 socket 没有满,控制指令随时能插进去。旧版存在的问题就是单通道串行处理,一条慢任务卡住后,整个远程会话基本就僵死了。

协议帧格式是固定头变长体。头部 16 字节,包含魔数、版本号、帧类型、包体长度、序列号。魔数的作用是快速过滤非协议流量,版本号用于双端协议兼容判断,序列号则用来处理请求和响应的对应关系。第一次读代码时建议先把这个结构体定义找出来,后面的所有逻辑都会围着它转。

3.2 加密层用的什么算法:升级版换掉了什么

3.1 版在加密层做了明显调整。旧版用的是单一 AES-128-CBC 加密,密钥写死在源码里,安全性形同虚设。升级版改成 AES-256-GCM 做数据加密,密钥不再硬编码,而是通过 ECDH 密钥协商在每次会话建立时动态生成。

流程上分三步:客户端连接服务端后,先发一个公钥交换请求;服务端返回自己的公钥,双方通过 ECDH 计算出会话密钥;之后所有通信都用这个会话密钥加上 AES-256-GCM 加密包体。GCM 模式自带认证标签,能同时保证机密性和完整性,防篡改,这是 CBC 模式做不到的。

值得注意的一个实现细节是:作者在 ECDH 的参数选择上固定了 P-256 曲线。这种选择在安全性和性能之间取了一个比较折中的方案,代码量少,OpenSSL 库直接支持,在低配机器上也能跑得动。如果你后续要改加密算法,只需要替换 common/crypto 目录下的实现文件,接口不用动。

配置密钥长度的地方在 common/config.h 里,改动后需要同时重新编译 server 和 client,否则两端生成的密钥协商参数不一致,握手阶段会直接失败。这也是升级版和旧版之间最容易出现兼容问题的地方——旧版 client 连新版 server,因为加密方式不同,连接会被 server 主动断开。

3.3 心跳与断线重连:为什么会话不会轻易掉

Star Rat 3.1 的心跳机制走了“两级探测”路线:应用层心跳和 TCP 层保活探测并存。应用层每隔 30 秒发送一次心跳包,服务端连续三次没收到心跳就判定客户端离线;TCP 层则设置了 KeepAlive 参数,默认探测间隔在注册表级别控制,这套机制主要用于网络物理断开时的快速感知。

重连策略是可配置的。默认是每 5 秒尝试重连一次,连续失败 10 次后把重连间隔拉长到 60 秒。这个设计的目的是在网络波动时能尽快恢复连接,同时又避免频繁重连对服务端造成压力。升级版新增了一个随机抖动机制——实际重连等待时间在默认值的基础上加 0~3 秒的随机数,避免大量客户端同时重连造成服务端拥塞。

这里需要留意一个可能性:如果你改了心跳间隔,而服务端的判定阈值没跟着调,客户端会被误判离线。比如客户端心跳改成 60 秒,服务端三次判定窗口依旧是 90 秒,那网络稍有延迟就会触发离线。配置时两端参数务必一起改。常见做法是客户端心跳间隔乘以 3 再乘以 1.2,作为服务端的判定阈值。

4. 功能模块的配置与二次开发:插件接口和编译参数怎么调

4.1 插件系统的实际用法:新功能怎么挂进去

Star Rat 3.1 引入了一个轻量插件框架,这个框架让扩展新功能不需要改动主程序源码,而是以动态库形式加载。插件接口很简单,核心就是一个导出函数表和一组事件回调注册函数。插件开发者在自己的动态库里实现接口定义即可,主程序通过统一的函数指针完成调用。

一个标准插件的目录结构包含 source 目录、一个 main.cpp、一个 plugin.def 定义文件以及一个 build.py 脚本。main.cpp 里实现两个必要函数:一个是返回插件信息字符串的函数,另一个是执行具体任务的函数。def 文件说明导出函数别名,编译时会生成对应的函数映射表,主程序加载时靠函数名来解析地址。

我在尝试写第一个插件时发现一个需要注意的地方:3.1 的插件运行在独立线程,但插件和主程序共享同一个日志队列。如果你的插件里直接写了 printf 或者 OutputDebugString,信息不会显示在界面日志里,必须调用主程序提供的 log 接口。接口函数地址在插件初始化时通过结构体传进来,保存好这个指针就能用。

4.2 标准插件的编译流程:一个最小可用的键盘记录插件

以最小可用的键盘记录插件为例,完整的编译流程分成三步。第一步,写插件源码,实现接口;第二步,用 build.py 脚本编译;第三步,把生成的 dll 放到 server 端的 plugin 目录,重启服务端即可被加载。这样既有实际操作,又能演示完整的链路。

import os import subprocess PLUGIN_NAME = "keylog_demo" SOURCE_DIR = "./src" OUTPUT_DIR = "../build/plugins" compile_cmd = [ "g++", "-std=c++17", "-shared", "-o", os.path.join(OUTPUT_DIR, PLUGIN_NAME + ".dll"), os.path.join(SOURCE_DIR, "main.cpp"), "-lws2_32", "-lwininet" ] if __name__ == "__main__": os.makedirs(OUTPUT_DIR, exist_ok=True) ret = subprocess.call(compile_cmd) if ret == 0: print(f"[OK] {PLUGIN_NAME}.dll generated.") else: print(f"[ERR] build failed with code {ret}")

这段脚本的关键点在于 -shared 参数,它告诉编译器生成动态库而不是可执行文件;-lws2_32 和 -lwininet 链接了 Windows 的 socket 与网络相关库,键盘记录不直接用到但插件运行时依赖这些底层组件。OUTPUT_DIR 直接指向 server 的插件目录,编译完成后不用手动复制。

插件加载成功后,服务端界面会多出一个插件标签页。注意一个边界条件:插件不支持热卸载,加载后要么用服务端界面的卸载按钮,要么直接停掉服务端进程再删文件。我曾经直接删除 dll 文件而没先卸载,结果服务端崩溃了几次才定位到原因——动态库在 Windows 下被加载后文件锁定的问题,删除时会有“文件被占用”之类的提示。

4.3 编译裁剪参数对照表:哪些开关影响体积和功能

Star Rat 3.1 的 build/config.json 里有一组模块开关,直接决定了 client 端哪些功能代码会被编译进去。理解这个开关的对应关系,有助于避免“功能没生效”这类假性问题——如果你关闭了屏幕截取模块,即便控制端下发指令,client 也不会有任何响应,而表面上连接一切正常。下面给出主要开关:

配置项默认值编译影响备注
module_keylogtrue键盘记录模块依赖 hook 库
module_screentrue屏幕截取模块禁用后可减小约400KB体积
module_filemgrtrue文件管理模块关闭后无法用图形化文件浏览
module_shelltrue远程 Shell 模块关闭后丧失命令行交互
module_upgradetrue自动升级模块关闭后无法远程更新客户端
module_persistencetrue开机自启模块关闭后重启计算机客户端不会自启

只保留 module_keylog 和 module_persistence 并关闭其余开关,client 主程序的体积大约是默认配置的 55%。这里要提醒你一个位置相关的细节:裁剪功能时,如果关闭了 module_upgrade,后续想通过服务端推送新版本就没法实现了。所以生产环境建议至少保留它,因为这是后期维护已安装客户端的后悔药。

5. 排查与避坑:编译失败、会话闪断和服务端假死

5.1 编译失败常见原因:链接错误与头文件缺失

编译 Star Rat 3.1 最常见的失败原因有两个:一是缺少基础依赖库导致链接错误,二是代码页不一致导致编译中断。

链接错误的典型场景是 fatal error LNK1104: cannot open file 'libcrypto.lib'。这条错误信息说明 OpenSSL 库没有正确安装或没有把 lib 目录加入 Visual Studio 的库依赖路径。常见解决方案是确认 OpenSSL 安装目录存在,然后在项目属性里的 VC++ 目录中添加包含目录和库目录两项。另一个常见错误是找不到 winsock2.h——这通常是因为 Windows SDK 版本选错,把项目工具集切到 Visual Studio 2019 的 v142 或更新版本再重试即可。

代码页错误的表现不太一样。如果你用的是中文 Windows 系统,Visual Studio 默认会把源文件按 GB2312 读取,而作者源码里的注释是按 UTF-8 编码写的。编译时报警告 C4819,如果开了“把警告视为错误”选项,编译会直接中断。解决方式是找到源文件,另存为 UTF-8 with BOM 格式,防止编译器读取时走错代码页。

5.2 会话闪断的三种原因:加密不匹配、心跳超时、端口复用冲突

会话闪断是所有远控项目里最让人头疼的问题,Star Rat 3.1 的闪断原因集中在三个方向。最隐蔽的是加密参数不匹配——server 是 3.1,client 是旧版或者加密模块替换过但参数不一致,连接在握手阶段就会被拒绝。排查痕迹非常明显:服务端日志里能看到“handshake failed”记录,客户端这边看到的现象却只是闪断。

第二种是心跳超时误判。在网络延迟比较高的环境里,如果默认 30 秒的心跳间隔没有根据实际延迟调整,服务端可能收不到心跳包而主动断开连接。解决方法是把心跳间隔调大,同时按 3.5 倍的系数调整服务端判定阈值,下面给出一个调整示例——把心跳间隔改为 60 秒,判定阈值对应改为 210 秒。

{ "heartbeat_interval": 60, "heartbeat_timeout": 210, "heartbeat_retry": 10 }

第三种是端口复用冲突。Windows 的 TIME_WAIT 状态会让你在短时间大量重连的场景下出现“端口已被占用”的错误。如果服务端和前一个客户端频繁建立与断开连接,端口短时间内被重复分配就会导致新连接失败。配合命令行检查端口占用情况,通常输出会提示端口等待状态:

netstat -ano | findstr 8080

这条命令的输出列会显示 8080 端口的监听状态和进程 PID。如果看到大量 TIME_WAIT 记录,说明是端口复用策略导致的。常见做法是调整注册表里的动态端口范围,执行下面的命令扩大可用端口池:

netsh int ipv4 set dynamicport tcp start=10000 num=1000

5.3 服务端界面假死:界面线程与业务线程没分离

历史版本中存在的一个典型问题,是服务端在同时管理大量会话时界面会失去响应,表现为窗口无法拖动、按钮点了没反应,但后台逻辑还在运行。这种假死的根源在于界面线程上执行了耗时的同步操作。

代码里 3.1 版已经做了大部分界面和业务线程的分离,但有几个入口点没有完全改干净。比如在线更新列表控件时,如果你用一个循环遍历所有会话并直接插入列表项,会话数量一旦上千,界面就会开始卡顿。排查加解决的办法有两条:一是找到这些操作发生的位置,把它们丢到工作线程里,只通过消息通知界面线程刷新数据;二是限制界面刷新频率,不同步更新每一条会话状态,而是设一个 500 毫秒的聚合刷新计时器,到时间一次性刷新可见区域。

这里提供一个建议性操作顺序:先看任务管理器里进程的 CPU 占用和线程数,排除是死循环还是线程阻塞;再看界面卡住时控制台是否有输出,判断业务逻辑是不是依然活着;最后用分析工具抓一次线程栈,确认界面线程到底卡在哪个函数。没有线程栈信息,全凭猜来解决,效率往往是偏低的。

6. 从源码到实战:交叉编译客户端与上线验证流程

6.1 交叉编译的完整命令与参数说明

Star Rat 3.1 的客户端交叉编译,整体流程可以拆成三步:一是准备 MinGW-w64 环境,二是调整 build 目录下的交叉编译脚本,三是执行脚本生成最终可执行文件。这里给出一个典型编译脚本的修改示例,它适用于 64 位 Windows 目标平台。

#!/bin/bash # build_client.sh export PATH=/c/mingw64/bin:$PATH cd ../client # 定义编译器 CC=x86_64-w64-mingw32-g++ CFLAGS="-std=c++17 -O2 -Wall -DWIN32_LEAN_AND_MEAN" LDFLAGS="-static-libgcc -static-libstdc++ -lws2_32 -lcrypt32" # 编译源文件 $CC $CFLAGS -o release/agent.exe \ src/main.cpp \ src/network.cpp \ src/crypto.cpp \ src/modules.cpp \ $LDFLAGS if [ $? -eq 0 ]; then echo "[OK] agent.exe generated" else echo "[ERR] build failed" fi

这里的核心点是 -static-libgcc 和 -static-libstdc++,这两个参数决定生成的 exe 是否依赖运行库。如果省略,目标机器上没有安装对应运行库的话程序会报错,找不到 libstdc++-6.dll。另一个要点是 -lcrypt32,链接 Windows 的 Crypt32 库,因为加解密模块调用了系统级证书和加密函数。完整跑一次编译大概需要一到两分钟,出现[OK] agent.exe generated就说明成功了。

6.2 上线验证三步走:先本机、再局域网、最后公网

拿到编译好的 agent.exe 不要直接投到公网,我习惯按三步做上线验证。第一步本机验证,把 server 和 agent 都放在同一台机器上运行,排除网络因素,只验证协议链路是否正常。这一步通过的标准是 server 面板出现一条新的会话记录,且能看到客户端的操作系统版本和计算机名。

第二步局域网验证,找一台同网段的机器运行 agent,确认三层可达环境下连接正常。这步暴露的问题往往是防火墙——Windows 自带的防火墙默认拦截入站连接,如果 server 端口没在防火墙放行,agent 连接必然失败。应对办法是放行特定端口,下面这条命令是导入 Windows 防火墙规则,允许来自内网的入站 TCP 连接到监听端口。

New-NetFirewallRule -DisplayName "StarRat31-TCP" ` -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow

第三步才是公网验证。建议先确认服务端所在机器的公网 IP,再用手机 4G/5G 热点跑一次 agent,确认真实外网环境能通。如果失败,优先排查服务端所在网络的上游路由器是否做了端口映射。公网环境不稳定时,可以先看 agent 是否发起了 TCP 连接请求,有连接但握手超时则多半是运营商封端口或中间设备拦截。

6.3 上线后的四个功能自检:别急着收工

上线成功后别急,四个核心功能各验一遍再收工。第一个是远程 Shell,下发一条 ipconfig 命令,输出能正常返回即可;第二个是文件管理,拉取客户端 C 盘目录列表,确认文件浏览不卡死;第三个是键盘记录,切换几个窗口敲一段字,回传记录里能看到对应窗口标题和键击内容;第四个是自动升级,把一份修改过的 agent 放到升级服务器,客户端执行升级指令后观察版本号变化。以上每个步骤的具体操作入口在服务端界面的会话右键菜单里都能找到,没有单独的菜单命令需要熟悉。

如果这四项都正常,这套 client-server 链路就算真正跑通了。从那以后我每次给项目做上线验证,都强制把上面四步全部走一遍,哪怕只是改了一行日志代码,也不省这一道流程。省掉这一步,你根本不知道哪次改动会让核心功能悄悄失效。希望这套验证方法和踩坑记录能帮你少走一段弯路。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进