Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1056 lines
19 KiB

/*
* XMN - A hex file browser. Lets you view/modify/update the contents of a
* binary file in hex/oct/dec.
*/
#include "xmn.h"
void _CRTAPI1
main(argc, argv)
int argc;
PCHAR *argv;
{
char line[LINESIZE];
int i;
PCHAR s;
ULONG savestrt, savelen;
if (argc != 2) {
printf("Usage: %s filename\n", *argv);
exit(1);
}
if (!setfile(*++argv)) {
exit(1);
}
printf("Type help or ? for help\n");
signal(SIGABRT, sig);
signal(SIGINT, sig);
setjmp(lj);
while (TRUE) {
savestrt = cmdstrt; savelen = cmdlen;
printf("\n- ");
if (!getline(line, TRUE))
continue;
i = getcom(line);
switch (i) {
case DB:
mode = BYTE;
dispb();
break;
case DW:
mode = WORD;
dispw();
break;
case DD:
mode = DWORD;
dispd();
break;
case DQ:
mode = QWORD;
dispq();
break;
case HEX:
defbase = 16;
defr1 = '9';
defr2 = 'A';
defr3 = 'F';
break;
case OCT:
defbase = 8;
defr1 = '7';
defr2 = '0';
defr3 = '7';
break;
case DEC:
defbase = 10;
defr1 = '9';
defr2 = '0';
defr3 = '9';
break;
case EB:
case EW:
case ED:
case EQ:
mode = i - CHANGE;
change(i - CHANGE);
break;
case NEWFILE:
chgfile(cm2);
break;
case QUIT:
quit();
break;
case HELP:
help();
break;
case NONE:
break;
case CALC:
calc();
break;
case DUMPPOOL:
bufrpool();
break;
case COALPOOL:
coalpool();
break;
case DEBUG:
if (cmpstr(cm2, "ON"))
debug = TRUE;
else debug = FALSE;
printf("Debug is %s\n", debug ? "ON" : "OFF");
break;
case SHELL:
s = line;
while (*++s != SPACE)
;
printf("%s\n", ++s);
system(s);
break;
case STATUS:
printf("File : %s %lx(%ld) bytes\n",
filename, filesize, filesize);
printf("File Pointer: %lx (%ld)\n", cmdstrt+cmdlen,
cmdstrt+cmdlen);
printf("Disp Mode : %s\n", (mode == BYTE) ? "BYTE" : \
((mode == WORD) ? "WORD" :
((mode == DWORD) ? "DWORD" : "QWORD")));
printf("Default inp : %s\n", (defbase == 10) ? "decimal" :
((defbase == 8) ? "octal" : "hex"));
printf("Debug is %s\n", debug ? "ON" : "OFF");
break;
case WRITE:
if (dirtywrite())
writeback();
break;
case CHGSIZE:
if ((hwrite = open(filename, O_BINARY|O_RDWR)) == hNULL) {
printf("\txmn: can't write \"%s\"\n", filename);
break;
}
printf("File size changed from %ld", filesize);
filesize = cmdstrt;
cmdstrt = savestrt;
cmdlen = savelen;
chsize(hwrite, filesize);
printf(" to %ld\n", filesize);
close(hwrite);
break;
case INVALID:
default:
printf("Invalid command, type help for help\n");
break;
}
}
}
/*
* Help - dump the command syntax
*/
void help()
{
int i;
printf("Commands:\n");
for (i = 0; cmds[i].cmd_val != NONE; i++)
if (cmds[i].cmd_pcmd != NULL && cmds[i].cmd_phlp != NULL)
printf("\t%s\t%s\n", cmds[i].cmd_pcmd, cmds[i].cmd_phlp);
printf("\n\t<range> is either <start addr> <endaddr>\n");
printf("\n\t or <startaddr> l <count>\n");
printf("\nDefaults are overridden by a preceding'0x' for hex, 0t for dec and '0' for octal");
}
/*
* Get a command. Parse the input line. Not very sophisticated but will do for
* the task in hand.
*/
int getcom(PCHAR line)
{
int i, cnt, val;
long strt, len;
cm1[0] = cm2[0] = cm3[0] = cm4[0] = 0;
ScanLine(line, cm1, cm2, cm3, cm4);
for (i = 0; cmds[i].cmd_val != NONE; i++) {
if (cmpstr(cm1, cmds[i].cmd_pcmd)) {
cnt = cmds[i].cmd_prm_cnt;
val = cmds[i].cmd_val;
if (val == DISPLAY || val == CHANGE)
val += mode;
if ((cnt == 0 && cm2[0]) || (cnt == 1 && cm3[0]))
error("Invalid parameter count");
if (cmds[i].cmd_prm_type == ASCII || cnt == 0)
return(val);
if (cm2[0] && !getnum(cm2, &strt))
return (NONE);
if (cm2[0])
cmdstrt = strt;
else
cmdstrt = cmdstrt+cmdlen;
cmdlen = 128 - (cmdstrt % 16);
if (cnt == 1)
return(val);
if (cm3[0]) {
if (cmpstr(cm3, "L")) {
if (cm4[0] == '\0')
error("Invalid parameter count");
else if (!getnum(cm4, &len))
return(NONE);
len *= val & MODEMASK;
}
else {
if (cm4[0])
error("Invalid parameter count");
if (!getnum(cm3, &len))
return (NONE);
len -= cmdstrt - 1;
}
if (cmdstrt < 0 || len < 0)
error("range should have positive values");
cmdlen = len;
return (val);
}
else return(val);
}
}
return (INVALID);
}
/*
* Our very own strcmp. Why did I write it ? Who knows !
*/
int cmpstr(register PCHAR s1, register PCHAR s2)
{
for (; *s1 != 0 && *s2 != 0 && *s1 == *s2; s1++, s2++)
;
return (*s1 == 0 && *s2 == 0);
}
/*
* A smarter version of atoi() which understands hex/oct/dec and our own
* environment
*/
int getnum(PCHAR s, PLONG pul)
{
long val = 0;
int base = defbase;
char c, r1 = defr1, r2 = defr2, r3 = defr3;
if (s[0] == '0') {
c = *++s;
if (c == 'X') {
base = 16; r1 = '9'; r2 = 'A'; r3 = 'F';
s++;
}
else if (c == 'T') {
s++;
base = 10; r1 = '9'; r2 = '0'; r3 = '9';
}
else {
base = 8; r1 = '7'; r2 = '0'; r3 = '7';
}
}
for (; c = *s; s++) {
if (c >= '0' && c <= r1)
val = val*base + c - '0';
else if (base == 16 && c >= r2 && c <= r3)
val = val*base + c - r2 + 10;
else {
printf("Not a number");
return (FALSE);
}
}
*pul = val;
return (TRUE);
}
/*
* Display a buffer as a sequence of bytes and also show the ASCII equivalents
*/
void dispb()
{
long i = 0, j, k;
PUCHAR puc;
puc = getbufr("DB");
j = (cmdstrt % 16);
if (j) {
if ((j + cmdlen) < 16)
k = j + cmdlen;
else k = 16;
printf("%06lx: ", cmdstrt - j);
for (i = 0; i < 16; i ++) {
if (i >= j && i < k)
printf("%02x ", puc[i-j]);
else printf(" ");
}
dispstr(&puc[0], k-j, j);
i = 16 - j;
}
k = i; j = cmdstrt + i;
for ( ; i < cmdlen; i++, j++) {
if (j % 16 == 0) {
dispstr(&puc[k], i-k, 0);
k = i;
printf("\n%06lx: ", j);
}
printf("%02x ", puc[i]);
}
if (j % 16 != 0) {
j = 16 - (j % 16);
while (j--)
printf(" ");
}
dispstr(&puc[k], i-k, 0);
}
/*
* Display a buffer as a sequence of words (short).
*/
void dispw()
{
PUSHORT pus;
long i, len;
pus = (PUSHORT)getbufr("DW");
len = cmdlen/2;
for (i = 0; i < len; i++) {
dispaddr(i, TRUE);
printf("%04x ", pus[i]);
}
}
/*
* Display a buffer as a sequence of dwords (long).
*/
void dispd()
{
PULONG pul;
long i, len;
pul = (PULONG)getbufr("DD");
len = cmdlen/4;
for (i = 0; i < len; i++) {
dispaddr(i, TRUE);
printf("%08lx ", pul[i]);
}
}
/*
* Display a buffer as a sequence of quads (exlong).
*/
void dispq()
{
PULONG pul;
long i =0, len;
pul = (PULONG)getbufr("DQ");
len = cmdlen/4;
for (i = 0; i < len; i++) {
dispaddr(i, TRUE);
printf("%08lx%08lx ", pul[i*2+1], pul[i*2]);
}
}
/*
* Allow editing a buffer full as bytes/words/longs.
*/
void change(int mode)
{
/*
* Entering a QWORD value is partially supported. The new value has
* to fit in the low dword.
*/
PUCHAR puc;
PUSHORT pus;
PULONG pul;
PCHAR s;
char line[LINESIZE];
long i = 0, j;
int l, k;
switch (mode) {
case BYTE:
s = "EB";
break;
case WORD:
s = "EW";
break;
case DWORD:
s = "ED";
break;
case QWORD:
s = "EQ";
break;
}
puc = getbufr(s);
pus = (PUSHORT)puc;
pul = (PULONG)puc;
do {
dispaddr(i, FALSE);
switch (mode) {
case BYTE:
printf("%02x ", *puc);
break;
case WORD:
printf("%04x ", *pus);
break;
case DWORD:
printf("%08lx ", *pul);
break;
case QWORD:
printf("%08lx%08lx ", pul[1], *pul);
break;
}
k = getl(line, &l);
if (l == 0) {
if (k == CR)
break;
else goto next;
}
if (!getnum(line, &j)) {
break;
}
switch (mode) {
case BYTE:
*puc = (UCHAR)j;
break;
case WORD:
*pus = (USHORT)j;
break;
case DWORD:
*pul = (ULONG)j;
break;
case QWORD:
*pul = 0;
pul[1] = (ULONG)j;
break;
}
bufdirty(cmdstrt);
next:
if (k == CR)
break;
i++;
switch (mode) {
case BYTE:
puc++;
break;
case WORD:
pus++;
break;
case QWORD:
pul++;
case DWORD:
pul++;
break;
}
} while (i*mode < cmdlen);
cmdlen = i*mode;
}
/*
* Display the byte offset address in hex only (can be extended to display
* in the current mode. Maybe later.
*/
void dispaddr(long indx, int ffill)
{
int i;
long j;
if (cmdstrt % mode == 0) {
if (ffill && indx == 0 && ((j = cmdstrt % 16) != 0)) {
printf("\n%06lx: ", cmdstrt - j);
for (i = 0; i < (int) (j/mode); i++) {
switch (mode) {
case QWORD:
printf(" ");
case DWORD:
printf(" ");
case WORD:
printf(" ");
case BYTE:
printf(" ");
}
}
}
else if ((cmdstrt + indx*mode) % 16 == 0 || indx == 0)
printf("\n%06lx: ", cmdstrt + indx*mode);
}
else {
if (indx*mode % 16 == 0)
printf("\n%06lx: ", cmdstrt + indx*mode);
}
}
/*
* For byte mode display, display the the ascii equivalents. Take care of
* positioning and non-display characters.
*/
void dispstr(PCHAR s, long n, long m)
{
while (m--)
printf(" ");
for (; n--; s++) {
if (*s < SPACE || *s >= MAXASCII)
printf(".");
else printf("%c", *s);
}
}
/*
* Read a line from the stdin. Uppercase (mostly) the string. For escape to
* shell, do not.
*/
int getline(PCHAR line, int fscan)
{
int i = 0;
char c;
int casecnv = TRUE;
while ((i < LINESIZE) && (c = (char)getchar()) != LF) {
if (c == EOF)
break;
if (i == 0 && c == '!' && fscan) {
casecnv = FALSE;
line[i++] = c;
line[i++] = SPACE;
continue;
}
if (casecnv)
line[i++] = (char)toupper(c);
else line[i++] = c;
}
line[i] = 0;
return (i);
}
/*
* Similar to getline but interactive. Used by the calculator.
*/
int getl(PCHAR line, PINT pi)
{
int i = 0;
char c;
while (i < LINESIZE) {
c = (char)getch();
if (c == SPACE || c == TAB || c == CR) {
if (c == CR) {
putch(CR); putch(LF);
}
else if (i != 0) {
putch(c);
}
break;
}
else if (c == BACKSPACE) {
if (i > 0) {
printf("%c %c", BACKSPACE, BACKSPACE);
i--;
}
continue;
}
else {
line[i++] = (char)toupper(c);
putch(c);
}
}
line[i] = 0;
*pi = i;
return (c);
}
int _CRTAPI1
sig()
{
static int cnt = 0;
if (++cnt >= 15)
quit();
printf("\07\n\tSignal\n");
longjmp(lj, 0);
return 0;
}
/*
* A small calculator which parses left to write.
*/
void calc()
{
char s[LINESIZE];
long value;
PCHAR next;
int cnt;
TOKEN tok;
int prvoper;
int flag = TRUE;
PCHAR getoken(PCHAR, PTOKEN);
printf(" Expressions are evaluated left to right\n");
printf(" Default input is %s\n", (defbase == 10) ? "decimal" :
((defbase == 8) ? "octal" : "hex"));
printf(" Blank line exits calculator mode\n");
while (TRUE) {
printf("CALC>");
if (getline(s, FALSE) == 0)
return;
if (*s == 'Q' || *s == 'E')
return;
next = s;
cnt = 0;
value = 0;
prvoper = VOID;
flag = TRUE;
while (flag && *next != '\0') {
next = getoken(next, &tok);
switch(tok.oper) {
case PLUS:
case MINUS:
case MUL:
case DIV:
if (prvoper != VOID || cnt == 0) {
printf("Illegal sequence\n");
flag = FALSE;
break;
}
prvoper = tok.oper;
break;
case VOID:
if (prvoper == VOID && cnt != 0) {
printf("Illegal sequence\n");
flag = FALSE;
break;
}
if (tok.inv) {
printf("Not a number\n");
flag = FALSE;
break;
}
switch (prvoper) {
case VOID:
case PLUS:
value += tok.tval;
break;
case MINUS:
value -= tok.tval;
break;
case MUL:
value *= tok.tval;
break;
case DIV:
value /= tok.tval;
break;
}
prvoper = VOID;
break;
}
cnt++;
}
if (flag)
printf("\t%lx (hex) 0%lo (oct) %ld (dec)\n", value, value, value);
}
}
/*
* Parse a token out of the line. Not quite strtok().
*/
PCHAR getoken(PCHAR s, PTOKEN ptok)
{
int i = 0;
char t[24];
ptok->oper = VOID;
while (*s == SPACE || *s == TAB)
s++;
while (*s && *s != SPACE && *s != TAB &&
*s != '+' && *s != '-' && *s != '*' && *s != '/')
t[i++] = *s++;
t[i] = 0;
if (i == 0) {
switch (*s) {
case '+':
ptok->oper = PLUS;
s++;
break;
case '-':
ptok->oper = MINUS;
s++;
break;
case '*':
ptok->oper = MUL;
s++;
break;
case '/':
ptok->oper = DIV;
s++;
break;
}
}
if (ptok->oper == VOID) {
ptok->inv = !getnum(t, &ptok->tval);
}
return (s);
}
/*
* Manage our pool of buffers. A little too fancy for what we need. Well why
* not. The following few routines do the following:
* bufrpool() dump the pool for the .p command.
* coalpool() collate and merge the pool.
* getbufr() manage the pool and return a buffer corr. to the off.
*
*/
void bufrpool()
{
register PBUFR pbfr;
coalpool();
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_buf != NULL) {
printf("%02d %06lx %04lx %s\n", pbfr-Bufr, pbfr->b_start,
pbfr->b_length, pbfr->b_dirty ? "dirty" : "clean");
}
}
}
void coalpool()
{
register PBUFR pbfr1, pbfr2;
PCHAR s;
bufsrt();
pbfr1 = &Bufr[MAXBUF - 1]; pbfr2 = &Bufr[MAXBUF - 2];
for (; pbfr1 > Bufr; pbfr1--, pbfr2--) {
if (pbfr1->b_buf == NULL || pbfr2->b_buf == NULL)
break;
if ((pbfr2->b_start + pbfr2->b_length) == pbfr1->b_start) {
if (debug)
printf("coalesce: %02d: %06lx %04lx + %02d: %06lx %04lx\n",
pbfr2-Bufr, pbfr2->b_start, pbfr2->b_length,
pbfr1-Bufr, pbfr1->b_start, pbfr1->b_length);
if ((s=malloc((int)(pbfr1->b_length + pbfr2->b_length)))==NULL)
break;
memcpy(s, pbfr2->b_buf, (int)pbfr2->b_length);
memcpy(s + (int)pbfr2->b_length, pbfr1->b_buf, (int)pbfr1->b_length);
free(pbfr1->b_buf);
free(pbfr2->b_buf);
pbfr2->b_buf =s ;
pbfr2->b_length += pbfr1->b_length;
pbfr2->b_dirty |= pbfr1->b_dirty;
pbfr1->b_buf = NULL;
pbfr1->b_start = 0;
pbfr1->b_length = 0;
pbfr1->b_dirty = FALSE;
}
}
bufsrt();
}
PCHAR getbufr(PCHAR str)
{
register PBUFR pbfr;
long strt, len;
PCHAR s;
if (cmdstrt >= filesize) {
printf("File offset exceeds file size\n");
longjmp(lj, 0);
}
coalpool();
strt = cmdstrt & ~15;
len = (cmdlen + cmdstrt + mode - strt + BUFRSIZE - 1) & ~BUFRMASK;
if ((strt + len) > filesize) {
len = filesize - strt;
cmdlen = filesize - cmdstrt;
}
if (debug)
printf("%s %lx %lx\n", str, cmdstrt, cmdlen);
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr && pbfr->b_buf != NULL; pbfr--) {
if (cmdstrt >= pbfr->b_start) {
if ((cmdstrt <= (pbfr->b_start + pbfr->b_length)) &&
(cmdstrt + cmdlen) <= (pbfr->b_start + pbfr->b_length))
return (pbfr->b_buf + (cmdstrt - pbfr->b_start));
else if ((pbfr->b_start + pbfr->b_length) == strt) {
if (debug)
printf("realloc: %02d: %lx %lx to %lx %lx\n",
pbfr-Bufr, pbfr->b_start, pbfr->b_length,
pbfr->b_start, pbfr->b_length + len);
if ((s = malloc((int)(pbfr->b_length + len))) == NULL)
break;
memcpy(s, pbfr->b_buf, (int)pbfr->b_length);
free(pbfr->b_buf);
pbfr->b_buf = s;
lseek(hread, strt, SEEK_SET);
pbfr->b_length += read(hread, s + pbfr->b_length, (int)len);
return(s + (cmdstrt - pbfr->b_start));
}
}
}
retry:
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_buf == NULL) {
pbfr->b_buf = malloc((int)len);
pbfr->b_start = strt;
lseek(hread, strt, SEEK_SET);
pbfr->b_length = read(hread, pbfr->b_buf, (int)len);
if (cmdlen > pbfr->b_length)
cmdlen = pbfr->b_length;
pbfr->b_dirty = FALSE;
return(pbfr->b_buf + cmdstrt - strt);
}
}
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_dirty == FALSE) {
free(pbfr->b_buf);
pbfr->b_buf = NULL;
goto retry;
}
}
printf("Out of buffers in pool\n");
if (dirtywrite()) {
writeback();
goto retry;
}
else {
printf("No buffers available, command aborted\n");
longjmp(lj, 0);
}
}
void bufdirty(long start)
{
register PBUFR pbfr;
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (start >= pbfr->b_start &&
start <= (pbfr->b_start + pbfr->b_length)) {
pbfr->b_dirty = TRUE;
return;
}
}
}
int dirtywrite()
{
register PBUFR pbfr;
int c;
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_dirty) {
printf("Dirty buffers in pool, write back ? ");
while (TRUE) {
c = getch();
c = toupper(c);
if (c == 'Y') {
printf("Yes\n");
break;
}
else if (c == 'N') {
printf("No\n");
break;
}
}
return(c == 'Y');
}
}
return (FALSE);
}
void writeback()
{
register PBUFR pbfr;
int dirty = FALSE;
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--)
dirty |= pbfr->b_dirty;
if (!dirty)
return;
if ((hwrite = open(filename, O_BINARY|O_RDWR)) == hNULL) {
printf("\txmn: can't write \"%s\"\n", filename);
printf("\tdirty buffers not written back\n");
return;
}
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_dirty) {
if ((lseek(hwrite, pbfr->b_start, SEEK_SET) != pbfr->b_start) ||
(write(hwrite, pbfr->b_buf, (int)pbfr->b_length)
!= (int)pbfr->b_length)) {
printf("\txmn: can't write \"%s\"\n", filename);
printf("\tdirty buffers not written back\n");
break;
}
pbfr->b_dirty = FALSE;
}
}
close(hwrite);
hwrite = hNULL;
}
void flushbufr()
{
register PBUFR pbfr;
for (pbfr = &Bufr[MAXBUF-1]; pbfr >= Bufr; pbfr--) {
if (pbfr->b_buf != NULL)
free(pbfr->b_buf);
pbfr->b_buf = NULL;
pbfr->b_start = 0;
pbfr->b_length = 0;
pbfr->b_dirty = FALSE;
}
}
void chgfile(PCHAR pchfile)
{
printf("Current file: %s\n", filename);
if (*pchfile == '\0') {
return;
}
if (dirtywrite)
writeback();
flushbufr();
if (setfile(pchfile)) {
printf("New file: %s\n", filename);
}
}
int setfile(PCHAR pchfile)
{
int hfile;
int i;
struct stat sbuf;
if ((hfile = open(pchfile, O_BINARY|O_RDONLY)) == hNULL) {
printf("xmn: can't open \"%s\"\n", pchfile);
return (FALSE);
}
if (hread != hNULL)
close(hread);
hread = hfile;
cmdstrt = cmdlen = 0;
stat(pchfile, &sbuf);
filesize = sbuf.st_size;
for (i = 0; pchfile[i]; i++)
filename[i] = pchfile[i];
filename[i] = '\0';
return (TRUE);
}
int _CRTAPI1
bufcmp(PBUFR p1, PBUFR p2)
{
return ((int)(p1->b_start + p1->b_length) - (int)(p2->b_start + p2->b_length));
}
void bufsrt()
{
int _CRTAPI1 bufcmp(PBUFR, PBUFR);
qsort(Bufr, MAXBUF, sizeof(BUFR), bufcmp);
}
void quit()
{
if (dirtywrite())
writeback();
flushbufr();
close(hread);
exit(0);
}
void error(PCHAR s)
{
printf("%s\n", s);
longjmp(lj, 0);
}
/*
* A version of scanf, which recognizes quoted strings. This one
* implicitly takes a format of the form "%s%s%s%s". Kind of kludgy
* but hey, I am not doing too much here.
*/
void ScanLine(PCHAR source, PCHAR tgt1, PCHAR tgt2, PCHAR tgt3, PCHAR tgt4)
{
int i, quote;
PCHAR pTgt, pTgtx[4];
pTgtx[0] = tgt1;
pTgtx[1] = tgt2;
pTgtx[2] = tgt3;
pTgtx[3] = tgt4;
tgt1[0] = tgt2[0] = tgt3[0] = tgt4[0] = 0;
quote = FALSE; pTgt = pTgtx[i = 0];
while ((*source != 0) && (i < 4)) {
if (source[0] == QUOTE)
quote = !quote;
else if ((source[0] == SPACE) && !quote) {
*pTgt = 0;
pTgt = pTgtx[++i];
}
else *pTgt++ = source[0];
source++;
}
if (i < 4)
*pTgt = 0;
}