一、概述
如果你接过一台被控嫌疑的 Windows 机器,需要回答委托方最常问的那个问题——"这台机器什么时候被谁控制了、控到了哪些主机"——你并不需要解出任何密码。设备 ID、对方识别码、连接时间线这三样在绝大多数远控工具里是明文存的,它们躺在几个配置文件和日志文件里,等着你用关键字去捞。
所以本篇要解决的第一个问题是:远控取证的瓶颈从来不是解密,而是定位与时间相关性。
分析员真正的困难不在密码学。你不知道工具把配置放在了哪,不知道哪个目录才是这台机器实际在用的那一份,也不知道"装过"和"被控过"之间那道沟要用什么证据才跨得过去。
本篇把"关键词全盘扫描 → 判定归属与时间 → 读明文字段 → 按格式分级处置"这条主链一次走通。明文能解决的,绝不多走一步。
第二个问题是边界划在哪里。本篇涉及的工具里有一半(Navicat、Xshell、FinalShell、WinSCP)根本不是远控软件,是业务客户端。
但它们保存的数据库与 SSH 凭据,在案件里的价值往往比远控痕迹更高。
哪些该固定、哪些能还原、哪些必须停在"存在"这一层,本章要交给你的就是这个判断。尤其是单向哈希字段——把它当成"解不开的加密"反复换工具,既浪费时间,又容易滑向越界。
第三个问题是合规。
本领域最容易犯的错不是技术错误,是性质错误:用检材里还原出来的凭据去连一下目标主机,看看是不是真的有效。
这个动作会把你的工作从"取证"变成"入侵",同时在远控服务端留下你的登录时间与来源 IP,污染证据。
这条红线的完整论证见 2.5,没有例外条款。
本篇处理的证据形态(这些文件名与字段名是判断"是什么工具、什么代次"的第一手依据):
| 工具 |
关键文件或位置 |
决定性的字段 |
| AnyDesk |
service.conf(%ProgramData% 或 %APPDATA%) |
ad.anynet.id、ad.anynet.pwd_hash + pwd_salt + token_salt |
| 向日葵 |
注册表 HKU\.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginInfo;旧版 config.ini |
fastcode、encry_pwd |
| TeamViewer |
Connections_incoming.txt(入向成功)、connections.txt(出向)、TeamViewer15_Logfile.log |
对方 ID、UTC 起止时间、认证事件行 |
| ToDesk |
%APPDATA%\ToDesk、C:\ProgramData\ToDesk\ |
access_pin_hash(PBKDF2 加盐,单向) |
| Navicat |
*.tncf 连接配置 |
主机名/端口/用户名为明文,密码为 AES |
| WinSCP |
注册表 HKCU\Software\Martin Prikryl\WinSCP 2\Sessions |
Password 值(代次决定是可逆变换还是 AES-256-CBC) |
| FinalShell |
conn.json |
password 字段(AES,密钥与本机绑定) |
| RustDesk |
RustDesk.toml |
配置项版本差异大,先看文件本身 |
记住这张表是起点,不是事实表。
同一款工具的配置位置会在版本间迁移,向日葵从 config.ini 移到注册表就是典型。所以任何时候只在一个位置拿到空值,正确动作是换位置再找一次,而不是下"不存在"的结论。
表里的路径最终要靠两件事确认:目录或键是否存在、文件或值的修改时间是否与本案时间窗吻合。
版本一变,这张表就过期。所以本篇的操作步骤刻意从"全盘关键字扫描"开始,而不是从查表开始。
本篇的处置主链是四步,每一步都有明确的产出与停手条件:
| 步骤 |
动作 |
产出 |
什么时候就该停 |
| 1 |
全盘关键字扫描 |
命中路径清单 + 大小 + 最后修改时间 |
零命中时先核对关键字覆盖面,再下结论 |
| 2 |
判定归属与时间相关性 |
系统级 or 哪个用户、是否落在时间窗内 |
时间不吻合就到此为止,不写"被控" |
| 3 |
读明文字段 |
设备 ID、IP、主机名、连接记录 |
多数案件到这一步就结束了 |
| 4 |
判断并还原密码字段 |
字段按明文 / 编码 / 加密 / 单向哈希四类定级 |
判定为单向哈希时到此为止,不换工具 |
"多数案件到第三步就结束了"这件事,值得单独强调。
委托方的问题往往是"这台机器被谁控过、什么时候",而这三个答案都是明文。
把口令解出来,技术上更有趣,交付上却毫无增量,却把处置风险从"取证"整体推向了"入侵"。
本篇第四章的合规红线不是形式主义,它就是这条判断的具体保障。
在取证流程中的位置:本篇属于分析环节的前段,紧跟在镜像制作之后。
上游依赖是镜像制作与只读挂载与安全接入。你需要一个只读挂载点,以及一份已经固定哈希的镜像。注册表取证里的 hive 离线提取手法,是本篇读取日葵、TeamViewer、WinSCP 三个工具的共同前置。
下游支撑的是时间线重建。定位与时间判定做完后,产出直接喂给时间线重建与其他痕迹源做交叉印证,跨文件的时序比较必须先统一到同一时间基准。同一批远控软件在 Linux 侧的行为侧形态见远程控制工具痕迹。
本篇的产出形态是一张结构化记录表:工具与版本、配置完整路径、归属主体、最后修改时间(UTC)、字段按 2.2 四类分级、关键明文值(脱敏)、关联日志路径与时间范围、处置记录(是否尝试还原、是否连接——后者恒为"否")。
这张表可以直接进报告,也可以作为时间线重建的输入。
工具是否安装、属于哪个用户、最后活动时间、是否存在成功入向连接、对方标识与 IP 是什么、连接时间窗落在哪里、访问口令是否被设置过、是否存在哈希或密文凭据材料及其类型。
以下三类信息需要其他证据补充。
单向哈希字段(pwd_hash、access_pin_hash)的明文——那不是能力不足,是单向函数的定义,换工具不会改变结果。设了主密码的 WinSCP 与绑定用户环境的 Navicat 的密码内容。还有连接是否真的发生过——配置存在只能证明装过,成功连接必须靠 Connections_incoming.txt 或详细日志里的认证事件行。
至于对方主机上发生了什么,那超出委托授权范围,本篇明确不涉及。
读者前提:你需要先掌握注册表 hive 的离线提取与查询(注册表取证)、能读懂 ini/toml/json/注册表值这几种配置载体、以及时间戳换算的基本纪律(时间戳换算)。
如果你的检材是刚做完镜像、还没固定过哈希,本篇的一切结论都不具备可辩护性,先回去补证据保管链与委托证明。口令哈希的处置方法论不在本篇,见通用密码恢复的授权与最小化要求。
这句话里每一个限定词都有对应章节——"全盘范围"对应第四章陷阱 1,"时间窗"对应陷阱 8,"成功入向"对应陷阱 9,"未使用凭据连接"对应陷阱 7。如果你写不出带这些限定词的句子,说明本篇的哪一节没读进去。
二、核心原理
2.1 配置定位:先分"每用户"还是"每机器"
同一款工具往往有两套配置目录,选错会得出"该机未安装"的错误结论:
| 安装形态 |
目录 |
取证含义 |
| 免安装/绿色版 |
%APPDATA%\<工具>、%USERPROFILE%\<工具> |
属于某个具体用户,时间戳能反映该用户的操作时间 |
| 正式安装版 |
%ProgramData%\<工具>、%ProgramFiles%\<工具> |
属于系统,全用户可见,可能是 IT 统一部署 |
判断原则:两处都要查。
只查 %APPDATA% 会漏掉 IT 部署的;只查 %ProgramData% 会漏掉员工自己偷偷装的——后者往往才是案情核心。
2.2 密码字段的四种格式
拿到配置后,密码字段只有四种可能。先判断格式,再决定要不要动手。
| 格式特征 |
实际是什么 |
典型场景 |
| 纯 ASCII 且直接可读 |
明文 |
多数国产远控的旧版本、Xshell 旧版、部分工具的 PIN |
可被 base64 -d 还原为可读 ASCII |
base64 编码,不是加密 |
WinSCP 旧格式、Docker auth |
| 固定长度 hex/base64,且伴随 salt/iv 字段 |
真正的加密(AES/RC4/自定义) |
WinSCP 新版(主密码保护)、Navicat 12+、FinalShell |
形如 $2y$/$6$/32 位 hex |
单向哈希 |
AnyDesk ad.anynet.pwd_hash、ToDesk access_pin_hash |
最后一类无法还原成明文,也不应该还原。 口令哈希的正确用法是作为"该设备设了口令"的证据固定下来,再结合弱口令规则做离线验证(方法论见 04)。不要因为解不出明文就换工具去撞库式尝试——那已经越过取证与攻击的界线。
2.3 WinSCP:可逆混淆,不是加密
WinSCP 的 Password 注册表值在旧版本里是一种可逆的单向字节变换,不是密码学加密:
存储值 = (0xFF - 明文字节) XOR 0xA3
这个变换是对称的。把同样的两步再做一次,就还原了。这是 WinSCP 早期实现的固有弱点。
取证要点:新旧版本差异是这一节最容易翻车的地方——
| 版本 |
存储形式 |
处理方式 |
| 旧版(未设主密码) |
Password 值 = 上面的可逆变换 |
按同一公式还原,明文可得 |
| 新版(设了主密码) |
密文为 AES-256-CBC,密钥由主密码派生 |
必须先拿到主密码,否则不可解 |
| 部分企业部署 |
改存 WinSCP.ini 配置文件 |
读文件而非注册表 |
第一条判断指令永远是"先确认有没有主密码"。 有主密码时,正确结论是"凭据受用户自定义主密码保护,本阶段不可解",而不是继续尝试变换公式——公式对上了也解不开加密格式。
2.4 版本差异是这个领域最大的坑
同一个工具的路径与格式在版本间反复变化,这是本节的实用核心:
- 向日葵:老版本
config.ini(C:\Program Files\Oray\SunLogin\SunloginClient\ 或 C:\ProgramData\Oray\SunloginClient\),新版本主要在注册表 HKU\.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginInfo,关键值 encry_pwd(本机验证码)与 fastcode(识别码)。只看 config.ini 会得出错误结论。
- AnyDesk:目录随安装方式在
%APPDATA%\AnyDesk(免安装)和 %ProgramData%\AnyDesk(已安装)之间切换;密码是 ad.anynet.pwd_hash + pwd_salt + token_salt 三个字段一组,缺一个都算不出。
- TeamViewer:
HKLM\SOFTWARE\WOW6432Node\TeamViewer(机器级)/HKCU\Software\TeamViewer(用户级),PermanentPassword 是二进制值;连接记录在安装目录的 Connections_incoming.txt(只记成功连接)与 %APPDATA%\TeamViewer\connections.txt(出向)。
- Navicat:12 起用 AES,密钥与当前操作系统用户环境绑定;新版进一步依赖平台安全存储(Windows DPAPI / macOS Keychain),跨机器/跨用户不可解。
- ToDesk:
%APPDATA%\ToDesk 与 C:\ProgramData\ToDesk\,PIN 以 access_pin_hash 形式保存(PBKDF2-SHA256 加盐),单向,不可还原。
通用做法:不要信任任何单一路径,包括我这张表。 表里的路径是起点,最终要靠两件事确认——目录/键是否存在、文件或值的修改时间是否与本案时间窗吻合。版本一变这张表就会过期。
2.5 ⚠️ 为什么"绝不连接"是硬红线
这是全文最重要的一段,请完整读完。
登录他人的远控账号、在他人设备上建立远程会话,在法律性质上属于"入侵行为"。后果与读取配置文件完全是两回事:
| 行为 |
法律与合规性质 |
| 读取检材中的配置文件、还原字段 |
取证行为(在授权范围内) |
| 用检材中的凭据连接目标主机 |
未经授权的入侵,即使"只是看看"也构成实质入侵 |
| 在目标主机上执行命令、查看数据 |
加重情节 |
具体风险有三条。
一、污染证据。 远控服务在你的连接时会写日志——登录时间、来源 IP、会话记录。委托方之后再看这台机器,会发现"取证人员连接过"。这会直接动摇证据的可靠性,甚至让整个检材在质证中被质疑。
二、越权取证。 委托方授权你检验的是"这台机器",不是"被控的那台机器"。对后者发起连接超出了授权范围。
三、行为本身构成犯罪。 即便目的是"确认凭据是否有效",这个确认过程本身就是未经许可的访问行为。
🚫 硬性禁止
本文及本板块所有操作,一律不得使用检材中还原出的任何凭据发起真实连接。
取证阶段的正确做法是:
- 只做离线解密,把还原结果作为**凭据固定(credential fixing)**记录在案。
- 需要验证凭据有效性时,由委托方在受控环境中自行验证,或由具备相应资质的机构在其授权范围内验证。
- 发现的凭据立即通过正式渠道上报委托方,不私存、不转作他用、不在非授权环境测试。
- 报告中凭据必须脱敏——完整口令不进报告正文,只记录存在性、类型、所属主体、发现路径。
报告中的正确表述:"在 DigiForensics.dd 的 %ProgramData%\AnyDesk\service.conf 中发现被控方 AnyDesk 账号的登录口令材料(ad.anynet.pwd_hash,SHA-256 形式,单向不可逆),可用于验证该设备是否设置了访问口令。本次分析未使用该材料对任何目标设备发起连接。"
三、操作步骤
步骤 1:全盘定位工具痕迹(不预设路径)
不要从路径表开始猜,先做一次全盘关键词扫描,让工具自己暴露位置:
关键词扫描:一次覆盖主流远控 + 数据库/SSH 客户端
$KW = 'AnyDesk','Sunlogin','Oray','ToDesk','TeamViewer','RustDesk','Navicat',
'Xshell','FinalShell','WinSCP','MRemote','FileZilla','ToDesk_Service'
$Out = 'D:\evidence\remote_tools_hits.csv'
'Hit,Path,Size,LastWrite' | Set-Content $Out
foreach ($k in $KW) {
Get-ChildItem C:\ -Recurse -Force -Filter "$k" -ErrorAction SilentlyContinue |
Select-Object -First 50 | ForEach-Object {
'{0},{1},{2},{3}' -f $k, $.FullName, $.Length, $_.LastWriteTimeUtc
} | Add-Content $Out
}
Get-Content $Out | Select-Object -First 40
只读要求:本命令只做枚举与导出,不打开、不修改、不启动任何被检程序。
永远不要在检材上双击运行远控客户端——启动它本身就会写日志、改配置、可能拉起服务。
步骤 2:判定配置归属与时间相关性
对每个命中目录,回答三个问题。
- 属于系统还是某个用户? 路径在
%ProgramData%/%ProgramFiles% = 系统;在 %APPDATA%/%Users\xxx = 该用户。
- 最后修改时间是否落在本案时间窗内? 这一条比"是否存在"更重要。存在但半年没动过的配置,说明的不是"近期被控"。
- 有多个用户目录吗? 多个
%APPDATA%\<工具> 意味着多台主机/多个操作人,每个都要单独固定。
步骤 3:读出明文字段——这一步通常就够了
向日葵:注册表(新版本)——识别码与验证码字段
reg query "HKU.DEFAULT\Software\Oray\SunLogin\SunloginClient\SunloginInfo"
向日葵:配置文件(旧版本)
cat "/mnt/image/Program