You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang模拟用户连接SQL Server时触发Socket异常(WSAEPROVIDERFAILEDINIT)

问题分析与解决方案

核心问题

错误WSAEPROVIDERFAILEDINIT(10106)的本质是模拟用户后的线程无法初始化Winsock服务提供者。Winsock的WSAStartup是线程关联操作,线程安全上下文切换(模拟用户)后,系统无法为新上下文加载Winsock所需的服务组件,导致套接字创建失败。

解决步骤

  1. 提前初始化Winsock
    在调用ImpersonateLoggedOnUser之前,先在当前线程完成Winsock初始化。因为Winsock初始化绑定线程上下文,模拟后再执行初始化会因权限/上下文不匹配失败。

  2. 调整LogonUser登录类型
    原代码使用的LOGON32_LOGON_NETWORK_CLEARTEXT属于受限登录令牌,权限不足以完成网络套接字初始化。建议替换为LOGON32_LOGON_INTERACTIVE或LOGON32_LOGON_BATCH,这类令牌拥有更完整的网络操作权限。

  3. 规范资源清理顺序
    确保WSACleanup在RevertToSelf之后调用,且与WSAStartup在同一线程执行,避免上下文混乱。

修改后的代码示例

package main

import (
    "database/sql"
    "fmt"
    "log"
    "runtime"
    "sync"
    "syscall"
    "unsafe"

    _ "github.com/microsoft/go-mssqldb"
    "golang.org/x/sys/windows"
)

const (
    user   = "username"
    pass   = "password"
    domain = "domain"
)

var (
    advapi32        = syscall.NewLazyDLL("advapi32.dll")
    ws2_32          = syscall.NewLazyDLL("ws2_32.dll")
    logonProc       = advapi32.NewProc("LogonUserW")
    impersonateProc = advapi32.NewProc("ImpersonateLoggedOnUser")
    revertSelfProc  = advapi32.NewProc("RevertToSelf")
    wsaStartupProc  = ws2_32.NewProc("WSAStartup")
    wsacleanupProc  = ws2_32.NewProc("WSACleanup")
)

// WSAData 定义Winsock初始化结构体
type WSAData struct {
    Version        uint16
    HighVersion    uint16
    Description    [257]uint16
    SystemStatus   [129]uint16
    MaxSockets     uint16
    MaxUdpDg       uint16
    VendorInfo     uintptr
}

func main() {
    log.SetFlags(0)
    var wg sync.WaitGroup
    wg.Add(1)
    go func(user, pass string) {
        defer wg.Done()
        runtime.LockOSThread()
        defer runtime.UnlockOSThread()
        
        log.Println("In a goroutine")
        
        // 1. 先初始化Winsock,在模拟用户之前
        var wsaData WSAData
        res, _, err := wsaStartupProc.Call(uintptr(0x202), uintptr(unsafe.Pointer(&wsaData)))
        if res != 0 {
            log.Fatalf("WSAStartup failed: %v", err)
        }
        defer func() {
            // 3. 先恢复身份,再清理Winsock
            mustRevertToSelf()
            _, _, _ = wsacleanupProc.Call()
        }()

        // 2. 使用交互式登录类型替代NETWORK_CLEARTEXT
        token, err := logonUser(user, pass, domain)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer closeHandle(token)

        u, _ := token.GetTokenUser()
        log.Println(u)
        r, _ := token.IsRestricted()
        log.Printf("token is restricted: %t", r)
        e := token.IsElevated()
        log.Printf("token is elevated: %t", e)
        
        err = impersonateUser(token)
        if err != nil {
            log.Println(err)
            return
        }
        
        connectToDB()
        log.Println("Impersonated")
    }(user, pass)
    wg.Wait()
    log.Println("OK")
}

func logonUser(user, pass, domain string) (windows.Token, error) {
    const (
        LOGON32_LOGON_INTERACTIVE = uintptr(2)
        LOGON32_PROVIDER_DEFAULT  = uintptr(0)
    )

    pUsername, _ := windows.UTF16PtrFromString(user)
    pDomain, _ := windows.UTF16PtrFromString(domain)
    pPassword, _ := windows.UTF16PtrFromString(pass)

    hToken := uintptr(0)
    res, _, err := logonProc.Call(
        uintptr(unsafe.Pointer(pUsername)),
        uintptr(unsafe.Pointer(pDomain)),
        uintptr(unsafe.Pointer(pPassword)),
        LOGON32_LOGON_INTERACTIVE,
        LOGON32_PROVIDER_DEFAULT,
        uintptr(unsafe.Pointer(&hToken)),
    )
    if res != 0 {
        return windows.Token(hToken), nil
    }

    return 0, err
}

func impersonateUser(token windows.Token) error {
    res, _, err := impersonateProc.Call(uintptr(token))
    if res == 0 {
        return err
    }
    return nil
}

func revertToSelf() error {
    rc, _, ec := syscall.SyscallN(revertSelfProc.Addr(), 0, 0, 0, 0)
    if rc == 0 {
        return ec
    }
    return nil
}

func mustRevertToSelf() {
    err := revertToSelf()
    if err != nil {
        panic(err)
    }
}

func closeHandle(token windows.Token) {
    err := token.Close()
    if err != nil {
        panic(err)
    }
}

func connectToDB() {
    // 集成身份验证时无需显式指定User ID和Password,自动使用模拟身份
    connectionStr := "Data Source=<host>;Initial Catalog=<Database>;Integrated Security=true;Encrypt=False;TrustServerCertificate=True"
    db, err := sql.Open("mssql", connectionStr)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer db.Close()
    err = db.Ping()
    if err != nil {
        log.Println(err.Error())
        return
    }
    log.Println("Database connection successful")
}

额外说明

  • 集成身份验证模式下,连接字符串无需添加User ID和Password,驱动会自动使用当前模拟的用户身份。
  • 确保模拟用户拥有目标SQL Server的登录权限及对应数据库的访问权限。
  • 若必须使用NETWORK_CLEARTEXT登录类型,需在本地安全策略中给该用户授予“作为操作系统的一部分操作”权限,但此权限过大,不推荐使用。

内容的提问来源于stack exchange,提问作者rajat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 22:57:48