From 7e5c5d42320ab39cd8f7d0d181745786196cb0b8 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 18 May 2017 06:53:07 +0100 Subject: Move MsiGetFileVersion out into its own file. --- Makefile.am | 2 +- fake-msi.c | 183 -------------------------------------------------------- version.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 184 deletions(-) create mode 100644 version.c diff --git a/Makefile.am b/Makefile.am index cc11cbd..668425e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,7 @@ libwinterop_so_la_SOURCES = fake-winterop.c fake-lib.c fake-lib.h \ memory.c memory.h libmsi_so_la_SOURCES = fake-msi.c fake-lib.c fake-lib.h md5.c \ -memory.c memory.h +memory.c memory.h version.c libpreload_la_SOURCES = preload.c libpreload_la_LDFLAGS = -ldl diff --git a/fake-msi.c b/fake-msi.c index 8e8d97d..2223211 100644 --- a/fake-msi.c +++ b/fake-msi.c @@ -17,189 +17,6 @@ #include "memory.h" #include "fake-lib.h" -uint32_t MsiGetFileVersionW(const char16_t *filename, - char16_t *version, uint32_t *version_size, - char16_t *language, uint32_t *language_size) -{ - char *fname = ascii(filename, true); - uint32_t toret = 1006; /* ERROR_FILE_INVALID == 'no version info found' */ - int fd = -1; - void *mapv = MAP_FAILED; - - if (version && *version_size) - *version = 0; - if (language && *language_size) - *language = 0; - - fd = open(fname, O_RDONLY); - if (fd < 0) - err(1, "%s: open", fname); - struct stat st; - if (fstat(fd, &st) < 0) - err(1, "%s: fstat", fname); - size_t fsize = st.st_size; - mapv = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); - if (mapv == MAP_FAILED) - err(1, "%s: mmap", fname); - unsigned char *map = (unsigned char *)mapv; - - if (le(map, fsize, 0, 2) != (('Z'<<8) | 'M')) { - warnx("MsiGetFileInfo(%s) -> no MZ", fname); - goto cleanup; - } - unsigned pe_pos = le(map, fsize, 0x3c, 4); - if (le(map, fsize, pe_pos, 4) != (('E'<<8) | 'P')) { - warnx("MsiGetFileInfo(%s) -> no PE", fname); - goto cleanup; - } - pe_pos += 4; /* skip to the main header */ - unsigned nsections = le(map, fsize, pe_pos + 2, 2); - unsigned opthdr_size = le(map, fsize, pe_pos + 16, 2); - unsigned opthdr_pos = pe_pos + 20; - /* bool sixtyfourbit = le(map, fsize, opthdr_pos, 2) == 0x020B; */ - unsigned secthdr_pos = opthdr_pos + opthdr_size; - while (nsections > 0) { - if (le(map, fsize, secthdr_pos+0, 1) == '.' && - le(map, fsize, secthdr_pos+1, 1) == 'r' && - le(map, fsize, secthdr_pos+2, 1) == 's' && - le(map, fsize, secthdr_pos+3, 1) == 'r' && - le(map, fsize, secthdr_pos+4, 1) == 'c' && - le(map, fsize, secthdr_pos+5, 1) == 0) - goto found_resource_section; - secthdr_pos += 0x28; - nsections--; - } - warnx("MsiGetFileInfo(%s) -> no .rsrc", fname); - goto cleanup; - - found_resource_section:; - unsigned rsrc_size = le(map, fsize, secthdr_pos+8, 4); - unsigned rsrc_offset = le(map, fsize, secthdr_pos+20, 4); - unsigned rsrc_vaddr = le(map, fsize, secthdr_pos+12, 4); - - unsigned res_dir_offset = rsrc_offset; - unsigned nnamed, nid; - nnamed = le(map, fsize, res_dir_offset+12, 2); - nid = le(map, fsize, res_dir_offset+14, 2); - for (unsigned i = nnamed; i < nnamed+nid; i++) { - unsigned id = le(map, fsize, res_dir_offset + 16 + 8*i, 4); - unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); - if (id == 16 && (entry & 0x80000000)) { - res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); - goto found_versioninfo_toplevel; - } - } - warnx("MsiGetFileInfo(%s) -> no top-level numeric key 16 for versioninfo", fname); - goto cleanup; - - found_versioninfo_toplevel: - nnamed = le(map, fsize, res_dir_offset+12, 2); - nid = le(map, fsize, res_dir_offset+14, 2); - for (unsigned i = 0; i < nnamed+nid; i++) { - unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); - if (entry & 0x80000000) { - res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); - goto found_versioninfo_2ndlevel; - } - } - warnx("MsiGetFileInfo(%s) -> no 2nd-level subdir", fname); - goto cleanup; - - found_versioninfo_2ndlevel: - nnamed = le(map, fsize, res_dir_offset+12, 2); - nid = le(map, fsize, res_dir_offset+14, 2); - for (unsigned i = 0; i < nnamed+nid; i++) { - unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); - if (!(entry & 0x80000000)) { - res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); - goto found_versioninfo_3rdlevel; - } - } - warnx("MsiGetFileInfo(%s) -> no 3rd-level resource data", fname); - goto cleanup; - - found_versioninfo_3rdlevel:; - unsigned versioninfo_offset = le(map, fsize, res_dir_offset, 4); - unsigned versioninfo_size = le(map, fsize, res_dir_offset+4, 4); - versioninfo_offset = rsrc_offset + versioninfo_offset - rsrc_vaddr; - - unsigned name_offset = versioninfo_offset + 6; - const char *next_name_chr = "VS_VERSION_INFO"; - do { - if (le(map, fsize, name_offset, 2) != *next_name_chr) - goto cleanup; /* identifying string didn't match */ - name_offset += 2; - } while (*next_name_chr++); - unsigned fixed_offset = (name_offset + 3) & ~3; - if (le(map, fsize, fixed_offset, 4) != 0xFEEF04BDU) { - warnx("MsiGetFileInfo(%s) -> no VS_FIXEDFILEINFO magic number", fname); - goto cleanup; - } - int four_part_version[4]; - four_part_version[0] = le(map, fsize, fixed_offset + 10, 2); - four_part_version[1] = le(map, fsize, fixed_offset + 8, 2); - four_part_version[2] = le(map, fsize, fixed_offset + 14, 2); - four_part_version[3] = le(map, fsize, fixed_offset + 12, 2); - unsigned child_offset = fixed_offset + - le(map, fsize, versioninfo_offset+2, 2); - unsigned lcid; - while (child_offset < versioninfo_offset + versioninfo_size) { - unsigned this_child_offset = child_offset; - child_offset += le(map, fsize, child_offset, 2); - child_offset = (child_offset + 3) &~ 3; - if (child_offset <= this_child_offset) { - warnx("MsiGetFileInfo(%s) -> bad length field", fname); - goto cleanup; - } - const char *next_name_chr = "VarFileInfo"; - name_offset = this_child_offset + 6; - do { - if (le(map, fsize, name_offset, 2) != *next_name_chr) - goto this_is_not_a_varfileinfo; - name_offset += 2; - } while (*next_name_chr++); - unsigned subchild_offset = (name_offset + 3) & ~3; - - next_name_chr = "Translation"; - name_offset = subchild_offset + 6; - do { - if (le(map, fsize, name_offset, 2) != *next_name_chr) { - warnx("MsiGetFileInfo(%s) -> child not called 'Translation'", fname); - goto cleanup; - } - name_offset += 2; - } while (*next_name_chr++); - subchild_offset = (name_offset + 3) & ~3; - lcid = le(map, fsize, subchild_offset, 2); - goto success; - this_is_not_a_varfileinfo: - continue; - } - warnx("MsiGetFileInfo(%s) -> no VarFileInfo found", fname); - goto cleanup; - - success:; - char verbuf[256], langbuf[256]; - snprintf(verbuf, sizeof(verbuf), "%d.%d.%d.%d", - four_part_version[0], four_part_version[1], - four_part_version[2], four_part_version[3]); - snprintf(langbuf, sizeof(langbuf), "%u", lcid); - warnx("MsiGetFileInfo(%s) -> version %s lang %s", fname, verbuf, langbuf); - if (version) - c16cpy(version, version_size, verbuf); - if (language) - c16cpy(language, language_size, langbuf); - toret = 0; - - cleanup: - if (mapv != MAP_FAILED) - munmap(mapv, fsize); - if (fd != -1) - close(fd); - sfree(fname); - return toret; -} - typedef struct MsiTypePrefix { enum { MAIN, VIEW, RECORD } type; } MsiTypePrefix; diff --git a/version.c b/version.c new file mode 100644 index 0000000..dd62fc4 --- /dev/null +++ b/version.c @@ -0,0 +1,196 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "memory.h" +#include "fake-lib.h" + +uint32_t MsiGetFileVersionW(const char16_t *filename, + char16_t *version, uint32_t *version_size, + char16_t *language, uint32_t *language_size) +{ + char *fname = ascii(filename, true); + uint32_t toret = 1006; /* ERROR_FILE_INVALID == 'no version info found' */ + int fd = -1; + void *mapv = MAP_FAILED; + + if (version && *version_size) + *version = 0; + if (language && *language_size) + *language = 0; + + fd = open(fname, O_RDONLY); + if (fd < 0) + err(1, "%s: open", fname); + struct stat st; + if (fstat(fd, &st) < 0) + err(1, "%s: fstat", fname); + size_t fsize = st.st_size; + mapv = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); + if (mapv == MAP_FAILED) + err(1, "%s: mmap", fname); + unsigned char *map = (unsigned char *)mapv; + + if (le(map, fsize, 0, 2) != (('Z'<<8) | 'M')) { + warnx("MsiGetFileInfo(%s) -> no MZ", fname); + goto cleanup; + } + unsigned pe_pos = le(map, fsize, 0x3c, 4); + if (le(map, fsize, pe_pos, 4) != (('E'<<8) | 'P')) { + warnx("MsiGetFileInfo(%s) -> no PE", fname); + goto cleanup; + } + pe_pos += 4; /* skip to the main header */ + unsigned nsections = le(map, fsize, pe_pos + 2, 2); + unsigned opthdr_size = le(map, fsize, pe_pos + 16, 2); + unsigned opthdr_pos = pe_pos + 20; + /* bool sixtyfourbit = le(map, fsize, opthdr_pos, 2) == 0x020B; */ + unsigned secthdr_pos = opthdr_pos + opthdr_size; + while (nsections > 0) { + if (le(map, fsize, secthdr_pos+0, 1) == '.' && + le(map, fsize, secthdr_pos+1, 1) == 'r' && + le(map, fsize, secthdr_pos+2, 1) == 's' && + le(map, fsize, secthdr_pos+3, 1) == 'r' && + le(map, fsize, secthdr_pos+4, 1) == 'c' && + le(map, fsize, secthdr_pos+5, 1) == 0) + goto found_resource_section; + secthdr_pos += 0x28; + nsections--; + } + warnx("MsiGetFileInfo(%s) -> no .rsrc", fname); + goto cleanup; + + found_resource_section:; + unsigned rsrc_size = le(map, fsize, secthdr_pos+8, 4); + unsigned rsrc_offset = le(map, fsize, secthdr_pos+20, 4); + unsigned rsrc_vaddr = le(map, fsize, secthdr_pos+12, 4); + + unsigned res_dir_offset = rsrc_offset; + unsigned nnamed, nid; + nnamed = le(map, fsize, res_dir_offset+12, 2); + nid = le(map, fsize, res_dir_offset+14, 2); + for (unsigned i = nnamed; i < nnamed+nid; i++) { + unsigned id = le(map, fsize, res_dir_offset + 16 + 8*i, 4); + unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); + if (id == 16 && (entry & 0x80000000)) { + res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); + goto found_versioninfo_toplevel; + } + } + warnx("MsiGetFileInfo(%s) -> no top-level numeric key 16 for versioninfo", fname); + goto cleanup; + + found_versioninfo_toplevel: + nnamed = le(map, fsize, res_dir_offset+12, 2); + nid = le(map, fsize, res_dir_offset+14, 2); + for (unsigned i = 0; i < nnamed+nid; i++) { + unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); + if (entry & 0x80000000) { + res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); + goto found_versioninfo_2ndlevel; + } + } + warnx("MsiGetFileInfo(%s) -> no 2nd-level subdir", fname); + goto cleanup; + + found_versioninfo_2ndlevel: + nnamed = le(map, fsize, res_dir_offset+12, 2); + nid = le(map, fsize, res_dir_offset+14, 2); + for (unsigned i = 0; i < nnamed+nid; i++) { + unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); + if (!(entry & 0x80000000)) { + res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); + goto found_versioninfo_3rdlevel; + } + } + warnx("MsiGetFileInfo(%s) -> no 3rd-level resource data", fname); + goto cleanup; + + found_versioninfo_3rdlevel:; + unsigned versioninfo_offset = le(map, fsize, res_dir_offset, 4); + unsigned versioninfo_size = le(map, fsize, res_dir_offset+4, 4); + versioninfo_offset = rsrc_offset + versioninfo_offset - rsrc_vaddr; + + unsigned name_offset = versioninfo_offset + 6; + const char *next_name_chr = "VS_VERSION_INFO"; + do { + if (le(map, fsize, name_offset, 2) != *next_name_chr) + goto cleanup; /* identifying string didn't match */ + name_offset += 2; + } while (*next_name_chr++); + unsigned fixed_offset = (name_offset + 3) & ~3; + if (le(map, fsize, fixed_offset, 4) != 0xFEEF04BDU) { + warnx("MsiGetFileInfo(%s) -> no VS_FIXEDFILEINFO magic number", fname); + goto cleanup; + } + int four_part_version[4]; + four_part_version[0] = le(map, fsize, fixed_offset + 10, 2); + four_part_version[1] = le(map, fsize, fixed_offset + 8, 2); + four_part_version[2] = le(map, fsize, fixed_offset + 14, 2); + four_part_version[3] = le(map, fsize, fixed_offset + 12, 2); + unsigned child_offset = fixed_offset + + le(map, fsize, versioninfo_offset+2, 2); + unsigned lcid; + while (child_offset < versioninfo_offset + versioninfo_size) { + unsigned this_child_offset = child_offset; + child_offset += le(map, fsize, child_offset, 2); + child_offset = (child_offset + 3) &~ 3; + if (child_offset <= this_child_offset) { + warnx("MsiGetFileInfo(%s) -> bad length field", fname); + goto cleanup; + } + const char *next_name_chr = "VarFileInfo"; + name_offset = this_child_offset + 6; + do { + if (le(map, fsize, name_offset, 2) != *next_name_chr) + goto this_is_not_a_varfileinfo; + name_offset += 2; + } while (*next_name_chr++); + unsigned subchild_offset = (name_offset + 3) & ~3; + + next_name_chr = "Translation"; + name_offset = subchild_offset + 6; + do { + if (le(map, fsize, name_offset, 2) != *next_name_chr) { + warnx("MsiGetFileInfo(%s) -> child not called 'Translation'", fname); + goto cleanup; + } + name_offset += 2; + } while (*next_name_chr++); + subchild_offset = (name_offset + 3) & ~3; + lcid = le(map, fsize, subchild_offset, 2); + goto success; + this_is_not_a_varfileinfo: + continue; + } + warnx("MsiGetFileInfo(%s) -> no VarFileInfo found", fname); + goto cleanup; + + success:; + char verbuf[256], langbuf[256]; + snprintf(verbuf, sizeof(verbuf), "%d.%d.%d.%d", + four_part_version[0], four_part_version[1], + four_part_version[2], four_part_version[3]); + snprintf(langbuf, sizeof(langbuf), "%u", lcid); + warnx("MsiGetFileInfo(%s) -> version %s lang %s", fname, verbuf, langbuf); + if (version) + c16cpy(version, version_size, verbuf); + if (language) + c16cpy(language, language_size, langbuf); + toret = 0; + + cleanup: + if (mapv != MAP_FAILED) + munmap(mapv, fsize); + if (fd != -1) + close(fd); + sfree(fname); + return toret; +} -- cgit v1.2.3-55-g6feb