ADCollector源码解析(一):ICollector/IDisplay/IResult接口设计与SOLID原则落地
【免费下载链接】ADCollectorA lightweight tool to quickly extract valuable information from the Active Directory environment for both attacking and defending.项目地址: https://gitcode.com/gh_mirrors/ad/ADCollector
ADCollector是一款轻量级Active Directory(AD)枚举工具,能快速从域环境中提取高价值信息(特权账户、委托配置、ACL、ADCS 等),兼顾攻防两方的 Recon 场景。本文作为ADCollector源码解析系列的第一篇,聚焦三个核心接口ICollector / IResult / IDisplay,带你读懂它「采集 → 结果 → 展示」三段式架构的设计思路,并逐条落地SOLID原则,帮助新手快速建立对这款 AD 枚举工具的整体认知。
想跟着读源码的话,先克隆仓库:
git clone https://gitcode.com/gh_mirrors/ad/ADCollector为什么AD枚举工具需要干净的架构
💡 先理解作者的困境,才能理解这套接口为什么存在。
从 README.md 可以看到,ADCollector 内置了 30+ 项枚举功能:域/林信息、信任关系、特权用户、无约束委托、SPN 账户、ASREP Roast 账户、ADCS 证书模板、SYSVOL 中的 GPP 口令……而且:
- 数据源各不相同:有的走 LDAP 查询,有的读 SYSVOL 文件,有的调用 Win32 原生 API;
- 输出格式各不相同:有的输出键值对表格,有的输出 SID 列表,有的输出 LDAP 对象属性。
如果全部塞进一个大函数,代码会变成"意大利面"。作者的解法是把一次枚举动作拆成三个独立角色:
| 角色 | 职责 | 核心抽象 | 代码位置 |
|---|---|---|---|
| 📥 采集 | 执行查询、返回数据 | ICollector | Collector/ |
| 📦 结果 | 标准化的数据容器 | IResult | Results/ |
| 🖥️ 展示 | 把数据渲染到终端 | IDisplay | Display/ |
三者各司其职,通过抽象接口松耦合。下面逐个拆解。
三个核心接口:ADCollector架构骨架
IResult接口设计:最小的统一结果契约
先看最简洁的一个 —— IResult.cs:
public interface IResult { string Title { get; set; } }整个接口只有一个Title属性,却承担了整个架构中"数据契约"的角色:
- 每个枚举任务的输出都被装进一个
IResult实现类,例如 LDAPResult.cs 装 LDAP 对象列表、ListResult.cs 装字符串列表(如嵌套组成员)、DLResult.cs 装键值对表格(如 LDAP 基本信息); - 采集层只管"生产"
IResult,展示层只管"消费"IResult,双方互不知道对方的具体类型,这是典型的依赖倒置。
ICollector接口设计:单方法的采集器
ICollector.cs 同样只有唯一方法:
public interface ICollector { IResult Collect(SearchString searchstring); }约定非常清晰:输入一个搜索字符串,输出一个结果对象。Collector/ 目录下有 4 个实现:
- CollectWithFilter.cs:通用 LDAP 过滤查询,内部把原始属性值转成可读字符串(如 SID、时间戳);
- CollectNestedGroupMembership.cs:通过
tokenGroups构造属性一次性拿到用户的嵌套组成员关系,避免逐层递归查询; - CollectAppliedGPO.cs:枚举对象实际生效的 GPO;
- CollectSYSVOL.cs:处理 SYSVOL 文件访问。
IDisplay接口设计:抽象类+静态方法的小心机
IDisplay.cs 有点特别,它不是接口,而是抽象类:
public abstract class IDisplay { public static void DisplayTitle(string Title) { ... } // 统一的绿色标题栏 public abstract void DisplayResult(IResult collectResult); }两个设计点值得新手注意:
DisplayTitle是静态方法:所有展示器共享同一套标题输出风格([-] Title:),保证终端输出格式统一,无需每个子类重复实现;DisplayResult是抽象方法:每个子类只负责一种数据的渲染,Display/ 目录下有 8+ 个实现,如DisplayLDAPObjects(LDAP 对象表)、DisplayList(纯列表)、DisplayDACL(访问控制列表)、DisplayADCS(证书服务信息)等。
一次完整枚举的数据流:从命令行到终端
把上面的接口串起来,ADCollector.cs 中的Collect方法就揭示了整条数据流:
命令行参数 (Options.cs) │ ▼ Program.ChooseOption() ── 分发功能开关 │ ▼ BuildSearchString ── 把功能翻译成 SearchString(搜索意图) │ ▼ ICollector.Collect() ── 采集:LDAP / SYSVOL / API │ ▼ IResult ── 统一数据容器 │ ▼ IDisplay.DisplayResult() ── 渲染输出🎯 其中 ADCollector.cs 的Collect(List<SearchString>)方法里有一个巧妙的"配对"逻辑:根据SearchString的具体类型,自动选出配套的采集器和展示器:
| SearchString 类型 | 采集器 | 展示器 |
|---|---|---|
LDAPSearchString | CollectWithFilter | DisplayLDAPObjects |
NestedGMSearchString | CollectNestedGroupMembership | DisplayList |
AppliedGPOSearchString | CollectAppliedGPO | DisplayDD |
SearchString/ 目录下的每种搜索字符串,在采集端和展示端都有一对一实现。搜索意图、采集、渲染被"类型"这条线串起来,新增功能时照着抄一套即可。
SOLID原则落地:逐条对照源码
S — 单一职责原则(SRP)
- 采集器只做采集:
CollectWithFilter不含任何打印逻辑; - 展示器只做渲染:
DisplayList不发起任何查询; - 连接管理独立在 Searcher.cs:它专门负责 LDAP 连接池、分页查询、根 DSE 信息初始化,采集器只需调用
Searcher.GetResultEntries()。
O — 开闭原则(OCP)
这套架构最大的红利:扩展新功能不需要修改已有代码。假设要新增"枚举域内所有计算机的某属性",只需:
- 写一个
SearchString子类描述查询意图; - 实现一个
ICollector子类(或直接复用CollectWithFilter); - 实现一个
IResult子类装数据; - 实现一个
IDisplay子类渲染。
四个新文件搞定,主流程 ADCollector.cs 几乎不动 —— 对扩展开放,对修改关闭。
L — 里氏替换原则(LSP)
IResult的实现类(LDAPResult、ListResult、DLResult…)都可以无缝替换进DisplayResult(IResult collectResult)的参数位置;ICollector的任意实现也都满足Collect的输入输出约定。抽象契约保证了"换而不坏"。
I — 接口隔离原则(ISP)
三个接口都刻意保持"最小化":IResult只有 1 个属性,ICollector只有 1 个方法,IDisplay只有 1 个抽象方法。实现类不会被迫依赖用不到的成员,这也让每个实现类的测试和维护成本极低。
D — 依赖倒置原则(DIP)
主流程只依赖抽象:ADCollector.Collect()中声明的是ICollector/IDisplay/IResult类型,而非具体类。采集层与展示层之间完全通过IResult解耦——即使把终端展示换成 JSON 导出或图形界面,采集逻辑一行都不用改。
⚖️ 一点客观评价:Program.cs 的
ChooseOption中仍按命令行开关直接 new 具体类做分发,这是轻量工具在"极致整洁"与"代码量"之间的务实取舍。对新手而言,读懂接口层的纯度远比纠结入口处的具体化更重要。
小结:这套接口设计教会我们什么
| 设计要点 | ADCollector 的做法 |
|---|---|
| 契约要小 | 三个接口平均不到 5 行代码 |
| 数据与视图分离 | IResult作为采集/展示之间的"信封" |
| 用类型驱动扩展 | SearchString子类同时决定采集器和展示器 |
| SOLID 不是口号 | 每条原则都能在 ICollector.cs、IDisplay.cs、IResult.cs 中找到对应代码 |
对于正在学习 C# 或软件设计的同学,ADCollector 是一个难得的"小而完整"的范本:代码量不大,但把接口设计、依赖倒置、策略替换这些核心概念用在了真实的安全工具场景里。建议的阅读路径是Program.cs → ADCollector.cs → 三接口 → 各实现类,对照本文的表格逐个文件验证,一个下午就能走通全部主流程。
下一篇将深入Searcher的 LDAP 连接池与分页查询实现,以及SearchString体系的构建细节,敬请关注 🚀
【免费下载链接】ADCollectorA lightweight tool to quickly extract valuable information from the Active Directory environment for both attacking and defending.项目地址: https://gitcode.com/gh_mirrors/ad/ADCollector
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考