Changeset 4831


Ignore:
Timestamp:
Jul 31, 2012, 3:54:47 PM (7 years ago)
Author:
wisk
Message:

change the method of hooking, now we disassemble the beginning of the targeted function and insert a jump to the new function.

Location:
zzuf/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • zzuf/trunk/AUTHORS

    r4253 r4831  
    99 Sami Liedes <sliedes#cc:hut:fi> (LD_PRELOAD conservation)
    1010 Corentin Delorme <codelorme@gmail.com> (remote host filtering)
     11 Kévin Szkudłapski <kszkudlapski@quarkslab.com> (win32 port)
    1112
  • zzuf/trunk/src/libzzuf/sys.c

    r4830 r4831  
    4848
    4949#if defined HAVE_WINDOWS_H
    50 static void insert_funcs(void *);
     50static void insert_funcs(void);
    5151
    5252/* TODO: get rid of this later */
     
    7474#if defined HAVE_WINDOWS_H
    7575
    76     MEMORY_BASIC_INFORMATION mbi;
    77     MODULEENTRY32 entry;
    78     void *list;
    79     int k;
    80 
    81     /* Enumerate all loaded objects and overwrite some functions */
    82     VirtualQuery(_zz_sys_init, &mbi, sizeof(mbi));
    83     list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
    84     entry.dwSize = sizeof(entry);
    85     for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
    86     {
    87         if(entry.hModule == mbi.AllocationBase)
    88             continue; /* Don't replace our own functions */
    89 
    90         fprintf(stderr, "diverting functions from %s\n", entry.szModule);
    91         insert_funcs(entry.hModule);
    92     }
    93     CloseHandle(list);
     76    insert_funcs();
    9477
    9578#elif defined HAVE_DLFCN_H
     
    11699#define MK_JMP_JD(dst, src) ((dst) - ((src) + 5))
    117100
    118 /*
    119  * This function hooks a windows API using the hotpatch method
    120  *     old_api must point to the original windows API.
    121  *     new_api must point to the hook function
    122  *     trampoline_api is filled by the function and contains the
    123  *     function to call to call the original API.
    124  *
    125  * Windows API should start with the following instructions
    126  * mov edi, edi
    127  * push ebp
    128  * mov ebp, esp
    129  * which makes a 5 bytes, the perfect size to insert a jmp to the new api
    130  */
    131 static int hook_hotpatch(uint8_t *old_api, uint8_t *new_api, uint8_t **trampoline_api)
    132 {
    133     int res = -1;
    134     uint8_t prolog[5];
    135     uint8_t jmp_prolog[5];
    136     static uint8_t const hotpatch_prolog[] = "\x8b\xff\x55\x8b\xec";
    137     uint8_t *trampoline;
     101/* zz_lde is a _very_ simple length disassemble engine.
     102 * x64 is not tested and should not work. */
     103static int zz_lde(uint8_t *code, int required_size)
     104{
     105    int insn_size = 0;
     106    static uint8_t modrm_size[] = { 0, 1, 4, 0 }; /* [reg ...], [reg ... + sbyte], [reg ... + sdword], reg */
     107
     108    while (insn_size < required_size)
     109    {
     110        uint8_t opcd = code[insn_size++];
     111
     112        /* Simple instructions should be placed here */
     113        switch (opcd)
     114        {
     115        case 0x68: insn_size += 4; continue; /* PUSH Iv */
     116        case 0x6a: insn_size += 1; continue; /* PUSH Ib */
     117        default: break;
     118        }
     119
     120        /* PUSH/POP rv */
     121        if ((opcd & 0xf0) == 0x50) continue;
     122
     123        /* MOV Ev, Gv or Gv, Ev */
     124        else if (opcd == 0x89 || opcd == 0x8b)
     125        {
     126            uint8_t modrm = code[insn_size++];
     127
     128            /* Does the instruciton have a SIB byte ? */
     129            if (((modrm & 0x7) == 0x4) && ((modrm >> 6) != 0x3))
     130                insn_size++;
     131
     132            insn_size += modrm_size[modrm >> 6];
     133
     134            continue;
     135        }
     136
     137
     138        /* If we can't disassemble the current instruction, we give up */
     139        return -1;
     140    }
     141
     142    return insn_size;
     143}
     144
     145/* This function allows to hook any API. To do so, it disassembles the beginning of the
     146 * targeted function and looks for, at least, 5 bytes (size of JMP Jd).
     147 * Then it writes a JMP Jv instruction to make the new_api executed.
     148 * Finally, trampoline_api contains a wrapper to call in order to execute the original API */
     149static int hook_inline(uint8_t *old_api, uint8_t *new_api, uint8_t **trampoline_api)
     150{
     151    int res             = -1;
     152    int patch_size      = 0;
     153    uint8_t *jmp_prolog = NULL;
     154    uint8_t *trampoline = NULL;
    138155    DWORD old_prot;
    139156
     157    /* if we can't get enough byte, we quit */
     158    if ((patch_size = zz_lde(old_api, 5)) == -1)
     159        return -1;
     160
     161    if ((jmp_prolog = malloc(patch_size)) == NULL) goto _out;
     162    memset(jmp_prolog, '\xcc', patch_size); /* We use 0xcc because the code after the jmp should be executed */
     163
    140164    *trampoline_api = NULL;
    141 
    142     /* Check if the targeted API contains the hotpatch feature */
    143     memcpy(prolog, old_api, sizeof(prolog));
    144     if (memcmp(prolog, hotpatch_prolog, sizeof(prolog))) goto _out;
    145165
    146166    jmp_prolog[0] = '\xe9'; /* jmp Jd */
    147167    *(uint32_t *)(&jmp_prolog[1]) = MK_JMP_JD(new_api, old_api);
    148168
    149     trampoline = malloc(10); /* size of hotpatch_prolog + sizeof of jmp Jd */
    150     memcpy(trampoline, hotpatch_prolog, sizeof(hotpatch_prolog) - 1);
    151     trampoline[5] = '\xe9'; /* jmp Jd */
    152     *(uint32_t *)&trampoline[6] = MK_JMP_JD(old_api + sizeof(hotpatch_prolog) - 1, trampoline + sizeof(hotpatch_prolog) - 1);
     169    trampoline = malloc(patch_size + 5); /* size of old byte + sizeof of jmp Jd */
     170    memcpy(trampoline, old_api, patch_size);
     171    *(uint8_t  *)&trampoline[patch_size]    = '\xe9'; /* jmp Jd */
     172    *(uint32_t *)&trampoline[patch_size + 1] = MK_JMP_JD(old_api + patch_size, trampoline + patch_size);
    153173
    154174    /* We must make the trampoline executable, this line is required because of DEP */
    155175    /* NOTE: We _must_ set the write protection, otherwise the heap allocator will crash ! */
    156     if (!VirtualProtect(trampoline, 10, PAGE_EXECUTE_READWRITE, &old_prot)) goto _out;
     176    if (!VirtualProtect(trampoline, patch_size + 5, PAGE_EXECUTE_READWRITE, &old_prot)) goto _out;
    157177
    158178    /* We patch the targeted API, so we must set it as writable */
    159     if (!VirtualProtect(old_api, sizeof(jmp_prolog), PAGE_EXECUTE_READWRITE, &old_prot)) goto _out;
    160     memcpy(old_api, jmp_prolog, sizeof(jmp_prolog));
    161     VirtualProtect(old_api, sizeof(jmp_prolog), old_prot, &old_prot); /* we don't care if this functon fails */
     179    if (!VirtualProtect(old_api, patch_size, PAGE_EXECUTE_READWRITE, &old_prot)) goto _out;
     180    memcpy(old_api, jmp_prolog, patch_size);
     181    VirtualProtect(old_api, patch_size, old_prot, &old_prot); /* we don't care if this functon fails */
    162182
    163183    *trampoline_api = trampoline;
     
    175195    }
    176196
     197    if (jmp_prolog != NULL) free(jmp_prolog);
     198
    177199    return res;
    178200}
    179201
    180 /*
    181  * Even if hook_hotpatch is working, it's look that some API don't use it anymore (kernel32!ReadFile)
    182  * So we stay with IAT hook at this time
    183  */
    184 #if 0
    185 static void insert_funcs(void *module)
    186 {
    187     static zzuf_table_t *list[] =
     202static void insert_funcs(void)
     203{
     204    static zzuf_table_t *list[] =
    188205    {
    189206        table_win32,
     
    212229            return;
    213230        }
    214         if (hook_hotpatch(old_api, diversion->new, &trampoline_api) < 0)
    215         {
    216             fprintf(stderr, "hook_hotpatch failed while hooking %s!%s\n", diversion->lib, diversion->name);
     231        if (hook_inline(old_api, diversion->new, &trampoline_api) < 0)
     232        {
     233            fprintf(stderr, "hook_inline failed while hooking %s!%s\n", diversion->lib, diversion->name);
    217234            return;
    218235        }
    219236        *diversion->old = trampoline_api;
    220237    }
    221 
    222     (void)module; /* not needed anymore */
    223 
    224 }
    225 #endif
    226 
    227 static void insert_funcs(void *module)
    228 {
    229     static zzuf_table_t *list[] =
    230     {
    231         table_win32,
    232     };
    233 
    234     zzuf_table_t *diversion;
    235     void *lib;
    236     unsigned long dummy;
    237     import_t import;
    238     thunk_t thunk;
    239     int k, j, i;
    240 
    241     import = (import_t)
    242         ImageDirectoryEntryToData(module, TRUE,
    243                                   IMAGE_DIRECTORY_ENTRY_IMPORT, &dummy);
    244     if(!import)
    245         return;
    246 
    247     for (k = 0, diversion = NULL; k < sizeof(list) / sizeof(*list); )
    248     {
    249         if (!diversion)
    250             diversion = list[k];
    251 
    252         if (!diversion->lib)
    253         {
    254             k++;
    255             diversion = NULL;
    256             continue;
    257         }
    258 
    259         fprintf(stderr, "diverting method %s (from %s)\n",
    260                         diversion->name, diversion->lib);
    261 
    262         lib = GetModuleHandleA(diversion->lib);
    263         *diversion->old = (void *)GetProcAddress(lib, diversion->name);
    264 
    265         for(j = 0; import[j].Name; j++)
    266         {
    267             char *name = (char *)module + import[j].Name;
    268             if(lstrcmpiA(name, diversion->lib) != 0)
    269                 continue;
    270 
    271             thunk = (thunk_t)((char *)module + import[j].FirstThunk);
    272             for(i = 0; thunk[i].u1.Function; i++)
    273             {
    274                 void **func = (void **)&thunk[i].u1.Function;
    275                 if(*func != *diversion->old)
    276                     continue;
    277 
    278                 /* FIXME: The StarCraft 2 hack uses two methods for function
    279                  * diversion. See HookSsdt() and HookHotPatch(). */
    280                 VirtualProtect(func, sizeof(func), PAGE_EXECUTE_READWRITE, &dummy);
    281                 WriteProcessMemory(GetCurrentProcess(), func, &diversion->new,
    282                                     sizeof(diversion->new), NULL);
    283             }
    284         }
    285 
    286         diversion++;
    287     }
    288 }
    289 #endif
     238}
     239
     240#endif
Note: See TracChangeset for help on using the changeset viewer.