windows: add resource extraction functions
These functions make it possible to read executable resource information
at runtime.
Change-Id: I00f260199ecda8daeb3417eaa9c02198663063b7
Reviewed-on: https://go-review.googlesource.com/c/sys/+/298173
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Jason A. Donenfeld
3 years ago
8 | 8 | import ( |
9 | 9 | errorspkg "errors" |
10 | 10 | "fmt" |
11 | "runtime" | |
11 | 12 | "sync" |
12 | 13 | "syscall" |
13 | 14 | "time" |
383 | 384 | //sys getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetThreadPreferredUILanguages |
384 | 385 | //sys getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetUserPreferredUILanguages |
385 | 386 | //sys getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetSystemPreferredUILanguages |
387 | //sys findResource(module Handle, name uintptr, resType uintptr) (resInfo Handle, err error) = kernel32.FindResourceW | |
388 | //sys SizeofResource(module Handle, resInfo Handle) (size uint32, err error) = kernel32.SizeofResource | |
389 | //sys LoadResource(module Handle, resInfo Handle) (resData Handle, err error) = kernel32.LoadResource | |
390 | //sys LockResource(resData Handle) (addr uintptr, err error) = kernel32.LockResource | |
386 | 391 | |
387 | 392 | // Process Status API (PSAPI) |
388 | 393 | //sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses |
1577 | 1582 | func (s *UNICODE_STRING) String() string { |
1578 | 1583 | return UTF16ToString(s.Slice()) |
1579 | 1584 | } |
1585 | ||
1586 | // FindResource resolves a resource of the given name and resource type. | |
1587 | func FindResource(module Handle, name, resType ResourceIDOrString) (Handle, error) { | |
1588 | var namePtr, resTypePtr uintptr | |
1589 | var name16, resType16 *uint16 | |
1590 | var err error | |
1591 | resolvePtr := func(i interface{}, keep **uint16) (uintptr, error) { | |
1592 | switch v := i.(type) { | |
1593 | case string: | |
1594 | *keep, err = UTF16PtrFromString(v) | |
1595 | if err != nil { | |
1596 | return 0, err | |
1597 | } | |
1598 | return uintptr(unsafe.Pointer(*keep)), nil | |
1599 | case ResourceID: | |
1600 | return uintptr(v), nil | |
1601 | } | |
1602 | return 0, errorspkg.New("parameter must be a ResourceID or a string") | |
1603 | } | |
1604 | namePtr, err = resolvePtr(name, &name16) | |
1605 | if err != nil { | |
1606 | return 0, err | |
1607 | } | |
1608 | resTypePtr, err = resolvePtr(resType, &resType16) | |
1609 | if err != nil { | |
1610 | return 0, err | |
1611 | } | |
1612 | resInfo, err := findResource(module, namePtr, resTypePtr) | |
1613 | runtime.KeepAlive(name16) | |
1614 | runtime.KeepAlive(resType16) | |
1615 | return resInfo, err | |
1616 | } | |
1617 | ||
1618 | func LoadResourceData(module, resInfo Handle) (data []byte, err error) { | |
1619 | size, err := SizeofResource(module, resInfo) | |
1620 | if err != nil { | |
1621 | return | |
1622 | } | |
1623 | resData, err := LoadResource(module, resInfo) | |
1624 | if err != nil { | |
1625 | return | |
1626 | } | |
1627 | ptr, err := LockResource(resData) | |
1628 | if err != nil { | |
1629 | return | |
1630 | } | |
1631 | h := (*unsafeheader.Slice)(unsafe.Pointer(&data)) | |
1632 | h.Data = unsafe.Pointer(ptr) | |
1633 | h.Len = int(size) | |
1634 | h.Cap = int(size) | |
1635 | return | |
1636 | } |
4 | 4 | package windows_test |
5 | 5 | |
6 | 6 | import ( |
7 | "bytes" | |
7 | 8 | "debug/pe" |
8 | 9 | "errors" |
9 | 10 | "fmt" |
561 | 562 | t.Errorf("expected os.Executable() to return same value as peb.Ldr.{entry}.FullDllName - want %#q; got %#q", osPath, pebPath) |
562 | 563 | } |
563 | 564 | } |
565 | ||
566 | func TestResourceExtraction(t *testing.T) { | |
567 | system32, err := windows.GetSystemDirectory() | |
568 | if err != nil { | |
569 | t.Errorf("unable to find system32 directory: %v", err) | |
570 | } | |
571 | cmd, err := windows.LoadLibrary(filepath.Join(system32, "cmd.exe")) | |
572 | if err != nil { | |
573 | t.Errorf("unable to load cmd.exe: %v", err) | |
574 | } | |
575 | defer windows.FreeLibrary(cmd) | |
576 | rsrc, err := windows.FindResource(cmd, windows.CREATEPROCESS_MANIFEST_RESOURCE_ID, windows.RT_MANIFEST) | |
577 | if err != nil { | |
578 | t.Errorf("unable to find cmd.exe manifest resource: %v", err) | |
579 | } | |
580 | manifest, err := windows.LoadResourceData(cmd, rsrc) | |
581 | if err != nil { | |
582 | t.Errorf("unable to load cmd.exe manifest resource data: %v", err) | |
583 | } | |
584 | if !bytes.Contains(manifest, []byte("</assembly>")) { | |
585 | t.Errorf("did not find </assembly> in manifest") | |
586 | } | |
587 | } |
2599 | 2599 | SECURITY_SQOS_PRESENT = 0x100000 |
2600 | 2600 | SECURITY_VALID_SQOS_FLAGS = 0x1f0000 |
2601 | 2601 | ) |
2602 | ||
2603 | // ResourceID represents a 16-bit resource identifier, traditionally created with the MAKEINTRESOURCE macro. | |
2604 | type ResourceID uint16 | |
2605 | ||
2606 | // ResourceIDOrString must be either a ResourceID, to specify a resource or resource type by ID, | |
2607 | // or a string, to specify a resource or resource type by name. | |
2608 | type ResourceIDOrString interface{} | |
2609 | ||
2610 | // Predefined resource names and types. | |
2611 | var ( | |
2612 | // Predefined names. | |
2613 | CREATEPROCESS_MANIFEST_RESOURCE_ID ResourceID = 1 | |
2614 | ISOLATIONAWARE_MANIFEST_RESOURCE_ID ResourceID = 2 | |
2615 | ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID ResourceID = 3 | |
2616 | ISOLATIONPOLICY_MANIFEST_RESOURCE_ID ResourceID = 4 | |
2617 | ISOLATIONPOLICY_BROWSER_MANIFEST_RESOURCE_ID ResourceID = 5 | |
2618 | MINIMUM_RESERVED_MANIFEST_RESOURCE_ID ResourceID = 1 // inclusive | |
2619 | MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID ResourceID = 16 // inclusive | |
2620 | ||
2621 | // Predefined types. | |
2622 | RT_CURSOR ResourceID = 1 | |
2623 | RT_BITMAP ResourceID = 2 | |
2624 | RT_ICON ResourceID = 3 | |
2625 | RT_MENU ResourceID = 4 | |
2626 | RT_DIALOG ResourceID = 5 | |
2627 | RT_STRING ResourceID = 6 | |
2628 | RT_FONTDIR ResourceID = 7 | |
2629 | RT_FONT ResourceID = 8 | |
2630 | RT_ACCELERATOR ResourceID = 9 | |
2631 | RT_RCDATA ResourceID = 10 | |
2632 | RT_MESSAGETABLE ResourceID = 11 | |
2633 | RT_GROUP_CURSOR ResourceID = 12 | |
2634 | RT_GROUP_ICON ResourceID = 14 | |
2635 | RT_VERSION ResourceID = 16 | |
2636 | RT_DLGINCLUDE ResourceID = 17 | |
2637 | RT_PLUGPLAY ResourceID = 19 | |
2638 | RT_VXD ResourceID = 20 | |
2639 | RT_ANICURSOR ResourceID = 21 | |
2640 | RT_ANIICON ResourceID = 22 | |
2641 | RT_HTML ResourceID = 23 | |
2642 | RT_MANIFEST ResourceID = 24 | |
2643 | ) |
204 | 204 | procFindNextFileW = modkernel32.NewProc("FindNextFileW") |
205 | 205 | procFindNextVolumeMountPointW = modkernel32.NewProc("FindNextVolumeMountPointW") |
206 | 206 | procFindNextVolumeW = modkernel32.NewProc("FindNextVolumeW") |
207 | procFindResourceW = modkernel32.NewProc("FindResourceW") | |
207 | 208 | procFindVolumeClose = modkernel32.NewProc("FindVolumeClose") |
208 | 209 | procFindVolumeMountPointClose = modkernel32.NewProc("FindVolumeMountPointClose") |
209 | 210 | procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") |
276 | 277 | procIsWow64Process2 = modkernel32.NewProc("IsWow64Process2") |
277 | 278 | procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW") |
278 | 279 | procLoadLibraryW = modkernel32.NewProc("LoadLibraryW") |
280 | procLoadResource = modkernel32.NewProc("LoadResource") | |
279 | 281 | procLocalAlloc = modkernel32.NewProc("LocalAlloc") |
280 | 282 | procLocalFree = modkernel32.NewProc("LocalFree") |
281 | 283 | procLockFileEx = modkernel32.NewProc("LockFileEx") |
284 | procLockResource = modkernel32.NewProc("LockResource") | |
282 | 285 | procMapViewOfFile = modkernel32.NewProc("MapViewOfFile") |
283 | 286 | procMoveFileExW = modkernel32.NewProc("MoveFileExW") |
284 | 287 | procMoveFileW = modkernel32.NewProc("MoveFileW") |
325 | 328 | procSetStdHandle = modkernel32.NewProc("SetStdHandle") |
326 | 329 | procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW") |
327 | 330 | procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW") |
331 | procSizeofResource = modkernel32.NewProc("SizeofResource") | |
328 | 332 | procSleepEx = modkernel32.NewProc("SleepEx") |
329 | 333 | procTerminateJobObject = modkernel32.NewProc("TerminateJobObject") |
330 | 334 | procTerminateProcess = modkernel32.NewProc("TerminateProcess") |
1746 | 1750 | return |
1747 | 1751 | } |
1748 | 1752 | |
1753 | func findResource(module Handle, name uintptr, resType uintptr) (resInfo Handle, err error) { | |
1754 | r0, _, e1 := syscall.Syscall(procFindResourceW.Addr(), 3, uintptr(module), uintptr(name), uintptr(resType)) | |
1755 | resInfo = Handle(r0) | |
1756 | if resInfo == 0 { | |
1757 | err = errnoErr(e1) | |
1758 | } | |
1759 | return | |
1760 | } | |
1761 | ||
1749 | 1762 | func FindVolumeClose(findVolume Handle) (err error) { |
1750 | 1763 | r1, _, e1 := syscall.Syscall(procFindVolumeClose.Addr(), 1, uintptr(findVolume), 0, 0) |
1751 | 1764 | if r1 == 0 { |
2370 | 2383 | return |
2371 | 2384 | } |
2372 | 2385 | |
2386 | func LoadResource(module Handle, resInfo Handle) (resData Handle, err error) { | |
2387 | r0, _, e1 := syscall.Syscall(procLoadResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0) | |
2388 | resData = Handle(r0) | |
2389 | if resData == 0 { | |
2390 | err = errnoErr(e1) | |
2391 | } | |
2392 | return | |
2393 | } | |
2394 | ||
2373 | 2395 | func LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error) { |
2374 | 2396 | r0, _, e1 := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(length), 0) |
2375 | 2397 | ptr = uintptr(r0) |
2391 | 2413 | func LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) { |
2392 | 2414 | r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) |
2393 | 2415 | if r1 == 0 { |
2416 | err = errnoErr(e1) | |
2417 | } | |
2418 | return | |
2419 | } | |
2420 | ||
2421 | func LockResource(resData Handle) (addr uintptr, err error) { | |
2422 | r0, _, e1 := syscall.Syscall(procLockResource.Addr(), 1, uintptr(resData), 0, 0) | |
2423 | addr = uintptr(r0) | |
2424 | if addr == 0 { | |
2394 | 2425 | err = errnoErr(e1) |
2395 | 2426 | } |
2396 | 2427 | return |
2809 | 2840 | return |
2810 | 2841 | } |
2811 | 2842 | |
2843 | func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) { | |
2844 | r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0) | |
2845 | size = uint32(r0) | |
2846 | if size == 0 { | |
2847 | err = errnoErr(e1) | |
2848 | } | |
2849 | return | |
2850 | } | |
2851 | ||
2812 | 2852 | func SleepEx(milliseconds uint32, alertable bool) (ret uint32) { |
2813 | 2853 | var _p0 uint32 |
2814 | 2854 | if alertable { |