news 2026/8/24 17:27:22

ADCollector源码解析(一):ICollector/IDisplay/IResult接口设计与SOLID原则落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ADCollector源码解析(一):ICollector/IDisplay/IResult接口设计与SOLID原则落地

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 对象属性。

如果全部塞进一个大函数,代码会变成"意大利面"。作者的解法是把一次枚举动作拆成三个独立角色:

角色职责核心抽象代码位置
📥 采集执行查询、返回数据ICollectorCollector/
📦 结果标准化的数据容器IResultResults/
🖥️ 展示把数据渲染到终端IDisplayDisplay/

三者各司其职,通过抽象接口松耦合。下面逐个拆解。

三个核心接口: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); }

两个设计点值得新手注意:

  1. DisplayTitle是静态方法:所有展示器共享同一套标题输出风格([-] Title:),保证终端输出格式统一,无需每个子类重复实现;
  2. 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 类型采集器展示器
LDAPSearchStringCollectWithFilterDisplayLDAPObjects
NestedGMSearchStringCollectNestedGroupMembershipDisplayList
AppliedGPOSearchStringCollectAppliedGPODisplayDD

SearchString/ 目录下的每种搜索字符串,在采集端和展示端都有一对一实现。搜索意图、采集、渲染被"类型"这条线串起来,新增功能时照着抄一套即可。

SOLID原则落地:逐条对照源码

S — 单一职责原则(SRP)

  • 采集器只做采集:CollectWithFilter不含任何打印逻辑;
  • 展示器只做渲染:DisplayList不发起任何查询;
  • 连接管理独立在 Searcher.cs:它专门负责 LDAP 连接池、分页查询、根 DSE 信息初始化,采集器只需调用Searcher.GetResultEntries()

O — 开闭原则(OCP)

这套架构最大的红利:扩展新功能不需要修改已有代码。假设要新增"枚举域内所有计算机的某属性",只需:

  1. 写一个SearchString子类描述查询意图;
  2. 实现一个ICollector子类(或直接复用CollectWithFilter);
  3. 实现一个IResult子类装数据;
  4. 实现一个IDisplay子类渲染。

四个新文件搞定,主流程 ADCollector.cs 几乎不动 —— 对扩展开放,对修改关闭。

L — 里氏替换原则(LSP)

IResult的实现类(LDAPResultListResultDLResult…)都可以无缝替换进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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 17:25:05

单片机毕业设计-基于 51/STM32 单片机的烟雾火焰温度综合安防控制系统设计 基于 51/STM32 单片机的消防环境多参量监测与自动通风灭火系统(017604)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/8/24 17:23:57

如何参与Holodex开发?从克隆代码到本地运行的完整教程

如何参与Holodex开发&#xff1f;从克隆代码到本地运行的完整教程 【免费下载链接】Holodex Holodex frontend source code 项目地址: https://gitcode.com/gh_mirrors/ho/Holodex Holodex 是一个开源的虚拟主播&#xff08;VTuber&#xff09;视频与直播平台&#xff0…

作者头像 李华
网站建设 2026/8/24 17:23:46

小白友好|OpenClaw 桌面端图形化安装全过程分享

&#x1f680;告别环境噩梦&#xff01;OpenClaw v3.0.2/v2.7.9 解压即用实操教程 适配系统&#xff1a;Windows10/11 64 位、macOS12 当前版本&#xff1a;Windows v3.0.2&#xff1b;macOS v2.7.9&#xff08;虾壳云版&#xff09; &#x1f3af;工具核心特点 整套部署采用可…

作者头像 李华
网站建设 2026/8/24 17:17:59

多智能体强化学习在对抗环境中的通信与信任机制研究

1. 项目概述&#xff1a;当AI特工潜入《Among Us》 如果你玩过《Among Us》&#xff0c;一定体验过那种在飞船舱内穿梭&#xff0c;一边修反应堆一边提防身边“内鬼”的紧张感。这个项目&#xff0c;就是把这种“狼人杀”式的社交推理游戏&#xff0c;变成了一个严肃的AI研究沙…

作者头像 李华
网站建设 2026/8/24 17:17:55

从PID到MPC:控制数学问题的核心框架与工程实践全解析

1. 项目概述&#xff1a;当“控制”遇上“数学”在工程、金融、乃至日常生活的决策中&#xff0c;我们常常面临一个核心挑战&#xff1a;如何让一个系统&#xff08;无论是机器、流程还是投资组合&#xff09;按照我们期望的方式运行&#xff0c;并抵抗各种内外部的干扰&#x…

作者头像 李华