如何在 Go 中管理 Windows 用户帐户?

2023-12-23

我需要能够从 Go 应用程序管理 Windows 本地用户帐户,并且似乎如果不使用 CGo,就没有本机绑定。

我最初的搜索让我发现人们说最好使用“exec.Command”来运行“net user”命令,但在解析响应代码时这似乎很混乱且不可靠。

我发现处理此类事情的函数位于 netapi32.dll 库中,但由于 Go 本身不支持 Windows 头文件,因此调用这些函数似乎并不容易。

举个例子https://github.com/golang/sys/tree/master/windows https://github.com/golang/sys/tree/master/windows看来 Go 团队已经重新定义了代码中的所有内容,然后调用 DLL 函数。

我很难将其包装在一起,但我已经得到了我想要的低级 API 模板,然后在其上包装了更高级别的 API,就像核心 Go 运行时一样。

type LMSTR          ????
type DWORD          ????
type LPBYTE         ????
type LPDWORD        ????
type LPWSTR         ????
type NET_API_STATUS DWORD;

type USER_INFO_1 struct {
    usri1_name              LPWSTR
    usri1_password          LPWSTR
    usri1_password_age      DWORD
    usri1_priv              DWORD
    usri1_home_dir          LPWSTR
    usri1_comment           LPWSTR
    usri1_flags             DWORD
    usri1_script_path       LPWSTR
}

type GROUP_USERS_INFO_0 struct {
    grui0_name              LPWSTR
}

type USER_INFO_1003 struct {
    usri1003_password       LPWSTR
}

const (
    USER_PRIV_GUEST         = ????
    USER_PRIV_USER          = ????
    USER_PRIV_ADMIN         = ????

    UF_SCRIPT               = ????
    UF_ACCOUNTDISABLE       = ????
    UF_HOMEDIR_REQUIRED     = ????
    UF_PASSWD_NOTREQD       = ????
    UF_PASSWD_CANT_CHANGE   = ????
    UF_LOCKOUT              = ????
    UF_DONT_EXPIRE_PASSWD   = ????
    UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = ????
    UF_NOT_DELEGATED        = ????
    UF_SMARTCARD_REQUIRED   = ????
    UF_USE_DES_KEY_ONLY     = ????
    UF_DONT_REQUIRE_PREAUTH = ????
    UF_TRUSTED_FOR_DELEGATION = ????
    UF_PASSWORD_EXPIRED     = ????
    UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = ????

    UF_NORMAL_ACCOUNT       = ????
    UF_TEMP_DUPLICATE_ACCOUNT = ????
    UF_WORKSTATION_TRUST_ACCOUNT = ????
    UF_SERVER_TRUST_ACCOUNT = ????
    UF_INTERDOMAIN_TRUST_ACCOUNT = ????

    NERR_Success            = ????
    NERR_InvalidComputer    = ????
    NERR_NotPrimary         = ????
    NERR_GroupExists        = ????
    NERR_UserExists         = ????
    NERR_PasswordTooShort   = ????
    NERR_UserNotFound       = ????
    NERR_BufTooSmall        = ????
    NERR_InternalError      = ????
    NERR_GroupNotFound      = ????
    NERR_BadPassword        = ????
    NERR_SpeGroupOp         = ????
    NERR_LastAdmin          = ????

    ERROR_ACCESS_DENIED     = ????
    ERROR_INVALID_PASSWORD  = ????
    ERROR_INVALID_LEVEL     = ????
    ERROR_MORE_DATA         = ????
    ERROR_BAD_NETPATH       = ????
    ERROR_INVALID_NAME      = ????
    ERROR_NOT_ENOUGH_MEMORY = ????
    ERROR_INVALID_PARAMETER = ????

    FILTER_TEMP_DUPLICATE_ACCOUNT = ????
    FILTER_NORMAL_ACCOUNT   = ????
    FILTER_INTERDOMAIN_TRUST_ACCOUNT = ????
    FILTER_WORKSTATION_TRUST_ACCOUNT = ????
    FILTER_SERVER_TRUST_ACCOUNT = ????
)

func NetApiBufferFree(Buffer LPVOID) (NET_API_STATUS);

