/* pevi.c - Portable Executable Version Information Jason Hood, 6 & 7 March, 2020. Display information from the version resource of a portable executable. Windows functions are used to load the resource, but the version data is processed directly (there's no enumeration function, plus I just copied my code from PeReader). v1.0.1, 11 March, 2020: - explicitly use the current directory if no path is given and the file exists (otherwise it could load files in the application directory); * distinguish between "not found" and "unsupported". v1.0.2, 2 & 3 August, 2023: - fix for incorrect String size; + show Language as name and decimal code page. */ #define PVERS "1.0.2" #define PDATE "3 August, 2023" #define UNICODE #define WIN32_LEAN_AND_MEAN #include #include #include #include #define FIELD "%-20s\t" #define WFIELD "%-20S\t" // VC6 doesn't like wsetargv when using /MD. int _dowildcard = 1; HANDLE hStdOut; UINT codepage; BOOL show_name; void put_wstring(LPCWSTR str) { if (hStdOut == NULL) { char astr[4096]; WideCharToMultiByte(codepage, 0, str, -1, astr, sizeof(astr), NULL, NULL); printf("%s", astr); } else { DWORD dummy; WriteConsole(hStdOut, str, wcslen(str), &dummy, NULL); } } void WriteVersionInformation(LPBYTE verinfo, LPCWSTR name) { union { VS_FIXEDFILEINFO* vs; LPBYTE b; LPWORD w; LPWSTR ws; } p; int len; WCHAR lang[64]; UINT lid, cp; p.b = verinfo + 0x28; // skip VS_VERSIONINFO header if (p.vs->dwSignature != 0xFEEF04BD) { if (show_name) printf(FIELD "Unrecognized signature (%08X)\n", "Note", p.vs->dwSignature); else { printf("\""); put_wstring(name); printf("\" has an unrecognized signature (%08X).\n", p.vs->dwSignature); } return; } printf(FIELD "%u.%u.%u.%u\n", "File version", p.vs->dwFileVersionMS >> 16, p.vs->dwFileVersionMS & 0xFFFF, p.vs->dwFileVersionLS >> 16, p.vs->dwFileVersionLS & 0xFFFF); printf(FIELD "%u.%u.%u.%u\n", "Product version", p.vs->dwProductVersionMS >> 16, p.vs->dwProductVersionMS & 0xFFFF, p.vs->dwProductVersionLS >> 16, p.vs->dwProductVersionLS & 0xFFFF); if (p.vs->dwFileFlags != 0) { LPCSTR comma = " ("; printf(FIELD "%08X", "File flags", p.vs->dwFileFlags); if (p.vs->dwFileFlags & 0x01) printf("%sdebug", comma), comma = ", "; if (p.vs->dwFileFlags & 0x02) printf("%sprerelease", comma), comma = ", "; if (p.vs->dwFileFlags & 0x04) printf("%spatched", comma), comma = ", "; if (p.vs->dwFileFlags & 0x08) printf("%sprivate build", comma), comma = ", "; if (p.vs->dwFileFlags & 0x10) printf("%sinfo inferred", comma), comma = ", "; if (p.vs->dwFileFlags & 0x20) printf("%sspecial build", comma), comma = ", "; if (*comma == ',') printf(")"); printf("\n"); } printf(FIELD "%08X (", "File OS", p.vs->dwFileOS); if (p.vs->dwFileOS == 0) printf("unknown"); else { switch (p.vs->dwFileOS >> 16) { case 1: printf("DOS"); break; case 2: printf("OS/2 16-bit"); break; case 3: printf("OS/2 32-bit"); break; case 4: printf("Windows NT"); break; case 5: printf("Windows CE"); break; } if ((p.vs->dwFileOS >> 16) && (p.vs->dwFileOS & 0xFFFF)) printf(" / "); switch (p.vs->dwFileOS & 0xFFFF) { case 1: printf("Windows 16-bit"); break; case 2: printf("Presentation Manager 16-bit"); break; case 3: printf("Presentation Manager 32-bit"); break; case 4: printf("Windows 32-bit"); break; } } printf(")\n"); printf(FIELD "%08X (", "File type", p.vs->dwFileType); switch (p.vs->dwFileType) { default: case 0: printf("unknown"); break; case 1: printf("application"); break; case 2: printf("DLL"); break; case 3: printf("device driver"); break; case 4: printf("font"); break; case 5: printf("virtual device"); break; case 7: printf("static library"); break; } printf(")\n"); if (p.vs->dwFileSubtype != 0) { printf(FIELD "%8X (", "File subtype", p.vs->dwFileSubtype); switch (p.vs->dwFileType) { default: printf("unknown"); break; case 3: switch (p.vs->dwFileSubtype) { default: case 0x00: printf("unknown"); break; case 0x01: printf("printer"); break; case 0x02: printf("keyboard"); break; case 0x03: printf("language"); break; case 0x04: printf("display"); break; case 0x05: printf("mouse"); break; case 0x06: printf("network"); break; case 0x07: printf("system"); break; case 0x08: printf("installable"); break; case 0x09: printf("sound"); break; case 0x0A: printf("communications"); break; case 0x0B: printf("inputmethod"); break; case 0x0C: printf("versioned printer"); break; } break; case 4: switch (p.vs->dwFileSubtype) { default: case 0: printf("unknown"); break; case 1: printf("raster"); break; case 2: printf("vector"); break; case 3: printf("truetype"); break; } break; } printf(")\n"); } if (p.vs->dwFileDateMS != 0 || p.vs->dwFileDateLS != 0) { // I was going to decode this, but I've only come across one // info with a date and that appears to be in Unix time. printf(FIELD "%08X%08X\n", "File creation date", p.vs->dwFileDateMS, p.vs->dwFileDateLS); } ++p.vs; if (wcscmp(p.ws + 3, L"VarFileInfo") == 0) { len = *p.w; len += len & 2; // align to DWORD p.b += len; } if (wcscmp(p.ws + 3, L"StringFileInfo") != 0) { printf(FIELD "\"StringFileInfo\" not detected\n", "Note"); return; } p.b += 0x24; // skip StringFileInfo header len = *p.w - 0x18; // skip StringTable header len += len & 2; swscanf(p.ws + 3, L"%4X%4X", &lid, &cp); VerLanguageName(lid, lang, sizeof(lang)/sizeof(*lang)); printf(FIELD "%S (%S; ", "Language", p.ws + 3, lang); if (cp == 0x4b0) printf("Unicode)\n"); else if (cp == 0) // assume CP_ACP printf("ANSI)\n"); else printf("CP%u)\n", cp); p.b += 0x18; while (len > 0) { DWORD sz; printf(WFIELD, p.ws + 3); sz = wcslen(p.ws + 3); sz += sz & 1; if (p.w[1] != 0) put_wstring(p.ws + 3 + sz + 1); printf("\n"); sz = *p.w; sz += sz & 1; sz += sz & 2; p.b += sz; len -= sz; } } int load(LPCWSTR name) { HMODULE pe; HRSRC rcVer; HGLOBAL hVer; LPBYTE ver; int i; if (show_name) { printf(FIELD, "File name"); put_wstring(name); printf("\n"); } for (i = 0;; ++i) { if (name[i] == '\0') { if (GetFileAttributes(name) == -1) pe = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE); else { WCHAR curname[MAX_PATH]; _snwprintf(curname, MAX_PATH, L".\\%s", name); pe = LoadLibraryEx(curname, NULL, LOAD_LIBRARY_AS_DATAFILE); } break; } else if (name[i] == '/' || name[i] == '\\') { pe = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE); break; } } if (pe == NULL) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { if (show_name) printf(FIELD "Not found\n", "Note"); else { printf("\""); put_wstring(name); printf("\" is not found.\n"); } } else { if (show_name) printf(FIELD "Unsupported file\n", "Note"); else { printf("\""); put_wstring(name); printf("\" is not supported.\n"); } } return 1; } rcVer = FindResource(pe, MAKEINTRESOURCE(1), RT_VERSION); if (rcVer == NULL) { if (show_name) printf(FIELD "Version resource not present\n", "Note"); else { printf("\""); put_wstring(name); printf("\" has no version resource.\n", name); } FreeLibrary(pe); return 1; } hVer = LoadResource(pe, rcVer); if (hVer == NULL) { if (show_name) printf(FIELD "Version resource not loaded (error %lu)\n", "Note", GetLastError()); else printf("Unable to load version resource (error %lu).\n", GetLastError()); FreeLibrary(pe); return 1; } ver = LockResource(hVer); if (ver == NULL) { if (show_name) printf(FIELD "Version resource not locked (error %lu)\n", "Note", GetLastError()); else printf("Unable to lock version resource (error %lu).\n", GetLastError()); FreeLibrary(pe); return 1; } WriteVersionInformation(ver, name); FreeLibrary(pe); return 0; } int wmain(int argc, wchar_t* argv[]) { if (argc == 1 || wcscmp(argv[1], L"/?") == 0 || wcscmp(argv[1], L"-?") == 0 || wcscmp(argv[1], L"--help") == 0) { printf("Portable Executable Version Information by Jason Hood .\n" "Version " PVERS " (" PDATE "). Freeware.\n" "http://misc.adoxa.vze.com/#pevi\n" "\n" "Display information from a PE's version resource.\n" "\n" "pevi FILE...\n"); return 0; } if (wcscmp(argv[1], L"--version") == 0) { printf("PEVI version " PVERS " (" PDATE ").\n"); return 0; } if (_isatty(1)) hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); else codepage = GetConsoleOutputCP(); show_name = (argc > 2); while (argv[1] != NULL) { load(argv[1]); ++argv; if (show_name && argv[1] != NULL) { printf("\n"); if (hStdOut == NULL) fflush(stdout); } } return 0; }