Skip to content

Commit 3dff6e1

Browse files
committed
windows: add LoadLibraryEx, add LazyDLL.System
Updates golang/go#14959 Change-Id: Ib91c359c3df919df0b30e584d38e56f79f3e3dc9 Reviewed-on: https://go-review.googlesource.com/21388 Reviewed-by: Russ Cox <[email protected]> Reviewed-by: Alex Brainman <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent 320cb01 commit 3dff6e1

File tree

5 files changed

+156
-35
lines changed

5 files changed

+156
-35
lines changed

windows/dll_windows.go

+108-16
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type DLL struct {
3131
}
3232

3333
// 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.
3438
func LoadDLL(name string) (dll *DLL, err error) {
3539
namep, err := UTF16PtrFromString(name)
3640
if err != nil {
@@ -162,29 +166,48 @@ func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
162166
// call to its Handle method or to one of its
163167
// LazyProc's Addr method.
164168
type LazyDLL struct {
165-
mu sync.Mutex
166-
dll *DLL // non nil once DLL is loaded
167169
Name string
170+
171+
// System determines whether the DLL must be loaded from the
172+
// Windows System directory, bypassing the normal DLL search
173+
// path.
174+
System bool
175+
176+
mu sync.Mutex
177+
dll *DLL // non nil once DLL is loaded
168178
}
169179

170180
// Load loads DLL file d.Name into memory. It returns an error if fails.
171181
// Load will not try to load DLL, if it is already loaded into memory.
172182
func (d *LazyDLL) Load() error {
173183
// Non-racy version of:
174-
// if d.dll == nil {
175-
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
176-
d.mu.Lock()
177-
defer d.mu.Unlock()
178-
if d.dll == nil {
179-
dll, e := LoadDLL(d.Name)
180-
if e != nil {
181-
return e
182-
}
183-
// Non-racy version of:
184-
// d.dll = dll
185-
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
186-
}
184+
// if d.dll != nil {
185+
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
186+
return nil
187187
}
188+
d.mu.Lock()
189+
defer d.mu.Unlock()
190+
if d.dll != nil {
191+
return nil
192+
}
193+
194+
// kernel32.dll is special, since it's where LoadLibraryEx comes from.
195+
// The kernel already special-cases its name, so it's always
196+
// loaded from system32.
197+
var dll *DLL
198+
var err error
199+
if d.Name == "kernel32.dll" {
200+
dll, err = LoadDLL(d.Name)
201+
} else {
202+
dll, err = loadLibraryEx(d.Name, d.System)
203+
}
204+
if err != nil {
205+
return err
206+
}
207+
208+
// Non-racy version of:
209+
// d.dll = dll
210+
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
188211
return nil
189212
}
190213

@@ -215,8 +238,9 @@ func NewLazyDLL(name string) *LazyDLL {
215238
// A LazyProc implements access to a procedure inside a LazyDLL.
216239
// It delays the lookup until the Addr method is called.
217240
type LazyProc struct {
218-
mu sync.Mutex
219241
Name string
242+
243+
mu sync.Mutex
220244
l *LazyDLL
221245
proc *Proc
222246
}
@@ -273,3 +297,71 @@ func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
273297
p.mustFind()
274298
return p.proc.Call(a...)
275299
}
300+
301+
var canDoSearchSystem32Once struct {
302+
sync.Once
303+
v bool
304+
}
305+
306+
func initCanDoSearchSystem32() {
307+
// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
308+
// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
309+
// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
310+
// systems that have KB2533623 installed. To determine whether the
311+
// flags are available, use GetProcAddress to get the address of the
312+
// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
313+
// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
314+
// flags can be used with LoadLibraryEx."
315+
canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
316+
}
317+
318+
func canDoSearchSystem32() bool {
319+
canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
320+
return canDoSearchSystem32Once.v
321+
}
322+
323+
func isBaseName(name string) bool {
324+
for _, c := range name {
325+
if c == ':' || c == '/' || c == '\\' {
326+
return false
327+
}
328+
}
329+
return true
330+
}
331+
332+
// loadLibraryEx wraps the Windows LoadLibraryEx function.
333+
//
334+
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
335+
//
336+
// If name is not an absolute path, LoadLibraryEx searches for the DLL
337+
// in a variety of automatic locations unless constrained by flags.
338+
// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
339+
func loadLibraryEx(name string, system bool) (*DLL, error) {
340+
loadDLL := name
341+
var flags uintptr
342+
if system {
343+
if canDoSearchSystem32() {
344+
const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
345+
flags = LOAD_LIBRARY_SEARCH_SYSTEM32
346+
} else if isBaseName(name) {
347+
// WindowsXP or unpatched Windows machine
348+
// trying to load "foo.dll" out of the system
349+
// folder, but LoadLibraryEx doesn't support
350+
// that yet on their system, so emulate it.
351+
windir, _ := Getenv("WINDIR") // old var; apparently works on XP
352+
if windir == "" {
353+
return nil, errString("%WINDIR% not defined")
354+
}
355+
loadDLL = windir + "\\System32\\" + name
356+
}
357+
}
358+
h, err := LoadLibraryEx(loadDLL, 0, flags)
359+
if err != nil {
360+
return nil, err
361+
}
362+
return &DLL{Name: name, Handle: h}, nil
363+
}
364+
365+
type errString string
366+
367+
func (s errString) Error() string { return string(s) }

windows/registry/syscall.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package registry
88

99
import "syscall"
1010

11-
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
11+
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -xsys -output zsyscall_windows.go syscall.go
1212

1313
const (
1414
_REG_OPTION_NON_VOLATILE = 0

windows/registry/zsyscall_windows.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
package registry
44

5-
import "unsafe"
6-
import "syscall"
5+
import (
6+
"golang.org/x/sys/windows"
7+
"syscall"
8+
"unsafe"
9+
)
710

811
var _ unsafe.Pointer
912

1013
var (
11-
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
12-
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
14+
modadvapi32 = &windows.LazyDLL{Name: "advapi32.dll", System: true}
15+
modkernel32 = &windows.LazyDLL{Name: "kernel32.dll", System: true}
1316

1417
procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
1518
procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")

windows/syscall_windows.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"unsafe"
1515
)
1616

17-
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
17+
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -xsys -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
1818

1919
type Handle uintptr
2020

@@ -84,6 +84,7 @@ func NewCallbackCDecl(fn interface{}) uintptr
8484

8585
//sys GetLastError() (lasterr error)
8686
//sys LoadLibrary(libname string) (handle Handle, err error) = LoadLibraryW
87+
//sys LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, err error) = LoadLibraryExW
8788
//sys FreeLibrary(handle Handle) (err error)
8889
//sys GetProcAddress(module Handle, procname string) (proc uintptr, err error)
8990
//sys GetVersion() (ver uint32, err error)

windows/zsyscall_windows.go

+38-13
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
package windows
44

5-
import "unsafe"
6-
import "syscall"
5+
import (
6+
"syscall"
7+
"unsafe"
8+
)
79

810
var _ unsafe.Pointer
911

1012
var (
11-
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
12-
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
13-
modshell32 = syscall.NewLazyDLL("shell32.dll")
14-
modmswsock = syscall.NewLazyDLL("mswsock.dll")
15-
modcrypt32 = syscall.NewLazyDLL("crypt32.dll")
16-
modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
17-
moddnsapi = syscall.NewLazyDLL("dnsapi.dll")
18-
modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
19-
modsecur32 = syscall.NewLazyDLL("secur32.dll")
20-
modnetapi32 = syscall.NewLazyDLL("netapi32.dll")
21-
moduserenv = syscall.NewLazyDLL("userenv.dll")
13+
modadvapi32 = &LazyDLL{Name: "advapi32.dll", System: true}
14+
modkernel32 = &LazyDLL{Name: "kernel32.dll", System: true}
15+
modshell32 = &LazyDLL{Name: "shell32.dll", System: true}
16+
modmswsock = &LazyDLL{Name: "mswsock.dll", System: true}
17+
modcrypt32 = &LazyDLL{Name: "crypt32.dll", System: true}
18+
modws2_32 = &LazyDLL{Name: "ws2_32.dll", System: true}
19+
moddnsapi = &LazyDLL{Name: "dnsapi.dll", System: true}
20+
modiphlpapi = &LazyDLL{Name: "iphlpapi.dll", System: true}
21+
modsecur32 = &LazyDLL{Name: "secur32.dll", System: true}
22+
modnetapi32 = &LazyDLL{Name: "netapi32.dll", System: true}
23+
moduserenv = &LazyDLL{Name: "userenv.dll", System: true}
2224

2325
procRegisterEventSourceW = modadvapi32.NewProc("RegisterEventSourceW")
2426
procDeregisterEventSource = modadvapi32.NewProc("DeregisterEventSource")
@@ -39,6 +41,7 @@ var (
3941
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
4042
procGetLastError = modkernel32.NewProc("GetLastError")
4143
procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
44+
procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW")
4245
procFreeLibrary = modkernel32.NewProc("FreeLibrary")
4346
procGetProcAddress = modkernel32.NewProc("GetProcAddress")
4447
procGetVersion = modkernel32.NewProc("GetVersion")
@@ -430,6 +433,28 @@ func _LoadLibrary(libname *uint16) (handle Handle, err error) {
430433
return
431434
}
432435

436+
func LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, err error) {
437+
var _p0 *uint16
438+
_p0, err = syscall.UTF16PtrFromString(libname)
439+
if err != nil {
440+
return
441+
}
442+
return _LoadLibraryEx(_p0, zero, flags)
443+
}
444+
445+
func _LoadLibraryEx(libname *uint16, zero Handle, flags uintptr) (handle Handle, err error) {
446+
r0, _, e1 := syscall.Syscall(procLoadLibraryExW.Addr(), 3, uintptr(unsafe.Pointer(libname)), uintptr(zero), uintptr(flags))
447+
handle = Handle(r0)
448+
if handle == 0 {
449+
if e1 != 0 {
450+
err = error(e1)
451+
} else {
452+
err = syscall.EINVAL
453+
}
454+
}
455+
return
456+
}
457+
433458
func FreeLibrary(handle Handle) (err error) {
434459
r1, _, e1 := syscall.Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0)
435460
if r1 == 0 {

0 commit comments

Comments
 (0)