| 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 |  | 
|---|