1 /* armpe_tester.c -- ARM/PE loader/tester for arm linux
2
3 This file is part of the UPX executable compressor.
4
5 Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6 Copyright (C) 1996-2020 Laszlo Molnar
7 Copyright (C) 2000-2020 John F. Reiser
8 All Rights Reserved.
9
10 UPX and the UCL library are free software; you can redistribute them
11 and/or modify them under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; see the file COPYING.
22 If not, write to the Free Software Foundation, Inc.,
23 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25 Markus F.X.J. Oberhumer Laszlo Molnar
26 <markus@oberhumer.com> <ezerotven+github@gmail.com>
27
28 John F. Reiser
29 <jreiser@users.sourceforge.net>
30 */
31
32 /*
33 The stub of a compressed wince file can be tested on an android
34 phone. Compress a wince file using "--strip-relocs=0", then copy it
35 to the phone using "adb push test.exe /data/local/tmp".
36 Cross compile this file using the "gcc-arm-linux-gnueabi",
37 "libc6-dev-armel-cross" and related debian packages.
38 Copy armpe_tester.out into /data/local/tmp/ too. Then use "adb shell"
39 to run the test program, and watch the output.
40
41 */
42
43 // arm-wince-pe-gcc -Wl,--image-base,0x400000
44
45
46 #include <stddef.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <errno.h>
51
52 #ifdef __i386__
53 # define UPX_MMAP_ADDRESS 0x20000000
54 #else
55 # define UPX_MMAP_ADDRESS 0x410000 // 0x10000
56 #endif
57
58 #ifdef __linux__
59 # include <sys/mman.h>
60 #else
61 void *VirtualAlloc(void *address, unsigned size, unsigned type, unsigned protect);
62 # define MEM_COMMIT 0x1000
63 # define PAGE_EXECUTE_READWRITE 0x0040
64 #endif
65
66 typedef size_t upx_uintptr_t;
67 typedef unsigned short LE16;
68 typedef unsigned int LE32;
69 #define get_le32(p) (* (const unsigned *) (p))
70 #define set_le32(p,v) (* (unsigned *) (p) = (v))
71 #define get_le16(p) (* (const unsigned short *) (p))
72
73 #if !defined(__packed_struct)
74 # define __packed_struct(s) struct s {
75 # define __packed_struct_end() };
76 #endif
77
78
79 __packed_struct(ddirs_t)
80 LE32 vaddr;
81 LE32 size;
82 __packed_struct_end()
83
84
85 __packed_struct(pe_header_t)
86 // 0x0
87 char _[4];
88 LE16 cpu;
89 LE16 objects;
90 char __[12];
91 LE16 opthdrsize;
92 LE16 flags;
93 // optional header
94 char ___[4];
95 LE32 codesize;
96 // 0x20
97 LE32 datasize;
98 LE32 bsssize;
99 LE32 entry;
100 LE32 codebase;
101 // 0x30
102 LE32 database;
103 // nt specific fields
104 LE32 imagebase;
105 LE32 objectalign;
106 LE32 filealign;
107 // 0x40
108 char ____[16];
109 // 0x50
110 LE32 imagesize;
111 LE32 headersize;
112 LE32 chksum;
113 LE16 subsystem;
114 LE16 dllflags;
115 // 0x60
116 char _____[20];
117 // 0x74
118 LE32 ddirsentries;
119 //
120 struct ddirs_t ddirs[16];
121 __packed_struct_end()
122
123
124 __packed_struct(pe_section_t)
125 char name[8];
126 LE32 vsize;
127 LE32 vaddr;
128 LE32 size;
129 LE32 rawdataptr;
130 char _[12];
131 LE32 flags;
132 __packed_struct_end()
133
134
135 __packed_struct(exe_header_t)
136 LE16 mz;
137 LE16 m512;
138 LE16 p512;
139 char _[18];
140 LE16 relocoffs;
141 char __[34];
142 LE32 nexepos;
143 __packed_struct_end()
144
145
146 enum {
147 PEDIR_EXPORT = 0,
148 PEDIR_IMPORT = 1,
149 PEDIR_RESOURCE = 2,
150 PEDIR_EXCEPTION = 3,
151 PEDIR_SEC = 4,
152 PEDIR_RELOC = 5,
153 PEDIR_DEBUG = 6,
154 PEDIR_COPYRIGHT = 7,
155 PEDIR_GLOBALPTR = 8,
156 PEDIR_TLS = 9,
157 PEDIR_LOADCONF = 10,
158 PEDIR_BOUNDIM = 11,
159 PEDIR_IAT = 12,
160 PEDIR_DELAYIMP = 13,
161 PEDIR_COMRT = 14
162 };
163
164
165 static struct pe_header_t ih;
166 static struct pe_section_t isections[4];
167 static FILE *f;
168 static void *vaddr;
169 static FILE *out;
170
171 #if 0
172 static int print(const char *format, ...)
173 {
174 va_list ap;
175 int ret;
176
177 va_start(ap, format);
178 ret = fprintf(out, format, ap);
179 fflush(out);
180 va_end(ap);
181 return ret;
182 }
183 #else
184 #define print printf
185 #endif
186
load(const char * file)187 static int load(const char *file)
188 {
189 struct exe_header_t h;
190 int ic;
191 unsigned pe_offset = 0;
192
193 if ((f = fopen(file, "rb")) == NULL)
194 return print("can not open file: %s\n", file);
195
196 for (ic = 0; ic < 20; ic++)
197 {
198 if (fseek(f, pe_offset, SEEK_SET)
199 || fread(&h, sizeof(h), 1, f) != 1)
200 return print("read error at %u\n", pe_offset);
201
202 if (h.mz == 'M' + 'Z'*256) // dos exe
203 {
204 if (h.relocoffs >= 0x40) // new format exe
205 pe_offset += h.nexepos;
206 else
207 pe_offset += h.p512 * 512 + h.m512 - h.m512 ? 512 : 0;
208 }
209 else if (get_le32(&h) == 'P' + 'E'*256)
210 break;
211 else
212 return print("bad header at %u\n", pe_offset);
213 }
214 if (ic == 20)
215 return print("pe header not found\n");
216 printf("pe header found at offset: %u\n", pe_offset);
217 if (fseek(f, pe_offset, SEEK_SET)
218 || fread(&ih, sizeof(ih), 1, f) != 1)
219 return print("can not load pe header\n");
220
221 print("ih.imagesize=0x%x\n", ih.imagesize);
222 if (ih.cpu != 0x1c0 && ih.cpu != 0x1c2)
223 return print("unsupported processor type: %x\n", ih.cpu);
224
225 if ((ih.objects != 3 && ih.objects != 4)
226 || fread(isections, sizeof(isections), 1, f) != 1)
227 return print("error reading section descriptors\n");
228
229 return 0;
230 }
231
read(void)232 static int read(void)
233 {
234 unsigned ic;
235 #ifdef __linux__
236 vaddr = mmap((void *) UPX_MMAP_ADDRESS, ih.imagesize,
237 PROT_WRITE | PROT_READ | PROT_EXEC,
238 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
239 if (((int) vaddr) == -1)
240 return print("mmap() failed: %d\n", errno);
241 print("mmap for %p (size %x) successful\n", vaddr, ih.imagesize);
242 #else
243 if ((vaddr = VirtualAlloc(0, ih.imagesize,
244 MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == 0)
245 return print("VirtualAlloc() failed\n");
246 print("VirtualAlloc() ok %x\n", vaddr);
247 #endif
248 for (ic = 1; ic <= (unsigned) ih.objects - 1; ic++)
249 if (fseek(f, isections[ic].rawdataptr, SEEK_SET)
250 || fread(vaddr + isections[ic].vaddr,
251 isections[ic].vsize, 1, f) != 1)
252 return print("error reading section %u\n", ic);
253 return 0;
254 }
255
dump(char n)256 static void dump(char n)
257 {
258 char buf[100];
259 #ifdef __linux__
260 snprintf(buf, sizeof(buf), "a.dump%c", n);
261 #else
262 snprintf(buf, sizeof(buf), "/a.dump%c", n);
263 #endif
264 FILE *f2 = fopen(buf, "wb");
265 fwrite(vaddr + 0x1000, ih.imagesize - 0x1000, 1, f2);
266 fclose(f2);
267 }
268
loadlibraryw(const unsigned short * name)269 static int loadlibraryw(const unsigned short *name)
270 {
271 return name[0] + name[1] * 0x100 + name[2] * 0x10000;
272 }
273
getprocaddressa(unsigned h,const char * proc)274 static int getprocaddressa(unsigned h, const char *proc)
275 {
276 unsigned p = (unsigned) proc;
277 if (p < 0x10000)
278 {
279 print("getprocaddressa called %c%c%c, ordinal %u\n",
280 h, h >> 8, h >> 16, p);
281 return h + p * 0x10000;
282 }
283 print("getprocaddressa called %c%c%c, name %s\n",
284 h, h >> 8, h >> 16, proc);
285 return h + proc[0] * 0x10000 + proc[1] * 0x1000000;
286 }
287
cachesync(unsigned v)288 static void cachesync(unsigned v)
289 {
290 print("cachesync called %u\n", v);
291 }
292
import(void)293 static int import(void)
294 {
295 if (ih.ddirs[PEDIR_IMPORT].vaddr == 0)
296 return print("no imports?\n");
297 print("loadlibraryw=%p,getprocaddressa=%p,cachesync=%p\n",
298 loadlibraryw, getprocaddressa, cachesync);
299 void *imports = vaddr + ih.ddirs[PEDIR_IMPORT].vaddr;
300 while (get_le32(imports + 12))
301 {
302 if (strcasecmp(vaddr + get_le32(imports + 12), "coredll.dll") == 0)
303 {
304 void *coredll_imports = vaddr + get_le32(imports + 16);
305 print("coredll_imports=%p\n", coredll_imports);
306 void *oft = vaddr + get_le32(imports);
307 unsigned pos = 0;
308 while (get_le32(oft + pos))
309 {
310 void *name = vaddr + get_le32(oft + pos) + 2;
311 print("name=%s\n", (char*) name);
312 if (strcasecmp(name, "loadlibraryw") == 0)
313 set_le32(coredll_imports + pos, (unsigned) loadlibraryw);
314 else if (strcasecmp(name, "getprocaddressa") == 0)
315 set_le32(coredll_imports + pos, (unsigned) getprocaddressa);
316 else if (strcasecmp(name, "cachesync") == 0)
317 set_le32(coredll_imports + pos, (unsigned) cachesync);
318 pos += 4;
319 }
320 return 0;
321 }
322 imports += 20;
323 }
324
325 print("coredll.dll not found");
326 return 1;
327 }
328
reloc(void)329 static int reloc(void)
330 {
331 if (ih.ddirs[PEDIR_RELOC].vaddr == 0)
332 return 0;
333 void *relocs = vaddr + ih.ddirs[PEDIR_RELOC].vaddr;
334 void *page = vaddr + get_le32(relocs);
335 unsigned size = get_le32(relocs + 4);
336 if (size != ih.ddirs[PEDIR_RELOC].size)
337 return print("only 1 page can be relocated\n");
338 unsigned num = (size - 8) / 2;
339 while (num--)
340 {
341 unsigned pos = get_le16(relocs + 8 + num * 2);
342 if (pos == 0)
343 continue;
344 if ((pos & 0xF000) != 0x3000)
345 return print("unknown relocation type: %x\n", pos);
346
347 void *r = page + (pos & 0xFFF);
348 set_le32(r, get_le32(r) - ih.imagebase + (unsigned) vaddr);
349 }
350 return 0;
351 }
352
dump2(int c)353 static void dump2(int c)
354 {
355 print("dump2 %c\n", c);
356 dump(c);
357 }
358
call(void)359 static void call(void)
360 {
361 #ifndef __i386__
362 void (*entry)(void (*)(int), unsigned) = vaddr + ih.entry;
363 entry(dump2, 1);
364 dump('z');
365 #endif
366 }
367
main2(int argc,char ** argv)368 static int main2(int argc, char **argv)
369 {
370 if (argc != 2)
371 return print("usage: %s arm_pe_file\n", argv[0]), 1;
372 if (load(argv[1]))
373 return 2;
374 if (read())
375 return 3;
376 dump('0');
377 if (import())
378 return 4;
379 dump('1');
380 if (reloc())
381 return 5;
382 dump('2');
383
384 call();
385 print("ok.\n");
386 return 0;
387 }
388
main(int argc,char ** argv)389 int main(int argc, char **argv)
390 {
391 out = stdout;
392 #ifndef __linux__
393 out = fopen("/wtest.log", "wt");
394 #endif
395 int ret = main2(argc, argv);
396 fclose(out);
397 return ret;
398 }
399
400 /* vim:set ts=4 sw=4 et: */
401