DragonFlyBSD Kernel Audit
DF-0820 / craft.c
← back to finding ↓ download raw
/*
 * craft.c - DF-0820 superblock geometry patcher.
 *
 * Reads a base UFS image (produced by `newfs`), binary-patches selected
 * struct fs fields at their on-disk offsets (relative to SBOFF=8192),
 * and writes a crafted image. Used to reproduce the ffs_mountfs
 * validation gaps cited in sys/vfs/ufs/ffs_vfsops.c:642-709.
 *
 * Field offsets (computed from sys/vfs/ufs/fs.h, sizeof(struct fs)=1384):
 *   fs_ncg=44 fs_bsize=48 fs_fsize=52 fs_frag=56 fs_sbsize=104
 *   fs_cssize=156 fs_cgsize=160 fs_ipg=184 fs_fpg=188
 *   fs_contigsumsize=1316 fs_magic=1372
 *
 * Usage: craft <base.img> <out.img> <field=value> [<field=value> ...]
 *   field=value sets a 4-byte little-endian int32 field.
 *   field may be a signed decimal (e.g. fs_ncg=-1) or 0x-prefixed hex.
 *
 * Build: cc -o craft craft.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>

#define SBOFF 8192

struct field { const char *name; size_t off; };
static const struct field fields[] = {
    {"fs_firstfield",     0},
    {"fs_sblkno",         8},
    {"fs_cgoffset",      24},
    {"fs_time",          32},
    {"fs_size",          36},
    {"fs_dsize",         40},
    {"fs_ncg",           44},
    {"fs_bsize",         48},
    {"fs_fsize",         52},
    {"fs_frag",          56},
    {"fs_fragshift",     96},
    {"fs_sbsize",       104},
    {"fs_id",           144},
    {"fs_csaddr",       152},
    {"fs_cssize",       156},
    {"fs_cgsize",       160},
    {"fs_ipg",          184},
    {"fs_fpg",          188},
    {"fs_contigsumsize",1316},
    {"fs_maxsymlinklen",1320},
    {"fs_postblformat", 1356},
    {"fs_magic",        1372},
    {NULL, 0}
};

static const struct field *findfield(const char *n){
    for (int i=0; fields[i].name; i++)
        if (strcmp(fields[i].name, n)==0) return &fields[i];
    return NULL;
}

int main(int argc, char **argv){
    if (argc < 4) {
        fprintf(stderr, "usage: %s base.img out.img field=val [field=val ...]\n", argv[0]);
        return 2;
    }
    const char *base = argv[1];
    const char *out  = argv[2];

    /* Read whole base image into memory. */
    FILE *f = fopen(base, "rb");
    if (!f) { perror("open base"); return 2; }
    fseek(f, 0, SEEK_END);
    long sz = ftell(f);
    fseek(f, 0, SEEK_SET);
    unsigned char *buf = malloc(sz);
    if (!buf) { perror("malloc"); return 2; }
    if ((long)fread(buf, 1, sz, f) != sz) { perror("read"); return 2; }
    fclose(f);

    /* Sanity: superblock magic at SBOFF+1372 should be 0x00011954. */
    uint32_t magic = buf[SBOFF+1372] | (buf[SBOFF+1373]<<8) |
                     (buf[SBOFF+1374]<<16) | ((uint32_t)buf[SBOFF+1375]<<24);
    if (magic != 0x011954) {
        fprintf(stderr, "warn: base image fs_magic=0x%x (expected 0x011954)\n", magic);
    }

    /* Apply patches. */
    for (int a=3; a<argc; a++) {
        char *arg = argv[a];
        char *eq = strchr(arg, '=');
        if (!eq) { fprintf(stderr, "bad arg '%s' (need field=value)\n", arg); return 2; }
        *eq = 0;
        const char *fname = arg;
        char *vstr = eq+1;
        const struct field *fl = findfield(fname);
        if (!fl) { fprintf(stderr, "unknown field '%s'\n", fname); return 2; }
        /* parse value: signed, with optional 0x */
        long long v;
        char *end;
        if (vstr[0]=='0' && vstr[1]=='x') v = strtoll(vstr, &end, 16);
        else v = strtoll(vstr, &end, 10);
        if (*end != 0) { fprintf(stderr, "bad value '%s' for %s\n", vstr, fname); return 2; }
        int32_t iv = (int32_t)v;
        size_t off = SBOFF + fl->off;
        if ((long)(off+4) > sz) { fprintf(stderr, "offset %zu out of range (img %ld)\n", off, sz); return 2; }
        buf[off+0] = (unsigned char)(iv & 0xff);
        buf[off+1] = (unsigned char)((iv >> 8) & 0xff);
        buf[off+2] = (unsigned char)((iv >> 16) & 0xff);
        buf[off+3] = (unsigned char)((iv >> 24) & 0xff);
        printf("patched %s @ file off %zu (struct off %zu) = %d (0x%08x)\n",
               fname, off, fl->off, iv, (unsigned)iv);
    }

    FILE *o = fopen(out, "wb");
    if (!o) { perror("open out"); return 2; }
    if ((long)fwrite(buf, 1, sz, o) != sz) { perror("write"); return 2; }
    fclose(o);
    free(buf);
    printf("wrote %s (%ld bytes)\n", out, sz);
    return 0;
}