/*
 # $Id: morpher.c,v 1.2 1997/08/06 14:43:29 fbm Exp fbm $
 # Copyright (C) 1997 Farrell McKay
 # All rights reserved.
 #
 # This file is part of the Fortify distribution, a toolkit for
 # upgrading the cryptographic strength of the Netscape Navigator
 # web browser, authored by Farrell McKay.
 #
 # This toolkit is provided to the recipient under the
 # following terms and conditions:-
 #   1.  This copyright notice must not be removed or modified.
 #   2.  This toolkit may not be reproduced or included in any commercial
 #       media distribution, or commercial publication (for example CD-ROM,
 #       disk, book, magazine, journal) without first obtaining the author's
 #       permission.
 #   3.  Subject to the above conditions being observed (1,2), this toolkit
 #       may be freely reproduced or redistributed.
 #   4.  This software is provided "as-is", without express or implied
 #       warranty.  In no event shall the author be liable for any direct,
 #       indirect or consequential damages however caused.
 #   5.  Subject to the above conditions being observed (1-4),
 #       this toolkit may be used at no cost to the recipient.
 #
 # Farrell McKay			Farrell.McKay@mpx.com.au
 */

#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "misc.h"
#include "morpher.h"

#ifdef	GNU_WIN32
typedef long	off_t;
#endif

static void
show_vec(FILE *f, unsigned char *src, int len, int format)
{
	if (len > 0) {
		if (format == VEC_NUM_FMT) {
			char pre = '[';
			while (len-- > 0) {
				fprintf(f, "%c%02x", pre, (unsigned int) *src++);
				pre = ' ';
			}
			fprintf(f, "]");
		} else if (format == VEC_STR_FMT) {
			fputc('"', f);
			for (; len-- > 0; src++) {
				if (isprint(*src))
					fputc(*src, f);
				else
					fprintf(f, "\\%03o", (unsigned int) *src);
			}
			fputc('"', f);
		}
	}
}

#define MAXMORPHLEN	1024

static int
apply_morphs(int tgt, morph_set *ms, int unmorph, int dowrite, int verbose)
{
	int		i;
	int		cmp;
	morph_t		*p;
	off_t		offset;
	unsigned char	tmp[MAXMORPHLEN];
	vec_t		*old, *new;
	int		errs = 0;

	for (i = 0; i < ms->morphs_sz; i++) {
		p = ms->morphs[i];
		if (p->is_context)
			continue;
		if (unmorph) {
			old = &p->new;
			new = &p->old;
		} else {
			old = &p->old;
			new = &p->new;
		}

		if (new->len > MAXMORPHLEN) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": woops, this morph is too big!\n",
				p->name);
			continue;
		}
		if (verbose) {
			cmp = memcmp((void *) old->data, (void *) new->data, old->len);
			if (cmp == 0) {
				fprintf(stderr,
					"Morph \"%s\": Warning: morph changes nothing ??!\n",
					p->name);
			}

			printf("At file offset 0x%06x, replacing ", p->filepos);
			show_vec(stdout, old->data, old->len, old->format);
			printf(" with ");
			show_vec(stdout, new->data, new->len, new->format);
			printf("\n");
		}

		offset = lseek(tgt, (off_t) p->filepos, SEEK_SET);
		if (offset == -1) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": error locating offset 0x%08x [run-time 0x%x]: ",
				p->name, p->filepos, p->base + p->offset);
			perror("");
			continue;
		}

		if (read(tgt, (char *) tmp, old->len) != old->len) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": error reading %d bytes at 0x%08x: ",
				p->name, old->len, p->filepos);
			perror("");
			continue;
		}

		cmp = memcmp((void *) tmp, (void *) old->data, (size_t) old->len);
		if (cmp != 0) {
			errs++;
			fprintf(stderr, "Morph \"%s\": data mismatch: expected ", p->name);
			show_vec(stderr, old->data, old->len, VEC_NUM_FMT);
			fprintf(stderr, ", found ");
			show_vec(stderr, tmp, old->len, VEC_NUM_FMT);
			fprintf(stderr, "\n");
			continue;
		}

		if (dowrite) {
			offset = lseek(tgt, (off_t) p->filepos, SEEK_SET);
			if (offset == -1) {
				errs++;
				fprintf(stderr,
					"Morph \"%s\": error repositioning at address 0x%08x [run-time 0x%x]: ",
					p->name, p->filepos, p->base + p->offset);
				perror("");
				continue;
			}
			if (write(tgt, (char *) new->data, new->len) != new->len) {
				errs++;
				fprintf(stderr,
					"Morph \"%s\": error writing data to 0x%08x [run-time 0x%x]: ",
					p->name, p->filepos, p->base + p->offset);
				perror("");
				continue;
			}
		}
	}
	return errs;
}

int
morpher(char *target_name, char *morph_file, int nowrite, int unmorph, int verbose)
{
	int		i;
	int		tgt;
	int		errs;
	FILE		*mf;
	morph_set	*ms;

	tgt = open(target_name, OPENFL(O_RDWR), 0666);
	if (tgt == -1) {
		fprintf(stderr, "Cannot open \"%s\": ", target_name);
		perror("");
		return 1;
	}

	mf = fopen(morph_file, "r");
	if (mf == NULL) {
		fprintf(stderr, "Cannot open \"%s\": ", morph_file);
		perror("");
		return 1;
	}

	ms = parse(mf);

	for (i = 0; i < ms->morphs_sz; i++) {
		morph_t *p = ms->morphs[i];
		p->filepos = p->base + p->offset - ms->offsets[p->seg];
	}

	errs = apply_morphs(tgt, ms, unmorph, 0, 0);
	if (errs == 0 && ms->nmorphs > 0 && nowrite == 0) {
		errs = apply_morphs(tgt, ms, unmorph, 1, verbose);
	}
	else {
		fprintf(stderr,
			"%d errors from %d potential morphs.\n",
			errs, ms->nmorphs);
		fprintf(stderr, "No changes made to \"%s\".\n", target_name);
	}

	close(tgt);
	fclose(mf);
	return ((errs > 0)? 1: 0);
}

#ifdef TEST_MAIN
static void
usage(char *prog_name)
{
	fprintf(stderr, "@(#) morpher 1.0 $Date: 1997/08/06 14:43:29 $\n");
	fprintf(stderr, "Usage: %s [-nuv] <target-file> <morphs-file>\n", prog_name);
	fprintf(stderr, "\t-n  no writes; check morph integrity only\n");
	fprintf(stderr, "\t-u  undo; reverse the morphs out of the program\n");
	fprintf(stderr, "\t-v  verbose\n");
	exit(1);
}

main(int argc, char *argv[])
{
	int		i;
	int		rtn;
	int		nowrite = 0;
	int		unmorph = 0;
	int		verbose = 0;
	char		*prog_name = argv[0];

	if (argc < 3 || argc > 4) {
		usage(prog_name);
	}

	argc--, argv++;
	if (argv[0][0] == '-') {
		char	*p;
		for (p = &argv[0][1]; *p; p++) {
			if (*p == '-')
				;
			else if (*p == 'n')
				nowrite = 1;
			else if (*p == 'u')
				unmorph = 1;
			else if (*p == 'v')
				verbose = 1;
			else
				usage(prog_name);
		}
		argc--, argv++;
	}

	rtn = morpher(argv[0], argv[1], nowrite, unmorph, verbose);
	return rtn;
}
#endif
