Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements #2

Open
keldnorman opened this issue Jan 17, 2020 · 13 comments
Open

Enhancements #2

keldnorman opened this issue Jan 17, 2020 · 13 comments

Comments

@keldnorman
Copy link

keldnorman commented Jan 17, 2020

sharplocker2

To get the current background image:

Image myimage = new Bitmap(@Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft\\Windows\\Themes\\TranscodedWallpaper"));

and to get the account picture:

RegistryKey AccountPictureReg = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\AccountPicture", true);
string AccountPictureFilename = AccountPictureReg.GetValue("SourceId").ToString();
AccountPictureReg.Close();

string AccountPicture = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft\\Windows\\AccountPictures\\" + AccountPictureFilename);

Console.WriteLine(" Value: " + AccountPicture);
@3top1a
Copy link

3top1a commented Feb 1, 2020

I made these changes & added username detection in my fork. Waiting for it to get merged.
*I used a different account picture detection, yours doesn't account for the default picture.

@Mezuse
Copy link

Mezuse commented Feb 2, 2020

hey just wondering where you would put this code? ^^^

@amydevs
Copy link

amydevs commented Feb 2, 2020

Would also be really cool if there were sign in options and support for multiple accounts to make it seem more believable, in the mean time I might try looking into ways to make this work.

@cftad
Copy link

cftad commented Feb 3, 2020

Would also be really cool if there were sign in options and support for multiple accounts to make it seem more believable, in the mean time I might try looking into ways to make this work.

@jy1263 I agree, I'm looking to do just that in a WPF fork (to give more control over the styling).
https://github.com/cftad/SharpLocker

@CL0Pinette
Copy link

There is one problem. the background image is not always the lockscreen image. I'm gonna try to replace the background image with the lockscreen image.

@amydevs
Copy link

amydevs commented Feb 6, 2020

@CL0Pinette been tryna figure that out but idk if thats possible without administrator privileges, because u can't access C:\ProgramData\Microsoft\Windows\SystemData<USER-SID>\ReadOnly\ without it.

@msvamp
Copy link

msvamp commented Feb 7, 2020

@CL0Pinette There is another idea.
A fairly large number of users have the Windows Spotlight feature turned on, which displays Bing-provided images on the lock screen. You can randomly grab one from %LOCALAPPDATA%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets as it doesn't require admin rights. Try to pick one with size > 250 or 300KB (or better, check its resolution or aspect ratio) so as to avoid picking a Start Menu tile image.
Also, I've noticed that Windows uses %WINDIR%\Web\Screen\img103.png as a fallback lock screen image when Spotlight is enabled but no Spotlight images have been downloaded yet, or if Spotlight is unavailable due to some reason.

I've also read that the registry keys OriginalFile_A or Creative\LandscapeAssetPath present in the location HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lock Screen\ contain the filesystem path to the current lock screen image in use, when Windows Spotlight is enabled. I've just enabled Spotlight on my PC and am yet to find out if it's true.

@KrypticCoconut
Copy link

KrypticCoconut commented Feb 9, 2020

Hello, keldnorman, where can i write that code for profile pin?

@phanirithvij
Copy link

phanirithvij commented Oct 10, 2020

@msvamp I think the originalfile_A that you mentioned is not related to the Windows spotlight.
I believe it corresponds to the lock screen with Picture.
image
I changed it a few times and the binary data does contain some of the parts of the files I've selected.

Also HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SystemProtectedUserData\<Your SID>\AnyoneRead\LockScreen has (Default) as ZACEDB or some permutation which changes when you select a different picture where each letter represents an image and the permutation represents the order in which stuff is shown in the settings page. Also, it's read-only.

I'd like to know where you read about Originalfile_A and how I can decode the binary data to the original file path tho...

@msvamp
Copy link

msvamp commented Oct 10, 2020

@phanirithvij I frankly don't remember where I found it. I did look through my history and only found some help articles and support pages that mention clearing certain entries in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lock Screen can help reset lock screen settings (and the wallpaper) to default to help people resolve issues with their lockscreen. (Maybe I didn't look deep enough in the history)

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SystemProtectedUserData\<Your SID>\AnyoneRead\LockScreen has (Default)

