如何在Golang中向MS-RPC发送DCE-RPC/SMB请求并调用MS-EVEN函数?
解决Golang调用MS-EVEN RPC的ELFTOpenELW函数问题
以下是针对你问题的具体落地思路,一步步排查并实现:
1. 确认MS-EVEN RPC绑定的核心参数
首先要确保RPC绑定的UUID和版本号完全正确,这是绑定成功的前提:
- MS-EVEN的UUID是
dfac7a5c-5f19-11d2-978e-0000f8757e2d,版本为0.0(可从Impacket的even.py模块中验证) - 绑定RPC时,必须指定这个UUID和版本,替换掉原ms-srvs对应的参数,同时确保安全上下文(比如NTLM认证的凭据、签名设置)和SMB连接一致。
2. 把Impacket的请求结构转成Golang的NDR兼容结构体
ELFTOpenELW是宽字符版本的函数,请求结构体需要严格遵循NDR编码规范:
- 参考Impacket中
ELFTOpenELWRequest的定义,转换成Golang结构体,注意字段的顺序、类型(比如宽字符串用[]uint16,整数用小端字节序) - 实现结构体的
MarshalNDR方法,参考go-smb中已有RPC结构体的NDR序列化逻辑,处理字符串长度、字段对齐等细节。例如:
type ELFTOpenELWRequest struct { ServerName []uint16 EventLogName []uint16 Reserved uint32 DesiredAccess uint32 } func (r *ELFTOpenELWRequest) MarshalNDR(w ndr.Writer) error { // 序列化ServerName:先写长度(含终止符),再写字节 if err := w.WriteUint32(uint32(len(r.ServerName))); err != nil { return err } for _, c := range r.ServerName { if err := w.WriteUint16(c); err != nil { return err } } // 按同样逻辑序列化EventLogName if err := w.WriteUint32(uint32(len(r.EventLogName))); err != nil { return err } for _, c := range r.EventLogName { if err := w.WriteUint16(c); err != nil { return err } } // 序列化Reserved和DesiredAccess if err := w.WriteUint32(r.Reserved); err != nil { return err } return w.WriteUint32(r.DesiredAccess) }
3. 抓包对比定位请求差异
因为Wireshark没有MS-EVEN解析器,直接用字节对比法排查:
- 先用Impacket运行调用ELFTOpenELW的代码,抓取完整的SMB+RPC流量
- 再用你的Go代码发起请求,抓取同样的流量
- 逐字节对比两个请求的RPC调用包,重点看:
- RPC头中的操作编号(ELFTOpenELW的操作编号是0x00,可从MS-EVEN文档或Impacket中确认)
- NDR序列化后的请求结构体部分,比如字符串长度、字段值是否和Impacket的一致
- 任何字节差异都可能是问题所在,比如宽字符串是否多写/少写了终止符,整数是否用了大端字节序
4. 复用go-smb的RPC框架补全逻辑
go-smb已经实现了SMB上的RPC基础通信,你只需要:
- 基于现有RPC客户端,创建MS-EVEN的绑定上下文
- 构造ELFTOpenELW的请求结构体,调用RPC的
Call方法 - 解析返回的响应结构体(同样需要实现
UnmarshalNDR方法)
5. 解析RPC错误码快速定位问题
如果调用失败,先解析返回的RPC错误码:
- 0x00000005:权限不足,检查SMB连接的凭据是否有访问事件日志的权限
- 0x000006BA:RPC绑定失败,检查UUID、版本或网络连通性
- 0x00000002:找不到指定的事件日志,检查EventLogName参数是否正确(比如"System"、"Application")
内容的提问来源于stack exchange,提问作者Defton Deftonovich
相关产品推荐
相关产品推荐