func NetUserAdd(servername LMSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (NET_API_STATUS);

func NetUserChangePassword(domainname LPCWSTR, username LPCWSTR, oldpassword LPCWSTR, newpassword LPCWSTR) (NET_API_STATUS);

func NetUserDel(servername LPCWSTR, username LPCWSTR) (NET_API_STATUS);

func NetUserEnum(servername LPCWSTR, level DWORD, filter DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD, resume_handle LPDWORD) (NET_API_STATUS);

func NetUserGetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD) (NET_API_STATUS);

func NetUserSetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, num_entries DWORD) (NET_API_STATUS);

func NetUserSetInfo(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (NET_API_STATUS);

将其包装在一起的最佳方法是什么?


(在我看来)使用 Windows DLL 是直接使用 Win32 API 的最佳方法。

如果您查看src/syscall在 Go 安装目录中,您可以找到一个名为mksyscall_windows.go https://golang.org/src/syscall/mksyscall_windows.go。这似乎是 Go 团队管理所有 DLL 包装器的方式。

Use go generate生成您的代码

看看如何syscall_windows.go https://golang.org/src/syscall/syscall_windows.go使用它。具体来说有以下几点go generate命令:

//go:生成 go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go

定义 Win32 API 类型

然后他们定义自己的类型。您需要自己手动执行此操作。

有时这是一个挑战,因为保留结构字段的大小和对齐至关重要。我用Visual Studio 社区版 https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx探究 Microsoft 定义的大量基本类型,以确定它们在 Go 中的等效类型。

Windows 对字符串使用 UTF16。所以你将把它们表示为*uint16. Use syscall.UTF16PtrFromString从 Go 字符串生成一个。

注释要导出的 Win32 API 函数

整个要点mksyscall_windows.go就是生成所有样板代码,这样你最终会得到一个为你调用 DLL 的 Go 函数。

这是通过添加注释(Go 注释)来完成的。

例如,在syscall_windows.go你有这些注释:

//sys   GetLastError() (lasterr error)
//...
//sys   CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) [failretval&0xff==0] = CreateHardLinkW

mksyscall_windows.go有文档注释可以帮助您弄清楚它是如何工作的。您还可以查看 go 生成的代码zsyscall_windows.go https://golang.org/src/syscall/zsyscall_windows.go.

Run go generate

很简单,只需运行:

go generate

Example:

对于您的示例,创建一个名为的文件win32_windows.go:

package win32

//go generate go run mksyscall_windows.go -output zwin32_windows.go win32_windows.go

type (
    LPVOID         uintptr
    LMSTR          *uint16
    DWORD          uint32
    LPBYTE         *byte
    LPDWORD        *uint32
    LPWSTR         *uint16
    NET_API_STATUS DWORD

    USER_INFO_1 struct {
        Usri1_name         LPWSTR
        Usri1_password     LPWSTR
        Usri1_password_age DWORD
        Usri1_priv         DWORD
        Usri1_home_dir     LPWSTR
        Usri1_comment      LPWSTR
        Usri1_flags        DWORD
        Usri1_script_path  LPWSTR
    }

    GROUP_USERS_INFO_0 struct {
        Grui0_name LPWSTR
    }

    USER_INFO_1003 struct {
        Usri1003_password LPWSTR
    }
)

const (
    // from LMaccess.h

    USER_PRIV_GUEST = 0
    USER_PRIV_USER  = 1
    USER_PRIV_ADMIN = 2

    UF_SCRIPT                          = 0x0001
    UF_ACCOUNTDISABLE                  = 0x0002
    UF_HOMEDIR_REQUIRED                = 0x0008
    UF_LOCKOUT                         = 0x0010
    UF_PASSWD_NOTREQD                  = 0x0020
    UF_PASSWD_CANT_CHANGE              = 0x0040
    UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x0080

    UF_TEMP_DUPLICATE_ACCOUNT    = 0x0100
    UF_NORMAL_ACCOUNT            = 0x0200
    UF_INTERDOMAIN_TRUST_ACCOUNT = 0x0800
    UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
    UF_SERVER_TRUST_ACCOUNT      = 0x2000

    UF_ACCOUNT_TYPE_MASK = UF_TEMP_DUPLICATE_ACCOUNT |
        UF_NORMAL_ACCOUNT |
        UF_INTERDOMAIN_TRUST_ACCOUNT |
        UF_WORKSTATION_TRUST_ACCOUNT |
        UF_SERVER_TRUST_ACCOUNT

    UF_DONT_EXPIRE_PASSWD                     = 0x10000
    UF_MNS_LOGON_ACCOUNT                      = 0x20000
    UF_SMARTCARD_REQUIRED                     = 0x40000
    UF_TRUSTED_FOR_DELEGATION                 = 0x80000
    UF_NOT_DELEGATED                          = 0x100000
    UF_USE_DES_KEY_ONLY                       = 0x200000
    UF_DONT_REQUIRE_PREAUTH                   = 0x400000
    UF_PASSWORD_EXPIRED                       = 0x800000
    UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000
    UF_NO_AUTH_DATA_REQUIRED                  = 0x2000000
    UF_PARTIAL_SECRETS_ACCOUNT                = 0x4000000
    UF_USE_AES_KEYS                           = 0x8000000

    UF_SETTABLE_BITS = UF_SCRIPT |
        UF_ACCOUNTDISABLE |
        UF_LOCKOUT |
        UF_HOMEDIR_REQUIRED |
        UF_PASSWD_NOTREQD |
        UF_PASSWD_CANT_CHANGE |
        UF_ACCOUNT_TYPE_MASK |
        UF_DONT_EXPIRE_PASSWD |
        UF_MNS_LOGON_ACCOUNT |
        UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED |
        UF_SMARTCARD_REQUIRED |
        UF_TRUSTED_FOR_DELEGATION |
        UF_NOT_DELEGATED |
        UF_USE_DES_KEY_ONLY |
        UF_DONT_REQUIRE_PREAUTH |
        UF_PASSWORD_EXPIRED |
        UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
        UF_NO_AUTH_DATA_REQUIRED |
        UF_USE_AES_KEYS |
        UF_PARTIAL_SECRETS_ACCOUNT

    FILTER_TEMP_DUPLICATE_ACCOUNT    = (0x0001)
    FILTER_NORMAL_ACCOUNT            = (0x0002)
    FILTER_INTERDOMAIN_TRUST_ACCOUNT = (0x0008)
    FILTER_WORKSTATION_TRUST_ACCOUNT = (0x0010)
    FILTER_SERVER_TRUST_ACCOUNT      = (0x0020)

    LG_INCLUDE_INDIRECT = (0x0001)

    // etc...
)

//sys NetApiBufferFree(Buffer LPVOID) (status NET_API_STATUS) = netapi32.NetApiBufferFree
//sys NetUserAdd(servername LMSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (status NET_API_STATUS) = netapi32.NetUserAdd
//sys NetUserChangePassword(domainname LPCWSTR, username LPCWSTR, oldpassword LPCWSTR, newpassword LPCWSTR) (status NET_API_STATUS) = netapi32.NetUserChangePassword
//sys NetUserDel(servername LPCWSTR, username LPCWSTR) (status NET_API_STATUS) = netapi32.NetUserDel
//sys NetUserEnum(servername LPCWSTR, level DWORD, filter DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD, resume_handle LPDWORD) (status NET_API_STATUS) = netapi32.NetUserEnum
//sys NetUserGetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD) (status NET_API_STATUS) = netapi32.NetUserGetGroups
//sys NetUserSetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, num_entries DWORD) (status NET_API_STATUS) = netapi32.NetUserSetGroups
//sys NetUserSetInfo(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (status NET_API_STATUS) = netapi32.NetUserSetInfo

运行后go generate(只要你复制了mksyscall_windows.go到同一目录)您将有一个名为“zwin32_windows.go”的文件(类似这样):

// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT

package win32

import "unsafe"
import "syscall"

var _ unsafe.Pointer

var (
    modnetapi32 = syscall.NewLazyDLL("netapi32.dll")

    procNetApiBufferFree      = modnetapi32.NewProc("NetApiBufferFree")
    procNetUserAdd            = modnetapi32.NewProc("NetUserAdd")
    procNetUserChangePassword = modnetapi32.NewProc("NetUserChangePassword")
    procNetUserDel            = modnetapi32.NewProc("NetUserDel")
    procNetUserEnum           = modnetapi32.NewProc("NetUserEnum")
    procNetUserGetGroups      = modnetapi32.NewProc("NetUserGetGroups")
    procNetUserSetGroups      = modnetapi32.NewProc("NetUserSetGroups")
    procNetUserSetInfo        = modnetapi32.NewProc("NetUserSetInfo")
)

func NetApiBufferFree(Buffer LPVOID) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall(procNetApiBufferFree.Addr(), 1, uintptr(Buffer), 0, 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserAdd(servername LMSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall6(procNetUserAdd.Addr(), 4, uintptr(servername), uintptr(level), uintptr(buf), uintptr(parm_err), 0, 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserChangePassword(domainname LPCWSTR, username LPCWSTR, oldpassword LPCWSTR, newpassword LPCWSTR) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall6(procNetUserChangePassword.Addr(), 4, uintptr(domainname), uintptr(username), uintptr(oldpassword), uintptr(newpassword), 0, 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserDel(servername LPCWSTR, username LPCWSTR) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall(procNetUserDel.Addr(), 2, uintptr(servername), uintptr(username), 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserEnum(servername LPCWSTR, level DWORD, filter DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD, resume_handle LPDWORD) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(servername), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(bufptr)), uintptr(prefmaxlen), uintptr(entriesread), uintptr(totalentries), uintptr(resume_handle), 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserGetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, bufptr *LPBYTE, prefmaxlen DWORD, entriesread LPDWORD, totalentries LPDWORD) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall9(procNetUserGetGroups.Addr(), 7, uintptr(servername), uintptr(username), uintptr(level), uintptr(unsafe.Pointer(bufptr)), uintptr(prefmaxlen), uintptr(entriesread), uintptr(totalentries), 0, 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserSetGroups(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, num_entries DWORD) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall6(procNetUserSetGroups.Addr(), 5, uintptr(servername), uintptr(username), uintptr(level), uintptr(buf), uintptr(num_entries), 0)
    status = NET_API_STATUS(r0)
    return
}

func NetUserSetInfo(servername LPCWSTR, username LPCWSTR, level DWORD, buf LPBYTE, parm_err LPDWORD) (status NET_API_STATUS) {
    r0, _, _ := syscall.Syscall6(procNetUserSetInfo.Addr(), 5, uintptr(servername), uintptr(username), uintptr(level), uintptr(buf), uintptr(parm_err), 0)
    status = NET_API_STATUS(r0)
    return
}

显然,大部分工作是将 Win32 类型转换为其 Go 等效类型。

随意在里面闲逛syscall包 - 他们通常已经定义了您可能感兴趣的结构。

ZOMG 真的吗??1! 2 工作量很大!

它比手工编写代码更好。并且不需要 CGo!

免责声明:我还没有测试上面的代码来验证它是否确实达到了您想要的效果。使用 Win32 API 本身就是一种乐趣。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Go 中管理 Windows 用户帐户? 的相关文章

  • 如何将未知字段类型的数据解组为 JSON

    我有这些 结构 type Results struct Gender string json gender Name struct First string json first Last string json last json nam
  • 为什么我们从 MultiByte 转换为 WideChar?

    我习惯于处理 ASCII 字符串 但现在使用 UNICODE 我对一些术语感到非常困惑 什么是多字节字符以及什么是widechar有什么不同 多字节是指在内存中包含多个字节的字符吗 widechar只是一个数据类型来表示吗 为什么我们要从M
  • 投射回更专业的界面

    我正在用 Go 编写一个游戏 在 C 中 我将所有实体类存储在 BaseEntity 类的数组中 如果一个实体需要在世界中移动 那么它将是一个从 BaseEntity 派生的 PhysEntity 但添加了方法 我尝试模仿这是 go pac
  • 使用 testify 模拟接口方法两次,输入和输出不同

    如何在 golang 测试中模拟接口方法两次 例如 type myCache interface Get key string data interface error type service struct cache myCache f
  • ASP.net 身份 - UserManager 如何访问角色?

    我们只在 Microsoft AspNet Identity 内部 我们甚至没有查看 Microsoft AspNet Identity EntityFramework 的基本实现 The UserManager类只接受一个IUserSto
  • R 脚本自动化时的不同结果

    以下命令对 pdf 文件执行 Ghostscript 这pdf file变量包含该 pdf 的路径 bbox lt system paste C gs gs8 64 bin gswin32c exe sDEVICE bbox dNOPAUS
  • 在 Go 中使用电子邮件地址创建证书签名请求 (CSR)

    我尝试使用 crypto x509 包生成 CSR 但没有找到将 emailAddress 字段添加到其主题中的方法 根据文档证书申请 http golang org pkg crypto x509 CertificateRequest结构
  • 导入错误:无法导入名称线程

    这是我第一次学习Python 我继续尝试线程这篇博文 http www saltycrane com blog 2008 09 simplistic python thread example 问题是它似乎已经过时了 import time
  • Windows7上python3.5无法安装BeautifulSoup4

    我已经从下载了 beautifulsoup4 4 5 3 tar gzhttps www crummy com software BeautifulSoup bs4 download 4 5 https www crummy com sof
  • 如何从任何进程关闭 Windows 上的套接字(ipv4 和 ipv6)连接?

    如何在 Windows 上关闭 tcp v4 和 tcp v6 连接 我不想终止具有开放连接的整个进程 因为这显然会将其他人踢出该进程 我需要从一个单独的进程执行此操作 因此无法访问套接字句柄等 我正在使用 Windows API 来获取
  • 以编程方式最小化/恢复窗口,跳过动画效果

    我需要对窗口列表执行多项操作 最小化其中一些 恢复其他 以便立即在两组或多组窗口之间切换 这样做的问题是最小化和恢复窗口时可以看到的动画 整个过程看起来很糟糕 所有这些动画都进进出出 上下移动 但是 我无法禁用这些动画 因为这是针对其他计算
  • 更改desktop.ini不会在Windows中自动更新文件夹图标

    我使用此批处理脚本将所有文件夹和子文件夹的图标更改为位于文件夹中的 ico 文件 但是 资源管理器中的文件夹图标不会改变除非我手动重命名desktop ini将资源管理器中的文件更改为其他内容 然后返回desktop ini或者例如将字母更
  • 用于验证 IIS 设置的 Powershell 脚本

    是否可以使用 Power Shell 脚本获取 IIS 设置 我希望使用脚本获取 检查以下信息 检查 Windows 身份验证提供程序是否正确列出 协商 NTLM 检查是否启用了 Windows 身份验证 Windows 身份验证高级设置
  • 错误“binary.Write:无效类型”是什么意思?

    下面显示的代码 我创建了一个结构类型并希望将其编码为二进制 但它显示binary Write invalid type main Stu错误 我读过一些类似的代码 但我找不到为什么我的代码不起作用 type Stu struct Name
  • 如何解决内存碎片

    我们偶尔会遇到这样的问题 长时间运行的服务器进程 在 Windows Server 2003 上运行 由于内存分配失败而引发异常 我们怀疑这些分配由于内存碎片而失败 因此 我们一直在寻找一些可能对我们有帮助的替代内存分配机制 我希望有人能告
  • 检测计算机何时解锁 Windows

    我用过这个优秀的方法 https stackoverflow com questions 20733441 lock windows workstation using python 20733443锁定 Windows 计算机 那部分工作
  • 设置 Form.KeyPreview = true 的缺点?

    我想知道 Form KeyPreview 属性实际上有什么用处 它为什么存在以及将其设置为 true 会带来什么 风险 我想它一定有some负面影响 否则它根本不应该存在 或者至少默认情况下是正确的 EDIT 我很清楚what确实如此 我问
  • 需要 TensorFlow 依赖项。如何在 Windows 上运行 TensorFlow

    我有兴趣让 TensorFlow 在 Windows 上运行 但目前我意识到这是不可能的 因为某些依赖项无法在 Windows 上使用 例如巴泽尔 之所以出现这种需求 是因为据我目前了解 从 TensorFlow 访问 GPU 的唯一方法是
  • 在 Windows 上不使用 OpenSSL 从 pfx 文件或证书存储中提取私钥

    正如标题所示 我想在不使用 OpenSSL 或任何其他第三方工具的情况下导出我的私钥 如果我需要一个 cer文件或 pfx我可以通过 MMC 或 PowerShell 轻松导出这些文件pkiclient但我找不到获取私钥的方法 https
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用

随机推荐