Go syscall call windows - go

Im currently trying to use user32.dll EnumWindows on Go but seems to not be working
var(
user32 = syscall.NewLazyDLL("user32.dll")
procEnumWindows = user32.NewProc("EnumWindows")
)
func EnumWindows() int {
ret, _, _ := procEnumWindows.Call(
syscall.NewCallback(enumWindowsProc),
uintptr(0),
)
return int(ret)
}
func enumWindowsProc(hwnd syscall.Handle, lparam uintptr) bool {
return true
}
Calling EnumWindows will give the following error:
panic: compileCallback: output parameter size is wrong
Im not sure how should I use the syscall package... I cant seem to find enough documentation on it
On the MSDN doc page it says that the callback should return a BOOL and thats what I am doing?

BOOL in WinAPI is declared as typedef int BOOL. So it doesn't match Go's bool. Specifications doesn't even mention what's the size it has. It's probably 1 byte but it doesn't say it. You should use int32 instead.

Related

Reading ETW providers using go

I'm trying to access the EnumerateTraceGuids function from Advapi32.dll in go.
I'm at the very early stage and still trying to decipher what is it that I must do. I have the following code that keeps giving me Error: 87, meaning ERROR_INVALID_PARAMETER.
I've used this file as a starting point though it's only writing and not reading :
https://github.com/moby/moby/blob/master/daemon/logger/etwlogs/etwlogs_windows.go
Official documentation for the function I'm trying to call is here :
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363713(v=vs.85).aspx
It requires GuidPropertiesArray [in, out] An array of pointers to TRACE_GUID_PROPERTIES structures. This structure is the following (https://msdn.microsoft.com/en-us/library/windows/desktop/aa364143(v=vs.85).aspx)
typedef struct _TRACE_GUID_PROPERTIES {
GUID Guid;
ULONG GuidType;
ULONG LoggerId;
ULONG EnableLevel;
ULONG EnableFlags;
BOOLEAN IsEnable;
} TRACE_GUID_PROPERTIES, *PTRACE_GUID_PROPERTIES;
I have the following code to try and do this :
package main
import (
"errors"
"fmt"
"syscall"
"unsafe"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
const (
win32CallSuccess = 0
MaxProv = 50
nbProviders = 50
)
var (
modAdvapi32 = windows.NewLazySystemDLL("Advapi32.dll")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type ulong int32
type TRACE_GUID_PROPERTIES struct {
Guid syscall.GUID
GuidType ulong
LoggerId ulong
EnableLevel ulong
EnableFlags ulong
IsEnable bool
}
func callEnumerateTraceGuids() error {
GuidPropertiesArray:= make([]TRACE_GUID_PROPERTIES, 1)
ptr := &GuidPropertiesArray[0]
ret, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(&ptr)), MaxProv, nbProviders)
if ret != win32CallSuccess {
errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
logrus.Error(errorMessage)
return errors.New(errorMessage)
}
return nil
}
func main() {
callEnumerateTraceGuids()
}
At this point I'm not sure what is it that I must do. I've tried a lot of variation of initializing the array without success.
Hoping someone can point me in the right direction.
Thanks !
Edit : Changed code based on comments but still getting the same error.
PS : This is my first time posting to stackoverflow and I've already been told that I'm lazy less than 12 hours after posting my question (yay!) so not sure I'm asking this right...I am not too familiar with go and never called windows DLL from go before and since I keep hitting that ERROR_INVALID_PARAMETER I thought of reaching out to try and pass this first wall to be able to grasp some concepts at the same time. Hope this helps understands my request (ie. I come in peace).
OK, I had a bit of free time and an access to a Windows XP box,
so I've decided to dust off my Windows programming skills
and wrote a working solution:
package main
import (
"golang.org/x/sys/windows"
"log"
"syscall"
"unsafe"
)
var (
modAdvapi32 = windows.NewLazySystemDLL("advapi32")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type traceGuidProperties struct {
guid syscall.GUID
guidType uint32
loggerId uint32
enableLevel uint32
enableFlags uint32
isEnable uint32
}
func enumerateTraceGuids(ptr **traceGuidProperties, count uint32, out *uint32) error {
rc, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(ptr)),
uintptr(count), uintptr(unsafe.Pointer(out)))
if rc != 0 {
return syscall.Errno(rc)
}
return nil
}
func enumTraceGuids() ([]*traceGuidProperties, error) {
var errMoreData = syscall.Errno(234)
var (
dummyProps traceGuidProperties
dummyPtr = &dummyProps
count uint32
)
err := enumerateTraceGuids(&dummyPtr, 0, &count)
if err != errMoreData {
return nil, err
}
items := make([]*traceGuidProperties, count)
for i := range items {
items[i] = new(traceGuidProperties)
}
for {
err = enumerateTraceGuids(&items[0], count, &count)
if err == nil {
break
}
if err != errMoreData {
return nil, err
}
for i := 0; i < int(count)-len(items); i++ {
items = append(items, new(traceGuidProperties))
}
}
return items[:count], nil
}
func main() {
log.SetFlags(0)
data, err := enumTraceGuids()
if err != nil {
log.Fatal(err)
}
log.Printf("len(data)=%d\n", len(data))
for i := range data {
log.Println(*(data[i]))
}
}
The key points:
I was wrong when I told you that
«you … should allocate an array of structs (not pointers)»—in fact
EnumerateTraceGuids indeed expects an array of pointers.
As hinted here,
there are two subtleties with how EnumerateTraceGuids works:
Contrary to what its documentation states,
it actually supports being called with its PropertyArrayCount
parameter set to 0, in which case it's expected to return ERROR_MORE_DATA
while having set GuidCount to the number of elements of the input
array required for the (next) call to complete successfully.
IOW, that way we know how many trace GUIDs the system currently
"knows about".
Still, even in this case, the function performs validity check
on the input array (see below).
As it turns out, the function expects an array of pointers to
TRACE_GUID_PROPERTIES blocks allocated by you.
In other words, if it says you it knows about 10 trace GUIDs,
you have to allocate 10 values of type TRACE_GUID_PROPERTIES,
then make an array of 10 pointers to those values and pass a pointer
to the 1st element of that array to the function.
Notice that there's an inherent race between changes occuring
in the system (those traces added or removed for any number of reasons)
and the calls to EnumerateTraceGuids.
This means if the first call to this function told you it "knows"
about 10 trace GUIDs, on the next call it may turn out
there's already 20 trace GUIDs, or 5 GUIDs
(or any other number of them FWIW).
So we account for both of these possibilities in the following way:
First we do a call with a pointer to a single (but valid)
TRACE_GUID_PROPERTIES value, allocated statically
(hence the function "sees" what looks like an array of a single element),
while telling the function the input "array" has zero elements.
We expect the function to fail with ERROR_MORE_DATA
and put the actual number of trace GUIDs it "knows" about into the variable
we've supplied it a pointer to.
We allocate that much TRACE_GUID_PROPERTIES memory blocks
the function indicated on the first call.
For this, we use the new() built-in function which behaves somewhat
like malloc() in the standard C library—it allocates the memory for
a value of the specified type and returns a pointer to the allocated
memory block.
We create an array of pointers to these allocated memory blocks
and call EnumerateTraceGuids again.
If it succeeds, we handle the possibility it returned less
elements than we've allocated, and reslice our slice.
If it fails with ERROR_MORE_DATA, we extend our slice with
whatever the number of elements is needed (allocating memory for their
TRACE_GUID_PROPERTIES blocks first), and try calling the function again.
The "magic number" 234 is the actual code for the ERROR_MORE_DATA value.
Sorry for the initial confusion.

