6

I originally posted this on Stack Overflow but was told that this may be a better place to post this question, so here goes;

I've created a small script which will automatically reboot a computer into it's recovery sequence (Windows Recovery Environment). Here is my code:

$testpath = test-path "bcdedit.txt"
if ($testpath -eq $true){
    Remove-item "bcdedit.txt"
}
bcdedit | Out-File "bcdedit.txt"
foreach($line in Get-Content .\bcdedit.txt) {
    if($line -match $regex){
        if($line -like 'recoverysequence*') {
            $variable = "{" + $line.Split('{')[-1]
        }
    }
}
bcdedit /bootsequence $variable
Remove-item "bcdedit.txt"
shutdown /r /t 1

This Powershell script is ran from my Electron (a Node.js-based framework) app. This app runs on hundreds of different computer models per day, gathers their hardware information and compares them with a database to retrieve the unit's UPC in a production environment.

However, I have this feature because I couldn't figure out a way to do what I really wanted to do. I wanted this script to actually reboot a computer, and boot into a USB drive's .EFI file. Rebooting to Windows Recovery Environment was a workaround that I implemented because it presents a button for booting into a USB flash drive. I'm posting here because I would really like to eliminate this step and just boot straight into the USB flash drive. The USB flash drive that is running the program will also contain a bootable .EFI file.

Now the question is: How can I write a code (in PowerShell, Electron, or Node.js) that reboots the computer and makes it boot from the USB flash drive?

I've tried looking into how to do this with bcdedit, and cannot find anything, and I don't really know any other search terms to try to find what I need, I'm at a loss here.

GrumpyCrouton
  • 131
  • 2
  • 8

3 Answers3

4

Ignoring the fact that this sounds like an XY problem since I see no explanation for the need to boot to an external drive to "gather hardware information" when you already have administrative access to the running OS...

It's pretty easy to get Windows to boot into a plugged in USB drive. You create a boot entry for the drive using bcdedit create, then tell Windows to boot into it. Creating boot entries is documented by Microsoft here

As per the documentation it may be easier to copy an existing entry:

bcdedit /copy {current} /d "My Boot Entry Description"

bcdedit will return the GUID of the new boot entry which you will need to extract:

The entry was successfully copied to {some-autogenerated-guid-here}.

Then set the new entry's options to point to your USB device. The minimum properties you'll need to change are the device, osdevice, and path. Depending on what it is you are booting, you may need to modify other values - just look at the bcdedit output for your existing Windows loader and/or PE/PXE loaders as examples.

bcdedit /set {your-new-boot-entry-guid} device partition=D:
bcdedit /set {your-new-boot-entry-guid} osdevice partition=D:
bcdedit /set {your-new-boot-entry-guid} path \Location\Of\Your\Bootloader.efi

Then tell Windows to boot to it as you're already doing:

bcdedit /bootsequence {your-new-boot-entry-guid}

Obviously, replace My Boot Entry Description with something useful, perhaps a unique identifier to help you locate the entry in future to reuse or delete it if you are running said script every day. Replace D: with whatever the drive letter is of the USB device you are trying to boot; if you are doing this on heterogeneous systems you may have to determine this programmatically as drive letters will vary depending on the system. And replace \Location\Of\Your\Bootloader.efi with, well, the relative path of the .EFI file you are trying to boot.

qasdfdsaq
  • 6,779
4

Rather than looking toward the Boot Manager, look at the Firmware Boot Manager, which runs even before the Boot Manager. On your system, start by running the command

bcdedit /enum FIRMWARE

which should show {fwbootmgr} along with entries for any bootable device partitions. Your USB device (assumed F:) should have an entry with lines like:

identifier              {9f9738d7-c2c0-11ea-89ac-806e6f6e6963}
device                  partition=F:

With your device's identifier in %Your_ID%, run:

bcdedit /set {fwbootmgr} bootsequence %Your_ID%

The bootsequence parameter will instruct the firmware boot manager to boot to that device/partition, the next time ONLY. After that, it will revert to its current "displayorder", which probably has {bootmgr} first.

Here's a Windows batch file snippet that may help with finding the {id} from your USB device's drive letter:

setlocal ENABLEDELAYEDEXPANSION

set DEVICE=partition=F: for /F "usebackq tokens=1-3" %%a in ( bcdedit /enum FIRMWARE ) DO ( if /I "%%a" == "identifier" set ID=%%b if /I "%%b" == "%DEVICE%" set F_ID=!ID! ) echo Found ID %F_ID% bcdedit /set {fwbootmgr} bootsequence %F_ID%

