aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--fake-msi.c183
-rw-r--r--version.c196
3 files changed, 197 insertions, 184 deletions
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 \
8memory.c memory.h 8memory.c memory.h
9 9
10libmsi_so_la_SOURCES = fake-msi.c fake-lib.c fake-lib.h md5.c \ 10libmsi_so_la_SOURCES = fake-msi.c fake-lib.c fake-lib.h md5.c \
11memory.c memory.h 11memory.c memory.h version.c
12 12
13libpreload_la_SOURCES = preload.c 13libpreload_la_SOURCES = preload.c
14libpreload_la_LDFLAGS = -ldl 14libpreload_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 @@
17#include "memory.h" 17#include "memory.h"
18#include "fake-lib.h" 18#include "fake-lib.h"
19 19
20uint32_t MsiGetFileVersionW(const char16_t *filename,
21 char16_t *version, uint32_t *version_size,
22 char16_t *language, uint32_t *language_size)
23{
24 char *fname = ascii(filename, true);
25 uint32_t toret = 1006; /* ERROR_FILE_INVALID == 'no version info found' */
26 int fd = -1;
27 void *mapv = MAP_FAILED;
28
29 if (version && *version_size)
30 *version = 0;
31 if (language && *language_size)
32 *language = 0;
33
34 fd = open(fname, O_RDONLY);
35 if (fd < 0)
36 err(1, "%s: open", fname);
37 struct stat st;
38 if (fstat(fd, &st) < 0)
39 err(1, "%s: fstat", fname);
40 size_t fsize = st.st_size;
41 mapv = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0);
42 if (mapv == MAP_FAILED)
43 err(1, "%s: mmap", fname);
44 unsigned char *map = (unsigned char *)mapv;
45
46 if (le(map, fsize, 0, 2) != (('Z'<<8) | 'M')) {
47 warnx("MsiGetFileInfo(%s) -> no MZ", fname);
48 goto cleanup;
49 }
50 unsigned pe_pos = le(map, fsize, 0x3c, 4);
51 if (le(map, fsize, pe_pos, 4) != (('E'<<8) | 'P')) {
52 warnx("MsiGetFileInfo(%s) -> no PE", fname);
53 goto cleanup;
54 }
55 pe_pos += 4; /* skip to the main header */
56 unsigned nsections = le(map, fsize, pe_pos + 2, 2);
57 unsigned opthdr_size = le(map, fsize, pe_pos + 16, 2);
58 unsigned opthdr_pos = pe_pos + 20;
59 /* bool sixtyfourbit = le(map, fsize, opthdr_pos, 2) == 0x020B; */
60 unsigned secthdr_pos = opthdr_pos + opthdr_size;
61 while (nsections > 0) {
62 if (le(map, fsize, secthdr_pos+0, 1) == '.' &&
63 le(map, fsize, secthdr_pos+1, 1) == 'r' &&
64 le(map, fsize, secthdr_pos+2, 1) == 's' &&
65 le(map, fsize, secthdr_pos+3, 1) == 'r' &&
66 le(map, fsize, secthdr_pos+4, 1) == 'c' &&
67 le(map, fsize, secthdr_pos+5, 1) == 0)
68 goto found_resource_section;
69 secthdr_pos += 0x28;
70 nsections--;
71 }
72 warnx("MsiGetFileInfo(%s) -> no .rsrc", fname);
73 goto cleanup;
74
75 found_resource_section:;
76 unsigned rsrc_size = le(map, fsize, secthdr_pos+8, 4);
77 unsigned rsrc_offset = le(map, fsize, secthdr_pos+20, 4);
78 unsigned rsrc_vaddr = le(map, fsize, secthdr_pos+12, 4);
79
80 unsigned res_dir_offset = rsrc_offset;
81 unsigned nnamed, nid;
82 nnamed = le(map, fsize, res_dir_offset+12, 2);
83 nid = le(map, fsize, res_dir_offset+14, 2);
84 for (unsigned i = nnamed; i < nnamed+nid; i++) {
85 unsigned id = le(map, fsize, res_dir_offset + 16 + 8*i, 4);
86 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
87 if (id == 16 && (entry & 0x80000000)) {
88 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
89 goto found_versioninfo_toplevel;
90 }
91 }
92 warnx("MsiGetFileInfo(%s) -> no top-level numeric key 16 for versioninfo", fname);
93 goto cleanup;
94
95 found_versioninfo_toplevel:
96 nnamed = le(map, fsize, res_dir_offset+12, 2);
97 nid = le(map, fsize, res_dir_offset+14, 2);
98 for (unsigned i = 0; i < nnamed+nid; i++) {
99 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
100 if (entry & 0x80000000) {
101 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
102 goto found_versioninfo_2ndlevel;
103 }
104 }
105 warnx("MsiGetFileInfo(%s) -> no 2nd-level subdir", fname);
106 goto cleanup;
107
108 found_versioninfo_2ndlevel:
109 nnamed = le(map, fsize, res_dir_offset+12, 2);
110 nid = le(map, fsize, res_dir_offset+14, 2);
111 for (unsigned i = 0; i < nnamed+nid; i++) {
112 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
113 if (!(entry & 0x80000000)) {
114 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
115 goto found_versioninfo_3rdlevel;
116 }
117 }
118 warnx("MsiGetFileInfo(%s) -> no 3rd-level resource data", fname);
119 goto cleanup;
120
121 found_versioninfo_3rdlevel:;
122 unsigned versioninfo_offset = le(map, fsize, res_dir_offset, 4);
123 unsigned versioninfo_size = le(map, fsize, res_dir_offset+4, 4);
124 versioninfo_offset = rsrc_offset + versioninfo_offset - rsrc_vaddr;
125
126 unsigned name_offset = versioninfo_offset + 6;
127 const char *next_name_chr = "VS_VERSION_INFO";
128 do {
129 if (le(map, fsize, name_offset, 2) != *next_name_chr)
130 goto cleanup; /* identifying string didn't match */
131 name_offset += 2;
132 } while (*next_name_chr++);
133 unsigned fixed_offset = (name_offset + 3) & ~3;
134 if (le(map, fsize, fixed_offset, 4) != 0xFEEF04BDU) {
135 warnx("MsiGetFileInfo(%s) -> no VS_FIXEDFILEINFO magic number", fname);
136 goto cleanup;
137 }
138 int four_part_version[4];
139 four_part_version[0] = le(map, fsize, fixed_offset + 10, 2);
140 four_part_version[1] = le(map, fsize, fixed_offset + 8, 2);
141 four_part_version[2] = le(map, fsize, fixed_offset + 14, 2);
142 four_part_version[3] = le(map, fsize, fixed_offset + 12, 2);
143 unsigned child_offset = fixed_offset +
144 le(map, fsize, versioninfo_offset+2, 2);
145 unsigned lcid;
146 while (child_offset < versioninfo_offset + versioninfo_size) {
147 unsigned this_child_offset = child_offset;
148 child_offset += le(map, fsize, child_offset, 2);
149 child_offset = (child_offset + 3) &~ 3;
150 if (child_offset <= this_child_offset) {
151 warnx("MsiGetFileInfo(%s) -> bad length field", fname);
152 goto cleanup;
153 }
154 const char *next_name_chr = "VarFileInfo";
155 name_offset = this_child_offset + 6;
156 do {
157 if (le(map, fsize, name_offset, 2) != *next_name_chr)
158 goto this_is_not_a_varfileinfo;
159 name_offset += 2;
160 } while (*next_name_chr++);
161 unsigned subchild_offset = (name_offset + 3) & ~3;
162
163 next_name_chr = "Translation";
164 name_offset = subchild_offset + 6;
165 do {
166 if (le(map, fsize, name_offset, 2) != *next_name_chr) {
167 warnx("MsiGetFileInfo(%s) -> child not called 'Translation'", fname);
168 goto cleanup;
169 }
170 name_offset += 2;
171 } while (*next_name_chr++);
172 subchild_offset = (name_offset + 3) & ~3;
173 lcid = le(map, fsize, subchild_offset, 2);
174 goto success;
175 this_is_not_a_varfileinfo:
176 continue;
177 }
178 warnx("MsiGetFileInfo(%s) -> no VarFileInfo found", fname);
179 goto cleanup;
180
181 success:;
182 char verbuf[256], langbuf[256];
183 snprintf(verbuf, sizeof(verbuf), "%d.%d.%d.%d",
184 four_part_version[0], four_part_version[1],
185 four_part_version[2], four_part_version[3]);
186 snprintf(langbuf, sizeof(langbuf), "%u", lcid);
187 warnx("MsiGetFileInfo(%s) -> version %s lang %s", fname, verbuf, langbuf);
188 if (version)
189 c16cpy(version, version_size, verbuf);
190 if (language)
191 c16cpy(language, language_size, langbuf);
192 toret = 0;
193
194 cleanup:
195 if (mapv != MAP_FAILED)
196 munmap(mapv, fsize);
197 if (fd != -1)
198 close(fd);
199 sfree(fname);
200 return toret;
201}
202
203typedef struct MsiTypePrefix { 20typedef struct MsiTypePrefix {
204 enum { MAIN, VIEW, RECORD } type; 21 enum { MAIN, VIEW, RECORD } type;
205} MsiTypePrefix; 22} 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 @@
1#include <stdio.h>
2#include <stdint.h>
3#include <stdbool.h>
4
5#include <fcntl.h>
6#include <sys/mman.h>
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <unistd.h>
10#include <err.h>
11
12#include "memory.h"
13#include "fake-lib.h"
14
15uint32_t MsiGetFileVersionW(const char16_t *filename,
16 char16_t *version, uint32_t *version_size,
17 char16_t *language, uint32_t *language_size)
18{
19 char *fname = ascii(filename, true);
20 uint32_t toret = 1006; /* ERROR_FILE_INVALID == 'no version info found' */
21 int fd = -1;
22 void *mapv = MAP_FAILED;
23
24 if (version && *version_size)
25 *version = 0;
26 if (language && *language_size)
27 *language = 0;
28
29 fd = open(fname, O_RDONLY);
30 if (fd < 0)
31 err(1, "%s: open", fname);
32 struct stat st;
33 if (fstat(fd, &st) < 0)
34 err(1, "%s: fstat", fname);
35 size_t fsize = st.st_size;
36 mapv = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0);
37 if (mapv == MAP_FAILED)
38 err(1, "%s: mmap", fname);
39 unsigned char *map = (unsigned char *)mapv;
40
41 if (le(map, fsize, 0, 2) != (('Z'<<8) | 'M')) {
42 warnx("MsiGetFileInfo(%s) -> no MZ", fname);
43 goto cleanup;
44 }
45 unsigned pe_pos = le(map, fsize, 0x3c, 4);
46 if (le(map, fsize, pe_pos, 4) != (('E'<<8) | 'P')) {
47 warnx("MsiGetFileInfo(%s) -> no PE", fname);
48 goto cleanup;
49 }
50 pe_pos += 4; /* skip to the main header */
51 unsigned nsections = le(map, fsize, pe_pos + 2, 2);
52 unsigned opthdr_size = le(map, fsize, pe_pos + 16, 2);
53 unsigned opthdr_pos = pe_pos + 20;
54 /* bool sixtyfourbit = le(map, fsize, opthdr_pos, 2) == 0x020B; */
55 unsigned secthdr_pos = opthdr_pos + opthdr_size;
56 while (nsections > 0) {
57 if (le(map, fsize, secthdr_pos+0, 1) == '.' &&
58 le(map, fsize, secthdr_pos+1, 1) == 'r' &&
59 le(map, fsize, secthdr_pos+2, 1) == 's' &&
60 le(map, fsize, secthdr_pos+3, 1) == 'r' &&
61 le(map, fsize, secthdr_pos+4, 1) == 'c' &&
62 le(map, fsize, secthdr_pos+5, 1) == 0)
63 goto found_resource_section;
64 secthdr_pos += 0x28;
65 nsections--;
66 }
67 warnx("MsiGetFileInfo(%s) -> no .rsrc", fname);
68 goto cleanup;
69
70 found_resource_section:;
71 unsigned rsrc_size = le(map, fsize, secthdr_pos+8, 4);
72 unsigned rsrc_offset = le(map, fsize, secthdr_pos+20, 4);
73 unsigned rsrc_vaddr = le(map, fsize, secthdr_pos+12, 4);
74
75 unsigned res_dir_offset = rsrc_offset;
76 unsigned nnamed, nid;
77 nnamed = le(map, fsize, res_dir_offset+12, 2);
78 nid = le(map, fsize, res_dir_offset+14, 2);
79 for (unsigned i = nnamed; i < nnamed+nid; i++) {
80 unsigned id = le(map, fsize, res_dir_offset + 16 + 8*i, 4);
81 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
82 if (id == 16 && (entry & 0x80000000)) {
83 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
84 goto found_versioninfo_toplevel;
85 }
86 }
87 warnx("MsiGetFileInfo(%s) -> no top-level numeric key 16 for versioninfo", fname);
88 goto cleanup;
89
90 found_versioninfo_toplevel:
91 nnamed = le(map, fsize, res_dir_offset+12, 2);
92 nid = le(map, fsize, res_dir_offset+14, 2);
93 for (unsigned i = 0; i < nnamed+nid; i++) {
94 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
95 if (entry & 0x80000000) {
96 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
97 goto found_versioninfo_2ndlevel;
98 }
99 }
100 warnx("MsiGetFileInfo(%s) -> no 2nd-level subdir", fname);
101 goto cleanup;
102
103 found_versioninfo_2ndlevel:
104 nnamed = le(map, fsize, res_dir_offset+12, 2);
105 nid = le(map, fsize, res_dir_offset+14, 2);
106 for (unsigned i = 0; i < nnamed+nid; i++) {
107 unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4);
108 if (!(entry & 0x80000000)) {
109 res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF);
110 goto found_versioninfo_3rdlevel;
111 }
112 }
113 warnx("MsiGetFileInfo(%s) -> no 3rd-level resource data", fname);
114 goto cleanup;
115
116 found_versioninfo_3rdlevel:;
117 unsigned versioninfo_offset = le(map, fsize, res_dir_offset, 4);
118 unsigned versioninfo_size = le(map, fsize, res_dir_offset+4, 4);
119 versioninfo_offset = rsrc_offset + versioninfo_offset - rsrc_vaddr;
120
121 unsigned name_offset = versioninfo_offset + 6;
122 const char *next_name_chr = "VS_VERSION_INFO";
123 do {
124 if (le(map, fsize, name_offset, 2) != *next_name_chr)
125 goto cleanup; /* identifying string didn't match */
126 name_offset += 2;
127 } while (*next_name_chr++);
128 unsigned fixed_offset = (name_offset + 3) & ~3;
129 if (le(map, fsize, fixed_offset, 4) != 0xFEEF04BDU) {
130 warnx("MsiGetFileInfo(%s) -> no VS_FIXEDFILEINFO magic number", fname);
131 goto cleanup;
132 }
133 int four_part_version[4];
134 four_part_version[0] = le(map, fsize, fixed_offset + 10, 2);
135 four_part_version[1] = le(map, fsize, fixed_offset + 8, 2);
136 four_part_version[2] = le(map, fsize, fixed_offset + 14, 2);
137 four_part_version[3] = le(map, fsize, fixed_offset + 12, 2);
138 unsigned child_offset = fixed_offset +
139 le(map, fsize, versioninfo_offset+2, 2);
140 unsigned lcid;
141 while (child_offset < versioninfo_offset + versioninfo_size) {
142 unsigned this_child_offset = child_offset;
143 child_offset += le(map, fsize, child_offset, 2);
144 child_offset = (child_offset + 3) &~ 3;
145 if (child_offset <= this_child_offset) {
146 warnx("MsiGetFileInfo(%s) -> bad length field", fname);
147 goto cleanup;
148 }
149 const char *next_name_chr = "VarFileInfo";
150 name_offset = this_child_offset + 6;
151 do {
152 if (le(map, fsize, name_offset, 2) != *next_name_chr)
153 goto this_is_not_a_varfileinfo;
154 name_offset += 2;
155 } while (*next_name_chr++);
156 unsigned subchild_offset = (name_offset + 3) & ~3;
157
158 next_name_chr = "Translation";
159 name_offset = subchild_offset + 6;
160 do {
161 if (le(map, fsize, name_offset, 2) != *next_name_chr) {
162 warnx("MsiGetFileInfo(%s) -> child not called 'Translation'", fname);
163 goto cleanup;
164 }
165 name_offset += 2;
166 } while (*next_name_chr++);
167 subchild_offset = (name_offset + 3) & ~3;
168 lcid = le(map, fsize, subchild_offset, 2);
169 goto success;
170 this_is_not_a_varfileinfo:
171 continue;
172 }
173 warnx("MsiGetFileInfo(%s) -> no VarFileInfo found", fname);
174 goto cleanup;
175
176 success:;
177 char verbuf[256], langbuf[256];
178 snprintf(verbuf, sizeof(verbuf), "%d.%d.%d.%d",
179 four_part_version[0], four_part_version[1],
180 four_part_version[2], four_part_version[3]);
181 snprintf(langbuf, sizeof(langbuf), "%u", lcid);
182 warnx("MsiGetFileInfo(%s) -> version %s lang %s", fname, verbuf, langbuf);
183 if (version)
184 c16cpy(version, version_size, verbuf);
185 if (language)
186 c16cpy(language, language_size, langbuf);
187 toret = 0;
188
189 cleanup:
190 if (mapv != MAP_FAILED)
191 munmap(mapv, fsize);
192 if (fd != -1)
193 close(fd);
194 sfree(fname);
195 return toret;
196}