This looks something interesting, though I have hardly found the (Default) value containing any important data.

where you read about Originalfile_A and how I can decode the binary data to the original file path tho...

I don't think Microsoft has disclosed anywhere about how the value is stored in the registry key, but I found a useful LockScreen class in the Windows Runtime API after I read this answer on SuperUser. This could be of some use to you -

[Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]::OriginalImageFile.AbsolutePath

PowerShell Command Output

The problem with this path is: it is the path of the original file while applying a lockscreen image. So this value remains unchanged although the original image is deleted in the future.

@phanirithvij
Copy link

phanirithvij commented Oct 10, 2020

@msvamp Thanks for your quick reply.
I did put together some go code which can get the spotlight lock screen wallpaper path exactly following this blog from winhelponline.

Not a C# dev (not a go dev either :) )

Expand code
package main

import (
	"encoding/hex"
	"errors"
	"io"
	"log"
	"os/user"
	"strings"

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

func main() {
	log.SetFlags(0)
	// log.SetFlags(log.LstdFlags | log.Lshortfile)
	d, err := getLockScreenPath()
	if err != nil {
		log.Fatal(err)
	}
	log.Println(d)
	// fmt.Println(d)
}

func getSysLockScreenConfig() {
	// sid, err := getUserSID()
	// if err != nil {
	// 	log.Fatal(err)
	// }
	data, err := hex.DecodeString("9a19a1622d473e54073d5bd08883f92a1852")
	if err != nil {
		log.Fatal(err)
	}
	log.Println(data)
	// S-1-5-21-1131672954-3644571216-278812857-1001\SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen
	ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
	// ret := sid + `\SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
	key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
	// key, err := registry.OpenKey(registry.USERS, ret, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
	}
	subValues, err := key.ReadValueNames(100)
	for _, v := range subValues {
		_, b, err := key.GetValue(v, nil)
		if err != nil {
			log.Fatal(err)
		}

		if b == registry.SZ || b == registry.EXPAND_SZ {
			// data, _, _ := key.GetStringValue(v)
			// log.Println("string", data)
		} else if b == registry.BINARY {
			bin, _, _ := key.GetBinaryValue(v)
			log.Println(v, "binary", len(bin))
			data := []byte{}
			for i := 0; i < len(bin); i++ {
				if bin[i] != 0 {
					data = append(data, bin[i])
				}
			}
			log.Println(data, len(data))
			// log.Println(string(data), len(data))
		} else if b == registry.DWORD || b == registry.DWORD_BIG_ENDIAN {
			// data, _, _ := key.GetIntegerValue(v)
			// log.Println("integer", data)
		} else {
			// var buf []byte
			// buf = make([]byte, n)
			// n, b, err = key.GetValue(v, buf)
			// if err != nil {
			// 	log.Fatal(err)
			// }
			// log.Println("unknown type", buf)
		}

		// log.Println(key.GetStringValue())
	}
	if (err != nil && err != io.EOF) || len(subValues) == 0 {
		log.Fatal(err)
	}
}

func getUserSID() (string, error) {
	uinstance, err := user.Current()
	if err != nil {
		return "", err
	}
	// https://pkg.go.dev/os/user#User
	// Uid is the Sid on windows
	return uinstance.Uid, err
}

// ErrNotSpotLight means currently windows spotlight is turned off
var ErrNotSpotLight = errors.New("Not Windows spotlight")

