1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package windows 6 7import ( 8 "sync" 9 "sync/atomic" 10 "syscall" 11 "unsafe" 12) 13 14// DLLError describes reasons for DLL load failures. 15type DLLError struct { 16 Err error 17 ObjName string 18 Msg string 19} 20 21func (e *DLLError) Error() string { return e.Msg } 22 23// Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file. 24func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno) 25func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno) 26 27// A DLL implements access to a single DLL. 28type DLL struct { 29 Name string 30 Handle Handle 31} 32 33// LoadDLL loads DLL file into memory. 34// 35// Warning: using LoadDLL without an absolute path name is subject to 36// DLL preloading attacks. To safely load a system DLL, use LazyDLL 37// with System set to true, or use LoadLibraryEx directly. 38func LoadDLL(name string) (dll *DLL, err error) { 39 namep, err := UTF16PtrFromString(name) 40 if err != nil { 41 return nil, err 42 } 43 h, e := loadlibrary(namep) 44 if e != 0 { 45 return nil, &DLLError{ 46 Err: e, 47 ObjName: name, 48 Msg: "Failed to load " + name + ": " + e.Error(), 49 } 50 } 51 d := &DLL{ 52 Name: name, 53 Handle: Handle(h), 54 } 55 return d, nil 56} 57 58// MustLoadDLL is like LoadDLL but panics if load operation failes. 59func MustLoadDLL(name string) *DLL { 60 d, e := LoadDLL(name) 61 if e != nil { 62 panic(e) 63 } 64 return d 65} 66 67// FindProc searches DLL d for procedure named name and returns *Proc 68// if found. It returns an error if search fails. 69func (d *DLL) FindProc(name string) (proc *Proc, err error) { 70 namep, err := BytePtrFromString(name) 71 if err != nil { 72 return nil, err 73 } 74 a, e := getprocaddress(uintptr(d.Handle), namep) 75 if e != 0 { 76 return nil, &DLLError{ 77 Err: e, 78 ObjName: name, 79 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), 80 } 81 } 82 p := &Proc{ 83 Dll: d, 84 Name: name, 85 addr: a, 86 } 87 return p, nil 88} 89 90// MustFindProc is like FindProc but panics if search fails. 91func (d *DLL) MustFindProc(name string) *Proc { 92 p, e := d.FindProc(name) 93 if e != nil { 94 panic(e) 95 } 96 return p 97} 98 99// Release unloads DLL d from memory. 100func (d *DLL) Release() (err error) { 101 return FreeLibrary(d.Handle) 102} 103 104// A Proc implements access to a procedure inside a DLL. 105type Proc struct { 106 Dll *DLL 107 Name string 108 addr uintptr 109} 110 111// Addr returns the address of the procedure represented by p. 112// The return value can be passed to Syscall to run the procedure. 113func (p *Proc) Addr() uintptr { 114 return p.addr 115} 116 117//go:uintptrescapes 118 119// Call executes procedure p with arguments a. It will panic, if more then 15 arguments 120// are supplied. 121// 122// The returned error is always non-nil, constructed from the result of GetLastError. 123// Callers must inspect the primary return value to decide whether an error occurred 124// (according to the semantics of the specific function being called) before consulting 125// the error. The error will be guaranteed to contain windows.Errno. 126func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 127 switch len(a) { 128 case 0: 129 return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) 130 case 1: 131 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) 132 case 2: 133 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) 134 case 3: 135 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) 136 case 4: 137 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) 138 case 5: 139 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) 140 case 6: 141 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) 142 case 7: 143 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) 144 case 8: 145 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) 146 case 9: 147 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) 148 case 10: 149 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) 150 case 11: 151 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) 152 case 12: 153 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) 154 case 13: 155 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0) 156 case 14: 157 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0) 158 case 15: 159 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14]) 160 default: 161 panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".") 162 } 163 return 164} 165 166// A LazyDLL implements access to a single DLL. 167// It will delay the load of the DLL until the first 168// call to its Handle method or to one of its 169// LazyProc's Addr method. 170type LazyDLL struct { 171 Name string 172 173 // System determines whether the DLL must be loaded from the 174 // Windows System directory, bypassing the normal DLL search 175 // path. 176 System bool 177 178 mu sync.Mutex 179 dll *DLL // non nil once DLL is loaded 180} 181 182// Load loads DLL file d.Name into memory. It returns an error if fails. 183// Load will not try to load DLL, if it is already loaded into memory. 184func (d *LazyDLL) Load() error { 185 // Non-racy version of: 186 // if d.dll != nil { 187 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil { 188 return nil 189 } 190 d.mu.Lock() 191 defer d.mu.Unlock() 192 if d.dll != nil { 193 return nil 194 } 195 196 // kernel32.dll is special, since it's where LoadLibraryEx comes from. 197 // The kernel already special-cases its name, so it's always 198 // loaded from system32. 199 var dll *DLL 200 var err error 201 if d.Name == "kernel32.dll" { 202 dll, err = LoadDLL(d.Name) 203 } else { 204 dll, err = loadLibraryEx(d.Name, d.System) 205 } 206 if err != nil { 207 return err 208 } 209 210 // Non-racy version of: 211 // d.dll = dll 212 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) 213 return nil 214} 215 216// mustLoad is like Load but panics if search fails. 217func (d *LazyDLL) mustLoad() { 218 e := d.Load() 219 if e != nil { 220 panic(e) 221 } 222} 223 224// Handle returns d's module handle. 225func (d *LazyDLL) Handle() uintptr { 226 d.mustLoad() 227 return uintptr(d.dll.Handle) 228} 229 230// NewProc returns a LazyProc for accessing the named procedure in the DLL d. 231func (d *LazyDLL) NewProc(name string) *LazyProc { 232 return &LazyProc{l: d, Name: name} 233} 234 235// NewLazyDLL creates new LazyDLL associated with DLL file. 236func NewLazyDLL(name string) *LazyDLL { 237 return &LazyDLL{Name: name} 238} 239 240// NewLazySystemDLL is like NewLazyDLL, but will only 241// search Windows System directory for the DLL if name is 242// a base name (like "advapi32.dll"). 243func NewLazySystemDLL(name string) *LazyDLL { 244 return &LazyDLL{Name: name, System: true} 245} 246 247// A LazyProc implements access to a procedure inside a LazyDLL. 248// It delays the lookup until the Addr method is called. 249type LazyProc struct { 250 Name string 251 252 mu sync.Mutex 253 l *LazyDLL 254 proc *Proc 255} 256 257// Find searches DLL for procedure named p.Name. It returns 258// an error if search fails. Find will not search procedure, 259// if it is already found and loaded into memory. 260func (p *LazyProc) Find() error { 261 // Non-racy version of: 262 // if p.proc == nil { 263 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil { 264 p.mu.Lock() 265 defer p.mu.Unlock() 266 if p.proc == nil { 267 e := p.l.Load() 268 if e != nil { 269 return e 270 } 271 proc, e := p.l.dll.FindProc(p.Name) 272 if e != nil { 273 return e 274 } 275 // Non-racy version of: 276 // p.proc = proc 277 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc)) 278 } 279 } 280 return nil 281} 282 283// mustFind is like Find but panics if search fails. 284func (p *LazyProc) mustFind() { 285 e := p.Find() 286 if e != nil { 287 panic(e) 288 } 289} 290 291// Addr returns the address of the procedure represented by p. 292// The return value can be passed to Syscall to run the procedure. 293func (p *LazyProc) Addr() uintptr { 294 p.mustFind() 295 return p.proc.Addr() 296} 297 298//go:uintptrescapes 299 300// Call executes procedure p with arguments a. It will panic, if more then 15 arguments 301// are supplied. 302// 303// The returned error is always non-nil, constructed from the result of GetLastError. 304// Callers must inspect the primary return value to decide whether an error occurred 305// (according to the semantics of the specific function being called) before consulting 306// the error. The error will be guaranteed to contain windows.Errno. 307func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 308 p.mustFind() 309 return p.proc.Call(a...) 310} 311 312var canDoSearchSystem32Once struct { 313 sync.Once 314 v bool 315} 316 317func initCanDoSearchSystem32() { 318 // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: 319 // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows 320 // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on 321 // systems that have KB2533623 installed. To determine whether the 322 // flags are available, use GetProcAddress to get the address of the 323 // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories 324 // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* 325 // flags can be used with LoadLibraryEx." 326 canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil) 327} 328 329func canDoSearchSystem32() bool { 330 canDoSearchSystem32Once.Do(initCanDoSearchSystem32) 331 return canDoSearchSystem32Once.v 332} 333 334func isBaseName(name string) bool { 335 for _, c := range name { 336 if c == ':' || c == '/' || c == '\\' { 337 return false 338 } 339 } 340 return true 341} 342 343// loadLibraryEx wraps the Windows LoadLibraryEx function. 344// 345// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx 346// 347// If name is not an absolute path, LoadLibraryEx searches for the DLL 348// in a variety of automatic locations unless constrained by flags. 349// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx 350func loadLibraryEx(name string, system bool) (*DLL, error) { 351 loadDLL := name 352 var flags uintptr 353 if system { 354 if canDoSearchSystem32() { 355 const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 356 flags = LOAD_LIBRARY_SEARCH_SYSTEM32 357 } else if isBaseName(name) { 358 // WindowsXP or unpatched Windows machine 359 // trying to load "foo.dll" out of the system 360 // folder, but LoadLibraryEx doesn't support 361 // that yet on their system, so emulate it. 362 windir, _ := Getenv("WINDIR") // old var; apparently works on XP 363 if windir == "" { 364 return nil, errString("%WINDIR% not defined") 365 } 366 loadDLL = windir + "\\System32\\" + name 367 } 368 } 369 h, err := LoadLibraryEx(loadDLL, 0, flags) 370 if err != nil { 371 return nil, err 372 } 373 return &DLL{Name: name, Handle: h}, nil 374} 375 376type errString string 377 378func (s errString) Error() string { return string(s) } 379