Move MsiGetFileVersion out into its own file.
This commit is contained in:
parent
9e3e915426
commit
7e5c5d4232
3 changed files with 197 additions and 184 deletions
|
@ -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
|
||||
|
|
183
fake-msi.c
183
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;
|
||||
|
|
196
version.c
Normal file
196
version.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
|
||||
#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;
|
||||
}
|
Loading…
Add table
Reference in a new issue