Skip to content

Instantly share code, notes, and snippets.

@jerblack
Created December 1, 2019 18:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jerblack/74804f17a5b4bbb1948d5925c077eb9f to your computer and use it in GitHub Desktop.
Save jerblack/74804f17a5b4bbb1948d5925c077eb9f to your computer and use it in GitHub Desktop.
GoLang: Using PendingFileRenameOperations in the Windows Registry to delete/rename files on reboot with Go.
package main
import (
"fmt"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"log"
"os"
"strings"
"syscall"
)
/*
Windows provides a mechanism for deleting and renaming files that are currently in use through the
"PendingFileRenameOperations" entry in the registry. This MULTI_SZ (multi string) entry is a sequence of null-terminated (\0)
strings alternating between source and destination, each prefixed by `\??\`
\??\src_path\0
\??\dst_path\0
\??\src_path\0
\0
\??\src_path\0
\??\dst_path\0
If the destination is empty, the source is deleted. The Golang registry package automatically handles the
parsing of these null-terminator characters, and returns an array of strings with the character stripped.
To append a value to the "PendingFileRenameOperations", you should first read the key to get any existing
values, and then append two additional strings in the proper format.
For renames, the two strings would be "\??\src_path" and "\??\dest_path"
For deletes, the two strings would be "\??\src_path" and ""
In both cases, the formatting with the null-terminator character is handled automatically by the registry package.
Windows parses this key during reboots and after handling all of the renames and deletes, it deletes the
"PendingFileRenameOperations" entry. When reading this entry for the existing values, account for the situation
where it may not exist, as shown in the example.
Since this key is part of the HKEY_LOCAL_MACHINE hive, you must be an administrator to write to it. You can
do this by running the example from an command prompt opened with "Run as Administrator" or just accept the UAC
prompt that this example will generate. If the example detects that it is not elevated, it will relaunch as
elevated and prompt you with UAC to allow it.
After running the example, the "PendingFileRenameOperations" entry will be created in the
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager" key if it does not exist,
and an additional value will be appended to the entry referencing FileToDelete.tmp, followed by an empty string.
*/
const (
regHive = registry.LOCAL_MACHINE
regKey = `SYSTEM\CurrentControlSet\Control\Session Manager`
regName = `PendingFileRenameOperations`
regType = registry.MULTI_SZ
)
func main() {
// Relaunch as Admin via UAC prompt is running as standard user
if !amIAdmin() {
runMeAdmin()
return
}
defer func() {
// UAC likely launched this in a new console window to run as admin, so this keeps the window up
// If you can see <press enter to continue> there were no errors
fmt.Println("<press enter to continue>")
var input string
_, _ = fmt.Scanln(&input)
}()
// Get existing values | add filename to delete with prefix to slice | add empty string to slice
existingVals := readKey()
prefix := `\??\`
fName := `C:\Windows\Temp\FileToDelete.tmp`
newVal := append(existingVals, prefix + fName, "")
// write new slice to registry
writeKey(newVal)
}
func readKey() []string {
k, err := registry.OpenKey(regHive, regKey, registry.QUERY_VALUE)
if err != nil {
log.Fatal(err)
}
defer k.Close()
regData, _, err := k.GetStringsValue(regName)
if err == registry.ErrNotExist {
return []string{}
}
if err != nil {
log.Fatal(err)
}
return regData
}
func writeKey(vals []string) {
k, err := registry.OpenKey(regHive, regKey, registry.SET_VALUE)
if err != nil {
log.Fatal(err)
}
defer k.Close()
err = k.SetStringsValue(regName, vals)
if err != nil {
log.Fatal(err)
}
}
func amIAdmin() bool {
_, err := os.Open("\\\\.\\PHYSICALDRIVE0")
admin := err == nil
return admin
}
func runMeAdmin() {
verb := "runas"
exe, _ := os.Executable()
args := strings.Join(os.Args[1:], " ")
cwd, _ := os.Getwd()
verbPtr, _ := syscall.UTF16PtrFromString(verb)
exePtr, _ := syscall.UTF16PtrFromString(exe)
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
argPtr, _ := syscall.UTF16PtrFromString(args)
var showCmd int32 = 1 //SW_NORMAL
err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
if err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment