[45] | 1 | /* $Id: $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * Bird's OS2LDR patch utility.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
| 7 | * Copyright (c) 2011 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
|
---|
| 8 | * All Rights Reserved.
|
---|
| 9 | *
|
---|
| 10 | */
|
---|
| 11 |
|
---|
| 12 |
|
---|
| 13 | /*******************************************************************************
|
---|
| 14 | * Header Files *
|
---|
| 15 | *******************************************************************************/
|
---|
| 16 | #include <errno.h>
|
---|
| 17 | #include <stdio.h>
|
---|
| 18 | #include <stdarg.h>
|
---|
| 19 | #include <stdlib.h>
|
---|
| 20 | #include <string.h>
|
---|
| 21 | #include <sys/stat.h>
|
---|
| 22 |
|
---|
| 23 |
|
---|
| 24 | static int failed(const char *pszFormat, ...)
|
---|
| 25 | {
|
---|
| 26 | va_list va;
|
---|
| 27 | va_start(va, pszFormat);
|
---|
| 28 | vfprintf(stderr, pszFormat, va);
|
---|
| 29 | va_end(va);
|
---|
| 30 | return 1;
|
---|
| 31 | }
|
---|
| 32 |
|
---|
| 33 |
|
---|
| 34 | static char *readfile(const char *pszFile, struct stat const *pSt)
|
---|
| 35 | {
|
---|
| 36 | char *pb = (char *)calloc(pSt->st_size + 16, 1);
|
---|
| 37 | if (pb)
|
---|
| 38 | {
|
---|
| 39 | errno = 0;
|
---|
| 40 | FILE *pFile = fopen(pszFile, "rb");
|
---|
| 41 | if (pFile)
|
---|
| 42 | {
|
---|
| 43 | errno = 0;
|
---|
| 44 | int rc = fread(pb, (unsigned)pSt->st_size, 1, pFile);
|
---|
| 45 | fclose(pFile);
|
---|
| 46 | if (rc == 1)
|
---|
| 47 | return pb;
|
---|
| 48 | failed("error: fread failed on '%s': %s\n", pszFile, strerror(errno));
|
---|
| 49 | }
|
---|
| 50 | else
|
---|
| 51 | failed("error: failed to open '%s': %s\n", pszFile, strerror(errno));
|
---|
| 52 |
|
---|
| 53 | free(pb);
|
---|
| 54 | }
|
---|
| 55 | else
|
---|
| 56 | failed("error: out of memory!\n");
|
---|
| 57 | return NULL;
|
---|
| 58 |
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 |
|
---|
| 62 | static void putu16(char *pb, unsigned uVal)
|
---|
| 63 | {
|
---|
| 64 | pb[0] = uVal & 0xff;
|
---|
| 65 | pb[1] = uVal >> 8;
|
---|
| 66 | }
|
---|
| 67 |
|
---|
| 68 |
|
---|
| 69 | static unsigned getu16(char const *pb)
|
---|
| 70 | {
|
---|
| 71 | unsigned u = *(unsigned char const *)(pb + 1);
|
---|
| 72 | u <<= 8;
|
---|
| 73 | u |= *(unsigned char const *)pb;
|
---|
| 74 | return u;
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 |
|
---|
| 78 | static int validatepatch(char const *pbPatch, unsigned cbPatch, char const *pbInput, unsigned cbInput)
|
---|
| 79 | {
|
---|
| 80 | if (strncmp(pbPatch, "bird", 4))
|
---|
| 81 | return failed("error: Bad patch signature!\n");
|
---|
| 82 |
|
---|
| 83 | unsigned uOrg = getu16(&pbPatch[4]);
|
---|
| 84 | if (uOrg <= cbInput)
|
---|
| 85 | return failed("error: The patch origin is lower or equal to the OS2LDR size: %#u <= %#u\n", uOrg, cbInput);
|
---|
| 86 | if (uOrg > 0xe0000)
|
---|
| 87 | return failed("error: The patch origin is too high: %#x\n", uOrg);
|
---|
| 88 |
|
---|
| 89 | unsigned cbTotal = uOrg + cbPatch;
|
---|
| 90 | cbTotal += 0x00001fff; /* Arena + alignment */
|
---|
| 91 | cbTotal &= 0xfffff000;
|
---|
| 92 | if (cbTotal > 0xf000)
|
---|
| 93 | return failed("error: The patched OS2LDR is too large: %#x (%u), max 0xf000\n", cbTotal, cbTotal);
|
---|
| 94 |
|
---|
| 95 | /*
|
---|
| 96 | * Verify the size patches.
|
---|
| 97 | */
|
---|
| 98 | const char *pb = &pbPatch[6];
|
---|
| 99 | if (strncmp(pb, "size", 4))
|
---|
| 100 | return failed("error: Bad patch header (size)!\n");
|
---|
| 101 | pb += 4;
|
---|
| 102 | unsigned cSizes = getu16(pb);
|
---|
| 103 | pb += 2;
|
---|
| 104 |
|
---|
| 105 | while (cSizes-- > 0)
|
---|
| 106 | {
|
---|
| 107 | unsigned offSize = getu16(pb);
|
---|
| 108 | pb += 2;
|
---|
| 109 | unsigned uOrgSize = getu16(pb);
|
---|
| 110 | pb += 2 + 2;
|
---|
| 111 | if (offSize + 2 > cbInput)
|
---|
| 112 | return failed("error: Size patch at %#x is beyond the end of the input file (%#x)!\n", offSize, cbInput);
|
---|
| 113 |
|
---|
| 114 | unsigned u = getu16(pbInput + offSize);
|
---|
| 115 | if (u != uOrgSize)
|
---|
| 116 | return failed("error: Size patch at %#x states a different value (%#x) than the input (%#x)!\n", offSize, uOrgSize, u);
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | /*
|
---|
| 120 | * Verify the jmp patches.
|
---|
| 121 | */
|
---|
| 122 | if (strncmp(pb, "jmps", 4))
|
---|
| 123 | return failed("error: Bad patch header (jmps)!\n");
|
---|
| 124 | unsigned cJmps = getu16(pb + 4);
|
---|
| 125 | pb += 6;
|
---|
| 126 |
|
---|
| 127 | for (unsigned iJmp = 0; iJmp < cJmps; iJmp++)
|
---|
| 128 | {
|
---|
| 129 | unsigned offJmp = getu16(pb + 0);
|
---|
| 130 | unsigned offDst = getu16(pb + 2);
|
---|
| 131 | unsigned cbLeadIn = getu16(pb + 4);
|
---|
| 132 | pb += 6;
|
---|
| 133 | if (cbLeadIn >= 16)
|
---|
| 134 | return failed("error: Jmp patch #%u at %#x: too many lead in bytes: %#x\n", iJmp, offJmp, cbLeadIn);
|
---|
| 135 | if (offJmp + cbLeadIn > cbInput || offJmp >= cbInput)
|
---|
| 136 | return failed("error: Jmp patch #%u at %#x is beyond the end of the input file (%#x)!\n", iJmp, offJmp, cbInput);
|
---|
| 137 | if (offDst < uOrg || offDst >= uOrg + cbPatch)
|
---|
| 138 | return failed("error: Jmp patch #%u at %#x destination is out of range: %#x\n", iJmp, offJmp, offDst);
|
---|
| 139 | if (memcmp(pbInput + offJmp, pb, cbLeadIn))
|
---|
| 140 | {
|
---|
| 141 | failed("error: Jmp patch #%u at %#x states other lead in bytes than the input\n", iJmp, offJmp);
|
---|
| 142 | for (unsigned off = 0; off < cbLeadIn; off++)
|
---|
| 143 | fprintf(stderr, " %#x+%u: %02x %02x\n", offJmp, off, (unsigned char)pb[off], (unsigned char)pbInput[offJmp + off]);
|
---|
| 144 | return 1;
|
---|
| 145 | }
|
---|
| 146 | pb += cbLeadIn + 3;
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | /* The end */
|
---|
| 150 | if (strncmp(pb, "end", 4))
|
---|
| 151 | return failed("error: Bad patch header (end)!\n");
|
---|
| 152 |
|
---|
| 153 | return 0;
|
---|
| 154 | }
|
---|
| 155 |
|
---|
| 156 |
|
---|
| 157 | static int linkpatch(char *pbOutput, unsigned *pcbOutput, char const *pbInput, unsigned cbInput,
|
---|
| 158 | char const *pbPatch, unsigned cbPatch)
|
---|
| 159 | {
|
---|
| 160 | if (strncmp(pbPatch, "bird", 4))
|
---|
| 161 | return failed("error: Doofus - bird!\n");
|
---|
| 162 |
|
---|
| 163 | /* Figure the size. */
|
---|
| 164 | unsigned const offPatch = getu16(&pbPatch[4]);
|
---|
| 165 | printf("offPatch=%#x\n", offPatch);
|
---|
| 166 | *pcbOutput = cbPatch + offPatch;
|
---|
| 167 |
|
---|
| 168 | /* Link the two input binaries. */
|
---|
| 169 | memset(pbOutput, 0, *pcbOutput);
|
---|
| 170 | memcpy(pbOutput, pbInput, cbInput);
|
---|
| 171 | memcpy(pbOutput + offPatch, pbPatch, cbPatch);
|
---|
| 172 |
|
---|
| 173 | /*
|
---|
| 174 | * Apply size patches
|
---|
| 175 | */
|
---|
| 176 | const char *pb = pbPatch + 6;
|
---|
| 177 | if (strncmp(pb, "size", 4))
|
---|
| 178 | return failed("error: Doofus - size!\n");
|
---|
| 179 | unsigned cSizes = getu16(pb + 4);
|
---|
| 180 | pb += 6;
|
---|
| 181 |
|
---|
| 182 | while (cSizes-- > 0)
|
---|
| 183 | {
|
---|
| 184 | unsigned offSize = getu16(pb);
|
---|
| 185 | signed iDelta = getu16(pb + 4);
|
---|
| 186 | pb += 6;
|
---|
| 187 | putu16(pbOutput + offSize, *pcbOutput + iDelta);
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | /*
|
---|
| 191 | * Apply the jmp patches.
|
---|
| 192 | */
|
---|
| 193 | if (strncmp(pb, "jmps", 4))
|
---|
| 194 | return failed("error: Doofus - jmps!\n");
|
---|
| 195 | unsigned cJmps = getu16(pb + 4);
|
---|
| 196 | pb += 6;
|
---|
| 197 |
|
---|
| 198 | while (cJmps-- > 0)
|
---|
| 199 | {
|
---|
| 200 | unsigned offJmp = getu16(pb + 0);
|
---|
| 201 | unsigned offDst = getu16(pb + 2);
|
---|
| 202 | unsigned cbLeadIn = getu16(pb + 4);
|
---|
| 203 | pb += 6 + cbLeadIn + 3;
|
---|
| 204 | #if 0 /* debug */
|
---|
| 205 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 206 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 207 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 208 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 209 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 210 | pbOutput[offJmp++] = (char)0xf4; /* hlt */
|
---|
| 211 | pbOutput[offJmp++] = (char)0xeb; /* jmp $-6 */
|
---|
| 212 | pbOutput[offJmp++] = (char)-6;
|
---|
| 213 | #endif
|
---|
| 214 | pbOutput[offJmp++] = (char)0x90; /* NOP */
|
---|
| 215 | pbOutput[offJmp++] = (char)0x90; /* NOP */
|
---|
| 216 | pbOutput[offJmp++] = (char)0x90; /* NOP */
|
---|
| 217 | pbOutput[offJmp++] = (char)0xe9; /* jmp rel16 */
|
---|
| 218 | putu16(&pbOutput[offJmp], offDst - offJmp - 2);
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | /* The end */
|
---|
| 222 | if (strncmp(pb, "end", 4))
|
---|
| 223 | return failed("error: Doofus - end!\n");
|
---|
| 224 | return 0;
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | int main(int argc, char **argv)
|
---|
| 228 | {
|
---|
| 229 | if (argc != 4)
|
---|
| 230 | return failed("syntax error: %s <os2ldr> <patch-binary> <output>\nargc=%d\n", argv[0], argc);
|
---|
| 231 |
|
---|
| 232 | const char *pszInput = argv[1];
|
---|
| 233 | const char *pszPatch = argv[2];
|
---|
| 234 | const char *pszOutput = argv[3];
|
---|
| 235 |
|
---|
| 236 | /*
|
---|
| 237 | * Check file existences and get the sizes of the the inputs.
|
---|
| 238 | */
|
---|
| 239 | struct stat StInput, StPatch;
|
---|
| 240 | if (stat(pszOutput, &StInput) == 0)
|
---|
| 241 | return failed("error: The output file '%s' exists already.\n", pszOutput);
|
---|
| 242 | if (errno != ENOENT)
|
---|
| 243 | return failed("error: Expected errno=%d (ENOENT), got %d: %s\n", ENOENT, errno, strerror(errno));
|
---|
| 244 |
|
---|
| 245 | if (stat(pszInput, &StInput) != 0)
|
---|
| 246 | return failed("error: stat(%s) -> %d: %s\n", pszInput, errno, strerror(errno));
|
---|
| 247 | if (stat(pszPatch, &StPatch) != 0)
|
---|
| 248 | return failed("error: stat(%s) -> %d: %s\n", pszPatch, errno, strerror(errno));
|
---|
| 249 |
|
---|
| 250 | if (StInput.st_size >= 0xe000)
|
---|
| 251 | return failed("error: %s is too big! %u bytes\n", pszInput, (unsigned)StInput.st_size);
|
---|
| 252 | if (StPatch.st_size >= 0x2000)
|
---|
| 253 | return failed("error: %s is too big! %u bytes\n", pszOutput, (unsigned)StInput.st_size);
|
---|
| 254 | if (StInput.st_size + StPatch.st_size >= 0xf000)
|
---|
| 255 | return failed("error: the input files are too big! %u bytes\n", (unsigned)(StInput.st_size + StPatch.st_size));
|
---|
| 256 |
|
---|
| 257 | /*
|
---|
| 258 | * Read the input files.
|
---|
| 259 | */
|
---|
| 260 | char *pbInput = readfile(pszInput, &StInput);
|
---|
| 261 | if (!pbInput)
|
---|
| 262 | return 1;
|
---|
| 263 |
|
---|
| 264 | char *pbPatch = readfile(pszPatch, &StPatch);
|
---|
| 265 | if (!pbInput)
|
---|
| 266 | return 1;
|
---|
| 267 |
|
---|
| 268 | /*
|
---|
| 269 | * Validate the patch and construct the output file.
|
---|
| 270 | */
|
---|
| 271 | int rc = validatepatch(pbPatch, (unsigned)StPatch.st_size, pbInput, (unsigned)StInput.st_size);
|
---|
| 272 | if (rc)
|
---|
| 273 | return rc;
|
---|
| 274 |
|
---|
| 275 | char *pbOutput = (char *)malloc(0x10000);
|
---|
| 276 | if (!pbOutput)
|
---|
| 277 | return failed("error: out of memory\n");
|
---|
| 278 |
|
---|
| 279 | unsigned cbOutput = 0;
|
---|
| 280 | rc = linkpatch(pbOutput, &cbOutput, pbInput, (unsigned)StInput.st_size, pbPatch, (unsigned)StPatch.st_size);
|
---|
| 281 | if (rc)
|
---|
| 282 | return rc;
|
---|
| 283 |
|
---|
| 284 | /*
|
---|
| 285 | * Write it to the output file.
|
---|
| 286 | */
|
---|
| 287 | errno = 0;
|
---|
| 288 | FILE *pFile = fopen(pszOutput, "wb");
|
---|
| 289 | if (!pFile)
|
---|
| 290 | return failed("error: Failed to create output file '%s': %s\n", pszOutput, strerror(errno));
|
---|
| 291 | rc = fwrite(pbOutput, cbOutput, 1, pFile);
|
---|
| 292 | if (rc != 1 || fclose(pFile) != 0)
|
---|
| 293 | return failed("error: Error writing output file: %s\n", strerror(errno));
|
---|
| 294 |
|
---|
| 295 | printf("Successfully created '%s'\n", pszOutput);
|
---|
| 296 | return 0;
|
---|
| 297 | }
|
---|
| 298 |
|
---|