-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle symlink extraction on Windows
License: MIT Signed-off-by: Dominic Della Valle <ddvpublic@gmail.com>
- Loading branch information
Showing
2 changed files
with
212 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package commands | ||
|
||
import ( | ||
"unsafe" | ||
|
||
"golang.org/x/sys/windows" | ||
"golang.org/x/sys/windows/registry" | ||
) | ||
|
||
const SE_PRIVILEGE_ENABLED = 0x00000002 | ||
const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 | ||
|
||
type Luid struct { | ||
LowPart uint32 | ||
HighPart int32 | ||
} | ||
|
||
type LUID_AND_ATTRIBUTES struct { | ||
Luid Luid | ||
Attributes uint32 | ||
} | ||
|
||
type TOKEN_PRIVILEGES struct { | ||
PrivilegeCount uint32 | ||
Privileges []LUID_AND_ATTRIBUTES | ||
} | ||
|
||
func init() { | ||
// [Done] Has Developer Mode privileges (Windows 14972+) | ||
if isLinkAware() { | ||
if isDevModeActive() { | ||
haveLinkCreatePriviledge = true | ||
return | ||
} | ||
} | ||
|
||
// [In-Progress] Has UAC privilege (Vista+) | ||
if !haveUACLinkPrivilege() { | ||
if !requestUACLinkPrivilege() { //try to gain it if we can | ||
return | ||
} | ||
haveLinkCreatePriviledge = haveUACLinkPrivilege() | ||
} else { | ||
haveLinkCreatePriviledge = true | ||
} | ||
} | ||
|
||
func loadSystemDLL(name string) (*windows.DLL, error) { | ||
modHandle, err := windows.LoadLibraryEx(name, 0, LOAD_LIBRARY_SEARCH_SYSTEM32) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &windows.DLL{Name: name, Handle: modHandle}, nil | ||
} | ||
|
||
func requestUACLinkPrivilege() bool { | ||
procHandle, err := windows.GetCurrentProcess() | ||
if err != nil { | ||
return false | ||
} | ||
|
||
var originalToken windows.Token | ||
if err := windows.OpenProcessToken(procHandle, windows.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, &originalToken); err != nil { | ||
return false | ||
} | ||
defer originalToken.Close() | ||
|
||
var linkLUID Luid | ||
if !lookupPrivilegeValue("", "SeCreateSymbolicLinkPrivilege", &linkLUID) { | ||
return false | ||
} | ||
|
||
var originalPrivs, newPrivs TOKEN_PRIVILEGES | ||
|
||
newPrivs.Privileges[0].Luid = linkLUID | ||
newPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED | ||
|
||
oldSize := uint32(unsafe.Sizeof(originalPrivs)) | ||
newSize := uint32(unsafe.Sizeof(newPrivs)) | ||
|
||
if !adjustTokenPrivileges(procHandle, false, &newPrivs, newSize, &originalPrivs, &oldSize) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
//TODO: | ||
func haveUACLinkPrivilege() bool { | ||
token, err := windows.OpenCurrentProcessToken() | ||
if err != nil { | ||
return false | ||
} | ||
defer token.Close() | ||
|
||
var linkLUID Luid | ||
if !lookupPrivilegeValue("", "SeCreateSymbolicLinkPrivilege", &linkLUID) { | ||
return false | ||
} | ||
|
||
//TODO: checkTokenMembership() | ||
|
||
return false | ||
} | ||
|
||
//TODO: | ||
func lookupPrivilegeValue(lpSystemName, lpName string, lpLuid *Luid) bool { | ||
mod, err := loadSystemDLL("Advapi32.dll") | ||
if err != nil { | ||
return false | ||
} | ||
defer mod.Release() | ||
|
||
proc, err := mod.FindProc("LookupPrivilegeValue") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
//proc.Call() | ||
return false | ||
} | ||
|
||
//TODO: | ||
func adjustTokenPrivileges(TokenHandle windows.Handle, DisableAllPrivileges bool, NewState *TOKEN_PRIVILEGES, BufferLength uint32, PreviousState *TOKEN_PRIVILEGES, ReturnLength *uint32) bool { | ||
return false | ||
} | ||
|
||
func isLinkAware() bool { | ||
major, _, build := rawWinver() | ||
if major < 10 { | ||
return false | ||
} | ||
if major == 10 && build < 14972 { // First version to allow symlink creation by regular users, in dev mode | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// TODO: Replace with `windows.GetVersion()` when this is resolved https://github.com/golang/go/issues/17835 | ||
func rawWinver() (major, minor, build uint32) { | ||
type rtlOSVersionInfo struct { | ||
dwOSVersionInfoSize uint32 | ||
dwMajorVersion uint32 | ||
dwMinorVersion uint32 | ||
dwBuildNumber uint32 | ||
dwPlatformId uint32 | ||
szCSDVersion [128]byte | ||
} | ||
|
||
ntoskrnl := windows.MustLoadDLL("ntoskrnl.exe") | ||
defer ntoskrnl.Release() | ||
proc := ntoskrnl.MustFindProc("RtlGetVersion") | ||
|
||
var verStruct rtlOSVersionInfo | ||
verStruct.dwOSVersionInfoSize = uint32(unsafe.Sizeof(verStruct)) | ||
proc.Call(uintptr(unsafe.Pointer(&verStruct))) | ||
|
||
return verStruct.dwMajorVersion, verStruct.dwMinorVersion, verStruct.dwBuildNumber | ||
} | ||
|
||
// see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development | ||
func isDevModeActive() bool { | ||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense") | ||
if err != nil { | ||
return false | ||
} | ||
|
||
return val != 0 | ||
} |