/* * ntloader -- Microsoft Windows NT6+ loader * Copyright (C) 2025 A1ive. * * ntloader is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * ntloader is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ntloader. If not, see . */ #include #include #include #include #include #include #include #include "ntloader.h" #include "reg.h" #include "bcd.h" #include "cmdline.h" #include "charset.h" #include "efi.h" static void bcd_replace_suffix (const wchar_t *src, const wchar_t *dst) { uint8_t *p = nt_cmdline->bcd; uint32_t ofs; const size_t len = sizeof(wchar_t) * 5; // . E F I \0 for (ofs = 0; ofs + len < nt_cmdline->bcd_length; ofs++) { if (memcmp (p + ofs, src, len) == 0) { memcpy (p + ofs, dst, len); DBG ("...patched BCD at %#x (%ls->%ls)\n", ofs, src, dst); } } } static void * bcd_find_hive (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname, uint32_t *len) { HKEY entry, elements, key; void *data = NULL; uint32_t type; if (reg_find_key (hive, objects, guid, &entry) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", guid); if (reg_find_key (hive, entry, BCD_REG_HKEY, &elements) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", BCD_REG_HKEY); if (reg_find_key (hive, elements, keyname, &key) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", keyname); if (reg_query_value (hive, key, BCD_REG_HVAL, (void **)&data, len, &type) != REG_ERR_NONE) die ("Can't find HVAL %ls\n", BCD_REG_HVAL); return data; } static void bcd_delete_key (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname) { HKEY entry, elements, key; if (reg_find_key (hive, objects, guid, &entry) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", guid); if (reg_find_key (hive, entry, BCD_REG_HKEY, &elements) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", BCD_REG_HKEY); if (reg_find_key (hive, elements, keyname, &key) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", keyname); if (reg_delete_key (hive, elements, key) != REG_ERR_NONE) die ("Can't delete HKEY %ls\n", keyname); } static inline void bcd_patch_bool (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname, uint8_t val) { uint8_t *data; uint32_t len; data = bcd_find_hive (hive, objects, guid, keyname, &len); if (len != sizeof (uint8_t)) die ("Invalid bool size %x\n", len); memcpy (data, &val, sizeof (uint8_t)); DBG ("...patched %ls->%ls (%c)\n", guid, keyname, val ? 'y' : 'n'); } static inline void bcd_patch_u64 (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname, uint64_t val) { uint8_t *data; uint32_t len; data = bcd_find_hive (hive, objects, guid, keyname, &len); if (len != sizeof (uint64_t)) die ("Invalid u64 size %x\n", len); memcpy (data, &val, sizeof (uint64_t)); DBG ("...patched %ls->%ls (%llx)\n", guid, keyname, val); } static inline void bcd_patch_sz (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname, const char *str) { uint16_t *data; uint32_t len; data = bcd_find_hive (hive, objects, guid, keyname, &len); memset (data, 0, len); utf8_to_ucs2 (data, len / sizeof (wchar_t), (uint8_t *) str); DBG ("...patched %ls->%ls (%ls)\n", guid, keyname, (wchar_t *) data); } static inline void bcd_patch_szw (hive_t *hive, HKEY objects, const wchar_t *guid, const wchar_t *keyname, const wchar_t *str) { uint16_t *data; uint32_t len; size_t wlen = wcslen (str) * sizeof (wchar_t); data = bcd_find_hive (hive, objects, guid, keyname, &len); if (wlen > len) die ("Invalid wchar size %zu\n", wlen); memset (data, 0, len); memcpy (data, str, wlen); DBG ("...patched %ls->%ls (%ls)\n", guid, keyname, str); } static inline void bcd_patch_dp (hive_t *hive, HKEY objects, uint32_t boottype, const wchar_t *guid, const wchar_t *keyname) { uint8_t *data; uint32_t len, ofs; uint8_t sdi[] = GUID_BIN_RAMDISK; data = bcd_find_hive (hive, objects, guid, keyname, &len); memset (data, 0, len); switch (boottype) { case NTBOOT_WIM: case NTBOOT_RAM: { if (len < 0x028a) die ("WIM device path (%ls->%ls) length error (%x)\n", guid, keyname, len); memcpy (data + 0x0000, sdi, sizeof (sdi)); data[0x0014] = 0x01; data[0x0018] = 0x7a; data[0x0019] = 0x02; // len - 0x10 data[0x0020] = 0x03; data[0x0038] = 0x01; data[0x003c] = 0x52; data[0x003d] = 0x02; // len - 0x38 data[0x0040] = 0x05; ofs = 0x0044; break; } case NTBOOT_VHD: { if (len < 0x02bc) die ("VHD device path (%ls->%ls) length error (%x)\n", guid, keyname, len); data[0x0010] = 0x08; data[0x0018] = 0xac; data[0x0019] = 0x02; // len - 0x10 data[0x0024] = 0x02; data[0x0027] = 0x12; data[0x0028] = 0x1e; data[0x0036] = 0x8e; data[0x0037] = 0x02; // len - 0x2e data[0x003e] = 0x06; data[0x005e] = 0x66; data[0x005f] = 0x02; // len - 0x56 data[0x0066] = 0x05; data[0x006a] = 0x01; data[0x006e] = 0x52; data[0x006f] = 0x02; // len - 0x6a data[0x0072] = 0x05; ofs = 0x0076; break; } case NTBOOT_WOS: case NTBOOT_REC: { if (len < 0x0058) die ("OS device path (%ls->%ls) length error (%x)\n", guid, keyname, len); ofs = 0x0010; break; } default: die ("Unsupported boot type %x\n", boottype); } /* os device */ if (nt_cmdline->fsuuid[0]) data[ofs + 0x00] = 0x06; // 05=boot, 06=disk else data[ofs + 0x00] = 0x05; data[ofs + 0x08] = 0x48; memcpy (data + ofs + 0x10, nt_cmdline->partid, 16); data[ofs + 0x24] = nt_cmdline->partmap; memcpy (data + ofs + 0x28, nt_cmdline->diskid, 16); if (boottype == NTBOOT_WIM || boottype == NTBOOT_VHD || boottype == NTBOOT_RAM) utf8_to_ucs2 ((uint16_t *)(data + ofs + 0x48), MAX_PATH, (uint8_t *)nt_cmdline->filepath); DBG ("...patched %ls->%ls (device%x)\n", guid, keyname, boottype); } void bcd_patch_data (void) { const wchar_t *entry_guid; HKEY root, objects; hive_t hive = { .size = nt_cmdline->bcd_length, .data = nt_cmdline->bcd, }; /* Open BCD hive */ if (reg_open_hive (&hive) != REG_ERR_NONE) die ("BCD hive load error.\n"); reg_find_root (&hive, &root); if (reg_find_key (&hive, root, BCD_REG_ROOT, &objects) != REG_ERR_NONE) die ("Can't find HKEY %ls\n", BCD_REG_ROOT); DBG ("BCD hive load OK.\n"); /* Check entry type */ switch (nt_cmdline->boottype) { case NTBOOT_VHD: entry_guid = GUID_VHDB; break; case NTBOOT_WOS: entry_guid = GUID_WOSB; break; case NTBOOT_WIM: case NTBOOT_RAM: default: entry_guid = GUID_WIMB; } /* Patch Objects->{BootMgr} */ bcd_patch_szw (&hive, objects, GUID_BOOTMGR, BCDOPT_OBJECT, entry_guid); // default object bcd_patch_szw (&hive, objects, GUID_BOOTMGR, BCDOPT_ORDER, entry_guid); // menu display order bcd_patch_u64 (&hive, objects, GUID_BOOTMGR, BCDOPT_TIMEOUT, nt_cmdline->timeout); // timeout /* Patch Objects->{Resume} */ bcd_patch_dp (&hive, objects, NTBOOT_REC, GUID_HIBR, BCDOPT_APPDEV); // app device bcd_patch_dp (&hive, objects, NTBOOT_REC, GUID_HIBR, BCDOPT_OSDDEV); // os device bcd_patch_sz (&hive, objects, GUID_HIBR, BCDOPT_WINLOAD, BCD_DEFAULT_WINRESUME); // resume bcd_patch_sz (&hive, objects, GUID_HIBR, BCDOPT_SYSROOT, BCD_DEFAULT_HIBERFIL); // hiberfil /* Patch Objects->{Ramdisk} */ if (nt_cmdline->boottype == NTBOOT_RAM) { bcd_delete_key (&hive, objects, GUID_RAMDISK, BCDOPT_SDIDEV); bcd_delete_key (&hive, objects, GUID_RAMDISK, BCDOPT_SDIPATH); bcd_patch_bool (&hive, objects, GUID_RAMDISK, BCDOPT_EXPORTCD, nt_cmdline->exportcd); bcd_patch_u64 (&hive, objects, GUID_RAMDISK, BCDOPT_IMGOFS, nt_cmdline->imgofs); } else { bcd_delete_key (&hive, objects, GUID_RAMDISK, BCDOPT_EXPORTCD); bcd_delete_key (&hive, objects, GUID_RAMDISK, BCDOPT_IMGOFS); } /* Patch Objects->{Options} */ bcd_patch_sz (&hive, objects, GUID_OPTN, BCDOPT_CMDLINE, nt_cmdline->loadopt); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_TESTMODE, nt_cmdline->testmode); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_DETHAL, nt_cmdline->hal); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_WINPE, nt_cmdline->minint); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_NOVGA, nt_cmdline->novga); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_NOVESA, nt_cmdline->novesa); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_ADVOPT, nt_cmdline->advmenu); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_OPTEDIT, nt_cmdline->optedit); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_TEXT, nt_cmdline->textmode); bcd_patch_u64 (&hive, objects, GUID_OPTN, BCDOPT_NX, nt_cmdline->nx); bcd_patch_u64 (&hive, objects, GUID_OPTN, BCDOPT_PAE, nt_cmdline->pae); /* Patch Objects->{Resolution} */ if (nt_cmdline->hires == NTARG_BOOL_NA) { bcd_delete_key (&hive, objects, GUID_OPTN, BCDOPT_HIGHRES); bcd_patch_u64 (&hive, objects, GUID_OPTN, BCDOPT_GFXMODE, nt_cmdline->gfxmode); } else { bcd_delete_key (&hive, objects, GUID_OPTN, BCDOPT_GFXMODE); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_HIGHRES, nt_cmdline->hires); } if (nt_cmdline->safemode) { bcd_patch_u64 (&hive, objects, GUID_OPTN, BCDOPT_SAFEMODE, nt_cmdline->safeboot); bcd_patch_bool (&hive, objects, GUID_OPTN, BCDOPT_ALTSHELL, nt_cmdline->altshell); } else { bcd_delete_key (&hive, objects, GUID_OPTN, BCDOPT_SAFEMODE); bcd_delete_key (&hive, objects, GUID_OPTN, BCDOPT_ALTSHELL); } /* Patch Objects->{Entry} */ bcd_patch_dp (&hive, objects, nt_cmdline->boottype, entry_guid, BCDOPT_APPDEV); // app device bcd_patch_dp (&hive, objects, nt_cmdline->boottype, entry_guid, BCDOPT_OSDDEV); // os device bcd_patch_sz (&hive, objects, entry_guid, BCDOPT_WINLOAD, nt_cmdline->winload); bcd_patch_sz (&hive, objects, entry_guid, BCDOPT_SYSROOT, nt_cmdline->sysroot); if (efi_systab) bcd_replace_suffix (L".exe", L".efi"); else bcd_replace_suffix (L".efi", L".exe"); }