深入解析 go-winio:Sliver 植入体中的高效 Win32 命名管道与异步 IO 工具库
网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载本篇技术指南围绕 Sliver 仓库中 vendored 的 go-winio 库展开它是一套专注于在 Go 语言中高效执行 Win32 IO 操作的工具集核心能力是让命名管道Named Pipe与普通文件句柄以非阻塞方式工作并可直接把命名管道当作net层的网络传输使用。读完本文你将理解 go-winio 基于 IO 完成端口IOCP的异步模型原理、命名管道监听/拨号 API 与配置项以及它在 Sliver 植入体的 Named Pipe Pivot命名管道代理链路中的真实用法。一、go-winio 是什么定位与适用场景go-winio在本仓库中位于 implant/vendor/github.com/lesnuages/go-winio/README.md是微软开源项目 go-winio 的一个 fork/镜像版本被 Sliver 以 vendor 方式引入植入体侧代码。它的核心定位是在 Go 中高效执行 Win32 IO 操作当前版本的功能重心集中在“访问命名管道和其他文件句柄”以及“把命名管道用作网络传输net transport”面向系统线程友好的异步模型代码依赖 IO 完成端口IO Completion Ports, IOCP避免 IO 阻塞系统线程从而让 Go 运行时可以把线程让出来调度其他 goroutine平台限制明确由于依赖 IOCP 机制该库仅支持Windows Vista 及更新版本的操作系统实现思想与 Go 标准库一致README 明确指出这与 Go 的net包中网络套接字的实现方式类似——即用异步 IO 换取更高的并发调度效率。值得注意的是README 还提到该项目的实现灵感来自 natefinch 的 npipe 库npipe 提供了另一种命名管道实现本文不展开外部链接仅作背景说明。从仓库结构看这一 vendor 目录共包含 12 个 Go 源文件pipe.go、file.go、backup.go、privilege.go、sd.go、ea.go、reparse.go、fileinfo.go、hvsock.go、backup.go 等覆盖命名管道、异步文件 IO、安全描述符、特权提升、备份读取等 Win32 能力。二、核心原理为什么选择 IO 完成端口IOCP2.1 从阻塞系统线程到复用线程调度 goroutine传统 Win32 同步 IO如直接调用ReadFile/WriteFile会让调用线程进入内核等待状态。在 Go 中如果一个 goroutine 阻塞在系统调用上运行时不得不为该调用保留一条操作系统线程造成线程资源浪费。go-winio 的做法在 file.go 中有完整实现进程初始化时通过CreateIoCompletionPort创建唯一的全局 IO 完成端口initIo()见 file.go并启动一个常驻的ioCompletionProcessorgoroutine 循环调用GetQueuedCompletionStatus等待完成事件每个被包装的句柄通过makeWin32File绑定到该完成端口并设置SetFileCompletionNotificationModes优化通知行为跳过完成端口直通等标志见 file.go每次读写都通过prepareIo()构造一个ioOperation内含syscall.Overlapped和结果 channel发起重叠 IO 后调用方在asyncIo()中通过 channel 等待结果而不是阻塞系统线程见 file.go。// 简化自 file.go 的 asyncIo 等待逻辑 var r ioResult select { case r -c.ch: // IO 完成端口回调后结果送达 channel err r.err if err syscall.ERROR_OPERATION_ABORTED { if f.closing.isSet() { err ErrFileClosed } } case -timeout: // 超时路径取消 IO 并再次等待 cancelIoEx(f.handle, c.o) r -c.ch err r.err if err syscall.ERROR_OPERATION_ABORTED { err ErrTimeout } }2.2 面向 net.Conn 的完整语义为了让包装后的句柄能无缝融入 Go 的网络编程模型win32File完整实现了读写、关闭、超时控制等语义超时通过deadlineHandler内部使用time.AfterFunc定时器 channel 关闭机制实现SetReadDeadline/SetWriteDeadline/SetDeadline见 file.go优雅关闭closeHandle()先原子标记 closing、调用CancelIoEx取消所有在途 IO、用WaitGroup等待其完成最后才关闭句柄保证关闭期间没有新的 IO 启动见 file.goEOF 语义读侧对ERROR_BROKEN_PIPE和零字节读取统一映射为io.EOF错误类型定义了ErrFileClosed句柄已关闭与ErrTimeout实现Timeout()与Temporary()接口的 i/o timeout 错误。这就是 README 所说的“与 Go net 包网络套接字实现类似”在win32File之上命名管道和 hvsock 都能以net.Conn/net.Listener的形式被上层直接使用。三、把命名管道当作网络传输ListenPipe / DialPipe3.1 服务端ListenPipe 与 PipeConfigpipe.go 是命名管道能力的核心。服务端通过ListenPipe(path string, c *PipeConfig)创建监听器返回net.Listener管道路径形如\\.\pipe\mypipe且要求该管道尚不存在。PipeConfig提供了四个配置项见 pipe.go配置字段类型含义SecurityDescriptorstringWindows 安全描述符SDDL 格式字符串用于控制谁可以连接管道MessageModebool管道工作于字节模式还是消息模式本实现统一按字节流读取唯一的实际差异是只有消息模式管道支持CloseWrite()RemoteClientModebool是否允许远程客户端访问管道对应FILE_PIPE_REJECT_REMOTE_CLIENTS的取舍InputBufferSize/OutputBufferSizeint32输入/输出缓冲区大小字节从实现细节看ListenPipe会先将 SDDL 字符串通过SddlToSecurityDescriptor转换为二进制安全描述符再调用makeServerPipeHandle。makeServerPipeHandle走的是ntdll 层NtCreateNamedPipeFile、RtlDosPathNameToNtPathName、RtlDefaultNpAcl而不是传统的CreateNamedPipeW这样可以精确控制首次创建的初始状态首次创建时first true只申请SYNCHRONIZE权限使管道处于初始断开状态阻塞客户端连接直到后续first false的调用再真正接受连接见 pipe.go。监听器内部运行一个listenerRoutinegoroutine通过两个 channelacceptCh与closeCh协调Accept()请求与Close()操作接受连接时若遇到客户端立即断开cERROR_NO_DATA还会自动重试见 pipe.go。3.2 客户端DialPipe 与超时/取消客户端侧提供三个拨号函数DialPipe(path string, timeout *time.Duration)按路径连接命名管道timeout为nil时默认2 秒超时超时后返回ErrTimeoutDialPipeContext(ctx context.Context, path string)以 context 的取消/截止时间控制拨号DialPipeAccess(ctx context.Context, path string, access uint32)额外指定访问权限默认GENERIC_READ|GENERIC_WRITE。拨号过程中如果遇到ERROR_PIPE_BUSY管道被占用tryDialPipe会每 10 毫秒重试一次直到成功或 context 取消见 pipe.go。连接成功后会通过GetNamedPipeInfo检查管道类型标志如果对端管道是消息模式PIPE_TYPE_MESSAGE则返回支持CloseWrite()的win32MessageBytePipe否则返回普通win32Pipe见 pipe.go。// 服务端 ln, err : winio.ListenPipe(\\.\pipe\mypipe, winio.PipeConfig{ SecurityDescriptor: D:P(A;;GA;;;WD), // 允许所有人仅示例 MessageMode: false, }) conn, err : ln.Accept() // 客户端 conn, err : winio.DialPipe(\\.\pipe\mypipe, nil) // nil 2 秒默认超时3.3 CloseWrite 的巧妙实现值得关注的是消息模式下CloseWrite()的实现go-winio 用零字节写入来模拟半关闭。零字节写入在普通字节模式下不会传输到对端只有消息模式管道才会把空消息传过去并让读者收到io.EOF。因此win32MessageBytePipe.Write会忽略零字节写入Read在收到因零字节消息导致的 EOF 后会永久置位readEOF见 pipe.go。四、Sliver 中的实战应用Named Pipe Pivot 客户端go-winio 在本仓库中并非孤立存在它是 Sliver 植入体命名管道 Pivot代理链路的关键依赖。见 implant/sliver/transports/pivotclients/namedpipe_windows.goimport github.com/lesnuages/go-winio func NamedPipePivotStartSession(uri *url.URL, opts *NamedPipePivotOptions) (*NetConnPivotClient, error) { address : \\\\ uri.Hostname() strings.Replace(uri.Path, /, \\, -1) // {{if .Config.Debug}} log.Printf(Pivot named pipe address: %s, address) // {{end}} conn, err : winio.DialPipe(address, nil) // 默认 2 秒超时 if err ! nil { return nil, err } pivot : NetConnPivotClient{ conn: conn, readMutex: sync.Mutex{}, writeMutex: sync.Mutex{}, readDeadline: opts.ReadDeadline, writeDeadline: opts.WriteDeadline, } err pivot.KeyExchange() // ... return pivot, nil }从源码结构可以看到的调用关系植入体把用户提供的 URI如namedpipe://host/pipe-name解析为 Windows 命名管道路径主机名拼接 URI 路径/替换为\得到形如\\host\pipe-name的地址通过winio.DialPipe建立连接DialPipe返回的net.Conn直接被塞进NetConnPivotClient作为后续 Pivot 会话的传输通道在已建立的连接之上执行密钥交换KeyExchange完成身份认证与会话密钥协商。这段代码被模板条件// {{if .Config.IncludeNamePipe}}包裹说明该功能是按需编译进植入体的——构建植入体时只有启用了 Named Pipe 支持才会引入 go-winio 依赖与对应代码路径。这正是“把命名管道作为 net transport”这一设计目标的直接落地C2 流量可以通过命名管道这一 Windows 原生 IPC 机制进行转发帮助代理流量穿越内网。五、其他值得关注的 Win32 工具能力除命名管道外该 vendor 包还提供一系列 Win32 IO 工具函数在需要精细化操作 Windows 文件系统时非常有用安全描述符SDDL转换sd.go 提供SddlToSecurityDescriptorSDDL 字符串 → 二进制安全描述符、SecurityDescriptorToSddl反向转换以及LookupSidByName账户名 → SID 字符串底层调用 advapi32 的ConvertStringSecurityDescriptorToSecurityDescriptorW等 API特权管理privilege.go 提供RunWithPrivilege/RunWithPrivileges临时为一次函数调用启用指定特权如SeBackupPrivilege、SeRestorePrivilege以及EnableProcessPrivileges/DisableProcessPrivileges进程级全局开关内部通过ImpersonateSelfAdjustTokenPrivileges实现且RunWithPrivileges会锁定 OS 线程以保证令牌切换的线程安全备份语义 IObackup.go 提供基于BackupRead/BackupWrite的BackupFileReader/BackupFileWriter以及OpenForBackup以FILE_FLAG_BACKUP_SEMANTICS打开文件/目录配合备份/还原特权可绕过部分访问检查和BackupStreamReader/BackupStreamWriter解析备份流中的多种流类型数据、EA、安全描述符、备用数据流、重解析点等扩展属性EA编解码ea.go 提供DecodeExtendedAttributes/EncodeExtendedAttributes用于读写FILE_FULL_EA_INFORMATION格式的缓冲区如配合BackupRead/ZwQueryEaFile重解析点Reparse Point编解码reparse.go 提供DecodeReparsePoint/EncodeReparsePoint支持符号链接symlink与挂载点mount point两类REPARSE_DATA_BUFFER结构并可自动把\\?\、UNC、盘符路径转换为 NT 路径文件基础信息查询fileinfo.go 通过GetFileInformationByHandleEx提供GetFileBasicInfo时间戳与属性、GetFileStandardInfo分配大小、链接数等、GetFileID卷序列号 文件 ID 对Hyper-V 套接字hvsock.go 实现了AF_HYPERVAF_HVSOCK地址族的HvsockListener/HvsockConn用于 Hyper-V 虚拟机与宿主/VM 之间的高速通信并提供了VsockServiceID将 AF_VSOCK 端口映射为 hvsock 服务 GUID。六、平台要求与使用前提结合 README 声明与源码实现使用该库需要满足以下前提操作系统仅支持 Windows且要求Windows Vista 或更新版本IOCP 是必要条件构建标签pipe.go、file.go、backup.go、privilege.go、sd.go、hvsock.go、fileinfo.go均带有// build windows构建约束本仓库 vendor 中还保留了旧的// build写法在非 Windows 平台编译时这些文件会被排除调用方式服务端ListenPipe要求目标管道路径尚不存在客户端DialPipe在管道繁忙时会以 10ms 间隔自动重试直到超时默认 2 秒或 context 取消依赖来源作为 Sliver 植入体的 vendored 依赖go-winio的引入路径由 go.mod 与 implant 侧的 vendor 目录共同管理使用方如 namedpipe_windows.go通过github.com/lesnuages/go-winio导入。七、小结go-winio 是 Sliver 在 Windows 平台上做高效进程间通信的关键底层库它以 IOCP 异步模型解决了 Win32 IO 阻塞系统线程的问题把命名管道、普通文件句柄乃至 Hyper-V 套接字统一抽象为 Go 的net.Conn/net.Listener语义并额外提供了安全描述符、特权、备份 IO、EA、重解析点等一批实用的 Win32 工具函数。在 Sliver 中它直接支撑了 Named Pipe Pivot 客户端的连接建立与后续 C2 流量转发是从 README 的一句话定位到植入体实战代码之间完整闭环的典型案例。赞分享网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载相关推荐22MB的便携式照片编辑器PhotoDemon如何颠覆你对免费软件的认知22MB的便携式照片编辑器PhotoDemon如何颠覆你对免费软件的认知 你是否曾因Photoshop的庞大体积和昂贵价格而却步是否在寻找一款既专业又便携云原生CLI镜像仓库go-winio 深入解析用 Go 高效驾驭 Win32 命名管道与底层 I/Ogo winio 深入解析用 Go 高效驾驭 Win32 命名管道与底层 I/O 导读 go winio 是微软开源的 Go 工具库专注于在 Go 中高效执机器学习深度学习数据可视化可观测性go-winio在 Go 中高效完成 Win32 命名管道 IO 与网络传输的底层库go winio在 Go 中高效完成 Win32 命名管道 IO 与网络传输的底层库 go winio 是微软开源的一组 Go 语言 Win32 IO 工具库后端认证鉴权数据库无服务开发工具云原生创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考