Search code examples
gocgo

Pass structure with pointers to go memory in C code


I'm relatively new in Go and studying how to operate with native API. Here is a particular example, need to verify that binary file is signed. For that, I want to use WinVerifyTrust windows API.

The problem: WinVerifyTrust supplies WINTRUST_DATA structure which contains a pointer to WINTRUST_FILE_INFO which in order contains a pointer to a string =)

As we know it is forbidden to pass a pointer to the memory which contains pointers to other go memory in C or syscall.

But I just need to do that. What needs to be done?

Here is a package which wraps function above https://github.com/itchio/ox/blob/master/winox/verifytrust_windows.go they do that in a forbidden way

winTrustData.FileOrCatalogOrBlobOrSgnrOrCert = uintptr(unsafe.Pointer(fileData))

trustErr := syscallex.WinVerifyTrust(syscall.Handle(0), &policyGUID, winTrustData)

it is not allowed to store uintptr to go object in the intermediate variable before passing to syscall

What I want to do os next:

winTrustFileInfoMemory := C.malloc(unsafe.Sizeof(windows.WinTrustFileInfo{}))
if winTrustFileInfoMemory == nil {
    return errors.New("out of memory")
}
defer C.free(winTrustFileInfoMemory)

winTrustFileInfo := (*windows.WinTrustFileInfo)(unsafe.Pointer(winTrustFileInfoMemory))
winTrustFileInfo.Size = uint32(unsafe.Sizeof(windows.WinTrustFileInfo{}))
....
winTrustData.FileOrCatalogOrBlobOrSgnrOrCert = unsafe.Pointer(winTrustFileInfoMemory)

but it looks really ugly, do we have an ability to somehow pin the memory or something like it?


Solution

  • Per the syscall docs:

    Deprecated: this package is locked down. Callers should use the corresponding package in the golang.org/x/sys repository instead.

    In your case, the corresponding function would be

    func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error)
    

    Note the trailing "Ex" in the function name. Internally, this calls:

    r0, _, _ := syscall.Syscall(procWinVerifyTrustEx.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data)))
    

    So really, the pointer rule is now being broken by sys/windows instead of you. Sorry there's no better idea. see here for more.