func getLockScreenRegKeySpotLight() (string, error) {
	// win.GetCurrentProcessId()
	sid, err := getUserSID()
	if err != nil {
		return "", err
	}
	ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Creative\` + sid
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, ret, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	windowsSpotlight, _, err := key.GetIntegerValue("RotatingLockScreenEnabled")
	if err != nil {
		return "", err
	}
	if windowsSpotlight == 1 {
		subKeys, err := key.ReadSubKeyNames(100)
		if (err != nil && err != io.EOF) || len(subKeys) == 0 {
			log.Println(err)
			log.Fatal(err, " Subkeys doesn't exist, possibly wrong place to look for lockscreen ", len(subKeys))
			return "", err
		}
		return ret + `\` + subKeys[len(subKeys)-1], nil
	}
	return "", ErrNotSpotLight
}

func getLockScreenPath() (string, error) {
	sid, err := getUserSID()
	if err != nil {
		log.Fatal(err)
	}
	lockScreenRegKey, err := getLockScreenRegKeySpotLight()
	if err != nil {
		if err == ErrNotSpotLight {
			// not windows spotlight could be one of picture/slideshow
			log.Println("Not windows spotlight")
			ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
			key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
			slideshowEnabled, _, err := key.GetIntegerValue("SlideshowEnabled")
			if err != nil {
				log.Println(err)
				return "", nil
			}
			if slideshowEnabled == 1 {
				// check what registry changes
				// This is close to impossible as I have non lead
				log.Println("Slide show")
				ret = `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
				key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
				spath, _, err := key.GetStringValue("SlideshowDirectoryPath1")
				if err != nil {
					if err == registry.ErrNotExist {
						log.Println("No slideshow directories selected by user")
					} else {
						log.Println(err)
					}
				} else {
					// TODO decode SlideshowDirectoryPath1 etc.
					log.Println("Slideshow path encoded", spath)
				}
			} else {
				// Picture
				// TODO decode OriginalFile_A
				// printing as a string shows some kind of pattern can try
				ret = `SOFTWARE\Microsoft\Windows\CurrentVersion\SystemProtectedUserData\` + sid + `\AnyoneRead\LockScreen`
				log.Println("Picture")
				key, err := registry.OpenKey(registry.LOCAL_MACHINE, ret, registry.READ)
				s, _, err := key.GetStringValue("")
				if err != nil {
					log.Println(err)
				}
				// In the order there can be Letters L in A-Z
				// And they keys OriginalFile_{L} may or may not exist
				// For eg. OriginalFile_Z does not exist
				log.Println("Order", strings.Split(s, ""))
				getSysLockScreenConfig()
			}
			return "", nil
		}
		log.Fatal(err)
		return "", err
	}
	// Spotlight
	log.Println("Spotlight")
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, lockScreenRegKey, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	p, _, err := key.GetStringValue("landscapeImage")
	if err != nil {
		return "", err
	}
	return p, nil
}

Look at getLockScreenRegKeySpotLight and getLockScreenPath functions and ignore other stuff.

Also @jy1263 I was able to access C:\ProgramData\Microsoft\Windows\SystemData\<SID>\ReadOnly without admin privileges.

@phanirithvij
Copy link

@jy1263 a python snippet to list contents of the C:\ProgramData\Microsoft\Windows\SystemData\<SID>\ReadOnly dir, which runs for on my machine without needing of admin rights

from pathlib import Path
# pip install pywin32
import win32security
import os


def get_user_sid():
    desc = win32security.GetFileSecurity(
        ".", win32security.OWNER_SECURITY_INFORMATION
    )
    sid = desc.GetSecurityDescriptorOwner()

    # https://www.programcreek.com/python/example/71691/win32security.ConvertSidToStringSid
    sid = win32security.ConvertSidToStringSid(sid)
    return sid


sid = get_user_sid()
dirname = Path(
    f'C:\ProgramData\Microsoft\Windows\SystemData\{sid}\ReadOnly')
for root, dirs, files in os.walk(dirname, topdown=False):
    for name in files:
        print(os.path.join(root, name))
    for name in dirs:
        print(os.path.join(root, name))

@phanirithvij
Copy link

For anyone interested

Go code to read lockscreen state
package main

import (
	"encoding/hex"
	"errors"
	"io"
	"log"
	"os"
	"os/user"
	"strconv"
	"strings"

	"github.com/spf13/afero"

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

func main() {
	log.SetFlags(0)
	// log.SetFlags(log.LstdFlags | log.Lshortfile)
	d, err := getLockScreenPath()
	if err != nil {
		log.Fatal(err)
	}
	log.Println(d)
	// fmt.Println(d)
}

func getSysLockScreenConfig() {
	// sid, err := getUserSID()
	// if err != nil {
	// 	log.Fatal(err)
	// }
	data, err := hex.DecodeString("9a19a1622d473e54073d5bd08883f92a1852")
	if err != nil {
		log.Fatal(err)
	}
	log.Println(data)
	// S-1-5-21-1131672954-3644571216-278812857-1001\SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen
	ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
	// ret := sid + `\SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
	key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
	// key, err := registry.OpenKey(registry.USERS, ret, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
	}
	subValues, err := key.ReadValueNames(100)
	for _, v := range subValues {
		_, b, err := key.GetValue(v, nil)
		if err != nil {
			log.Fatal(err)
		}

		if b == registry.SZ || b == registry.EXPAND_SZ {
			// data, _, _ := key.GetStringValue(v)
			// log.Println("string", data)
		} else if b == registry.BINARY {
			bin, _, _ := key.GetBinaryValue(v)
			log.Println(v, "binary", len(bin))
			data := []byte{}
			for i := 0; i < len(bin); i++ {
				if bin[i] != 0 {
					data = append(data, bin[i])
				}
			}
			// log.Println(data, len(data))
			log.Println(string(data), len(data))
		} else if b == registry.DWORD || b == registry.DWORD_BIG_ENDIAN {
			// data, _, _ := key.GetIntegerValue(v)
			// log.Println("integer", data)
		} else {
			// var buf []byte
			// buf = make([]byte, n)
			// n, b, err = key.GetValue(v, buf)
			// if err != nil {
			// 	log.Fatal(err)
			// }
			// log.Println("unknown type", buf)
		}

		// log.Println(key.GetStringValue())
	}
	if (err != nil && err != io.EOF) || len(subValues) == 0 {
		log.Fatal(err)
	}
}

