|
|
1.1 root 1: /* 1.1.1.13! root 2: Copyright (c) 2008-2011 TrueCrypt Developers Association. All rights reserved. 1.1 root 3: 1.1.1.12 root 4: Governed by the TrueCrypt License 3.0 the full text of which is contained in 1.1.1.11 root 5: the file License.txt included in TrueCrypt binary and source code distribution 6: packages. 1.1 root 7: */ 8: 9: #include "Crc.h" 10: #include "Crypto.h" 11: #include "Password.h" 12: #include "Volumes.h" 13: 14: #include "Platform.h" 15: #include "Bios.h" 16: #include "BootConfig.h" 17: #include "BootMain.h" 18: #include "BootDefs.h" 19: #include "BootCommon.h" 20: #include "BootConsoleIo.h" 21: #include "BootDebug.h" 22: #include "BootDiskIo.h" 23: #include "BootEncryptedIo.h" 1.1.1.2 root 24: #include "BootMemory.h" 1.1.1.6 root 25: #include "BootStrings.h" 1.1 root 26: #include "IntFilter.h" 27: 28: 29: static void InitScreen () 30: { 31: ClearScreen(); 32: 1.1.1.8 root 33: const char *title = 1.1.1.5 root 34: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 35: " TrueCrypt Boot Loader " 36: #else 37: " TrueCrypt Rescue Disk " 38: #endif 1.1.1.10 root 39: VERSION_STRING "\r\n"; 1.1.1.8 root 40: 41: Print (title); 1.1 root 42: 1.1.1.5 root 43: PrintRepeatedChar ('\xDC', TC_BIOS_MAX_CHARS_PER_LINE); 1.1.1.6 root 44: 45: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 46: if (CustomUserMessage[0]) 47: { 48: PrintEndl(); 49: Print (CustomUserMessage); 50: } 51: #endif 52: 1.1 root 53: PrintEndl (2); 54: } 55: 56: 57: static void PrintMainMenu () 58: { 1.1.1.6 root 59: if (PreventBootMenu) 60: return; 61: 1.1 root 62: Print (" Keyboard Controls:\r\n"); 1.1.1.5 root 63: Print (" [Esc] "); 64: 65: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 66: 1.1.1.6 root 67: Print ((BootSectorFlags & TC_BOOT_CFG_MASK_HIDDEN_OS_CREATION_PHASE) != TC_HIDDEN_OS_CREATION_PHASE_NONE 68: ? "Boot Non-Hidden System (Boot Manager)" 1.1.1.5 root 69: : "Skip Authentication (Boot Manager)"); 70: 71: #else // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 72: 73: Print ("Skip Authentication (Boot Manager)"); 74: Print ("\r\n [F8] "); Print ("Repair Options"); 75: 76: #endif // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1.1 root 77: 78: PrintEndl (3); 79: } 80: 81: 82: static bool IsMenuKey (byte scanCode) 83: { 1.1.1.5 root 84: #ifdef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1.1 root 85: return scanCode == TC_MENU_KEY_REPAIR; 1.1.1.5 root 86: #else 87: return false; 88: #endif 1.1 root 89: } 90: 91: 92: static bool AskYesNo (const char *message) 93: { 94: Print (message); 95: Print ("? (y/n): "); 96: while (true) 97: { 98: switch (GetKeyboardChar()) 99: { 100: case 'y': 101: case 'Y': 1.1.1.3 root 102: case 'z': 1.1.1.6 root 103: case 'Z': 104: Print ("y\r\n"); 1.1 root 105: return true; 106: 107: case 'n': 108: case 'N': 1.1.1.6 root 109: Print ("n\r\n"); 1.1 root 110: return false; 111: 112: default: 113: Beep(); 114: } 115: } 116: } 117: 118: 119: static int AskSelection (const char *options[], size_t optionCount) 120: { 121: for (int i = 0; i < optionCount; ++i) 122: { 123: Print ("["); Print (i + 1); Print ("] "); 124: Print (options[i]); 125: PrintEndl(); 126: } 127: Print ("[Esc] Cancel\r\n\r\n"); 128: 129: Print ("To select, press 1-9: "); 130: 131: char str; 132: 133: while (true) 134: { 135: if (GetString (&str, 1) == 0) 136: return 0; 137: 138: if (str >= '1' && str <= optionCount + '0') 139: return str - '0'; 140: 141: Beep(); 142: PrintBackspace(); 143: } 144: } 145: 146: 1.1.1.6 root 147: static byte AskPassword (Password &password) 1.1 root 148: { 149: size_t pos = 0; 150: byte scanCode; 151: byte asciiCode; 152: 1.1.1.5 root 153: Print ("Enter password"); 1.1.1.6 root 154: Print (PreventNormalSystemBoot ? " for hidden system:\r\n" : ": "); 1.1 root 155: 156: while (true) 157: { 158: asciiCode = GetKeyboardChar (&scanCode); 159: 160: switch (scanCode) 161: { 162: case TC_BIOS_KEY_ENTER: 1.1.1.2 root 163: ClearBiosKeystrokeBuffer(); 1.1 root 164: PrintEndl(); 1.1.1.2 root 165: 166: password.Length = pos; 167: return scanCode; 1.1 root 168: 169: case TC_BIOS_KEY_BACKSPACE: 170: if (pos > 0) 171: { 172: if (pos < MAX_PASSWORD) 173: PrintBackspace(); 174: else 1.1.1.6 root 175: PrintCharAtCursor (' '); 1.1 root 176: 177: --pos; 178: } 179: continue; 180: 181: default: 182: if (scanCode == TC_BIOS_KEY_ESC || IsMenuKey (scanCode)) 183: { 184: burn (password.Text, sizeof (password.Text)); 1.1.1.2 root 185: ClearBiosKeystrokeBuffer(); 186: 1.1 root 187: PrintEndl(); 188: return scanCode; 189: } 190: } 191: 192: if (!IsPrintable (asciiCode) || pos == MAX_PASSWORD) 193: { 194: Beep(); 195: continue; 196: } 197: 198: password.Text[pos++] = asciiCode; 199: if (pos < MAX_PASSWORD) 200: PrintChar ('*'); 201: else 1.1.1.6 root 202: PrintCharAtCursor ('*'); 1.1 root 203: } 204: } 205: 206: 207: static void ExecuteBootSector (byte drive, byte *sectorBuffer) 208: { 209: Print ("Booting...\r\n"); 210: CopyMemory (sectorBuffer, 0x0000, 0x7c00, TC_LB_SIZE); 1.1.1.3 root 211: 1.1.1.6 root 212: BootStarted = true; 213: 1.1.1.3 root 214: uint32 addr = 0x7c00; 215: __asm 216: { 217: cli 1.1.1.8 root 218: mov dl, drive // Boot drive 219: mov dh, 0 1.1.1.3 root 220: xor ax, ax 1.1.1.8 root 221: mov si, ax 1.1.1.3 root 222: mov ds, ax 223: mov es, ax 224: mov ss, ax 1.1.1.8 root 225: mov sp, 0x7c00 1.1.1.3 root 226: sti 1.1.1.8 root 227: 1.1.1.3 root 228: jmp cs:addr 229: } 1.1 root 230: } 231: 232: 1.1.1.6 root 233: static bool OpenVolume (byte drive, Password &password, CRYPTO_INFO **cryptoInfo, uint32 *headerSaltCrc32, bool skipNormal, bool skipHidden) 1.1 root 234: { 1.1.1.6 root 235: int volumeType; 236: bool hiddenVolume; 1.1 root 237: uint64 headerSec; 1.1.1.5 root 238: 1.1.1.3 root 239: AcquireSectorBuffer(); 1.1 root 240: 1.1.1.6 root 241: for (volumeType = 1; volumeType <= 2; ++volumeType) 1.1 root 242: { 1.1.1.6 root 243: hiddenVolume = (volumeType == 2); 1.1.1.5 root 244: 1.1.1.6 root 245: if (hiddenVolume) 246: { 247: if (skipHidden || PartitionFollowingActive.Drive != drive || PartitionFollowingActive.SectorCount <= ActivePartition.SectorCount) 248: continue; 1.1.1.5 root 249: 1.1.1.6 root 250: headerSec = PartitionFollowingActive.StartSector + TC_HIDDEN_VOLUME_HEADER_OFFSET / TC_LB_SIZE; 251: } 252: else 1.1.1.5 root 253: { 1.1.1.6 root 254: if (skipNormal) 255: continue; 256: 257: headerSec.HighPart = 0; 258: headerSec.LowPart = TC_BOOT_VOLUME_HEADER_SECTOR; 1.1.1.5 root 259: } 260: 1.1.1.6 root 261: if (ReadSectors (SectorBuffer, drive, headerSec, 1) != BiosResultSuccess) 262: continue; 263: 264: if (ReadVolumeHeader (!hiddenVolume, (char *) SectorBuffer, &password, cryptoInfo, nullptr) == ERR_SUCCESS) 1.1.1.5 root 265: { 1.1.1.6 root 266: // Prevent opening a non-system hidden volume 267: if (hiddenVolume && !((*cryptoInfo)->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM)) 268: { 269: crypto_close (*cryptoInfo); 270: continue; 271: } 272: 273: if (headerSaltCrc32) 274: *headerSaltCrc32 = GetCrc32 (SectorBuffer, PKCS5_SALT_SIZE); 1.1.1.5 root 275: 1.1.1.6 root 276: break; 277: } 1.1 root 278: } 279: 1.1.1.3 root 280: ReleaseSectorBuffer(); 1.1.1.6 root 281: return volumeType != 3; 1.1 root 282: } 283: 284: 1.1.1.2 root 285: static bool CheckMemoryRequirements () 286: { 287: uint16 codeSeg; 288: __asm mov codeSeg, cs 289: if (codeSeg == TC_BOOT_LOADER_LOWMEM_SEGMENT) 290: { 1.1.1.10 root 291: PrintErrorNoEndl ("BIOS reserved too much memory: "); 1.1.1.2 root 292: 293: uint16 memFree; 294: __asm 295: { 296: push es 297: xor ax, ax 298: mov es, ax 299: mov ax, es:[0x413] 300: mov memFree, ax 301: pop es 302: } 303: 1.1.1.6 root 304: Print (memFree); 1.1.1.8 root 305: PrintEndl(); 1.1.1.6 root 306: Print (TC_BOOT_STR_UPGRADE_BIOS); 1.1.1.5 root 307: 1.1.1.2 root 308: return false; 309: } 310: 311: return true; 312: } 313: 314: 1.1.1.5 root 315: static bool MountVolume (byte drive, byte &exitKey, bool skipNormal, bool skipHidden) 1.1 root 316: { 317: BootArguments *bootArguments = (BootArguments *) TC_BOOT_LOADER_ARGS_OFFSET; 1.1.1.3 root 318: int incorrectPasswordCount = 0; 1.1 root 319: 1.1.1.5 root 320: EraseMemory (bootArguments, sizeof (*bootArguments)); 321: 1.1 root 322: // Open volume header 323: while (true) 324: { 1.1.1.6 root 325: exitKey = AskPassword (bootArguments->BootPassword); 1.1 root 326: 327: if (exitKey != TC_BIOS_KEY_ENTER) 328: return false; 329: 1.1.1.5 root 330: if (OpenVolume (BootDrive, bootArguments->BootPassword, &BootCryptoInfo, &bootArguments->HeaderSaltCrc32, skipNormal, skipHidden)) 1.1 root 331: break; 332: 1.1.1.5 root 333: if (GetShiftFlags() & TC_BIOS_SHIFTMASK_CAPSLOCK) 334: Print ("Warning: Caps Lock is on.\r\n"); 335: 1.1.1.4 root 336: Print ("Incorrect password.\r\n\r\n"); 1.1.1.3 root 337: 1.1.1.6 root 338: if (++incorrectPasswordCount == 4) 1.1.1.3 root 339: { 1.1.1.6 root 340: #ifdef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1.1.1.10 root 341: Print ("If you are sure the password is correct, the key data may be damaged.\r\n" 342: "If so, use 'Repair Options' > 'Restore key data'.\r\n\r\n"); 1.1.1.6 root 343: #else 1.1.1.3 root 344: Print ("If you are sure the password is correct, the key data may be damaged. Boot your\r\n" 345: "TrueCrypt Rescue Disk and select 'Repair Options' > 'Restore key data'.\r\n\r\n"); 1.1.1.6 root 346: #endif 1.1.1.3 root 347: } 1.1 root 348: } 349: 350: // Setup boot arguments 1.1.1.5 root 351: bootArguments->BootLoaderVersion = VERSION_NUM; 1.1 root 352: bootArguments->CryptoInfoOffset = (uint16) BootCryptoInfo; 353: bootArguments->CryptoInfoLength = sizeof (*BootCryptoInfo); 1.1.1.10 root 354: 1.1.1.5 root 355: if (BootCryptoInfo->hiddenVolume) 356: bootArguments->HiddenSystemPartitionStart = PartitionFollowingActive.StartSector << TC_LB_SIZE_BIT_SHIFT_DIVISOR; 357: 1.1.1.10 root 358: if (ExtraBootPartitionPresent) 359: bootArguments->Flags |= TC_BOOT_ARGS_FLAG_EXTRA_BOOT_PARTITION; 360: 1.1 root 361: TC_SET_BOOT_ARGUMENTS_SIGNATURE (bootArguments->Signature); 362: 1.1.1.5 root 363: // Setup virtual encrypted partition 1.1 root 364: if (BootCryptoInfo->EncryptedAreaLength.HighPart != 0 || BootCryptoInfo->EncryptedAreaLength.LowPart != 0) 365: { 366: EncryptedVirtualPartition.Drive = BootDrive; 367: 1.1.1.5 root 368: EncryptedVirtualPartition.StartSector = BootCryptoInfo->EncryptedAreaStart >> TC_LB_SIZE_BIT_SHIFT_DIVISOR; 369: 370: HiddenVolumeStartUnitNo = EncryptedVirtualPartition.StartSector; 371: HiddenVolumeStartSector = PartitionFollowingActive.StartSector; 372: HiddenVolumeStartSector += EncryptedVirtualPartition.StartSector; 373: 374: EncryptedVirtualPartition.SectorCount = BootCryptoInfo->EncryptedAreaLength >> TC_LB_SIZE_BIT_SHIFT_DIVISOR; 375: 376: EncryptedVirtualPartition.EndSector = EncryptedVirtualPartition.SectorCount - 1; 377: EncryptedVirtualPartition.EndSector += EncryptedVirtualPartition.StartSector; 1.1 root 378: } 379: else 380: { 381: // Drive not encrypted 1.1.1.5 root 382: EncryptedVirtualPartition.Drive = TC_INVALID_BIOS_DRIVE; 1.1 root 383: } 384: 385: return true; 386: } 387: 388: 1.1.1.10 root 389: static bool GetSystemPartitions (byte drive) 390: { 391: size_t partCount; 392: 393: if (!GetActivePartition (drive)) 394: return false; 395: 396: // Find partition following the active one 397: GetDrivePartitions (drive, &PartitionFollowingActive, 1, partCount, false, &ActivePartition); 398: 399: // If there is an extra boot partition, use the partitions following it. 400: // The real boot partition is determined in BootEncryptedDrive(). 401: if (ActivePartition.SectorCount.HighPart == 0 && ActivePartition.SectorCount.LowPart <= TC_MAX_EXTRA_BOOT_PARTITION_SIZE / TC_LB_SIZE 402: && PartitionFollowingActive.Drive != TC_INVALID_BIOS_DRIVE) 403: { 404: ExtraBootPartitionPresent = true; 405: 406: ActivePartition = PartitionFollowingActive; 407: GetDrivePartitions (drive, &PartitionFollowingActive, 1, partCount, false, &ActivePartition); 408: } 409: 410: return true; 411: } 412: 413: 1.1 root 414: static byte BootEncryptedDrive () 415: { 1.1.1.5 root 416: BootArguments *bootArguments = (BootArguments *) TC_BOOT_LOADER_ARGS_OFFSET; 1.1.1.6 root 417: byte exitKey; 418: BootCryptoInfo = NULL; 1.1 root 419: 1.1.1.10 root 420: if (!GetSystemPartitions (BootDrive)) 1.1 root 421: goto err; 422: 1.1.1.6 root 423: if (!MountVolume (BootDrive, exitKey, PreventNormalSystemBoot, false)) 1.1 root 424: return exitKey; 425: 1.1.1.2 root 426: if (!CheckMemoryRequirements ()) 427: goto err; 1.1.1.5 root 428: 429: if (BootCryptoInfo->hiddenVolume) 430: { 431: EncryptedVirtualPartition = ActivePartition; 432: bootArguments->DecoySystemPartitionStart = ActivePartition.StartSector << TC_LB_SIZE_BIT_SHIFT_DIVISOR; 1.1.1.2 root 433: } 434: 1.1.1.10 root 435: if (ExtraBootPartitionPresent && !GetActivePartition (BootDrive)) 436: goto err; 437: 1.1.1.13! root 438: if (ReadWriteMBR (false, ActivePartition.Drive) != BiosResultSuccess) ! 439: goto err; ! 440: ! 441: bootArguments->BootDriveSignature = *(uint32 *) (SectorBuffer + 0x1b8); ! 442: 1.1.1.2 root 443: if (!InstallInterruptFilters()) 444: goto err; 1.1 root 445: 1.1.1.5 root 446: bootArguments->BootArgumentsCrc32 = GetCrc32 ((byte *) bootArguments, (byte *) &bootArguments->BootArgumentsCrc32 - (byte *) bootArguments); 447: 1.1.1.3 root 448: while (true) 1.1 root 449: { 1.1.1.3 root 450: // Execute boot sector of the active partition 1.1.1.5 root 451: if (ReadSectors (SectorBuffer, ActivePartition.Drive, ActivePartition.StartSector, 1) == BiosResultSuccess) 1.1.1.3 root 452: { 1.1.1.6 root 453: if (*(uint16 *) (SectorBuffer + 510) != 0xaa55) 454: { 455: PrintError (TC_BOOT_STR_NO_BOOT_PARTITION); 456: GetKeyboardChar(); 457: } 458: 1.1.1.5 root 459: ExecuteBootSector (ActivePartition.Drive, SectorBuffer); 1.1.1.3 root 460: } 461: 462: GetKeyboardChar(); 1.1 root 463: } 464: 465: err: 1.1.1.3 root 466: if (BootCryptoInfo) 467: { 468: crypto_close (BootCryptoInfo); 469: BootCryptoInfo = NULL; 470: } 471: 1.1.1.5 root 472: EncryptedVirtualPartition.Drive = TC_INVALID_BIOS_DRIVE; 473: EraseMemory ((void *) TC_BOOT_LOADER_ARGS_OFFSET, sizeof (BootArguments)); 1.1.1.2 root 474: 1.1 root 475: byte scanCode; 476: GetKeyboardChar (&scanCode); 477: return scanCode; 478: } 479: 480: 481: static void BootMenu () 482: { 483: BiosResult result; 484: Partition partitions[16]; 485: Partition bootablePartitions[9]; 486: size_t partitionCount; 487: size_t bootablePartitionCount = 0; 488: 489: for (byte drive = TC_FIRST_BIOS_DRIVE; drive <= TC_LAST_BIOS_DRIVE; ++drive) 490: { 1.1.1.5 root 491: if (GetDrivePartitions (drive, partitions, array_capacity (partitions), partitionCount, false, nullptr, true) == BiosResultSuccess) 1.1 root 492: { 493: for (size_t i = 0; i < partitionCount; ++i) 494: { 495: const Partition &partition = partitions[i]; 1.1.1.3 root 496: result = ReadSectors (SectorBuffer, drive, partition.StartSector, 1); 1.1 root 497: 1.1.1.3 root 498: if (result == BiosResultSuccess && *(uint16 *) (SectorBuffer + TC_LB_SIZE - 2) == 0xaa55) 1.1 root 499: { 500: // Windows writes boot loader on all NTFS/FAT filesytems it creates and, therefore, 501: // NTFS/FAT partitions must have the boot indicator set to be considered bootable. 502: if (!partition.Active 1.1.1.3 root 503: && (*(uint32 *) (SectorBuffer + 3) == 0x5346544e // 'NTFS' 1.1.1.8 root 504: || *(uint32 *) (SectorBuffer + 3) == 0x41465845 && SectorBuffer[7] == 'T' // 'exFAT' 1.1.1.3 root 505: || *(uint16 *) (SectorBuffer + 54) == 0x4146 && SectorBuffer[56] == 'T' // 'FAT' 506: || *(uint16 *) (SectorBuffer + 82) == 0x4146 && SectorBuffer[84] == 'T')) 1.1 root 507: { 508: continue; 509: } 510: 511: // Bootable sector found 512: if (bootablePartitionCount < array_capacity (bootablePartitions)) 513: bootablePartitions[bootablePartitionCount++] = partition; 514: } 515: } 516: } 517: } 518: 519: if (bootablePartitionCount < 1) 520: { 1.1.1.6 root 521: PrintError (TC_BOOT_STR_NO_BOOT_PARTITION); 1.1 root 522: GetKeyboardChar(); 523: return; 524: } 525: 526: char partChar; 527: while (true) 528: { 529: InitScreen(); 530: Print ("Bootable Partitions:\r\n"); 531: PrintRepeatedChar ('\xC4', 20); 532: Print ("\r\n"); 533: 534: for (size_t i = 0; i < bootablePartitionCount; ++i) 535: { 536: const Partition &partition = bootablePartitions[i]; 537: Print ("["); Print (i + 1); Print ("] "); 538: Print ("Drive: "); Print (partition.Drive - TC_FIRST_BIOS_DRIVE); 539: Print (", Partition: "); Print (partition.Number + 1); 1.1.1.5 root 540: Print (", Size: "); PrintSectorCountInMB (partition.SectorCount); PrintEndl(); 1.1 root 541: } 542: 543: if (bootablePartitionCount == 1) 544: { 545: // There's only one bootable partition so we'll boot it directly instead of showing boot manager 546: partChar = '1'; 547: } 548: else 549: { 550: Print ("[Esc] Cancel\r\n\r\n"); 551: Print ("Press 1-9 to select partition: "); 552: 553: if (GetString (&partChar, 1) == 0) 554: return; 555: 556: PrintEndl(); 557: 558: if (partChar < '1' || partChar > '0' + bootablePartitionCount) 559: { 560: Beep(); 561: continue; 562: } 563: } 564: 565: const Partition &partition = bootablePartitions[partChar - '0' - 1]; 566: 1.1.1.3 root 567: if (ReadSectors (SectorBuffer, partition.Drive, partition.StartSector, 1) == BiosResultSuccess) 1.1 root 568: { 1.1.1.3 root 569: ExecuteBootSector (partition.Drive, SectorBuffer); 1.1 root 570: } 571: } 572: } 573: 574: 1.1.1.5 root 575: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 576: 1.1.1.10 root 577: static bool CopySystemPartitionToHiddenVolume (byte drive, byte &exitKey) 1.1.1.5 root 578: { 579: bool status = false; 580: 581: uint64 sectorsRemaining; 582: uint64 sectorOffset; 583: sectorOffset.LowPart = 0; 584: sectorOffset.HighPart = 0; 585: 586: int fragmentSectorCount = 0x7f; // Maximum safe value supported by BIOS 587: int statCount; 588: 589: if (!CheckMemoryRequirements ()) 590: goto err; 591: 1.1.1.10 root 592: if (!GetSystemPartitions (drive)) 1.1.1.5 root 593: goto err; 594: 595: if (PartitionFollowingActive.Drive == TC_INVALID_BIOS_DRIVE) 1.1.1.6 root 596: TC_THROW_FATAL_EXCEPTION; 597: 598: // Check if BIOS can read the last sector of the hidden system 599: AcquireSectorBuffer(); 600: 601: if (ReadSectors (SectorBuffer, PartitionFollowingActive.Drive, PartitionFollowingActive.EndSector - (TC_VOLUME_HEADER_GROUP_SIZE / TC_LB_SIZE - 2), 1) != BiosResultSuccess 1.1.1.8 root 602: || GetCrc32 (SectorBuffer, sizeof (SectorBuffer)) != OuterVolumeBackupHeaderCrc) 1.1.1.5 root 603: { 1.1.1.10 root 604: PrintErrorNoEndl ("Your BIOS does not support large drives"); 605: Print (IsLbaSupported (PartitionFollowingActive.Drive) ? " due to a bug" : "\r\n- Enable LBA in BIOS"); 606: PrintEndl(); 1.1.1.6 root 607: Print (TC_BOOT_STR_UPGRADE_BIOS); 608: 609: ReleaseSectorBuffer(); 1.1.1.5 root 610: goto err; 611: } 612: 1.1.1.6 root 613: ReleaseSectorBuffer(); 614: 1.1.1.5 root 615: if (!MountVolume (drive, exitKey, true, false)) 616: return false; 617: 618: sectorsRemaining = EncryptedVirtualPartition.SectorCount; 619: 620: if (!(sectorsRemaining == ActivePartition.SectorCount)) 1.1.1.7 root 621: TC_THROW_FATAL_EXCEPTION; 1.1.1.5 root 622: 623: InitScreen(); 624: Print ("\r\nCopying system to hidden volume. To abort, press Esc.\r\n\r\n"); 625: 626: while (sectorsRemaining.HighPart != 0 || sectorsRemaining.LowPart != 0) 627: { 628: if (EscKeyPressed()) 629: { 630: Print ("\rIf aborted, copying will have to start from the beginning (if attempted again).\r\n"); 631: if (AskYesNo ("Abort")) 632: break; 633: } 634: 635: if (sectorsRemaining.HighPart == 0 && sectorsRemaining.LowPart < fragmentSectorCount) 1.1.1.10 root 636: fragmentSectorCount = (int) sectorsRemaining.LowPart; 1.1.1.5 root 637: 638: if (ReadWriteSectors (false, TC_BOOT_LOADER_BUFFER_SEGMENT, 0, drive, ActivePartition.StartSector + sectorOffset, fragmentSectorCount, false) != BiosResultSuccess) 1.1.1.7 root 639: { 640: Print ("To fix bad sectors: 1) Terminate 2) Encrypt and decrypt sys partition 3) Retry\r\n"); 641: crypto_close (BootCryptoInfo); 642: goto err; 643: } 1.1.1.5 root 644: 645: AcquireSectorBuffer(); 646: 647: for (int i = 0; i < fragmentSectorCount; ++i) 648: { 649: CopyMemory (TC_BOOT_LOADER_BUFFER_SEGMENT, i * TC_LB_SIZE, SectorBuffer, TC_LB_SIZE); 650: 651: uint64 s = HiddenVolumeStartUnitNo + sectorOffset + i; 652: EncryptDataUnits (SectorBuffer, &s, 1, BootCryptoInfo); 653: 654: CopyMemory (SectorBuffer, TC_BOOT_LOADER_BUFFER_SEGMENT, i * TC_LB_SIZE, TC_LB_SIZE); 655: } 656: 657: ReleaseSectorBuffer(); 658: 659: if (ReadWriteSectors (true, TC_BOOT_LOADER_BUFFER_SEGMENT, 0, drive, HiddenVolumeStartSector + sectorOffset, fragmentSectorCount, false) != BiosResultSuccess) 1.1.1.7 root 660: { 661: crypto_close (BootCryptoInfo); 662: goto err; 663: } 1.1.1.5 root 664: 665: sectorsRemaining = sectorsRemaining - fragmentSectorCount; 666: sectorOffset = sectorOffset + fragmentSectorCount; 667: 668: if (!(statCount++ & 0xf)) 669: { 670: Print ("\rRemaining: "); 671: PrintSectorCountInMB (sectorsRemaining); 672: } 673: } 674: 675: crypto_close (BootCryptoInfo); 676: 677: if (sectorsRemaining.HighPart == 0 && sectorsRemaining.LowPart == 0) 678: { 679: status = true; 680: Print ("\rCopying completed."); 681: } 682: 683: PrintEndl (2); 684: goto ret; 685: 686: err: 687: exitKey = TC_BIOS_KEY_ESC; 688: GetKeyboardChar(); 689: 690: ret: 691: EraseMemory ((void *) TC_BOOT_LOADER_ARGS_OFFSET, sizeof (BootArguments)); 692: return status; 693: } 694: 695: 696: #else // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 697: 698: 1.1 root 699: static void DecryptDrive (byte drive) 700: { 701: byte exitKey; 1.1.1.5 root 702: if (!MountVolume (drive, exitKey, false, true)) 1.1 root 703: return; 704: 1.1.1.5 root 705: BootArguments *bootArguments = (BootArguments *) TC_BOOT_LOADER_ARGS_OFFSET; 1.1 root 706: 707: bool headerUpdateRequired = false; 708: uint64 sectorsRemaining = EncryptedVirtualPartition.EndSector + 1 - EncryptedVirtualPartition.StartSector; 709: uint64 sector = EncryptedVirtualPartition.EndSector + 1; 710: 1.1.1.5 root 711: int fragmentSectorCount = 0x7f; // Maximum safe value supported by BIOS 1.1 root 712: int statCount; 713: 1.1.1.5 root 714: bool skipBadSectors = false; 715: 1.1.1.6 root 716: Print ("\r\nUse only if Windows cannot start. Decryption under Windows is much faster\r\n" 717: "(in TrueCrypt, select 'System' > 'Permanently Decrypt').\r\n\r\n"); 1.1.1.5 root 718: 719: if (!AskYesNo ("Decrypt now")) 720: { 721: crypto_close (BootCryptoInfo); 722: goto ret; 723: } 724: 725: if (EncryptedVirtualPartition.Drive == TC_INVALID_BIOS_DRIVE) 1.1 root 726: { 1.1.1.3 root 727: // Drive already decrypted 1.1 root 728: sectorsRemaining.HighPart = 0; 729: sectorsRemaining.LowPart = 0; 730: } 731: else 732: { 1.1.1.3 root 733: Print ("\r\nTo safely interrupt and defer decryption, press Esc.\r\n" 734: "WARNING: You can turn off power only after you press Esc.\r\n\r\n"); 1.1 root 735: } 736: 737: while (sectorsRemaining.HighPart != 0 || sectorsRemaining.LowPart != 0) 738: { 1.1.1.5 root 739: if (EscKeyPressed()) 740: break; 1.1 root 741: 742: if (sectorsRemaining.HighPart == 0 && sectorsRemaining.LowPart < fragmentSectorCount) 1.1.1.10 root 743: fragmentSectorCount = (int) sectorsRemaining.LowPart; 1.1 root 744: 745: sector = sector - fragmentSectorCount; 746: 747: if (!(statCount++ & 0xf)) 748: { 749: Print ("\rRemaining: "); 1.1.1.5 root 750: PrintSectorCountInMB (sectorsRemaining); 1.1 root 751: } 752: 1.1.1.5 root 753: if (ReadWriteSectors (false, TC_BOOT_LOADER_BUFFER_SEGMENT, 0, drive, sector, fragmentSectorCount, skipBadSectors) == BiosResultSuccess) 1.1 root 754: { 1.1.1.5 root 755: AcquireSectorBuffer(); 1.1 root 756: 1.1.1.5 root 757: for (int i = 0; i < fragmentSectorCount; ++i) 758: { 759: CopyMemory (TC_BOOT_LOADER_BUFFER_SEGMENT, i * TC_LB_SIZE, SectorBuffer, TC_LB_SIZE); 1.1 root 760: 1.1.1.5 root 761: uint64 s = sector + i; 762: DecryptDataUnits (SectorBuffer, &s, 1, BootCryptoInfo); 1.1 root 763: 1.1.1.5 root 764: CopyMemory (SectorBuffer, TC_BOOT_LOADER_BUFFER_SEGMENT, i * TC_LB_SIZE, TC_LB_SIZE); 765: } 1.1.1.3 root 766: 1.1.1.5 root 767: ReleaseSectorBuffer(); 768: 769: if (ReadWriteSectors (true, TC_BOOT_LOADER_BUFFER_SEGMENT, 0, drive, sector, fragmentSectorCount, skipBadSectors) != BiosResultSuccess && !skipBadSectors) 770: goto askBadSectorSkip; 771: } 772: else if (!skipBadSectors) 773: goto askBadSectorSkip; 1.1 root 774: 775: sectorsRemaining = sectorsRemaining - fragmentSectorCount; 776: headerUpdateRequired = true; 1.1.1.5 root 777: continue; 778: 779: askBadSectorSkip: 780: if (!AskYesNo ("Skip all bad sectors")) 781: break; 782: 783: skipBadSectors = true; 784: sector = sector + fragmentSectorCount; 785: fragmentSectorCount = 1; 1.1 root 786: } 787: 788: crypto_close (BootCryptoInfo); 789: 790: if (headerUpdateRequired) 791: { 1.1.1.3 root 792: AcquireSectorBuffer(); 1.1 root 793: uint64 headerSector; 794: headerSector.HighPart = 0; 795: headerSector.LowPart = TC_BOOT_VOLUME_HEADER_SECTOR; 796: 1.1.1.3 root 797: // Update encrypted area size in volume header 1.1 root 798: 1.1.1.3 root 799: CRYPTO_INFO *headerCryptoInfo = crypto_open(); 1.1.1.5 root 800: while (ReadSectors (SectorBuffer, drive, headerSector, 1) != BiosResultSuccess); 1.1 root 801: 1.1.1.6 root 802: if (ReadVolumeHeader (TRUE, (char *) SectorBuffer, &bootArguments->BootPassword, NULL, headerCryptoInfo) == 0) 1.1.1.3 root 803: { 804: DecryptBuffer (SectorBuffer + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); 1.1 root 805: 1.1.1.3 root 806: uint64 encryptedAreaLength = sectorsRemaining << TC_LB_SIZE_BIT_SHIFT_DIVISOR; 1.1 root 807: 1.1.1.3 root 808: for (int i = 7; i >= 0; --i) 809: { 1.1.1.5 root 810: SectorBuffer[TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH + i] = (byte) encryptedAreaLength.LowPart; 1.1.1.3 root 811: encryptedAreaLength = encryptedAreaLength >> 8; 1.1 root 812: } 813: 1.1.1.5 root 814: uint32 headerCrc32 = GetCrc32 (SectorBuffer + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); 815: 816: for (i = 3; i >= 0; --i) 817: { 818: SectorBuffer[TC_HEADER_OFFSET_HEADER_CRC + i] = (byte) headerCrc32; 819: headerCrc32 >>= 8; 820: } 821: 1.1.1.3 root 822: EncryptBuffer (SectorBuffer + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, headerCryptoInfo); 1.1 root 823: } 824: 1.1.1.3 root 825: crypto_close (headerCryptoInfo); 826: 1.1.1.5 root 827: while (WriteSectors (SectorBuffer, drive, headerSector, 1) != BiosResultSuccess); 1.1.1.3 root 828: ReleaseSectorBuffer(); 1.1 root 829: } 830: 1.1.1.3 root 831: if (sectorsRemaining.HighPart == 0 && sectorsRemaining.LowPart == 0) 832: Print ("\rDrive decrypted.\r\n"); 833: else 834: Print ("\r\nDecryption deferred.\r\n"); 835: 1.1 root 836: GetKeyboardChar(); 1.1.1.5 root 837: ret: 838: EraseMemory (bootArguments, sizeof (*bootArguments)); 1.1 root 839: } 840: 841: 842: static void RepairMenu () 843: { 844: DriveGeometry bootLoaderDriveGeometry; 1.1.1.6 root 845: 846: if (GetDriveGeometry (BootLoaderDrive, bootLoaderDriveGeometry, true) != BiosResultSuccess) 1.1 root 847: { 1.1.1.6 root 848: // Some BIOSes may fail to get the geometry of an emulated floppy drive 849: bootLoaderDriveGeometry.Cylinders = 80; 850: bootLoaderDriveGeometry.Heads = 2; 851: bootLoaderDriveGeometry.Sectors = 18; 1.1 root 852: } 853: 854: while (true) 855: { 856: InitScreen(); 857: Print ("Available "); Print ("Repair Options"); Print (":\r\n"); 858: PrintRepeatedChar ('\xC4', 25); 1.1.1.5 root 859: PrintEndl(); 1.1 root 860: 861: enum 862: { 863: RestoreNone = 0, 864: DecryptVolume, 865: RestoreTrueCryptLoader, 866: RestoreVolumeHeader, 867: RestoreOriginalSystemLoader 868: }; 869: 870: static const char *options[] = { "Permanently decrypt system partition/drive", "Restore TrueCrypt Boot Loader", "Restore key data (volume header)", "Restore original system loader" }; 871: 1.1.1.5 root 872: int selection = AskSelection (options, 873: (BootSectorFlags & TC_BOOT_CFG_FLAG_RESCUE_DISK_ORIG_SYS_LOADER) ? array_capacity (options) : array_capacity (options) - 1); 1.1 root 874: 875: PrintEndl(); 876: 1.1.1.2 root 877: switch (selection) 1.1 root 878: { 1.1.1.2 root 879: case RestoreNone: 880: return; 881: 882: case DecryptVolume: 1.1.1.5 root 883: DecryptDrive (BootDrive); 1.1.1.2 root 884: continue; 885: 886: case RestoreOriginalSystemLoader: 1.1.1.6 root 887: if (!AskYesNo ("Is the system partition/drive decrypted")) 1.1.1.3 root 888: { 889: Print ("Please decrypt it first.\r\n"); 890: GetKeyboardChar(); 1.1.1.2 root 891: continue; 1.1.1.3 root 892: } 1.1.1.4 root 893: break; 1.1 root 894: } 895: 896: bool writeConfirmed = false; 897: BiosResult result; 898: 899: uint64 sector; 900: sector.HighPart = 0; 901: ChsAddress chs; 902: 1.1.1.3 root 903: byte mbrPartTable[TC_LB_SIZE - TC_MAX_MBR_BOOT_CODE_SIZE]; 904: AcquireSectorBuffer(); 905: 1.1 root 906: for (int i = (selection == RestoreVolumeHeader ? TC_BOOT_VOLUME_HEADER_SECTOR : TC_MBR_SECTOR); 907: i < TC_BOOT_LOADER_AREA_SECTOR_COUNT; ++i) 908: { 1.1.1.5 root 909: sector.LowPart = i; 910: 911: if (selection == RestoreOriginalSystemLoader) 912: sector.LowPart += TC_ORIG_BOOT_LOADER_BACKUP_SECTOR; 913: else if (selection == RestoreTrueCryptLoader) 914: sector.LowPart += TC_BOOT_LOADER_BACKUP_RESCUE_DISK_SECTOR; 1.1 root 915: 916: // The backup medium may be a floppy-emulated bootable CD. The emulation may fail if LBA addressing is used. 917: // Therefore, only CHS addressing can be used. 918: LbaToChs (bootLoaderDriveGeometry, sector, chs); 919: sector.LowPart = i; 920: 921: if (i == TC_MBR_SECTOR) 922: { 1.1.1.3 root 923: // Read current partition table 924: result = ReadSectors (SectorBuffer, TC_FIRST_BIOS_DRIVE, sector, 1); 1.1 root 925: if (result != BiosResultSuccess) 926: goto err; 927: 1.1.1.3 root 928: memcpy (mbrPartTable, SectorBuffer + TC_MAX_MBR_BOOT_CODE_SIZE, sizeof (mbrPartTable)); 929: } 930: 931: result = ReadSectors (SectorBuffer, BootLoaderDrive, chs, 1); 932: if (result != BiosResultSuccess) 933: goto err; 934: 935: if (i == TC_MBR_SECTOR) 936: { 937: // Preserve current partition table 938: memcpy (SectorBuffer + TC_MAX_MBR_BOOT_CODE_SIZE, mbrPartTable, sizeof (mbrPartTable)); 1.1 root 939: } 940: 941: // Volume header 942: if (i == TC_BOOT_VOLUME_HEADER_SECTOR) 943: { 944: if (selection == RestoreTrueCryptLoader) 945: continue; 946: 947: if (selection == RestoreVolumeHeader) 948: { 949: while (true) 950: { 1.1.1.10 root 951: bool validHeaderPresent = false; 952: uint32 masterKeyScheduleCrc; 953: 1.1 root 954: Password password; 955: byte exitKey = AskPassword (password); 956: 957: if (exitKey != TC_BIOS_KEY_ENTER) 958: goto abort; 959: 960: CRYPTO_INFO *cryptoInfo; 961: 1.1.1.3 root 962: CopyMemory (SectorBuffer, TC_BOOT_LOADER_BUFFER_SEGMENT, 0, TC_LB_SIZE); 963: ReleaseSectorBuffer(); 964: 1.1 root 965: // Restore volume header only if the current one cannot be used 1.1.1.5 root 966: if (OpenVolume (TC_FIRST_BIOS_DRIVE, password, &cryptoInfo, nullptr, false, true)) 1.1 root 967: { 1.1.1.10 root 968: validHeaderPresent = true; 969: masterKeyScheduleCrc = GetCrc32 (cryptoInfo->ks, sizeof (cryptoInfo->ks)); 1.1 root 970: crypto_close (cryptoInfo); 971: } 972: 1.1.1.3 root 973: AcquireSectorBuffer(); 974: CopyMemory (TC_BOOT_LOADER_BUFFER_SEGMENT, 0, SectorBuffer, TC_LB_SIZE); 975: 1.1.1.6 root 976: if (ReadVolumeHeader (TRUE, (char *) SectorBuffer, &password, &cryptoInfo, nullptr) == 0) 1.1 root 977: { 1.1.1.10 root 978: if (validHeaderPresent) 979: { 980: if (masterKeyScheduleCrc == GetCrc32 (cryptoInfo->ks, sizeof (cryptoInfo->ks))) 981: { 982: Print ("Original header preserved.\r\n"); 983: goto err; 984: } 985: 986: Print ("WARNING: Drive 0 contains a valid header!\r\n"); 987: } 988: 1.1 root 989: crypto_close (cryptoInfo); 990: break; 991: } 992: 1.1.1.4 root 993: Print ("Incorrect password.\r\n\r\n"); 1.1 root 994: } 995: } 996: } 997: 1.1.1.3 root 998: if (!writeConfirmed && !AskYesNo ("Modify drive 0")) 1.1 root 999: goto abort; 1000: writeConfirmed = true; 1001: 1.1.1.3 root 1002: if (WriteSectors (SectorBuffer, TC_FIRST_BIOS_DRIVE, sector, 1) != BiosResultSuccess) 1.1 root 1003: goto err; 1004: } 1005: done: 1006: switch (selection) 1007: { 1008: case RestoreTrueCryptLoader: 1009: Print ("TrueCrypt Boot Loader"); 1010: break; 1011: 1012: case RestoreVolumeHeader: 1013: Print ("Header"); 1014: break; 1015: 1016: case RestoreOriginalSystemLoader: 1017: Print ("System loader"); 1018: break; 1019: } 1020: Print (" restored.\r\n"); 1021: 1022: err: GetKeyboardChar(); 1.1.1.3 root 1023: abort: ReleaseSectorBuffer(); 1.1 root 1024: } 1025: } 1026: 1.1.1.5 root 1027: #endif // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1028: 1.1 root 1029: 1030: #ifndef DEBUG 1031: extern "C" void _acrtused () { } // Required by linker 1032: #endif 1033: 1034: 1035: void main () 1036: { 1037: __asm mov BootLoaderDrive, dl 1038: __asm mov BootSectorFlags, dh 1039: 1.1.1.3 root 1040: #ifdef TC_BOOT_TRACING_ENABLED 1.1 root 1041: InitDebugPort(); 1042: #endif 1043: 1.1.1.3 root 1044: #ifdef TC_BOOT_STACK_CHECKING_ENABLED 1045: InitStackChecker(); 1046: #endif 1047: 1.1.1.6 root 1048: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1049: ReadBootSectorUserConfiguration(); 1.1.1.12 root 1050: #elif defined (TC_WINDOWS_BOOT_AES) 1051: EnableHwEncryption (!(BootSectorFlags & TC_BOOT_CFG_FLAG_RESCUE_DISABLE_HW_ENCRYPTION)); 1.1.1.6 root 1052: #endif 1053: 1.1.1.5 root 1054: InitVideoMode(); 1055: InitScreen(); 1056: 1057: // Determine boot drive 1.1 root 1058: BootDrive = BootLoaderDrive; 1059: if (BootDrive < TC_FIRST_BIOS_DRIVE) 1060: BootDrive = TC_FIRST_BIOS_DRIVE; 1061: 1.1.1.5 root 1062: // Query boot drive geometry 1.1.1.10 root 1063: if (GetDriveGeometry (BootDrive, BootDriveGeometry) != BiosResultSuccess) 1.1 root 1064: { 1065: BootDrive = TC_FIRST_BIOS_DRIVE; 1.1.1.10 root 1066: if (GetDriveGeometry (BootDrive, BootDriveGeometry) != BiosResultSuccess) 1067: { 1068: #ifdef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1069: Print ("- Connect system drive to (SATA) port 1\r\n"); 1070: #endif 1071: GetKeyboardChar(); 1072: } 1073: else 1074: BootDriveGeometryValid = true; 1.1 root 1075: } 1076: else 1.1.1.10 root 1077: BootDriveGeometryValid = true; 1.1.1.5 root 1078: 1079: #ifdef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1080: 1081: // Check whether the user is not using the Rescue Disk to create a hidden system 1082: 1083: if (ReadWriteMBR (false, BootDrive, true) == BiosResultSuccess 1084: && *(uint32 *) (SectorBuffer + 6) == 0x65757254 1085: && *(uint32 *) (SectorBuffer + 10) == 0x70797243 1086: && (SectorBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET] & TC_BOOT_CFG_MASK_HIDDEN_OS_CREATION_PHASE) != TC_HIDDEN_OS_CREATION_PHASE_NONE) 1087: { 1088: PrintError ("It appears you are creating a hidden OS."); 1089: if (AskYesNo ("Is this correct")) 1090: { 1091: Print ("Please remove the Rescue Disk from the drive and restart."); 1092: while (true); 1093: } 1094: } 1095: 1096: #endif // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1097: 1098: 1099: // Main menu 1100: 1.1 root 1101: while (true) 1102: { 1.1.1.5 root 1103: byte exitKey; 1.1 root 1104: InitScreen(); 1105: 1.1.1.5 root 1106: #ifndef TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1107: 1108: // Hidden system setup 1109: byte hiddenSystemCreationPhase = BootSectorFlags & TC_BOOT_CFG_MASK_HIDDEN_OS_CREATION_PHASE; 1110: 1111: if (hiddenSystemCreationPhase != TC_HIDDEN_OS_CREATION_PHASE_NONE) 1112: { 1.1.1.6 root 1113: PreventNormalSystemBoot = true; 1.1.1.5 root 1114: PrintMainMenu(); 1115: 1116: if (hiddenSystemCreationPhase == TC_HIDDEN_OS_CREATION_PHASE_CLONING) 1117: { 1.1.1.10 root 1118: if (CopySystemPartitionToHiddenVolume (BootDrive, exitKey)) 1.1.1.5 root 1119: { 1.1.1.6 root 1120: BootSectorFlags = (BootSectorFlags & ~TC_BOOT_CFG_MASK_HIDDEN_OS_CREATION_PHASE) | TC_HIDDEN_OS_CREATION_PHASE_WIPING; 1.1.1.5 root 1121: UpdateBootSectorConfiguration (BootLoaderDrive); 1122: } 1123: else if (exitKey == TC_BIOS_KEY_ESC) 1124: goto bootMenu; 1125: else 1126: continue; 1127: } 1128: } 1129: else 1130: PrintMainMenu(); 1131: 1132: exitKey = BootEncryptedDrive(); 1133: 1134: #else // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1.1 root 1135: 1.1.1.5 root 1136: PrintMainMenu(); 1137: exitKey = BootEncryptedDrive(); 1138: 1.1 root 1139: if (exitKey == TC_MENU_KEY_REPAIR) 1140: { 1141: RepairMenu(); 1142: continue; 1143: } 1144: 1.1.1.5 root 1145: #endif // TC_WINDOWS_BOOT_RESCUE_DISK_MODE 1146: 1147: bootMenu: 1.1.1.6 root 1148: if (!PreventBootMenu) 1149: BootMenu(); 1.1 root 1150: } 1151: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.