| /*++ | |
| Copyright (c) 2004 - 2007, Intel Corporation | |
| All rights reserved. This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| Module Name: | |
| SetStamp.c | |
| Abstract: | |
| Set Date/Time Stamp of Portable Executable (PE) format file | |
| --*/ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #define LINE_MAXLEN 80 | |
| // | |
| // Utility Name | |
| // | |
| #define UTILITY_NAME "SetStamp" | |
| // | |
| // Utility version information | |
| // | |
| #define UTILITY_MAJOR_VERSION 0 | |
| #define UTILITY_MINOR_VERSION 1 | |
| void | |
| Version ( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| Displays the standard utility information to SDTOUT | |
| Arguments: | |
| None | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| printf ("%s v%d.%d -Utility to set Date/Time Stamp for Portable Executable (PE) format file.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION); | |
| printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n"); | |
| } | |
| void | |
| Usage ( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| print usage of setstamp command | |
| Arguments: | |
| void | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| Version(); | |
| // | |
| // print usage of command | |
| // | |
| printf ("\nUsage: SetStamp <PE-File> <TIME-File>\n"); | |
| } | |
| int | |
| GetDateTime ( | |
| FILE *fp, | |
| time_t *ltime | |
| ) | |
| /*++ | |
| Routine Description: | |
| Read the date and time from TIME file. If the date/time string is | |
| "NOW NOW", write the current date and time to TIME file and set it to | |
| ltime. Else, set the date and time of TIME file to ltime. | |
| Arguments: | |
| fp - The pointer of TIME file | |
| ltime - Date and time | |
| Returns: | |
| = 0 - Success | |
| = -1 - Failed | |
| --*/ | |
| { | |
| char buffer[LINE_MAXLEN]; | |
| struct tm stime; | |
| struct tm *now; | |
| if (fgets (buffer, LINE_MAXLEN, fp) == NULL) { | |
| printf ("Error: Cannot read TIME file.\n"); | |
| return -1; | |
| } | |
| // | |
| // compare the value with "NOW NOW", write TIME file if equal | |
| // | |
| if (strncmp (buffer, "NOW NOW", 7) == 0) { | |
| // | |
| // get system current time and date | |
| // | |
| time (ltime); | |
| now = localtime (ltime); | |
| if (now == NULL) { | |
| printf ("Error: Cannot get local time.\n"); | |
| return -1; | |
| } | |
| if (strftime (buffer, LINE_MAXLEN, "%Y-%m-%d %H:%M:%S", now) == 0) { | |
| printf ("Error: Cannot format time string.\n"); | |
| return -1; | |
| } | |
| // | |
| // write TIME file | |
| // | |
| if (fseek (fp, 0, SEEK_SET) != 0) { | |
| printf ("Error: Cannot move location of TIME file.\n"); | |
| return -1; | |
| } | |
| if (fputs (buffer, fp) == EOF) { | |
| printf ("Error: Cannot write time string to TIME file.\n"); | |
| return -1; | |
| } | |
| // | |
| // ltime has been set as current time and date, return | |
| // | |
| return 0; | |
| } | |
| // | |
| // get the date and time from buffer | |
| // | |
| if (6 != sscanf ( | |
| buffer, | |
| "%d-%d-%d %d:%d:%d", | |
| &stime.tm_year, | |
| &stime.tm_mon, | |
| &stime.tm_mday, | |
| &stime.tm_hour, | |
| &stime.tm_min, | |
| &stime.tm_sec | |
| )) { | |
| printf ("Error: Invaild date or time!\n"); | |
| return -1; | |
| } | |
| // | |
| // in struct, Month (0 - 11; Jan = 0). So decrease 1 from it | |
| // | |
| stime.tm_mon -= 1; | |
| // | |
| // in struct, Year (current year minus 1900) | |
| // and only the dates can be handled from Jan 1, 1970 to Jan 18, 2038 | |
| // | |
| // | |
| // convert 0 -> 100 (2000), 1 -> 101 (2001), ..., 38 -> 138 (2038) | |
| // | |
| if (stime.tm_year <= 38) { | |
| stime.tm_year += 100; | |
| } | |
| // | |
| // convert 1970 -> 70, 2000 -> 100, ... | |
| // | |
| else if (stime.tm_year >= 1970) { | |
| stime.tm_year -= 1900; | |
| } | |
| // | |
| // convert the date and time to time_t format | |
| // | |
| *ltime = mktime (&stime); | |
| if (*ltime == (time_t) - 1) { | |
| printf ("Error: Invalid date or time!\n"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| ReadFromFile ( | |
| FILE *fp, | |
| long offset, | |
| void *buffer, | |
| int size | |
| ) | |
| /*++ | |
| Routine Description: | |
| read data from a specified location of file | |
| Arguments: | |
| fp - file pointer | |
| offset - number of bytes from beginning of file | |
| buffer - buffer used to store data | |
| size - size of buffer | |
| Returns: | |
| = 0 - Success | |
| = -1 - Failed | |
| --*/ | |
| { | |
| // | |
| // set file pointer to the specified location of file | |
| // | |
| if (fseek (fp, offset, SEEK_SET) != 0) { | |
| printf ("Error: Cannot move the current location of the file.\n"); | |
| return -1; | |
| } | |
| // | |
| // read data from the file | |
| // | |
| if (fread (buffer, size, 1, fp) != 1) { | |
| printf ("Error: Cannot read data from the file.\n"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| WriteToFile ( | |
| FILE *fp, | |
| long offset, | |
| void *buffer, | |
| int size | |
| ) | |
| /*++ | |
| Routine Description: | |
| write data to a specified location of file | |
| Arguments: | |
| fp - file pointer | |
| offset - number of bytes from beginning of file | |
| buffer - buffer used to store data | |
| size - size of buffer | |
| Returns: | |
| = 0 - Success | |
| = -1 - Failed | |
| --*/ | |
| { | |
| // | |
| // set file pointer to the specified location of file | |
| // | |
| if (fseek (fp, offset, SEEK_SET) != 0) { | |
| printf ("Error: Cannot move the current location of the file.\n"); | |
| return -1; | |
| } | |
| // | |
| // write data to the file | |
| // | |
| if (fwrite (buffer, size, 1, fp) != 1) { | |
| perror ("Error: Cannot write data to the file.\n"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| SetStamp ( | |
| FILE *fp, | |
| time_t ltime | |
| ) | |
| /*++ | |
| Routine Description: | |
| set Date/Time Stamp of the file | |
| Arguments: | |
| fp - file pointer | |
| ltime - time and date | |
| Returns: | |
| = 0 - Success | |
| = -1 - Failed | |
| --*/ | |
| { | |
| unsigned char header[4]; | |
| unsigned long offset; | |
| unsigned long NumberOfRvaAndSizes; | |
| unsigned int nvalue; | |
| unsigned long lvalue; | |
| // | |
| // read the header of file | |
| // | |
| if (ReadFromFile (fp, 0, header, 2) != 0) { | |
| return -1; | |
| } | |
| // | |
| // "MZ" -- the header of image file (PE) | |
| // | |
| if (strncmp ((char *) header, "MZ", 2) != 0) { | |
| printf ("Error: Invalid Image file.\n"); | |
| return -1; | |
| } | |
| // | |
| // At location 0x3C, the stub has the file offset to the | |
| // PE signature. | |
| // | |
| if (ReadFromFile (fp, 0x3C, &offset, 4) != 0) { | |
| return -1; | |
| } | |
| // | |
| // read the header of optional | |
| // | |
| if (ReadFromFile (fp, offset, header, 4) != 0) { | |
| return -1; | |
| } | |
| // | |
| // "PE\0\0" -- the signature of optional header | |
| // | |
| if (strncmp ((char *) header, "PE\0\0", 4) != 0) { | |
| printf ("Error: Invalid PE format file.\n"); | |
| return -1; | |
| } | |
| // | |
| // Add 8 to skip PE signature (4-byte), Machine (2-byte) and | |
| // NumberOfSection (2-byte) | |
| // | |
| offset += 8; | |
| if (WriteToFile (fp, offset, <ime, 4) != 0) { | |
| return -1; | |
| } | |
| // | |
| // Add 16 to skip COFF file header, and get to optional header. | |
| // | |
| offset += 16; | |
| // | |
| // Check the magic field, 0x10B for PE32 and 0x20B for PE32+ | |
| // | |
| if (ReadFromFile (fp, offset, &nvalue, 2) != 0) { | |
| return -1; | |
| } | |
| // | |
| // If this is PE32 image file, offset of NumberOfRvaAndSizes is 92. | |
| // Else it is 108. | |
| // | |
| switch (nvalue & 0xFFFF) { | |
| case 0x10B: | |
| offset += 92; | |
| break; | |
| case 0x20B: | |
| offset += 108; | |
| break; | |
| default: | |
| printf ("Error: Sorry! The Magic value is unknown.\n"); | |
| return -1; | |
| } | |
| // | |
| // get the value of NumberOfRvaAndSizes | |
| // | |
| if (ReadFromFile (fp, offset, &NumberOfRvaAndSizes, 4) != 0) { | |
| return -1; | |
| } | |
| // | |
| // Date/time stamp exists in Export Table, Import Table, Resource Table, | |
| // Debug Table and Delay Import Table. And in Import Table and Delay Import | |
| // Table, it will be set when bound. So here only set the date/time stamp | |
| // of Export Table, Resource Table and Debug Table. | |
| // | |
| // | |
| // change date/time stamp of Export Table, the offset of Export Table | |
| // is 4 + 0 * 8 = 4. And the offset of stamp is 4. | |
| // | |
| if (NumberOfRvaAndSizes >= 1) { | |
| if (ReadFromFile (fp, offset + 4, &lvalue, 4) != 0) { | |
| return -1; | |
| } | |
| if (lvalue != 0) { | |
| if (WriteToFile (fp, lvalue + 4, <ime, 4) != 0) { | |
| return -1; | |
| } | |
| } | |
| } | |
| // | |
| // change date/time stamp of Resource Table, the offset of Resource Table | |
| // is 4 + 2 * 8 = 20. And the offset of stamp is 4. | |
| // | |
| if (NumberOfRvaAndSizes >= 3) { | |
| if (ReadFromFile (fp, offset + 20, &lvalue, 4) != 0) { | |
| return -1; | |
| } | |
| if (lvalue != 0) { | |
| if (WriteToFile (fp, lvalue + 4, <ime, 4) != 0) { | |
| return -1; | |
| } | |
| } | |
| } | |
| // | |
| // change date/time stamp of Debug Table, offset of Debug Table | |
| // is 4 + 6 * 8 = 52. And the offset of stamp is 4. | |
| // | |
| if (NumberOfRvaAndSizes >= 7) { | |
| if (ReadFromFile (fp, offset + 52, &lvalue, 4) != 0) { | |
| return -1; | |
| } | |
| if (lvalue != 0) { | |
| if (WriteToFile (fp, lvalue + 4, <ime, 4) != 0) { | |
| return -1; | |
| } | |
| } | |
| // | |
| // change the date/time stamp of Debug Data | |
| // | |
| if (ReadFromFile (fp, lvalue + 24, &lvalue, 4) != 0) { | |
| return -1; | |
| } | |
| // | |
| // get the signature of debug data | |
| // | |
| if (ReadFromFile (fp, lvalue, header, 2) != 0) { | |
| return -1; | |
| } | |
| // | |
| // "NB" - the signature of Debug Data | |
| // Need Review: (From Spec. is "NB05", From .dll is "NB10") | |
| // | |
| if (strncmp ((char *) header, "NB", 2) == 0) { | |
| if (WriteToFile (fp, lvalue + 8, <ime, 4) != 0) { | |
| return -1; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| int | |
| main ( | |
| int argc, | |
| char *argv[] | |
| ) | |
| { | |
| FILE *fp; | |
| time_t ltime; | |
| if (argc == 1) { | |
| Usage(); | |
| return -1; | |
| } | |
| if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) || | |
| (strcmp(argv[1], "-?") == 0) || (strcmp(argv[1], "/?") == 0)) { | |
| Usage(); | |
| return -1; | |
| } | |
| if ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0)) { | |
| Version(); | |
| return -1; | |
| } | |
| // | |
| // check the number of parameters | |
| // | |
| if (argc != 3) { | |
| Usage (); | |
| return -1; | |
| } | |
| // | |
| // open the TIME file, if not exists, return | |
| // | |
| fp = fopen (argv[2], "r+"); | |
| if (fp == NULL) { | |
| return 0; | |
| } | |
| // | |
| // get time and date from file | |
| // | |
| if (GetDateTime (fp, <ime) != 0) { | |
| fclose (fp); | |
| return -1; | |
| } | |
| // | |
| // close the TIME file | |
| // | |
| fclose (fp); | |
| // | |
| // open the PE file | |
| // | |
| fp = fopen (argv[1], "r+b"); | |
| if (fp == NULL) { | |
| printf ("Error: Cannot open the PE file!\n"); | |
| return -1; | |
| } | |
| // | |
| // set time and date stamp to the PE file | |
| // | |
| if (SetStamp (fp, ltime) != 0) { | |
| fclose (fp); | |
| return -1; | |
| } | |
| printf ("Set Date/Time Stamp to %s", ctime (<ime)); | |
| // | |
| // close the PE file | |
| // | |
| fclose (fp); | |
| return 0; | |
| } |