Handling LPTSTR in golang with lxn/win

I have this piece of code which runs without returning err but simply doesn't do its job because it doesn't return the expected value.
The idea is to use SHGetSpecialFolderPath in order to retrieve the path to the Windows directory (C:\Windows for example). This api call has the following signature:
BOOL SHGetSpecialFolderPath(
HWND hwndOwner,
_Out_ LPTSTR lpszPath,
_In_ int csidl,
_In_ BOOL fCreate );
I know it is deprecated, but still available even on current Windows versions. I have to use this API because I need to support Windows versions older than Windows 7 (I know that these are old or even end of life)
This is the piece of code:
target := "XXX...XXX" // hard coded string with more than 600 characters
buffer, err := syscall.UTF16PtrFromString(target)
if err != nil {
fmt.Println("conversion of string:", err)
}
result := win.SHGetSpecialFolderPath(0, buffer, win.CSIDL_WINDOWS, false)
if err != nil {
fmt.Println("result of get folder:", err)
}
fmt.Println("folder retrieved ok: ", result)
fmt.Println("folder: ", target)
}
None of the err is set, the API call returns true but the string is unchanged:
folder retrieved ok: true
folder: XXX...XXX
The result is the same on Windows 10 x64 and on my testing VM running Windows XP SP3 (I know that XP is inherently unsafe)
I have seen examples how to use LPTRSTR with unsafe and uintptr here on SO and other sites but none of them compile on my version of golang (which is go version go1.10.1 windows/amd64, I compiled with GOARCH=386)
Approach the problem in a logical, systematic fashion.
Carefully read the Microsoft documentation for the function.
SHGetSpecialFolderPath function
Carefully read the lxn/win package documentation for the function.
package win
import "github.com/lxn/win"
func SHGetSpecialFolderPath
func SHGetSpecialFolderPath(hwndOwner HWND, lpszPath *uint16, csidl CSIDL, fCreate bool) bool
Now, using the documentation, implement the function call in Go. Go Unicode strings are UTF-8 encoded. Windows Unicode strings are UTF-16 encoded.
package main
import (
"fmt"
"syscall"
"github.com/lxn/win"
)
func main() {
buf := make([]uint16, win.MAX_PATH)
rv := win.SHGetSpecialFolderPath(win.HWND(0), &buf[0], win.CSIDL_WINDOWS, false)
fmt.Println(rv)
path := syscall.UTF16ToString(buf)
fmt.Println(path)
}
Output:
true
C:\Windows