I hope that helps.

1

its another two years later, but I recently did something similar, and I think this might also work for your case.

Instead of looking towards the Windows Boot Manger, or even the Firmware Boot Manager, instead we modify the EFI directly, to indirectly modify the BIOS Boot Order, without actually having to modify the BIOS.

Basically, I have a Windows and a Linux(Ubuntu) Partition on separate drives, with separate Bootloaders on each drive. They can either use separate EFI Partitions, or even share the same EFI Partition.

What the BIOS does on boot in simple terms is it looks for EFI Partition on all drives, and then for .EFI files on these partitions, and generates the boot entries from any Bootloaders it finds. These can be either one per EFI partition or several. Usually they will be installed in separate folders.

What I did was write a script, that would rename all the .EFI files of the currently active OS to a different File extension (like .BAK), and vice versa for the other OS that I want to boot to. This causes the BIOS to discover only the Boot Manager of the other OS upon reboot and boot to that, as if it was the only drive in the system. To return to the original OS, you have a second script on the other OS that does the same thing in reverse.

On Linux, you can modify the current EFI partition directly at /boot/efi (if you are root), and if you have a second EFI partition, you can mount it manually just like any fat32 partition.

On Windows, you can programmatically mount EFI partitions by using diskpart scripts.

The below example scripts use a shared EFI partition, but it should work just as well with two EFI partitions, you would just have to mount both of them.

Example batch script to switch from Windows to Ubuntu:

@echo off
echo Mounting EFI Partition...
diskpart /s C:\Users\permissionBRICK\source\script\diskpart_boot_linux.txt
echo Enabling Linux Bootmgr...

move M:\EFI\ubuntu\grubx64.bak M:\EFI\ubuntu\grubx64.efi move M:\EFI\ubuntu\mmx64.bak M:\EFI\ubuntu\mmx64.efi move M:\EFI\ubuntu\shimx64.bak M:\EFI\ubuntu\shimx64.efi echo Disabling Windows Bootmgr... move M:\EFI\Microsoft\Boot\memtest.efi M:\EFI\Microsoft\Boot\memtest.bak move M:\EFI\Microsoft\Boot\bootmgfw.efi M:\EFI\Microsoft\Boot\bootmgfw.bak move M:\EFI\Microsoft\Boot\bootmgr.efi M:\EFI\Microsoft\Boot\bootmgr.bak

echo Unmounting EFI Partition... diskpart /s C:\Users\permissionBRICK\source\script\diskpart_boot_linux_done.txt echo Done. Rebooting in 10 sec. shutdown -r -t 1

Example diskpart_boot_linux.txt:

sel disk 2
sel part 1
assign letter=M

Example diskpart_boot_linux_done.txt:

sel disk 2
sel part 1
remove

Find out the Disk and Partition numbers by running diskpart in console and using list disk and list part Beware that these may change if you add more drives to the system.

Example bash script to boot from Ubuntu into Windows:

sudo mv /boot/efi/EFI/Microsoft/memtest.bak /boot/efi/EFI/Microsoft/memtest.efi
sudo mv /boot/efi/EFI/Microsoft/bootmgfw.bak /boot/efi/EFI/Microsoft/bootmgfw.efi
sudo mv /boot/efi/EFI/Microsoft/bootmgr.bak /boot/efi/EFI/Microsoft/bootmgr.efi
sudo mv /boot/efi/EFI/ubuntu/grubx64.EFI /boot/efi/EFI/ubuntu/grubx64.bak
sudo mv /boot/efi/EFI/ubuntu/mmx64.EFI /boot/efi/EFI/ubuntu/mmx64.bak
sudo mv /boot/efi/EFI/ubuntu/shimx64.EFI /boot/efi/EFI/ubuntu/shimx64.bak
sudo reboot

The downside of this method is that unless you use the scripts, you will only ever to be able to boot to the current OS, unless you run system repair, not even via BIOS Manual Boot device select. Also, this will only work if you have no other Boot devices in your Boot order like Network Boot etc, since some BIOSes will shuffle around the boot order every time you run the script since it is technically a new Boot Option every time, so to have it work seamlessly, make sure the two boot drives are the only available Boot Options in the Boot order, so the currently selected OS will always be the only Boot Option available.

The upside of using this, is that it even works with SecureBoot and BitLocker enabled, you don't even need to pause BitLocker to set up the dual boot, as long as the first boot device is Windows Boot Manager, the TPM is happy, and if you boot Linux, Bitlocker is not active anyway.