郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,如果您不同意请关闭该页面!任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!
前言
在学Go相关的免杀,来提高木马的存活性,看到一些有意思的东西记下来
加载DLL
要在Go中加载DLL,可以使用syscall.NewLazyDLL
或syscall.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 API
GetLastError
的结果,这是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结构来替代它。这时我们可以参考syscall
中SecurityAttributes的定义。
在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 uint32
, LPVOID (* void)
对应uintptr
,BOOL
对应uint32
。所以在你不知道用什么类型来表示C中对应的结构时,你可以去看看syscall
或go.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
|