在Go DLL Syscall中传递指向指针的指针。

huangapple go评论94阅读模式
英文:

Pass pointer to a pointer in Go DLL Syscall

问题

如果我有以下的C# DllImport,它导入了一个非C#的DLL,并且我想将其转换为Go语言,我应该如何操作?

    [DllImport("my.dll", EntryPoint = "Greet", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Greet(IntPtr name, ref IntPtr greetings);

我在解决如何传递指向指针的问题上遇到了困难,这对于greetings参数是必需的(我假设由于类型是ref IntPtr,我对C#并不熟悉)。DLL函数将填充我提供的指针所指向的内存,我将在随后的系统调用中使用它。以下是我目前的代码:

package main

import (
	"fmt"
	"syscall"
	"unsafe"
)

var (
	MyDll = syscall.MustLoadDLL("my.dll")
	greet = MyDll.MustFindProc("Greet")
)

func Greet(name string) error {
	nameP, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return err
	}
	// 我需要为greetings提供一个指向指针的指针。我该如何在这里分配一些内存,
	// 然后传递指向其指针的指针?我尝试了这个:创建一个具有零值的句柄,
	// 然后取其指针,然后将指向指针的指针作为第二个参数传递给Call,
	// 但是在调用之后,指针变为nil。
	handle := syscall.Handle(0)
	handleP := &handle
	r1, _, _ := greet.Call(uintptr(unsafe.Pointer(nameP)), uintptr(unsafe.Pointer(&handleP)))
	if r1 == 0 {
		return fmt.Errorf("there was an error")
	}
	return nil
}

我对任何建议都持开放态度,包括可能帮助我更好地理解这个syscall和unsafe的链接和资源。谢谢!

英文:

If I have the following C# DllImport which is importing a non-C# DLL and I want to port it over to Go, how would I go about doing that?

    [DllImport("my.dll", EntryPoint = "Greet", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Greet(IntPtr name, ref IntPtr greetings);

I've run into problems figuring out how to pass a pointer to a pointer which is needed for the greetings parameter (I assume since the type is ref IntPtr, I'm not that familiar at all with C#). The dll function will populate the memory pointed to by the pointer that I provide which I'll use in subsequent syscalls. Here's what I've got so far,

package main

import (
	"fmt"
	"syscall"
	"unsafe"
)

var (
	MyDll = syscall.MustLoadDLL("my.dll")
	greet = MyDll.MustFindProc("Greet")
)

func Greet(name string) error {
	nameP, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return err
	}
	// I need to provide a pointer to a pointer for greetings. How can I allocate some memory here
	// then pass a pointer to its pointer? I tried this: create a handle with a zero-value, then
	// take a pointer to it, then pass a pointer to the pointer as the second parameter to Call but
	// the pointer becomes nil after the Call.
	handle := syscall.Handle(0)
	handleP := &handle
	r1, _, _ := greet.Call(uintptr(unsafe.Pointer(nameP)), uintptr(unsafe.Pointer(&handleP)))
	if r1 == 0 {
		return fmt.Errorf("there was an error")
	}
	return nil
}

I'm open to any and all suggestions including links and resources that might help me get a better grasp on this syscall and unsafe stuff. Thanks!

答案1

得分: 2

首先,如果你能展示一下C#的Greet方法的使用方式,那会很有帮助。一个方法单独存在时很难理解,特别是当参数相当于void **(表示任何东西都可以传入)时。

简而言之,ref IntPtr可能只是一个**struct{},你不需要分配任何结构体。库会为你管理内存。你只需要给它一个指向“*MyStruct”的指针,这样它就可以将你的“*MyStruct”实际指向内部资源。

ref

C#中的ref关键字在文档中有很好的解释。基本上,它允许对任何类型进行引用传递。
在C#中,以下声明

void Increment(ref value) { ... }

int counter = 10;
Increment(ref counter);
// counter现在是11
Increment(counter); // 不会编译,必须使用'ref'传递
Increment(null); // 不会编译

等价于C++

void Increment(int& value) { ... }

int counter;
Increment(counter);
// counter现在是11
Increment(null); // 不会编译

引用不能为null。

IntPtr

IntPtr通常用于表示指针,并允许在本机和CLR(C#)程序之间进行互操作。

如果一个C程序有以下签名

void Increment(int* value);

一个C#程序可以以几种方式调用它

[DllImport("example.dll")]
static unsafe extern void Increment(int* value); // 这种方式允许传入null

unsafe {
    int counter = 10;
    Increment(&10);
}

,

[DllImport("example.dll")]
static extern void Increment(ref int value);

int counter = 10;
Increment(ref counter);

如果一个C程序有以下签名

void AllocateStruct(struct MyStruct** ppStruct);
void IncrementStruct(struct MyStruct* pStruct);

那么

[DllImport("example.dll")]
static extern void AllocateStruct(ref IntPtr ppStruct);
// static unsafe extern void AllocateStruct(MyStruct** ppStruct)

[DllImport("example.dll")]
static extern void IncrementStruct(IntPtr pStruct);
// static unsafe extern void IncrementStruct(MyStruct* pStruct)

IntPtr pMyStruct;
AllocateStruct(ref pMyStruct);
IncrementStruct(pMyStruct);
// 释放My Struct

// 如果你需要进入结构体内部
// MyStruct myStruct = Marshal.StructureToPtr<MyStruct>(pMyStruct)
// 通常你不需要(也不应该)直接操作结构体,所以将其保持为IntPtr是完全可以接受的。

从上面的示例中,你可以看到MyStruct更像是一个令牌/引用而不是其他任何东西。ref IntPtr允许你传递一个引用到位置,你将在库为你分配之后用来存储你的令牌/引用。然后,所有其他方法通常只使用引用IntPtr来对其进行后续操作。基本上是面向对象编程,但没有类。

“真实”生活中使用ref IntPtr的示例

这个示例有点快速而粗糙,错误处理还有很多需要改进的地方。
它展示了调用相同的GetSecurityInfoLookupAccountSid Win32库函数的CC#Go版本。

我能找到的ref IntPtr的唯一真实用例是type**/void**,这样库就可以为你分配内存,或者给你一个它已经分配的内存的指针。

C

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include "accctrl.h"
#include "aclapi.h"
#pragma comment(lib, "advapi32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    // --- 获取执行程序的文件路径和句柄 -----

    LPTSTR executablePath = argv[0];
    _tprintf(TEXT("Opening File %s\n"), executablePath);

    HANDLE hFile = CreateFile(
        executablePath,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) return EXIT_FAILURE;

    // -------------------------------------------------

    // --------- 获取文件的所有者SID ---------

    PSID pSidOwner = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;

    DWORD dwRtnCode = GetSecurityInfo(
        hFile,
        SE_FILE_OBJECT,
        OWNER_SECURITY_INFORMATION,
        &pSidOwner,
        NULL,
        NULL,
        NULL,
        &pSD);

    if (dwRtnCode != ERROR_SUCCESS) return EXIT_FAILURE;

    // -------------------------------------------------

    // -------

    TCHAR AcctName[MAX_PATH];
    DWORD dwAcctName = MAX_PATH;

    TCHAR DomainName[MAX_PATH];
    DWORD dwDomainName = MAX_PATH;

    SID_NAME_USE eUse = SidTypeUnknown;

    BOOL bRtnBool = LookupAccountSid(
        NULL,           // 本地计算机
        pSidOwner,
        &AcctName,
        &dwAcctName,
        DomainName,
        &dwDomainName,
        &eUse);

    if (bRtnBool == FALSE) return EXIT_FAILURE;

    _tprintf(TEXT("Account Owner = %s\n"), AcctName);
    _tprintf(TEXT("Account Owner's Domain = %s\n"), DomainName);

    return 0;
}

C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

public class Example
{
    [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
    static extern uint GetSecurityInfo(
        IntPtr handle,
        uint ObjectType,
        uint SecurityInfo,
        ref IntPtr ppsidOwner, // <--- HERE
        IntPtr ppsidGroup, // 有点技巧性(在安全的C#中,你必须“传递引用”,在C中,你可以传递指向指针的指针或null)
        IntPtr ppDacl,
        IntPtr ppSacl,
        ref IntPtr ppSecurityDescriptor // <--- HERE
    );

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool LookupAccountSid(
        string lpSystemName,
        IntPtr Sid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out uint peUse);

    const uint ERROR_SUCCESS = 0;
    const uint OWNER_SECURITY_INFORMATION = 0x00000001;
    const uint SE_FILE_OBJECT = 1;

    public static void Main()
    {

        // 获取执行程序的文件路径和句柄
        string executablePath = Environment.GetCommandLineArgs().GetValue(0).ToString();
        IntPtr hFile = File.Open(executablePath, FileMode.Open, FileAccess.Read, FileShare.Read)
            .SafeFileHandle.DangerousGetHandle();

        IntPtr pSidOwner = IntPtr.Zero; // 一些你不应该分配或修改的内部结构体(充当令牌)
        IntPtr pSD = IntPtr.Zero; // 一些你不应该分配或修改的内部结构体(充当令牌)

        // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

        uint dwRtnCode = GetSecurityInfo(
            hFile,
            SE_FILE_OBJECT,
            OWNER_SECURITY_INFORMATION,
            ref pSidOwner, // <--- HERE
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            ref pSD // <--- HERE
        );

        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

        if (dwRtnCode != ERROR_SUCCESS) throw new InvalidOperationException("GetSecurityInfo Failed");

        StringBuilder name = new StringBuilder(50);
        uint cchName = (uint)name.Capacity;
        StringBuilder domainName = new StringBuilder(50);
        uint cchDomainName = (uint)domainName.Capacity;
        uint sidUse;

        LookupAccountSid(
            null,
            pSidOwner,
            name,
            ref cchName,
            domainName,
            ref cchDomainName,
            out sidUse);

        Console.WriteLine("Account Owner = {0}", name);
        Console.WriteLine("Account Owner's Domain = {0}", domainName);

        // 请在完成后释放pSD
    }


}

Go

这是我写的第二个Go程序,所以可能有一些明显的错误(除了缺少错误检查之外)

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "os"
)

var (
    advapi32, _ = syscall.LoadLibrary("advapi32.dll")
    kernel32, _ = syscall.LoadLibrary("kernel32.dll")
    createFileW, _ = syscall.GetProcAddress(kernel32, "CreateFileW")
    getSecurityInfo, _ = syscall.GetProcAddress(advapi32, "GetSecurityInfo")
    lookupAccountSidW, _ = syscall.GetProcAddress(advapi32, "LookupAccountSidW")
)

type SE_OBJECT_TYPE uint32
const (SE_FILE_OBJECT = 1)

type SECURITY_INFORMATION uint32
const (OWNER_SECURITY_INFORMATION = 0x00000001)

const (
    GENERIC_READ    = 0x80000000
    FILE_SHARE_READ = 0x00000001
    OPEN_EXISTING   = 0x00000003
    FILE_ATTRIBUTE_NORMAL   = 0x00000080
)

type Handle uintptr

func CreateFile(
    name string,
    access uint32,
    mode uint32,
    sa *uint, // *SecurityAttributes,
    createmode uint32,
    attrs uint32,
    templatefile *uint,
) (handle Handle, err error) {

    utf16name, _ := syscall.UTF16PtrFromString(name)

    r0, _, _ := syscall.Syscall9(
        uintptr(createFileW), 7, 
        uintptr(unsafe.Pointer(utf16name)), 
        uintptr(access), 
        uintptr(mode), 
        uintptr(unsafe.Pointer(sa)), 
        uintptr(createmode), 
        uintptr(attrs), 
        uintptr(unsafe.Pointer(templatefile)),
        0, 0)
    handle = Handle(r0)
    return
}

func GetSecurityInfo(
    handle Handle, 
    objectType SE_OBJECT_TYPE, 
    securityInformation SECURITY_INFORMATION, 
    owner **struct{}, 
    group **struct{}, 
    dacl **struct{}, 
    sacl **struct{}, 
    sd **struct{}, //**SECURITY_DESCRIPTOR,
) (ret error) {
    r0, _, _ := syscall.Syscall9(
        uintptr(getSecurityInfo), 8, 
        uintptr(handle), 
        uintptr(objectType), 
        uintptr(securityInformation), 
        uintptr(unsafe.Pointer(owner)), 
        uintptr(unsafe.Pointer(group)), 
        uintptr(unsafe.Pointer(dacl)), 
        uintptr(unsafe.Pointer(sacl)), 
        uintptr(unsafe.Pointer(sd)), 
        0)
    if r0 != 0 {
        ret = syscall.Errno(r0)
    }
    return
}

func LookupAccountSid(
    systemName *uint16,
    sid *struct{}, // *SID,
    name *uint16,
    nameLen *uint32, 
    refdDomainName *uint16,
    refdDomainNameLen *uint32,
    use *uint32,
) (err error) {
    r, _, e := syscall.Syscall9(
        uintptr(lookupAccountSidW), 7, 
        uintptr(unsafe.Pointer(systemName)), 
        uintptr(unsafe.Pointer(sid)),
        uintptr(unsafe.Pointer(name)),
        uintptr(unsafe.Pointer(nameLen)),
        uintptr(unsafe.Pointer(refdDomainName)),
        uintptr(unsafe.Pointer(refdDomainNameLen)),
        uintptr(unsafe.Pointer(use)),
        0, 0)
    if r == 0 {
        err = e
    }
    return
}

func main() {
    defer syscall.FreeLibrary(advapi32)
    defer syscall.FreeLibrary(kernel32)

    // 获取执行程序的文件路径和句柄
    var hFile, _ = CreateFile(os.Args[0], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nil);

    // defer LocalFree(Handle(unsafe.Pointer(pSD))) // 请在完成后释放pSD

    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

    var pSD *struct{} //*SECURITY_DESCRIPTOR
    var pSidOwner *struct{}
    GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, nil, nil, nil, &pSD)

    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    nameLen := uint32(50)
    name := make([]uint16, nameLen)

    domainLen := uint32(50)
    domainName := make([]uint16, domainLen)

    var sidUse uint32

    LookupAccountSid(nil, pSidOwner, &name[0], &nameLen, &domainName[0], &domainLen, &sidUse)

    var n = syscall.UTF16ToString(name)
    var dn = syscall.UTF16ToString(domainName)

    fmt.Printf("Account Owner = %s\n", n)
    fmt.Printf("Account Owner's Domain = %s\n", dn)
}

以上示例展示了如何调用相同的GetSecurityInfoLookupAccountSid Win32库函数的CC#Go版本。

英文:

Firstly, it would help if you could show how the C# Greet method is used. A method in isolation its quite hard to understand especially when the parameter is the equivalent of void ** which means anything can go in.

TL;DR
ref IntPtr is probably just a **struct{} where you don't have to allocate any struct. The library will simply manage the memory for you. You just need to give it a pointer to "*MyStruct" so that it can change you "*MyStruct" to actually point to the internal resource.

REF

The C# ref keyword is quite well explained in the docs. Basically it allows a pass by reference for any type.
The following declaration in C#

void Increment(ref value) { ... }

int counter = 10;
Increment(ref counter);
// counter is now 11
Increment(counter); // won&#39;t compile must be passed with &#39;ref&#39;
Increment(null); // won&#39;t compile

is equivalent to C++

void Increment(int&amp; value) { ... }

int counter;
Increment(counter);
// counter is now 11
Increment(null); // won&#39;t compile

A reference which should not be null.

IntPtr

IntPtr is usually used to represent pointers and allows for interop between native and CLR (C#) programs.

If a C program has the following signature

void Increment(int* value);

a C# program could call it in one of a few ways

[DllImport(&quot;example.dll&quot;)]
static unsafe extern void Increment(int* value); // this way allows null to be passed in

unsafe {
    int counter = 10;
    Increment(&amp;10);
}

,

[DllImport(&quot;example.dll&quot;)]
static extern void Increment(ref int value);

int counter = 10;
Increment(ref counter);

If a C program has the following signature

void AllocateStruct(struct MyStruct** ppStruct);
void IncrementStruct(struct MyStruct* pStruct);

then

[DllImport(&quot;example.dll&quot;)]
static extern void AllocateStruct(ref IntPtr ppStruct);
// static unsafe extern void AllocateStruct(MyStruct** ppStruct)

[DllImport(&quot;example.dll&quot;)]
static extern void IncrementStruct(IntPtr pStruct);
// static unsafe extern void IncrementStruct(MyStruct* pStruct);

IntPtr pMyStruct;
AllocateStruct(ref pMyStruct);
IncrementStruct(pMyStruct);
// Free My Struct

// If you need to get inside the struct then
// MyStruct myStruct = Marshal.StructureToPtr&lt;MyStruct&gt;(pMyStruct)
// Often you don&#39;t (need to) or (should not) manipulate the struct directly so keeping it as IntPtr is perfectly acceptable.

From the example above you can see MyStruct is more of a token/reference than anything else. ref IntPtr allows you to pass a reference to the location which you will use to store your token/reference after the library allocates it on your behalf. Then all the other methods will usually just use the reference IntPtr to perform subsequent manipulations on it. Basically Object Orientated Programming without classes.

"Real" Life Example with ref IntPtr

It is a bit quick and dirty and error handling leave a lot to be desired.
It shows the C, C# and Go versions which call the same GetSecurityInfo
and LookupAccountSid Win32 library functions.

The only real use case I can find for ref IntPtr is type**/void** so that the library can allocate the memory for you or give you a pointer to memory it has already allocated.

C

#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;
#include &lt;tchar.h&gt;
#include &quot;accctrl.h&quot;
#include &quot;aclapi.h&quot;
#pragma comment(lib, &quot;advapi32.lib&quot;)

int _tmain(int argc, _TCHAR* argv[])
{
    // --- get the executing program&#39;s file path and handle -----

    LPTSTR executablePath = argv[0];
    _tprintf(TEXT(&quot;Opening File %s\n&quot;), executablePath);

    HANDLE hFile = CreateFile(
        executablePath,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) return EXIT_FAILURE;

    // -------------------------------------------------

    // --------- Get the owner SID of the file ---------

    PSID pSidOwner = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;

    DWORD dwRtnCode = GetSecurityInfo(
        hFile,
        SE_FILE_OBJECT,
        OWNER_SECURITY_INFORMATION,
        &amp;pSidOwner,
        NULL,
        NULL,
        NULL,
        &amp;pSD);

    if (dwRtnCode != ERROR_SUCCESS) return EXIT_FAILURE;

    // -------------------------------------------------

    // ------- 

    TCHAR AcctName[MAX_PATH];
    DWORD dwAcctName = MAX_PATH;

    TCHAR DomainName[MAX_PATH];
    DWORD dwDomainName = MAX_PATH;

    SID_NAME_USE eUse = SidTypeUnknown;

    BOOL bRtnBool = LookupAccountSid(
        NULL,           // local computer
        pSidOwner,
        &amp;AcctName,
        &amp;dwAcctName,
        DomainName,
        &amp;dwDomainName,
        &amp;eUse);

    if (bRtnBool == FALSE) return EXIT_FAILURE;

    _tprintf(TEXT(&quot;Account Owner = %s\n&quot;), AcctName);
    _tprintf(TEXT(&quot;Account Owner&#39;s Domain = %s\n&quot;), DomainName);

    return 0;
}

C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

public class Example
{
    [DllImport(&quot;advapi32.dll&quot;, SetLastError = true, CallingConvention = CallingConvention.Winapi)]
    static extern uint GetSecurityInfo(
        IntPtr handle,
        uint ObjectType,
        uint SecurityInfo,
        ref IntPtr ppsidOwner, // &lt;-- HERE
        IntPtr ppsidGroup, // bit hacky (in safe C# you must &quot;pass a reference&quot; in C you can pass a pointer to a pointer or null)
        IntPtr ppDacl,
        IntPtr ppSacl,
        ref IntPtr ppSecurityDescriptor // &lt;-- HERE
    );

    [DllImport(&quot;advapi32.dll&quot;, CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool LookupAccountSid(
        string lpSystemName,
        IntPtr Sid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out uint peUse);

    const uint ERROR_SUCCESS = 0;
    const uint OWNER_SECURITY_INFORMATION = 0x00000001;
    const uint SE_FILE_OBJECT = 1;

    public static void Main()
    {

        // get the executing program&#39;s file path and handle
        string executablePath = Environment.GetCommandLineArgs().GetValue(0).ToString();
        IntPtr hFile = File.Open(executablePath, FileMode.Open, FileAccess.Read, FileShare.Read)
            .SafeFileHandle.DangerousGetHandle();

        IntPtr pSidOwner = IntPtr.Zero; // some internal struct you shouldn&#39;t allocate or modify (acts like a token)
        IntPtr pSD = IntPtr.Zero; // some internal struct you shouldn&#39;t allocate or modify (acts like a token)

        // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

        uint dwRtnCode = GetSecurityInfo(
            hFile,
            SE_FILE_OBJECT,
            OWNER_SECURITY_INFORMATION,
            ref pSidOwner, // &lt;-- HERE
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            ref pSD // &lt;-- HERE
        );

        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

        if (dwRtnCode != ERROR_SUCCESS) throw new InvalidOperationException(&quot;GetSecurityInfo Failed&quot;);

        StringBuilder name = new StringBuilder(50);
        uint cchName = (uint)name.Capacity;
        StringBuilder domainName = new StringBuilder(50);
        uint cchDomainName = (uint)domainName.Capacity;
        uint sidUse;

        LookupAccountSid(
            null,
            pSidOwner,
            name,
            ref cchName,
            domainName,
            ref cchDomainName,
            out sidUse);

        Console.WriteLine(&quot;Account Owner = {0}&quot;, name);
        Console.WriteLine(&quot;Account Owner&#39;s Domain = {0}&quot;, domainName);

        // PLEASE FREE pSD once done 
    }


}

Go

my second Go program I have ever written so there are probably some blaring mistakes (other than the lack of error checking)

package main

import (
	&quot;fmt&quot;
	&quot;syscall&quot;
	&quot;unsafe&quot;
	&quot;os&quot;
)

var (
	advapi32, _ = syscall.LoadLibrary(&quot;advapi32.dll&quot;)
	kernel32, _ = syscall.LoadLibrary(&quot;kernel32.dll&quot;)
	createFileW, _ = syscall.GetProcAddress(kernel32, &quot;CreateFileW&quot;)
	getSecurityInfo, _ = syscall.GetProcAddress(advapi32, &quot;GetSecurityInfo&quot;)
	lookupAccountSidW, _ = syscall.GetProcAddress(advapi32, &quot;LookupAccountSidW&quot;)
)

type SE_OBJECT_TYPE uint32
const (SE_FILE_OBJECT = 1)

type SECURITY_INFORMATION uint32
const (OWNER_SECURITY_INFORMATION = 0x00000001)

const (
	GENERIC_READ    = 0x80000000
	FILE_SHARE_READ = 0x00000001
	OPEN_EXISTING   = 0x00000003
	FILE_ATTRIBUTE_NORMAL   = 0x00000080
)

type Handle uintptr

func CreateFile(
	name string,
	access uint32,
	mode uint32,
	sa *uint, // *SecurityAttributes,
	createmode uint32,
	attrs uint32,
	templatefile *uint,
) (handle Handle, err error) {

	utf16name, _ := syscall.UTF16PtrFromString(name)

	r0, _, _ := syscall.Syscall9(
		uintptr(createFileW), 7, 
		uintptr(unsafe.Pointer(utf16name)), 
		uintptr(access), 
		uintptr(mode), 
		uintptr(unsafe.Pointer(sa)), 
		uintptr(createmode), 
		uintptr(attrs), 
		uintptr(unsafe.Pointer(templatefile)),
		0, 0)
	handle = Handle(r0)
	return
}

func GetSecurityInfo(
	handle Handle, 
	objectType SE_OBJECT_TYPE, 
	securityInformation SECURITY_INFORMATION, 
	owner **struct{}, 
	group **struct{}, 
	dacl **struct{}, 
	sacl **struct{}, 
	sd **struct{}, //**SECURITY_DESCRIPTOR,
) (ret error) {
	r0, _, _ := syscall.Syscall9(
		uintptr(getSecurityInfo), 8, 
		uintptr(handle), 
		uintptr(objectType), 
		uintptr(securityInformation), 
		uintptr(unsafe.Pointer(owner)), 
		uintptr(unsafe.Pointer(group)), 
		uintptr(unsafe.Pointer(dacl)), 
		uintptr(unsafe.Pointer(sacl)), 
		uintptr(unsafe.Pointer(sd)), 
		0)
	if r0 != 0 {
		ret = syscall.Errno(r0)
	}
	return
}

func LookupAccountSid(
	systemName *uint16,
	sid *struct{}, // *SID,
	name *uint16,
	nameLen *uint32, 
	refdDomainName *uint16,
	refdDomainNameLen *uint32,
	use *uint32,
) (err error) {
	r, _, e := syscall.Syscall9(
		uintptr(lookupAccountSidW), 7, 
		uintptr(unsafe.Pointer(systemName)), 
		uintptr(unsafe.Pointer(sid)),
		uintptr(unsafe.Pointer(name)),
		uintptr(unsafe.Pointer(nameLen)),
		uintptr(unsafe.Pointer(refdDomainName)),
		uintptr(unsafe.Pointer(refdDomainNameLen)),
		uintptr(unsafe.Pointer(use)),
		0, 0)
	if r == 0 {
		err = e
	}
	return
}

func main() {
	defer syscall.FreeLibrary(advapi32)
    defer syscall.FreeLibrary(kernel32)

	// get the executing program&#39;s file path and handle
	var hFile, _ = CreateFile(os.Args[0], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nil);

	// defer LocalFree(Handle(unsafe.Pointer(pSD))) // PLEASE FREE pSD once done 

	// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

	var pSD *struct{} //*SECURITY_DESCRIPTOR
	var pSidOwner *struct{}
	GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &amp;pSidOwner, nil, nil, nil, &amp;pSD)

	// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

	nameLen := uint32(50)
	name := make([]uint16, nameLen)

	domainLen := uint32(50)
	domainName := make([]uint16, domainLen)

	var sidUse uint32

	LookupAccountSid(nil, pSidOwner, &amp;name[0], &amp;nameLen, &amp;domainName[0], &amp;domainLen, &amp;sidUse)

	var n = syscall.UTF16ToString(name)
	var dn = syscall.UTF16ToString(domainName)

	fmt.Printf(&quot;Account Owner = %s\n&quot;, n)
	fmt.Printf(&quot;Account Owner&#39;s Domain = %s\n&quot;, dn)
}

huangapple
  • 本文由 发表于 2021年9月4日 02:54:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/69049497.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定