INF: The Segmented Executable Checksum

ID Number: Q81889

5.0x 5.1x 5.20 5.30 | 5.0x 5.1x 5.20 | 5.0x 5.1x 5.20 5.30

MS-DOS | OS/2 | WINDOWS

Summary:

A segmented executable needs to have more information stored in its

executable header than an MS-DOS executable does. This expanded header

consists of the old MS-DOS executable header, and adds information in

a new executable header.

A 16-bit checksum is created by LINK and stored in bytes 12-13h of the

old executable header. MS-DOS does not use this value. Bytes 08-0Bh

are set aside for a 32-bit checksum in the new header. The 16-bit

checksum is calculated any time an MS-DOS program is created. This

value is ignored by Windows and OS/2. For almost any other type of

executable file (Windows .EXEs and DLLs, OS/2 1.x .EXEs and DLLs), the

32-bit checksum is calculated.

More Information:

The following information on how to calculate the 16-bit checksum is

from page 122 of the "MS-DOS Encyclopedia:"

12-13H (Complemented Checksum) This word contains the one's

complement of the summation of all words in the .EXE file. Current

versions of MS-DOS basically ignore this word when they load a .EXE

program; however, future versions might not. When LINK generates an

.EXE file, it adds together all the contents of the .EXE file

(including the .EXE header) by treating the entire file as a long

sequence of 16-bit words. During this addition, LINK gives the

Complemented Checksum word (12-13H) a temporary value of 0000H. If

the file consists of an odd number of bytes, then the final byte is

treated as a word with a high byte of 00H. Once LINK has totaled

all words in the .EXE file, it performs a one's complement

operation on the total and records the answer in the .EXE file

header at offsets 12-13H. The validity of a .EXE file can then be

checked by performing the same word-totaling process as LINK

performed. The total should be FFFFH, because the total will

include LINK's calculated complemented checksum, which is designed

to give the file the FFFFH total.

The sample code below is one implementation of this.

Page 1488 of the "MS-DOS Encyclopedia" indicates that the 32-bit

checksum should be zero. Link versions 5.2 and earlier do compute a

value, but it will not result in a total of FFFFh when doing a

word-totalling process similar to the one for a 16-bit checksum.

Starting with link 5.3, the 16-bit and 32-bit checksum is no

longer computed. The bytes reserved will be set to zero.

Sample Code

-----------

/* Compile options needed: none

*/

#include <stdio.h>

#include <stdlib.h>

#include <io.h>

void Calc16ChkSum( FILE *fp );

void main( int, char ** );

FILE *fp;

unsigned long NewHdrOffset, lPageCnt, FileSize = 0L;

unsigned int PageCnt;

unsigned char NewHdrIndc;

#define NEWHDRINDC 0x18 /* Location in old header for indicator

of new header in EXE */

#define NEWHDROFFSET 0x3C /* Location if new header offset */

void main( int argc, char * argv[] )

{

if( argc != 2 )

{

printf( "\n\nUsage: %s <EXEfilename>\n\n", argv[0] );

exit( -1 );

}

if( (fp = fopen (argv[1], "rb")) == NULL )

{

printf( "\n\nError: Unable to open file : %s\n\n", argv[1] );

exit( -1 );

}

fseek( fp, NEWHDRINDC, SEEK_SET );

fread( &NewHdrIndc, sizeof(char), 1, fp );

if( (int)NewHdrIndc >= 0x40 ) /* Then it's a new EXE header */

{

printf( "\n%s has new .EXE header information.\n",argv[1] );

printf( "32-bit checksum not available.\n" );

}

else /* Get size and calculate 16-bit checksum */

{

fseek( fp,0,SEEK_SET );

/* Read past the signature */

fread( &PageCnt, sizeof(int), 1, fp );

/* Read the last page size */

fread( &PageCnt, sizeof(int), 1, fp );

FileSize = PageCnt;

/* Read the full page count */

fread( &PageCnt, sizeof(int), 1, fp );

lPageCnt = (long) PageCnt; /* Considering large programs */

if( FileSize == 0L )

FileSize = lPageCnt * 512L;

else

FileSize += (lPageCnt - 1L) * 512L;

Calc16ChkSum( fp );

}

fcloseall( );

}

void Calc16ChkSum( FILE *fp )

{

unsigned int sum16, NxtInt, x;

unsigned char NxtChar;

sum16 = 0;

fseek( fp, 0, SEEK_SET );

for( x = 0L ; x < FileSize / 2L ; x++ )

{

fread( &NxtInt, sizeof(int), 1, fp );

sum16 += NxtInt;

}

/* Make sure and get the last byte if odd size. */

if( FileSize % 2 )

{

fread( &NxtChar, sizeof(char), 1, fp );

sum16 += (unsigned int) NxtChar;

}

printf( "\nThe 16 bit checksum should be FFFF\n" );

printf( "The calculated checksum is %X\n\n",sum16 );

}

Additional reference words: 5.01.2 5.01.20 5.01.21 5.02 5.03 5.05

5.1 5.10 5.11 5.13 5.15 5.2 5.20 5.30