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