5

Go使用WindowsApi笔记

 3 years ago
source link: https://www.ascotbe.com/2021/09/04/GoUseWindowsApi/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
ascotbe

Go使用WindowsApi笔记

发表于2021-09-04|更新于2021-09-04
阅读量:5

郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

在学Go相关的免杀,来提高木马的存活性,看到一些有意思的东西记下来

1

加载DLL

要在Go中加载DLL,可以使用syscall.NewLazyDLLsyscall.LoadLibrary 以及syscall.MustLoadDLL

  • NewLazyDLL返回一个*LazyDLL,懒加载,只在第一次调用其函数时才加载库;
  • LoadLibrary是立即加载DLL库。

syscall.NewLazyDLL

package main

import (
"syscall"
"unsafe"
)

func main() {
user32 := syscall.NewLazyDLL("user32.dll")
MessageBoxW := user32.NewProc("MessageBoxW")
MessageBoxW.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("windows下的第一种调用方式"))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ascotbe"))), uintptr(0))
}

syscall.LoadLibrary

package main

import (
"syscall"
"unsafe"
)

const (
MB_YESNOCANCEL = 0x00000003
)

func main() {
user32, _ := syscall.LoadLibrary("user32.dll")
messageBox, _ := syscall.GetProcAddress(user32, "MessageBoxW")
_, _, callErr := syscall.Syscall9(messageBox,
4,
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("第二种调用方式"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ascotbe"))),
MB_YESNOCANCEL,
0, 0, 0, 0, 0)
if callErr != 0 {

}
}

syscall.MustLoadDLL

package main

import (
"syscall"
"unsafe"
)

const (
MB_YESNOCANCEL = 0x00000003
)

var (
user32 = syscall.MustLoadDLL("user32.dll")
MessageBoxW = user32.MustFindProc("MessageBoxW")
)

func main() {

_, _, eeee := MessageBoxW.Call(0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("第三种调用方式"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ascotbe"))),
MB_YESNOCANCEL)
if eeee != nil {
}

}

不管调用哪个API,Call的模式都是一样的。

而且syscall.Syscall函数始终返回r1,r2 uintptr,err error, 就最近的实践(windows_amd64)来看,基本可以确定:

  • r1 始终返回 syscall的值;
  • r2 暂且使用;
  • err 返回调用Windows APIGetLastError的结果,这是syscall自动调用的。

而你传入Call中的值必须全部是uintptr,不管你原来的类型是什么

API函数签名

在实际调用DLL函数之前,我们必须要了解一下过程所需要的参数,类型,大小。Microsoft将此描述为Windows API文档的一部分。如CreateJobObjectA的过程签名如下:

HANDLE CreateJobObjectA(
LPSECURITY_ATTRIBUTES lpJobAttributes,
LPCSTR lpName
);

也就是说,CreateJobObjectA需要一个指向LPSECURITY_ATTRIBUTES结构的指针,和一个指向C-String的指针(ASCII编码,技术上是Windows-1252编码 ;它与ASCII兼容)。

C结构与Go结构

在文档中我们可以搜索到,LPSECURITY_ATTRIBUTES是这么定义的:

typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

这时,我们就必须构造一个类似的Go结构来替代它。这时我们可以参考syscallSecurityAttributes的定义。

在Windows API中,我们可以看到,SecurityAttributes是这么定义的:

typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

而Go中SecurityAttributes的定义为:

type SecurityAttributes struct {
Length uint32
SecurityDescriptor uintptr
InheritHandle uint32
}

由此我们大概知道, DWORD对应Go uint32LPVOID (* void)对应uintptrBOOL对应uint32。所以在你不知道用什么类型来表示C中对应的结构时,你可以去看看syscallgo.sys库中找找,或许能有收获。Windows一些参考类型这里也有描述。

然而,了解下面这些常见C类型与Go类型的对应关系会非常有用。

type (
BOOL uint32
BOOLEAN byte
BYTE byte
DWORD uint32
DWORD64 uint64
HANDLE uintptr
HLOCAL uintptr
LARGE_INTEGER int64
LONG int32
LPVOID uintptr
SIZE_T uintptr
UINT uint32
ULONG_PTR uintptr
ULONGLONG uint64
WORD uint16
)
https://razeencheng.com/post/breaking-all-the-rules-using-go-to-call-windows-api.html

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK