From fcae51a446463f970efffa0a08359dadb438554f Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 6 Oct 2022 23:39:32 +0100 Subject: [PATCH] [grub] force server download for nonstandard GRUB (Fedora 37, openSUSE Live, GeckoLinux) * This reverts most of 3528ca773dca5fbb3d5396a3e3d35dd8bcaf8d4e in order to download 'core.img' from our server instead of patching it. * Also solve the issue of downloading a custom 'core.img' for Fedora 37, that introduced a new 'grub_debug_is_enabled' symbol without altering their GRUB version string. * This is accomplished by doing what the distro maintainers should have done on their own, by appending a custom suffix to the GRUB version string. --- res/grub2/grub2_version.h | 32 ----------------- src/db.h | 7 ++-- src/format.c | 31 ++-------------- src/iso.c | 74 ++++++++++++++++++--------------------- src/rufus.h | 19 +--------- src/rufus.rc | 10 +++--- 6 files changed, 48 insertions(+), 125 deletions(-) diff --git a/res/grub2/grub2_version.h b/res/grub2/grub2_version.h index 9291a657..b16653d2 100644 --- a/res/grub2/grub2_version.h +++ b/res/grub2/grub2_version.h @@ -5,35 +5,3 @@ #pragma once #define GRUB2_PACKAGE_VERSION "2.06" - -/* - * Also include the 'core.img' patch data for distros using a - * NONSTANDARD '/boot/grub2/' prefix directory (openSUSE, Gecko, ...) - * This is basically a diff of the 'core.img' generated with - * 'grub-mkimage -p/boot/grub' vs 'grub-mkimage -p/boot/grub2' - */ - -// For GRUB 2.04 -static const chunk_t grub_204_chunk1_src = { 0x0208, 1, { 0xce } }; -static const chunk_t grub_204_chunk1_rep = { 0x0208, 1, { 0xcf } }; -static const chunk_t grub_204_chunk2_src = { 0x7cf8, 6, { 0x63, 0x25, 0x7e, 0x04, 0xf6, 0x14 } }; -static const chunk_t grub_204_chunk2_rep = { 0x7cf8, 7, { 0x62, 0xdb, 0x77, 0x57, 0x0c, 0x4e, 0x00 } }; - -// For GRUB 2.06 -static const chunk_t grub_206_chunk1_src = { 0x0208, 1, { 0xcf } }; -static const chunk_t grub_206_chunk1_rep = { 0x0208, 1, { 0xd0 } }; -static const chunk_t grub_206_chunk2_src = { 0x95f9, 6, { 0xac, 0x1a, 0xc6, 0x4f, 0x45, 0x2c } }; -static const chunk_t grub_206_chunk2_rep = { 0x95f9, 7, { 0xab, 0xe7, 0xe4, 0x0a, 0x2e, 0x38, 0x00 } }; - -const grub_patch_t grub_patch[2] = { - { "2.04", { - { &grub_204_chunk1_src, &grub_204_chunk1_rep }, - { &grub_204_chunk2_src, &grub_204_chunk2_rep }, - } - }, - { "2.06", { - { &grub_206_chunk1_src, &grub_206_chunk1_rep }, - { &grub_206_chunk2_src, &grub_206_chunk2_rep }, - } - }, -}; diff --git a/src/db.h b/src/db.h index 0e88851a..dcdbe751 100644 --- a/src/db.h +++ b/src/db.h @@ -21,18 +21,20 @@ #pragma once /* - * NB: Table data was generated from the files/ directory on the server with: - * find . -not -name "*.txt" -not -name "*.sig" -not -name "*.sh" -not -name "*pre*" -type f -print0 | xargs -0 sha256sum | sort | uniq -w 64 | awk -F '' '{ printf " "; for(i=1; i<=64; i+=2) {printf "0x%s%s, ", $i,$(i+1);}} {print "// " substr($0,69)}' + * NB: Table data was generated from the https://github.com/pbatard/rufus-web/tree/gh-pages/files/ + * directory using the https://github.com/pbatard/rufus-web/blob/gh-pages/files/gendb.sh script. */ static uint8_t sha256db[] = { 0x01, 0x21, 0x98, 0x20, 0xd9, 0x1c, 0x28, 0x7b, 0x35, 0x28, 0x06, 0xf1, 0xb6, 0xa8, 0x0d, 0x4a, 0x8a, 0xf4, 0x5c, 0xf5, 0x04, 0x83, 0xed, 0x6b, 0xe0, 0xc6, 0x0e, 0x7a, 0x66, 0x9c, 0x67, 0x87, // syslinux-6.04/pre2/ldlinux.bss + 0x11, 0x0c, 0x50, 0x1b, 0xfa, 0x9e, 0x72, 0xa8, 0x8c, 0xdb, 0xb8, 0xba, 0x11, 0xe1, 0xf0, 0x76, 0x1a, 0xec, 0x28, 0xbf, 0x04, 0x44, 0x67, 0xff, 0x38, 0x2c, 0x06, 0x95, 0xd5, 0x1f, 0x8a, 0x83, // grub-2.04-nonstandard/core.img 0x12, 0xbd, 0x22, 0xd2, 0xb3, 0x69, 0x56, 0x0f, 0x89, 0xb8, 0x50, 0x7e, 0x7e, 0x74, 0xeb, 0xc5, 0xea, 0x44, 0x91, 0x48, 0x75, 0xf0, 0xa4, 0xcb, 0x1e, 0xa6, 0xfb, 0x4e, 0xc9, 0x89, 0x58, 0x17, // syslinux-6.03/pre6/ldlinux.sys 0x15, 0x5f, 0x36, 0x7b, 0xb1, 0x30, 0xfe, 0x05, 0x5c, 0x79, 0x9f, 0x88, 0xb3, 0xc0, 0xc1, 0xa0, 0x0a, 0x18, 0x05, 0x78, 0x22, 0x69, 0xcf, 0x7e, 0x54, 0xaa, 0x61, 0xbd, 0xe3, 0x8e, 0x05, 0x92, // syslinux-6.03/pre3/ldlinux.bss 0x1c, 0xb7, 0x8b, 0x98, 0xbc, 0xd6, 0x76, 0x7b, 0x01, 0x44, 0xf5, 0x00, 0xaf, 0x81, 0xef, 0x4f, 0x3c, 0x54, 0xea, 0xaf, 0xe3, 0xc9, 0x4e, 0x1f, 0xd6, 0x24, 0x68, 0x41, 0x4e, 0x98, 0x92, 0x25, // syslinux-6.03/pre20/ldlinux.bss 0x1c, 0xc6, 0x32, 0x21, 0xfd, 0xf4, 0x46, 0xfc, 0xda, 0xc6, 0xc0, 0x56, 0x35, 0x79, 0x54, 0xc1, 0x5b, 0x61, 0x75, 0xca, 0x1b, 0xc2, 0xa4, 0x9f, 0x85, 0x52, 0xec, 0xca, 0x28, 0xac, 0x3e, 0x51, // syslinux-6.02/ldlinux.bss 0x22, 0x96, 0x82, 0xac, 0x61, 0xb8, 0x8b, 0x11, 0x25, 0xfc, 0xd7, 0xe6, 0x9f, 0x4e, 0x7f, 0x46, 0x7f, 0x68, 0xc5, 0x14, 0x9e, 0xb9, 0x37, 0x1a, 0x98, 0xd8, 0xf2, 0x78, 0x41, 0x40, 0xad, 0x88, // syslinux-5.00/ldlinux.sys 0x25, 0xd1, 0x38, 0xf3, 0x8b, 0x17, 0x35, 0x79, 0x3e, 0xee, 0x89, 0x19, 0xa3, 0xa0, 0xe5, 0xc9, 0x97, 0x9d, 0x2f, 0xac, 0xc0, 0xb5, 0x70, 0x74, 0x24, 0xe8, 0x04, 0x98, 0x1d, 0x25, 0xf9, 0xc9, // syslinux-6.03/pre10/ldlinux.sys + 0x28, 0xf8, 0x1d, 0xd8, 0x72, 0x0e, 0x6e, 0xfd, 0xc5, 0x30, 0x44, 0xf6, 0xf9, 0xa6, 0xc2, 0x8b, 0x6b, 0x89, 0x54, 0x23, 0xfb, 0x4c, 0xbc, 0x2d, 0xa2, 0xfb, 0xd6, 0x29, 0x70, 0xd6, 0xd1, 0xd6, // grub-2.06-nonstandard/core.img 0x29, 0xcd, 0xfc, 0x24, 0x0d, 0x08, 0xe4, 0xd6, 0x42, 0x0d, 0x1f, 0x05, 0x7a, 0x0e, 0xb0, 0xb1, 0x07, 0x88, 0x91, 0x65, 0xa8, 0xeb, 0x16, 0x3c, 0x31, 0x72, 0xaa, 0xfc, 0xee, 0x02, 0xac, 0xdd, // grub-2.06~rc1/core.img 0x2b, 0x07, 0x20, 0x67, 0xf9, 0xcd, 0x6e, 0x8c, 0x62, 0xe7, 0x82, 0xdc, 0x4a, 0xe2, 0x3b, 0x19, 0x2d, 0xe6, 0x28, 0xe7, 0x67, 0x0c, 0x84, 0x68, 0xed, 0x38, 0x0d, 0x36, 0x4a, 0xa7, 0x0b, 0xf1, // grub-2.06/core.img 0x2b, 0x31, 0x7b, 0x12, 0xab, 0xff, 0x49, 0x66, 0x48, 0x78, 0xdf, 0xe0, 0xb5, 0x00, 0xb8, 0x50, 0x0f, 0x93, 0xf1, 0xd5, 0xe4, 0xd5, 0x28, 0x45, 0x95, 0xcc, 0x2c, 0x15, 0x6c, 0x74, 0x4f, 0x79, // grub-2.02~beta3/core.img @@ -71,6 +73,7 @@ static uint8_t sha256db[] = { 0x6a, 0xbc, 0xdc, 0x80, 0x3a, 0x30, 0x85, 0xea, 0x5f, 0x9d, 0xa1, 0xb4, 0x3e, 0xdb, 0x2e, 0xad, 0xa2, 0x75, 0x36, 0x0d, 0xb8, 0x11, 0xc9, 0xac, 0xf5, 0x9a, 0x55, 0x5f, 0x67, 0x7b, 0x2d, 0x8b, // syslinux-6.03/pre12/ldlinux.bss 0x73, 0xb6, 0x27, 0x67, 0xa1, 0x62, 0x00, 0xb9, 0xaf, 0x19, 0x3a, 0x7d, 0x5c, 0x94, 0xe9, 0xc2, 0x94, 0xc6, 0xdb, 0xb6, 0xd5, 0xb1, 0x7c, 0x15, 0x03, 0x8c, 0x9f, 0x31, 0x73, 0xc9, 0xa7, 0xbc, // syslinux-6.04/ldlinux.sys 0x75, 0x6f, 0x89, 0x25, 0x23, 0xc6, 0x8d, 0x27, 0x32, 0x28, 0x8b, 0x5a, 0xd4, 0x2d, 0x7d, 0xc7, 0x4e, 0xa7, 0xa7, 0x08, 0x9b, 0x04, 0x2b, 0x12, 0x5f, 0x5d, 0x74, 0x7f, 0xf3, 0x20, 0xa0, 0x77, // syslinux-4.07/vesamenu.c32 + 0x76, 0xd1, 0x2b, 0x17, 0xfd, 0x05, 0xd3, 0x10, 0xb0, 0x5d, 0x00, 0xa8, 0x38, 0x77, 0x06, 0xc5, 0xce, 0x35, 0xd4, 0xb0, 0x50, 0xf2, 0x23, 0x9d, 0x15, 0xd3, 0xc9, 0xac, 0x92, 0xc5, 0xdd, 0xbc, // grub-2.06-fedora-nonstandard/core.img 0x77, 0x9a, 0x5e, 0xbd, 0x69, 0xd3, 0x28, 0x5e, 0xb9, 0xed, 0x4a, 0xc7, 0xc0, 0x4d, 0x2d, 0x15, 0xcb, 0xa1, 0x8a, 0x1f, 0x97, 0xc7, 0xc4, 0xbe, 0x62, 0x48, 0x93, 0xa9, 0xe1, 0xb0, 0x89, 0x2e, // syslinux-6.03/pre9/ldlinux.sys 0x78, 0x64, 0x8e, 0xf0, 0xc5, 0x00, 0x41, 0x75, 0xb9, 0xa8, 0xea, 0x33, 0x30, 0x14, 0xea, 0x02, 0xc9, 0x17, 0xf8, 0x23, 0xe7, 0x7a, 0x3e, 0xc9, 0xac, 0xd9, 0xd2, 0x2b, 0x46, 0x02, 0xf3, 0x6d, // syslinux-6.03/pre13/ldlinux.sys 0x7d, 0xa9, 0xc5, 0x21, 0x76, 0xb8, 0xaf, 0x01, 0x64, 0xea, 0x39, 0x21, 0x22, 0x44, 0xb1, 0x0a, 0xa0, 0xc7, 0x97, 0xe7, 0x65, 0xbb, 0x6b, 0x92, 0x69, 0xb5, 0x8b, 0xc9, 0xe5, 0x0a, 0x9f, 0x18, // syslinux-5.01/ldlinux.bss diff --git a/src/format.c b/src/format.c index 155390bf..5d3b5ce2 100644 --- a/src/format.c +++ b/src/format.c @@ -74,7 +74,6 @@ extern uint32_t dur_mins, dur_secs; extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files; extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing; extern BOOL write_as_image, use_vds, write_as_esp, is_vds_available; -extern const grub_patch_t grub_patch[2]; uint8_t *grub2_buf = NULL, *sec_buf = NULL; long grub2_len; @@ -911,8 +910,8 @@ static BOOL WriteSBR(HANDLE hPhysicalDrive) { // TODO: Do we need anything special for 4K sectors? DWORD size, max_size, br_size = 0x200; - int i, j, r, sub_type = boot_type; - uint8_t *buf = NULL, *patched = NULL; + int r, sub_type = boot_type; + uint8_t *buf = NULL; FAKE_FD fake_fd = { 0 }; FILE* fp = (FILE*)&fake_fd; @@ -955,32 +954,6 @@ static BOOL WriteSBR(HANDLE hPhysicalDrive) return FALSE; } } - // TODO: Compute the projected increase in size instead of harcoding it - if (img_report.has_grub2 == 2 && ((patched = malloc(size + 16)) != NULL)) { - memcpy(patched, buf, size); - // Patch GRUB for nonstandard prefix directory - for (i = 0; i < ARRAYSIZE(grub_patch); i++) { - if (strcmp(img_report.grub2_version, grub_patch[i].version) == 0) { - for (j = 0; j < ARRAYSIZE(grub_patch[i].patch); j++) { - if (memcmp(&patched[grub_patch[i].patch[j].src->offset], grub_patch[i].patch[j].src->data, - grub_patch[i].patch[j].src->size) != 0) { - uprintf("ERROR: Did not find expected source data for GRUB patch"); - free(patched); - return FALSE; - } - memcpy(&patched[grub_patch[i].patch[j].rep->offset], grub_patch[i].patch[j].rep->data, - grub_patch[i].patch[j].rep->size); - if (grub_patch[i].patch[j].rep->size > grub_patch[i].patch[j].src->size) - size += grub_patch[i].patch[j].rep->size - grub_patch[i].patch[j].src->size; - } - safe_free(grub2_buf); - grub2_buf = patched; - buf = grub2_buf; - uprintf("Patched Grub 2.0 SBR for NONSTANDARD prefix"); - break; - } - } - } break; case BT_MAX: uprintf("Writing protective message SBR"); diff --git a/src/iso.c b/src/iso.c index 3cbcda85..348067e3 100644 --- a/src/iso.c +++ b/src/iso.c @@ -82,7 +82,6 @@ RUFUS_IMG_REPORT img_report; int64_t iso_blocking_status = -1; extern BOOL preserve_timestamps, enable_ntfs_compression; extern char* archive_path; -extern const grub_patch_t grub_patch[2]; BOOL enable_iso = TRUE, enable_joliet = TRUE, enable_rockridge = TRUE, has_ldlinux_c32; #define ISO_BLOCKING(x) do {x; iso_blocking_status++; } while(0) static const char* psz_extract_dir; @@ -851,16 +850,18 @@ void GetGrubVersion(char* buf, size_t buf_size) // not having it mention GNU anywhere. See: // https://src.fedoraproject.org/rpms/grub2/blob/rawhide/f/0024-Don-t-say-GNU-Linux-in-generated-menus.patch const char* grub_version_str[] = { "GRUB version %s", "GRUB version %s" }; + const char* grub_debug_is_enabled_str = "grub_debug_is_enabled"; char *p, unauthorized[] = {'<', '>', ':', '|', '*', '?', '\\', '/'}; size_t i, j; + BOOL has_grub_debug_is_enabled = FALSE; for (i = 0; i < buf_size; i++) { for (j = 0; j < ARRAYSIZE(grub_version_str); j++) { - if (memcmp(&buf[i], grub_version_str[j], strlen(grub_version_str[j]) + 1) == 0) { + if (memcmp(&buf[i], grub_version_str[j], strlen(grub_version_str[j]) + 1) == 0) static_strcpy(img_report.grub2_version, &buf[i + strlen(grub_version_str[j]) + 1]); - break; - } } + if (memcmp(&buf[i], grub_debug_is_enabled_str, strlen(grub_debug_is_enabled_str)) == 0) + has_grub_debug_is_enabled = TRUE; } // Sanitize the string for (p = &img_report.grub2_version[0]; *p; p++) { @@ -873,6 +874,35 @@ void GetGrubVersion(char* buf, size_t buf_size) // But seriously, these guys should know better than "security" through obscurity... if (img_report.grub2_version[0] == '0') img_report.grub2_version[0] = 0; + + // For some obscure reason, openSUSE have decided that their Live images should + // use /boot/grub2/ as their prefix directory instead of the standard /boot/grub/ + // This creates a MAJOR issue because the prefix directory is hardcoded in + // 'core.img', and Rufus must install a 'core.img', that is not provided by the + // ISO, for the USB to boot (since even trying to pick the one from ISOHybrid + // does usually not guarantees the presence of the FAT driver which is mandatory + // for ISO boot). + // Therefore, when *someone* uses a nonstandard GRUB prefix directory, our base + // 'core.img' can't work with their image, since it isn't able to load modules + // like 'normal.mod', that are required to access the configuration files. Oh and + // you can forget about direct editing the prefix string inside 'core.img' since + // GRUB are forcing LZMA compression for BIOS payloads. And it gets even better, + // because even if you're trying to be smart and use GRUB's earlyconfig features + // to do something like: + // if [ -e /boot/grub2/i386-pc/normal.mod ]; then set prefix = ... + // you still must embed 'configfile.mod' and 'normal.mod' in 'core.img' in order + // to do that, which ends up tripling the file size... + // Also, as mentioned above, Fedora have started applying *BREAKING* patches + // willy-nilly, without bothering to alter the GRUB version string. + // Soooo, since the universe is conspiring against us and since we have already + // have a facility for it, we'll use it to dowload the relevant 'core.img' by + // appending a missing version suffix as needed... + if (img_report.grub2_version[0] != 0) { + if (has_grub_debug_is_enabled) + strcat(img_report.grub2_version, "-fedora"); + if (img_report.has_grub2 > 1) + strcat(img_report.grub2_version, "-nonstandard"); + } } BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) @@ -1120,41 +1150,7 @@ out: DeleteFileU(path); } if (img_report.grub2_version[0] != 0) { - // (Insert "Why is it always you three" meme, with Fedora, Manjaro and openSUSE) - // For some obscure reason, openSUSE have decided that their Live images should - // use /boot/grub2/ as their prefix directory instead of the standard /boot/grub/ - // This creates a MAJOR issue because the prefix directory is hardcoded in - // 'core.img', and Rufus must install a 'core.img', that is not provided by the - // ISO, for the USB to boot (since even trying to pick the one from ISOHybrid - // does usually not guarantees the presence of the FAT driver which is mandatory - // for ISO boot). - // Therefore, when *someone* uses a nonstandard GRUB prefix directory, our base - // 'core.img' can't work with their image, since it isn't able to load modules - // like 'normal.mod', that are required to access the configuration files. Oh and - // you can forget about direct editing the prefix string inside 'core.img' since - // GRUB are forcing LZMA compression for BIOS payloads. And it gets even better, - // because even if you're trying to be smart and use GRUB's earlyconfig features - // to do something like: - // if [ -e /boot/grub2/i386-pc/normal.mod ]; then set prefix = ... - // you still must embed 'configfile.mod' and 'normal.mod' in 'core.img' in order - // to do that, which ends up tripling the file size... - // Soooo, since the universe is conspiring against us and in order to cut a long - // story short about developers making annoying decisions, we'll take advantage - // of the fact that the LZMA replacement section for the 2.04 and 2.06 'core.img' - // when using '/boot/grub2' as a prefix is very small and always located at the - // very end the file to patch the damn thing and get on with our life! - uprintf(" Detected Grub version: %s%s", img_report.grub2_version, - img_report.has_grub2 > 1 ? " with NONSTANDARD prefix" : ""); - if (img_report.has_grub2 > 1) { - for (k = 0; k < ARRAYSIZE(grub_patch); k++) { - if (strcmp(img_report.grub2_version, grub_patch[k].version) == 0) - break; - } - if (k >= ARRAYSIZE(grub_patch)) { - uprintf(" • Don't have a prefix patch for this version => DROPPED!"); - img_report.has_grub2 = 0; - } - } + uprintf(" Detected Grub version: %s", img_report.grub2_version); } else { uprintf(" Could not detect Grub version"); img_report.has_grub2 = 0; diff --git a/src/rufus.h b/src/rufus.h index c96f8c9d..41ee21c2 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -398,7 +398,7 @@ typedef struct { uint16_t sl_version; // Syslinux/Isolinux version char sl_version_str[12]; char sl_version_ext[32]; - char grub2_version[32]; + char grub2_version[64]; } RUFUS_IMG_REPORT; /* Isolate the Syslinux version numbers */ @@ -693,23 +693,6 @@ extern void StrArrayClear(StrArray* arr); extern void StrArrayDestroy(StrArray* arr); #define IsStrArrayEmpty(arr) (arr.Index == 0) -/* Patch structs for GRUB */ -typedef struct { - const uint32_t offset; - const uint32_t size; - const uint8_t data[]; -} chunk_t; - -typedef struct { - const chunk_t* src; - const chunk_t* rep; -} patch_t; - -typedef struct { - const char* version; - const patch_t patch[2]; -} grub_patch_t; - /* * typedefs for the function prototypes. Use the something like: * PF_DECL(FormatEx); diff --git a/src/rufus.rc b/src/rufus.rc index 5c229ca8..0888df80 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.21.1937" +CAPTION "Rufus 3.21.1938" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,21,1937,0 - PRODUCTVERSION 3,21,1937,0 + FILEVERSION 3,21,1938,0 + PRODUCTVERSION 3,21,1938,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.21.1937" + VALUE "FileVersion", "3.21.1938" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2022 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.21.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.21.1937" + VALUE "ProductVersion", "3.21.1938" END END BLOCK "VarFileInfo"