You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

417 lines
12 KiB

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