@@ -31,6 +31,10 @@ type DLL struct {
31
31
}
32
32
33
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.
34
38
func LoadDLL (name string ) (dll * DLL , err error ) {
35
39
namep , err := UTF16PtrFromString (name )
36
40
if err != nil {
@@ -162,29 +166,48 @@ func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
162
166
// call to its Handle method or to one of its
163
167
// LazyProc's Addr method.
164
168
type LazyDLL struct {
165
- mu sync.Mutex
166
- dll * DLL // non nil once DLL is loaded
167
169
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
168
178
}
169
179
170
180
// Load loads DLL file d.Name into memory. It returns an error if fails.
171
181
// Load will not try to load DLL, if it is already loaded into memory.
172
182
func (d * LazyDLL ) Load () error {
173
183
// 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
187
187
}
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 ))
188
211
return nil
189
212
}
190
213
@@ -215,8 +238,9 @@ func NewLazyDLL(name string) *LazyDLL {
215
238
// A LazyProc implements access to a procedure inside a LazyDLL.
216
239
// It delays the lookup until the Addr method is called.
217
240
type LazyProc struct {
218
- mu sync.Mutex
219
241
Name string
242
+
243
+ mu sync.Mutex
220
244
l * LazyDLL
221
245
proc * Proc
222
246
}
@@ -273,3 +297,71 @@ func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
273
297
p .mustFind ()
274
298
return p .proc .Call (a ... )
275
299
}
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 ) }
0 commit comments