Windows callback function in Golang

I wanna make push subscription to Windows Event Log in Golang
How exactly should I pass a callback function?
EVT_SUBSCRIBE_CALLBACK is the pointer of function, like
typedef DWORD ( WINAPI *EVT_SUBSCRIBE_CALLBACK)(
EVT_SUBSCRIBE_NOTIFY_ACTION Action,
PVOID UserContext,
EVT_HANDLE Event
);
So, my variant looks like this:
func logCallback() syscall.Handle {
cb := func(_ uintptr, _ uintptr, _ uintptr) uint64 {
fmt.Printf("callback called %v", data)
return 0
}
ptr := syscall.NewCallback(cb)
return syscall.Handle(ptr) // type syscall.Handle uintptr
}
I get successfully subscribed handler with no errors, but it still doesn't work.
Any ideas why? Where should I look first?
When using syscall make sure the to include import "C" at the top of your file. Glad it helped you.

golang, call GetVolumeInformation winapi function

Tries to call GetVolumeInformation function from golang. Want to get volume name.
Use spec's of api:
BOOL WINAPI GetVolumeInformation(
_In_opt_ LPCTSTR lpRootPathName,
_Out_opt_ LPTSTR lpVolumeNameBuffer,
_In_ DWORD nVolumeNameSize,
_Out_opt_ LPDWORD lpVolumeSerialNumber,
_Out_opt_ LPDWORD lpMaximumComponentLength,
_Out_opt_ LPDWORD lpFileSystemFlags,
_Out_opt_ LPTSTR lpFileSystemNameBuffer,
_In_ DWORD nFileSystemNameSize
);
Use code:
// test
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var lpRootPathName = "C:\\"
var lpVolumeNameBuffer string
var nVolumeNameSize uint64
var lpVolumeSerialNumber uint64
var lpMaximumComponentLength uint64
var lpFileSystemFlags uint64
var lpFileSystemNameBuffer string
var nFileSystemNameSize uint32
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpRootPathName))),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&nVolumeNameSize)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer)),
uintptr(unsafe.Pointer(&nFileSystemNameSize)),
0)
fmt.Println(ret, callErr, lpVolumeNameBuffer)
}
... and finally have error :(
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x456b11]
Don't understand and google cant'd help with calling winapi functions and returng string as result.
Thank's.
Package unsafe
Package unsafe contains operations that step around the type safety of
Go programs.
type Pointer
type Pointer *ArbitraryType
Pointer represents a pointer to an arbitrary type. There are four
special operations available for type Pointer that are not available
for other types.
1) A pointer value of any type can be converted to a Pointer.
2) A Pointer can be converted to a pointer value of any type.
3) A uintptr can be converted to a Pointer.
4) A Pointer can be converted to a uintptr.
Pointer therefore allows a program to defeat the type system and read
and write arbitrary memory. It should be used with extreme care.
You failed to heed the warning that unsafe.Pointer "should be used with extreme care."
Try this:
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var RootPathName = `C:\`
var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
var nVolumeNameSize = uint32(len(VolumeNameBuffer))
var VolumeSerialNumber uint32
var MaximumComponentLength uint32
var FileSystemFlags uint32
var FileSystemNameBuffer = make([]uint16, 255)
var nFileSystemNameSize uint32 = syscall.MAX_PATH + 1
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(RootPathName))),
uintptr(unsafe.Pointer(&VolumeNameBuffer[0])),
uintptr(nVolumeNameSize),
uintptr(unsafe.Pointer(&VolumeSerialNumber)),
uintptr(unsafe.Pointer(&MaximumComponentLength)),
uintptr(unsafe.Pointer(&FileSystemFlags)),
uintptr(unsafe.Pointer(&FileSystemNameBuffer[0])),
uintptr(nFileSystemNameSize),
0)
fmt.Println(ret, callErr, syscall.UTF16ToString(VolumeNameBuffer))
}
I don't know the exact problem you are having but I think it is likely because you are not using the functions in https://github.com/golang/go/blob/master/src/syscall/syscall_windows.go related to converting from the format that comes out of the kernel to what Go needs. Look at other callers to UTF16ToString, like in env_windows.go, to see how they are used.

Using Golang to get Windows idle time (GetLastInputInfo or similar)

Is there an example or method of getting a Windows system's idle time using Go?
I've been looking at the documentation at the Golang site but I think I'm missing how to access (and use) the API to get system information including the idle time.
Go's website is hardcoded to show the documentation for the standard library packages on Linux. You will need to get godoc and run it yourself:
go get golang.org/x/tools/cmd/godoc
godoc --http=:6060
then open http://127.0.0.1:6060/ in your web browser.
Of note is package syscall, which provides facilities for accessing functions in DLLs, including UTF-16 helpers and callback generation functions.
Doing a quick recursive search of the Go tree says it doesn't have an API for GetLastInputInfo() in particular, so unless I'm missing something, you should be able to call that function from the DLL directly:
user32 := syscall.MustLoadDLL("user32.dll") // or NewLazyDLL() to defer loading
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // or NewProc() if you used NewLazyDLL()
// or you can handle the errors in the above if you want to provide some alternative
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err will always be non-nil; you need to check r1 (the return value)
if r1 == 0 { // in this case
panic("error getting last input info: " + err.Error())
}
Your case involves a structure. As far as I know, you can just recreate the structure flat (keeping fields in the same order), but you must convert any int fields in the original to int32, otherwise things will break on 64-bit Windows. Consult the Windows Data Types page on MSDN for the appropriate type equivalents. In your case, this would be
var lastInputInfo struct {
cbSize uint32
dwTime uint32
}
Because this (like so many structs in the Windows API) has a cbSize field that requires you to initialize it with the size of the struct, we must do so too:
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
Now we just need to pass a pointer to that lastInputInfo variable to the function:
r1, _, err := getLastInputInfo.Call(
uintptr(unsafe.Pointer(&lastInputInfo)))
and just remember to import syscall and unsafe.
All args to DLL/LazyDLL.Call() are uintptr, as is the r1 return. The _ return is never used on Windows (it has to do with the ABI used).
Since I went over most of what you need to know to use the Windows API in Go that you can't gather from reading the syscall docs, I will also say (and this is irrelevant to the above question) that if a function has both ANSI and Unicode versions, you should use the Unicode versions (W suffix) and the UTF-16 conversion functions in package syscall for best results.
I think that's all the info you (or anyone, for that matter) will need to use the Windows API in Go programs.
Regarding for answer from andlabs. This is ready for use example:
import (
"time"
"unsafe"
"syscall"
"fmt"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
getLastInputInfo = user32.MustFindProc("GetLastInputInfo")
getTickCount = kernel32.MustFindProc("GetTickCount")
lastInputInfo struct {
cbSize uint32
dwTime uint32
}
)
func IdleTime() time.Duration {
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
currentTickCount, _, _ := getTickCount.Call()
r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
if r1 == 0 {
panic("error getting last input info: " + err.Error())
}
return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}
func main() {
t := time.NewTicker(1 * time.Second)
for range t.C {
fmt.Println(IdleTime())
}
}
This is code print idle time every second. Try run and don't touch mouse/keyboard

Resources