ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

scan4all 协议逆向实战:用 struc 库完成 C 风格结构体的二进制打包与解包

scan4all 协议逆向实战:用 struc 库完成 C 风格结构体的二进制打包与解包 scan4all 协议逆向实战用 struc 库完成 C 风格结构体的二进制打包与解包【免费下载链接】scan4allOfficial repository vuls Scan: 15000PoCs; 23 kinds of application password crack; 7000Web fingerprints; 146 protocols and 90000 rules Port scanning; Fuzz, HW, awesome BugBounty( ͡° ͜ʖ ͡°)...项目地址: https://gitcode.com/GitHub_Trending/sca/scan4all本篇以 scan4all 仓库中内嵌依赖 struc 的官方文档为核心系统讲解如何用 struc 以声明式标签struct tag完成 C 风格结构体的二进制序列化与反序列化。读完本文你将掌握 struc 的 tag 语法类型、大小端、sizeof长度链接、数组/切片语法、支持的数据类型清单并能参照 scan4all 内置 GRDP/RDP 协议栈pkg/kscan/lib/grdp的真实 PDU 实现独立编写面向二进制协议与网络抓包解析的序列化代码。为什么安全扫描工具需要 strucscan4all 是一个覆盖 Web、主机、网络层的多场景漏洞扫描器除了大量 HTTP 层的 PoC 探测外其 pkg/kscan/lib/grdp 目录内置了一套完整的远程桌面协议RDP客户端实现用于对 3389 等端口做协议级探测。这类协议全部基于 C 风格二进制结构体定义定宽整型、小端字节序、带“长度前缀”的变长字段这与 Go 的encoding/binary手工逐字段读写方式天然冲突——用标准库需要为每个结构体写大段样板代码。struc 的定位正是替代这类样板代码README 明确写道它exists to pack and unpack C-style structures from bytes, which is useful for binary files and network protocols可视为encoding/binary的易用性替代。同时它缓存反射数据性能目标与encoding/binary相当。在 scan4all 中的引入方式与使用面当前仓库通过 Go module 直接依赖 strucgo.mod 中声明的版本为github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40该版本源码被完整 vendor 在 vendor/github.com/lunixbochs/struc 下包含struc.go入口与 Options、packer.go打包器、parse.gotag 解析、types.go类型注册、binary.go字节序处理等文件可在无网络环境下阅读与审计其实现细节。仓库中对 struc 的调用集中在 GRDP 协议栈的 7 个源文件中覆盖 RDP 协议从底层传输到应用层的多个 PDU 类型pkg/kscan/lib/grdp/protocol/pdu/data.goShare Data PDU、Demand/Confirm Active 等核心消息struc.Pack/struc.Unpack调用最密集pkg/kscan/lib/grdp/protocol/nla/ntlm.goNTLM 认证消息Negotiate/Challenge/Authenticate的二进制结构pkg/kscan/lib/grdp/protocol/rfb/rfb.goRFBRemote FrameBuffer像素格式、帧更新请求等以及 pdu/caps.go、t125/gcc/gcc.go、x224/x224.go、sec/sec.go。从源码结构看scan4all 对 RDP 的攻击面探测正是建立在这套 struc 序列化层之上先用 struc 把 PDU 打包为字节流发送再用 struc 从响应流中解包逐层还原协议会话状态。结构体定义与 tag 语法struc 的核心用法是给结构体字段加struc:...标签。README 给出的示例结构体如下type Example struct { Var int struc:int32,sizeofStr Str string Weird []byte struc:[8]int64 Var []int struc:[]int32,little }tag 的解析规则为裸值bare value不加任何keyvalue时整段字符串被解析为类型 大小端的组合例如struc:little、struc:int16、struc:bigsizeof字段名表示该字段是一个数值用于跟踪另一个字段的长度。Pack()时 struc 会根据被跟踪字段的当前实际长度自动回填该数值Unpack()时则用它来决定目标字段应读取多少字节类型前缀字段可以用一个 Go 类型承载另一种二进制宽度例如 Go 的int存储、按 16 位 int 编解码。README 中更完整的示例展示了这些规则的叠加type Example struct { A int struc:big // B will be encoded/decoded as a 16-bit int (a short) // but is stored as a native int in the struct B int struc:int16 // the sizeof key links a buffers size to any int field Size int struc:int8,little,sizeofStr Str string // you can get freaky if you want Str2 string struc:[5]int64 }可以看到三个关键点A用大端默认宽度传输B用 Go 原生int承载但线上只占 2 字节Size与Str通过sizeof构成长度前缀 变长负载的经典 C 结构体模式。大小端格式与识别类型struc 支持两种字节序big大端为默认显式声明little时切换为小端。Windows 系网络协议RDP、NTLM 等几乎全部是小端这也是 data.go 中所有标签统一写little的原因。识别的类型清单摘自 README分类类型特殊pad忽略字段内容底层由等长零字节[length]byte支撑用于协议对齐填充布尔/字节bool、byte整型int8/uint8、int16/uint16、int32/uint32、int64/uint64浮点float32、float64类型可以配合[]语法表示数组或切片如[]int64、[8]int32。两条约束需要注意裸切片类型没有显式[size]的[]T必须通过sizeof链接一个长度字段struc 需要它来确定边界私有字段在打包与解包时被忽略可以用来在结构体中携带不参与线上编码的内部状态。长度链接字段sizeof 与 sizefrom 的实战用法sizeof是 struc 最有价值的机制它把 C 结构中长度前缀 变长缓冲区的样板逻辑变成声明式。scan4all 仓库里有两组典型实例。实例一RDP Demand Active PDU。data.go 中type DemandActivePDU struct { SharedId uint32 struc:little LengthSourceDescriptor uint16 struc:little,sizeofSourceDescriptor LengthCombinedCapabilities uint16 struc:little SourceDescriptor []byte struc:sizefromLengthSourceDescriptor NumberCapabilities uint16 struc:little,sizeofCapabilitySets Pad2Octets uint16 struc:little CapabilitySets []Capability struc:sizefromNumberCapabilities SessionId uint32 struc:little }这里出现了双向链接LengthSourceDescriptor用sizeof声明我是 SourceDescriptor 的长度而SourceDescriptor用sizefrom反向声明我的长度由那个字段决定。CapabilitySets同理。打包时长度字段自动计算解包时长度字段决定读取范围无需任何手写读写逻辑。实例二NTLM 的 AV_PAIR。ntlm.go 中type AVPair struct { Id uint16 struc:little Len uint16 struc:little,sizeofValue Value []byte struc:little }这是 NTLM 消息中最典型的2 字节 ID 2 字节长度 变长值三元组struc 用一行sizeofValue就覆盖了全部编解码语义。逐字段对齐以 ShareDataHeader 为例对于全定宽的结构体struc 的标签甚至可以直接当作字节级结构体文档阅读。data.go 的共享数据头type ShareDataHeader struct { SharedId uint32 struc:little Padding1 uint8 struc:little StreamId uint8 struc:little UncompressedLength uint16 struc:little PDUType2 uint8 struc:little CompressedType uint8 struc:little CompressedLength uint16 struc:little }4 1 1 2 1 1 2 12 字节与 RDP 规范中 Share Data Header 的线格式逐字段对应。类似的定宽结构还包括 ntlm.go 的NegotiateMessage固定 32 字节 Payload 用[32]byte声明和 rfb.go 中 19 字节的 RFBPixelFormat含RedShift/GreenShift/BlueShift等逐字节字段。调用入口Pack 与 Unpackstruc 的对外 API 只有两个入口函数均以io接口io.Writer/io.Reader为第一参数与 README 的示例一致func main() { var buf bytes.Buffer t : Example{1, 2, 0, test, test2} err : struc.Pack(buf, t) // 结构体 - 字节流 o : Example{} err struc.Unpack(buf, o) // 字节流 - 结构体 }仓库中的真实调用链可以印证这一模式打包侧ntlm.go 中struc.Pack(buff, m)把 NTLM 消息写入发送缓冲rfb.go 中struc.Pack(buff, NewPixelFormat())生成像素格式协商数据解包侧rfb.go 中struc.Unpack(r, header)从网络读取器还原ServerCutTextHeaderntlm.go 中struc.Unpack(r, avPair)逐个解析 TargetInfo 里的 AV_PAIR 链表。入口函数的参数校验逻辑位于 struc.goprep()会解引用指针并校验结构体OptionsByteAlign对齐字节数、PtrSize指针位宽、Order字节序提供全局级默认行为PtrSize非法值会被Validate()拒绝。一个值得注意的细节skip 标签README 只列举了类型与sizeof但仓库实际代码中还使用了另一种标签skip。ntlm.go 的NegotiateMessage中Version NVersion struc:little Payload [32]byte struc:skipskip表示打包时跳过该字段、直接写入预置内存内容或反之保留原文。NTLM 消息尾部的 32 字节 Payload 是消息体内联的域名/工作站字符串其布局由前面的DomainNameBufferOffset等偏移字段决定不适合让 struc 参与计算——仓库选择先整体Pack定宽头部再手工拼装 Payloadskip在此承担了保留位的角色。这一用法提示对偏移量内联缓冲区这类 C 结构体惯用手法struc 与少量手写代码组合才能完整覆盖协议细节。性能表现README 给出的基准测试结论原始文档发布时点的测量值可作为量级参考struc 的反射式实现快于等价的标准库样板代码而无反射的手工编码是性能上限。BenchmarkEncode 1000000 1265 ns/op BenchmarkStdlibEncode 1000000 1855 ns/op BenchmarkManualEncode 5000000 284 ns/op BenchmarkDecode 1000000 1259 ns/op BenchmarkStdlibDecode 1000000 1656 ns/op BenchmarkManualDecode 20000000 89.0 ns/op其中BenchmarkEncode/BenchmarkDecode使用 strucStdlib系列为等价的encoding/binary代码Manual完全不使用反射、代表生成代码可逼近的性能上限。对扫描器这类单协议会话打包次数有限的场景struc 与标准库处于同一量级易用性收益远大于性能差距。小结从 struc 到协议栈的阅读路线struc 的价值在于把读规范 → 画结构体 → 手写读写循环压缩成读规范 → 声明带 tag 的结构体。在 scan4all 中这套机制支撑了 GRDP 协议栈对 RDP 全链路的字节级建模x224连接请求/接受→secTLS 握手→t125/gcc协商→pduShare Data 与能力集→nlaNTLM 认证每一层都是struc 结构体 少量手工 Payload 处理。如果你需要为新的私有协议编写探测逻辑可参照 pdu/data.go 的模式定宽字段直接标little变长字段用sizeof/sizefrom成对链接内联偏移缓冲区用skip留位最后统一走struc.Pack/struc.Unpack收发。【免费下载链接】scan4allOfficial repository vuls Scan: 15000PoCs; 23 kinds of application password crack; 7000Web fingerprints; 146 protocols and 90000 rules Port scanning; Fuzz, HW, awesome BugBounty( ͡° ͜ʖ ͡°)...项目地址: https://gitcode.com/GitHub_Trending/sca/scan4all创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表