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