func getUserSID() (string, error) {
	uinstance, err := user.Current()
	if err != nil {
		return "", err
	}
	// https://pkg.go.dev/os/user#User
	// Uid is the Sid on windows
	return uinstance.Uid, err
}

// ErrNotSpotLight means currently windows spotlight is turned off
var ErrNotSpotLight = errors.New("Not Windows spotlight")

func getLockScreenRegKeySpotLight() (string, error) {
	// win.GetCurrentProcessId()
	sid, err := getUserSID()
	if err != nil {
		return "", err
	}
	ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Creative\` + sid
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, ret, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	windowsSpotlight, _, err := key.GetIntegerValue("RotatingLockScreenEnabled")
	if err != nil {
		return "", err
	}
	if windowsSpotlight == 1 {
		subKeys, err := key.ReadSubKeyNames(100)
		if (err != nil && err != io.EOF) || len(subKeys) == 0 {
			log.Println(err)
			log.Fatal(err, " Subkeys doesn't exist, possibly wrong place to look for lockscreen ", len(subKeys))
			return "", err
		}
		return ret + `\` + subKeys[len(subKeys)-1], nil
	}
	return "", ErrNotSpotLight
}

func getLockScreenPath() (string, error) {
	sid, err := getUserSID()
	if err != nil {
		log.Fatal(err)
	}
	lockScreenRegKey, err := getLockScreenRegKeySpotLight()
	if err != nil {
		if err == ErrNotSpotLight {
			// not windows spotlight could be one of picture/slideshow
			log.Println("Not windows spotlight")
			ret := `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
			key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
			slideshowEnabled, _, err := key.GetIntegerValue("SlideshowEnabled")
			if err != nil {
				log.Println(err)
				return "", nil
			}
			if slideshowEnabled == 1 {
				// check what registry changes
				// This is close to impossible as I have non lead
				log.Println("Slide show")
				ret = `SOFTWARE\Microsoft\Windows\CurrentVersion\Lock Screen`
				key, err := registry.OpenKey(registry.CURRENT_USER, ret, registry.READ)
				spath, _, err := key.GetStringValue("SlideshowDirectoryPath1")
				if err != nil {
					if err == registry.ErrNotExist {
						log.Println("No slideshow directories selected by user")
					} else {
						log.Println(err)
					}
				} else {
					// TODO decode SlideshowDirectoryPath1 etc.
					log.Println("Slideshow path encoded", spath)
					log.Fatal("Slideshow not IMPLEMENTED")
				}
			} else {
				// Picture
				// TODO decode OriginalFile_A
				// printing as a string shows some kind of pattern can try
				ret = `SOFTWARE\Microsoft\Windows\CurrentVersion\SystemProtectedUserData\` + sid + `\AnyoneRead\LockScreen`
				log.Println("Picture")
				key, err := registry.OpenKey(registry.LOCAL_MACHINE, ret, registry.READ)
				s, _, err := key.GetStringValue("")
				if err != nil {
					log.Println(err)
				}
				// In the order there can be Letters L in A-Z
				// And they keys OriginalFile_{L} may or may not exist
				// For eg. OriginalFile_Z does not exist
				if len(s) == 0 {
					log.Fatal("No pictures selected by user")
				}
				log.Println("Order", s)
				var labelx byte = s[0]
				filename, err := getFakeLockScreenFilePath(labelx)
				if err != nil {
					if _, ok := err.(*os.PathError); ok {
						// labled file doesn't exist
						// might be one from C:\Windows\Web\Screen
						webScreenDir := `C:\Windows\Web\Screen`
						// eg. Z is C:\Windows\Web\Screen\img100.jpg
						// TODO Assuming Z -> 100, Y -> 101, X -> 102 etc.
						inum := 190 - labelx
						if inum >= 100 && inum <= 105 {
							str := strconv.FormatInt((int64)(inum), 10)
							filename = webScreenDir + `\img` + str + ".jpg"
							_, err := os.Stat(filename)
							if err != nil {
								// dirty trick? No. Windows is dirty
								// if jpg not found png
								filename = webScreenDir + `\img` + str + ".png"
								_, err := os.Stat(filename)
								if err != nil {
									return "", err
								}
							}
							return filename, nil
						}
						return "", errors.New("Couldn't get the image")
					}
					log.Fatal(err)
				}
				// getSysLockScreenConfig()
				// sysDataRecover()
				return filename, nil
			}
			return "", nil
		}
		log.Fatal(err)
		return "", err
	}
	// Spotlight
	log.Println("Spotlight")
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, lockScreenRegKey, registry.READ)
	defer key.Close()
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	p, _, err := key.GetStringValue("landscapeImage")
	if err != nil {
		return "", err
	}
	return p, nil
}

func sysDataRecover() {
	sid, err := getUserSID()
	if err != nil {
		log.Fatal(err)
	}
	systemData := `C:\ProgramData\Microsoft\Windows\SystemData\` + sid + `\ReadOnly`
	base := afero.NewOsFs()
	folder, err := base.Open(systemData)
	defer folder.Close()
	if err != nil {
		log.Fatal(err)
	}
	afero.Walk(base, folder.Name(), func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Fatal(err)
		}
		// TODO file can be PNG with .jpg extension
		// Need to check file magic info or something
		if !info.IsDir() && strings.HasSuffix(info.Name(), "LockScreen.jpg") {
			log.Println(path, info.Size())
		}
		return nil
	})
}

func getFakeLockScreenFilePath(label byte) (string, error) {
	sid, err := getUserSID()
	if err != nil {
		return "", err
	}
	systemData := `C:\ProgramData\Microsoft\Windows\SystemData\` + sid + `\ReadOnly\LockScreen_` + string(label) + `\LockScreen.jpg`
	lockscrfile, err := os.Open(systemData)
	defer lockscrfile.Close()
	if err != nil {
		return "", err
	}
	return lockscrfile.Name(), nil
}

go get -u -v github.com/spf13/afero golang.org/x/sys/windows/registry
Then go run file.go or go build file.go && ./file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants