source: zzuf/trunk/src/libzzuf/sys.c @ 4839

Last change on this file since 4839 was 4839, checked in by Sam Hocevar, 8 years ago

win32: add some console handling function diversions.

  • Property svn:keywords set to Id
File size: 11.4 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006-2012 Sam Hocevar <sam@hocevar.net>
4 *                2012 Kévin Szkudłapski <kszkudlapski@quarkslab.com>
5 *                All Rights Reserved
6 *
7 *  This program is free software. It comes without any warranty, to
8 *  the extent permitted by applicable law. You can redistribute it
9 *  and/or modify it under the terms of the Do What The Fuck You Want
10 *  To Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14/*
15 *  sys.c: system-dependent initialisation
16 */
17
18#include "config.h"
19
20/* Need this for RTLD_NEXT */
21#define _GNU_SOURCE
22
23#if defined HAVE_STDINT_H
24#   include <stdint.h>
25#elif defined HAVE_INTTYPES_H
26#   include <inttypes.h>
27#endif
28
29#ifdef HAVE_DLFCN_H
30#   include <dlfcn.h>
31#endif
32
33#if defined HAVE_WINDOWS_H
34#   include <windows.h>
35#   include <imagehlp.h>
36#   include <tlhelp32.h>
37#   define import_t PIMAGE_IMPORT_DESCRIPTOR
38#   define thunk_t PIMAGE_THUNK_DATA
39#endif
40
41#include <stdio.h>
42
43#include "sys.h"
44#include "lib-load.h"
45
46#if defined HAVE_DLFCN_H
47void *_zz_dl_lib = RTLD_NEXT;
48#endif
49
50#if defined HAVE_WINDOWS_H
51static void insert_funcs(void);
52#endif
53
54void _zz_sys_init(void)
55{
56#if defined HAVE_WINDOWS_H
57
58    insert_funcs();
59
60#elif defined HAVE_DLFCN_H
61    /* If glibc is recent enough, we use dladdr() to get its address. This
62     * way we are sure that the symbols we load are the most recent version,
63     * or we may get weird problems. We choose fileno as a random symbol to
64     * get, because we know we don't divert it. */
65#   if HAVE_DLADDR
66    Dl_info di;
67    if (dladdr(&fileno, &di) != 0)
68    {
69        void *lib = dlopen(di.dli_fname, RTLD_NOW);
70        if (lib)
71            _zz_dl_lib = lib;
72    }
73#   endif
74#else
75    /* Nothing to do on our platform */
76#endif
77}
78
79#if defined HAVE_WINDOWS_H
80
81#define MK_JMP_JD(dst, src) ((dst) - ((src) + 5))
82
83static int modrm_sib_size(uint8_t* code)
84{
85    static uint8_t modrm_size[] = { 0, 1, 4, 0 }; /* [reg ...], [reg ... + sbyte], [reg ... + sdword], reg */
86    uint8_t modrm = *code;
87
88    if (modrm == 0x05) /* [(rip) + sdword] */ return 1 + 4;
89
90    /* Does this instruction have a SIB byte ? */
91    return 1 + (!!(((modrm & 0x7) == 0x4) && ((modrm >> 6) != 0x3))) + modrm_size[modrm >> 6];
92}
93
94/* zz_lde is a _very_ simple length disassemble engine. */
95static int zz_lde(uint8_t *code)
96{
97    int insn_size = 0;
98
99    uint8_t opcd = code[insn_size++];
100    int imm_size = 4; /* Iv */
101
102#ifdef _M_AMD64
103    // REX prefix
104    if ((opcd & 0xf8) == 0x48)
105    {
106        imm_size = 8; /* REX.W */
107        opcd = code[insn_size++];
108    }
109#endif
110
111    /* Simple instructions should be placed here */
112    switch (opcd)
113    {
114    case 0x68:
115        return (insn_size + 4); /* PUSH Iv */
116    case 0x6a:
117        return (insn_size + 1); /* PUSH Ib */
118    case 0x90:
119        return insn_size;       /* NOP     */
120    case 0xb8:
121    case 0xb9:
122    case 0xba:
123    case 0xbb:
124    case 0xbc:
125    case 0xbd:
126    case 0xbe:
127    case 0xbf:
128        return insn_size + 5;   /* MOV immediate */
129    default:
130        break;
131    }
132
133    /* PUSH/POP rv */
134    if ((opcd & 0xf0) == 0x50)
135        return insn_size;
136
137    /* MNEM E?, G? or G?, E? */
138    switch (opcd)
139    {
140    case 0x89: /* mov Ev, Gv */
141    case 0x8b: /* mov Gv, Ev */
142        return (insn_size + modrm_sib_size(code + insn_size));
143
144    case 0x80: /* Group#1 Eb, Ib */
145    case 0x82: /* Group#1 Eb, Ib */
146    case 0x83: /* Group#1 Ev, Ib */
147        return (insn_size + (modrm_sib_size(code + insn_size) + 1));
148
149    case 0x81: /* Group#1 Ev, Iz */
150        return (insn_size + (modrm_sib_size(code + insn_size) + 4));
151
152    case 0xff:
153        if ((code[insn_size] & 0x38) == 0x30) /* PUSH Ev */
154            return (insn_size + modrm_sib_size(code + insn_size));
155        break;
156
157    default:
158        fprintf(stderr, "unknown opcode %02x\n", opcd);
159        break;
160    }
161
162    return 0;
163}
164
165/* This function returns the required size to insert a patch */
166static int compute_patch_size(uint8_t *code, int required_size)
167{
168    int patch_size = 0;
169    while (patch_size < required_size)
170    {
171        int insn_size = zz_lde(code);
172        if (insn_size == 0)
173            return -1;
174        patch_size += insn_size;
175    }
176    return patch_size;
177}
178
179static void make_jmp32(uint8_t *src, uint8_t *dst, uint8_t *code)
180{
181    *(uint8_t  *)(code + 0) = 0xe9;             /* JMP Jd */
182    *(uint32_t *)(code + 1) = (uint32_t)MK_JMP_JD(dst, src);
183}
184
185static void make_jmp64(uint8_t *dst, uint8_t *code)
186{
187    memcpy(code, "\x48\xb8", 2);                /* MOV rAX, Iq */
188    *(uint64_t *)(code + 2) = (uint64_t)dst;
189    memcpy(code + 10, "\xff\xe0", 2);           /* JMP rAX */
190}
191
192/* This function allocates and fills a trampoline for the function pointed by code. It also tries to handle some relocations. */
193static int make_trampoline(uint8_t *code, size_t patch_size, uint8_t **trampoline_buf, size_t *trampoline_size)
194{
195    uint8_t *trampoline;
196
197    *trampoline_buf  = NULL;
198    *trampoline_size = 0;
199
200#ifdef _M_AMD64
201    {
202        size_t code_offset = 0;
203        size_t trampoline_offset = 0;
204        const size_t reloc_size  = -7 /* size of mov rax, [rip + ...] */ +10 /* size of mov rax, Iq */;
205
206        trampoline = malloc(patch_size + reloc_size + 13); /* Worst case */
207        if (trampoline == NULL)
208            return -1;
209        memset(trampoline, 0xcc, patch_size + 13);
210
211        while (code_offset < patch_size)
212        {
213            int insn_size = zz_lde(code + code_offset);
214            if (insn_size == 0)
215                return -1;
216
217            /* mov rax, [rip + ...] is the signature for stack cookie */
218            if (!memcmp(code + code_offset, "\x48\x8b\x05", 3))
219            {
220                uint64_t *cookie_address = (uint64_t *)(code + code_offset + insn_size + *(uint32_t *)(code + code_offset + 3));
221                patch_size              += reloc_size;
222
223                memcpy(trampoline + trampoline_offset, "\x48\xb8", 2); /* MOV rAX, Iq */
224                *(uint64_t *)(trampoline + trampoline_offset + 2) = *cookie_address;
225                trampoline_offset += 10;
226            }
227            else
228            {
229                *trampoline_size += insn_size;
230                memcpy(trampoline + trampoline_offset, code + code_offset, insn_size);
231                trampoline_offset += insn_size;
232            }
233
234            code_offset += insn_size;
235        }
236
237
238        /* We can't use make_jmp64 since rAX is used by the __security_cookie */
239        memcpy(trampoline + trampoline_offset, "\x49\xba", 2); /* MOV r10, Iq */
240        *(uint64_t *)(trampoline + trampoline_offset + 2) = (uint64_t)(code + code_offset);
241        memcpy(trampoline + trampoline_offset + 10, "\x41\xff\xe2", 3); /* JMP r10 */
242
243        *trampoline_buf  = trampoline;
244        *trampoline_size = trampoline_offset;
245        return 0;
246    }
247#elif _M_IX86
248    trampoline = malloc(patch_size + 5);
249    if (trampoline == NULL) return -1;
250    memcpy(trampoline, code, patch_size);
251    make_jmp32(trampoline + patch_size, code + patch_size, trampoline + patch_size);
252
253    *trampoline_size = patch_size;
254    *trampoline_buf  = trampoline;
255    return 0;
256#else
257#   error Unsupported architecture !
258#endif
259}
260
261/*
262 * Sometimes Windows APIs are a stub and contain only a JMP to the real function.
263 * To avoid to relocate a JMP, we use the destination address.
264 */
265static int relocate_hook(uint8_t **code)
266{
267    uint8_t *cur_code = *code;
268
269#ifdef _M_AMD64
270    // we ignore the REX prefix
271    if ((*cur_code & 0xf8) == 0x48)
272        ++cur_code;
273#endif
274
275    /* JMP Jd */
276    if (*cur_code == 0xe9)
277    {
278        *cur_code += (5 + *(uint32_t *)(cur_code + 1));
279        return 0;
280    }
281
282    /* JMP [(rip)+addr] */
283    else if (!memcmp(cur_code, "\xff\x25", 2))
284    {
285#ifdef _M_AMD64
286        uint8_t **dst_addr = (uint8_t **)(cur_code + 6 + *(uint32_t *)(cur_code + 2));
287        *code = *dst_addr;
288#elif _M_IX86
289        /* UNTESTED ! */
290        uint8_t **dst_addr = *(uint32_t *)(*cur_code + 2);
291        *code = *dst_addr;
292#else
293#   error Unsupported architecture !
294#endif
295        return 0;
296    }
297
298    return -1;
299}
300
301/* This function allows to hook any API. To do so, it disassembles the beginning of the
302 * targeted function and looks for, at least, 5 bytes (size of JMP Jd).
303 * Then it writes a JMP Jv instruction to make the new_api executed.
304 * Finally, trampoline_api contains a wrapper to call in order to execute the original API */
305static int hook_inline(uint8_t *old_api, uint8_t *new_api, uint8_t **trampoline_api)
306{
307    int res                 = -1;
308    int required_size       = 5;
309    int patch_size          = 0;
310    uint8_t jmp_prolog[12];
311    uint8_t *trampoline     = NULL;
312    size_t trampoline_size  = 0;
313    DWORD old_prot;
314    uint8_t *reloc_old_api  = old_api;
315
316    while ((relocate_hook(&reloc_old_api)) >= 0)
317        old_api = reloc_old_api;
318
319    *trampoline_api = NULL;
320
321    memset(jmp_prolog, 0xcc, sizeof(jmp_prolog));
322
323#ifdef _M_AMD64
324    if (new_api - old_api > 0xffffffff)
325    {
326        required_size = 12;
327        make_jmp64(new_api, jmp_prolog);
328    }
329    else make_jmp32(old_api, new_api, jmp_prolog);
330#elif _M_IX86
331    make_jmp32(old_api, new_api, jmp_prolog);
332#else
333#   error Unsupported architecture !
334#endif
335
336    /* if we can't get enough byte, we quit */
337    if ((patch_size = compute_patch_size(old_api, required_size)) == -1)
338    {
339        fprintf(stderr, "cannot compute patch size\n");
340        return -1;
341    }
342
343    if (make_trampoline(old_api, patch_size, &trampoline, &trampoline_size) < 0)
344    {
345        fprintf(stderr, "cannot make trampoline\n");
346        goto _out;
347    }
348
349    /* We must make the trampoline executable, this line is required because of DEP */
350    /* NOTE: We _must_ set the write protection, otherwise the heap allocator will crash ! */
351    if (!VirtualProtect(trampoline, trampoline_size, PAGE_EXECUTE_READWRITE, &old_prot))
352    {
353        fprintf(stderr, "cannot make the trampoline writable\n");
354        goto _out;
355    }
356
357    /* We patch the targeted API, so we must set it as writable */
358    if (!VirtualProtect(old_api, patch_size, PAGE_EXECUTE_READWRITE, &old_prot))
359    {
360        fprintf(stderr, "cannot make old API writable\n");
361        goto _out;
362    }
363    memcpy(old_api, jmp_prolog, patch_size);
364    VirtualProtect(old_api, patch_size, old_prot, &old_prot); /* we don't care if this functon fails */
365
366    *trampoline_api = trampoline;
367
368    res = 0;
369
370_out:
371    if (res < 0)
372    {
373        if (*trampoline_api)
374        {
375            free(*trampoline_api);
376            trampoline_api = NULL;
377        }
378    }
379
380    return res;
381}
382
383static void insert_funcs(void)
384{
385    static zzuf_table_t *list[] =
386    {
387        table_win32,
388    };
389
390    zzuf_table_t *diversion;
391    HMODULE lib;
392
393    for (diversion = *list; diversion->lib; diversion++)
394    {
395        uint8_t *old_api;
396        uint8_t *trampoline_api;
397
398        /* most of the time, the dll is already loaded */
399        if ((lib = GetModuleHandleA(diversion->lib)) == NULL)
400        {
401           if ((lib = LoadLibraryA(diversion->lib)) == NULL)
402           {
403               fprintf(stderr, "unable to load %s\n", diversion->lib);
404               continue;
405           }
406        }
407        if ((old_api = (uint8_t *)GetProcAddress(lib, diversion->name)) == NULL)
408        {
409            fprintf(stderr, "unable to get pointer to %s\n", diversion->name);
410            continue;
411        }
412        if (hook_inline(old_api, diversion->new, &trampoline_api) < 0)
413        {
414            fprintf(stderr, "hook_inline failed while hooking %s!%s\n", diversion->lib, diversion->name);
415            continue;
416        }
417        *diversion->old = trampoline_api;
418    }
419}
420
421#endif
Note: See TracBrowser for help on using the repository browser.