instruction_length(): extended
[captive.git] / src / libcaptive / ldr / loader.c
1 /* $Id$
2  * reactos ldr/ (loader) emulation of libcaptive
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/ldr.h"        /* self */
23 #include "captive/ldr_exports.h"        /* self */
24 #include "reactos/internal/ldr.h"       /* self */
25 #include "captive/unicode.h"
26 #include "captive/rtl-file.h"
27 #include <glib/gtypes.h>
28 #include <glib/gmessages.h>
29 #include <glib/gmem.h>
30 #include "reactos/napi/types.h"
31 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
32 #include <fcntl.h>
33 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
34 #include "captive/macros.h"
35 #include <stdarg.h>
36 #include "reactos/ddk/rtl.h"    /* for InsertTailList() */
37 #include "reactos/internal/debug.h"     /* for assert() */
38 #include <glib/gutils.h>        /* for g_path_get_basename() */
39 #include <gmodule.h>
40
41
42 /* map: ExportAddress -> (struct captive_ModuleList_patchpoint *) */
43 static GHashTable *captive_ModuleList_patchpoint_hash;
44
45 /* initialize 'captive_ModuleList_patchpoint_hash' */
46 static void captive_ModuleList_patchpoint_hash_init(void)
47 {
48         if (captive_ModuleList_patchpoint_hash)
49                 return;
50         captive_ModuleList_patchpoint_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
51 }
52
53
54 /* map: ExportAddress -> (CHAR *)funcname */
55 static GHashTable *captive_ModuleList_function_disable_hash;
56
57 /* initialize 'captive_ModuleList_function_disable_hash' */
58 static void captive_ModuleList_function_disable_hash_init(void)
59 {
60         if (captive_ModuleList_function_disable_hash)
61                 return;
62         captive_ModuleList_function_disable_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
63 }
64
65
66 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
67 NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
68
69
70 /* 'ntoskrnl/ldr/loader.c' file-scoped declaration: */
71 VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
72
73
74 static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8,const gchar *Filename_bslash_utf8)
75 {
76 MODULE_OBJECT *r;
77 GModule *gmodule;
78 MODULE_TEXT_SECTION *ModuleTextSection;
79 gboolean errbool;
80
81         g_return_val_if_fail(Filename_utf8!=NULL,NULL);
82         g_return_val_if_fail(Filename_bslash_utf8!=NULL,NULL);
83         g_return_val_if_fail(TRUE==g_module_supported(),NULL);
84
85         gmodule=g_module_open(Filename_utf8,
86                         0);     /* Flags; !LAZY */
87         if (!gmodule)
88                 g_message(g_module_error());
89         g_assert(gmodule!=NULL);
90
91         captive_new0(r);
92
93         errbool=g_module_symbol(gmodule,"DriverEntry",&r->EntryPoint);
94         g_assert(errbool==TRUE);
95
96         r->Base=(PVOID)gmodule;
97         r->Flags=MODULE_FLAG_COFF;      /* some weird value - reactos uses MODULE_FLAG_PE */
98   
99         RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8)->Buffer);
100   LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
101
102         /* r->Length=0; */
103   
104   /* Insert module */
105         InsertTailList(&ModuleListHead,&r->ListEntry);
106
107         captive_new0(ModuleTextSection);
108         /* ModuleTextSection->Base=0; */
109         /* ModuleTextSection->Length=0; */
110         ModuleTextSection->Name=g_memdup(r->BaseName.Buffer,
111                         (captive_ucs2_strlen(r->BaseName.Buffer)+1)*sizeof(captive_ucs2));
112         /* ModuleTextSection->OptionalHeader=NULL; */
113         InsertTailList(&ModuleTextListHead,&ModuleTextSection->ListEntry);
114         r->TextSection=ModuleTextSection;
115
116         return r;
117 }
118
119
120 /**
121  * LdrLoadModule:
122  * @Filename: Filename of module to load based on host OS disk.
123  * Loading of already loaded module is forbidden.
124  * @ModuleObject: Returns initialized module object.
125  *
126  * Load and initialize module to reactos using host OS functions.
127  *
128  * Returns: STATUS_SUCCESS if the module was loaded successfuly during the call.
129  */
130 NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObjectp)
131 {
132 PVOID ModuleLoadBase;
133 PMODULE_OBJECT Module;
134 NTSTATUS err;
135 gchar *Filename_utf8=(/* de-const */ gchar *)captive_UnicodeString_to_utf8_alloca(Filename);
136 gchar *Filename_bslash_utf8,*s;
137 UNICODE_STRING *Filename_bslash;
138
139         *ModuleObjectp=NULL;
140
141         /* Open the Module */
142         ModuleLoadBase=captive_rtl_file_mmap(
143                         NULL,   /* lenp */
144                         Filename_utf8,  /* path */
145                         O_RDONLY,       /* open_flags */
146                         PROT_READ,      /* mmap_prot */
147                         MAP_SHARED);    /* mmap_flags */
148         /* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
149         g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
150
151         /* Filename=~s#/#\\#g -> Filename_bslash to satisfy basename-derivations by reactos. */
152         Filename_bslash_utf8=g_strdup(Filename_utf8);
153         for (s=Filename_bslash_utf8;(s=strchr(s,'/'));s++)
154                 *s='\\';
155         Filename_bslash=captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8);
156
157         if ((('M'<<8U)|('Z'<<0U))==GUINT16_FROM_BE(*(const guint16 *)ModuleLoadBase)) {
158                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"MZ/PE-32");
159                 /* examine/relocate the module */
160                 err=LdrProcessModule(ModuleLoadBase,Filename_bslash,&Module);
161                 if (!NT_SUCCESS(err)) {
162                         g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
163                         goto err_captive_rtl_file_munmap;
164                         }
165                 }
166         else {
167                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
168                 Module=captive_LdrLoadModule_gmodule(Filename_utf8,Filename_bslash_utf8);
169                 g_assert(Module!=NULL);
170                 }
171
172         /* we were successful */
173         captive_rtl_file_munmap(ModuleLoadBase);
174         *ModuleObjectp=Module;
175
176         /* Hook for KDB on loading a driver. */
177         KDB_LOADDRIVER_HOOK(Filename_bslash,Module);
178
179         return STATUS_SUCCESS;
180
181         /* Error recoveries */
182 err_captive_rtl_file_munmap:
183         captive_rtl_file_munmap(ModuleLoadBase);
184
185         g_return_val_if_reached(err);
186 }
187
188
189 /**
190  * captive_LdrpLoadAndCallImage:
191  * @ModuleObjectp: Returns PMODULE_OBJECT successfuly loaded.
192  * @ModuleName: Filename of module to load based on host OS disk.
193  * Loading of already loaded module is forbidden despite original
194  * LdrpLoadAndCallImage().
195  * @DriverEntry_DriverObject: argument #DriverObject of #PDRIVER_INITIALIZE call.
196  * @DriverEntry_RegistryPath: argument #RegistryPath of #PDRIVER_INITIALIZE call.
197  *
198  * Corresponds to reactos LdrpLoadAndCallImage() but it also provides arguments
199  * to pass to #PDRIVER_INITIALIZE call of module driver initialization.
200  *
201  * Returns: STATUS_SUCCESS if the driver module was loaded and initialized
202  * successfuly during this call. Ignore returned @ModuleObjectp if function failed.
203  */
204 NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,PUNICODE_STRING ModuleName,
205                 PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath)
206 {
207 PDRIVER_INITIALIZE DriverEntry;
208 PMODULE_OBJECT ModuleObject_tmp;
209 NTSTATUS err;
210
211         /* check for duplicity */
212         g_return_val_if_fail(LdrGetModuleObject(ModuleName)==NULL,STATUS_IMAGE_ALREADY_LOADED);
213
214         /* provide our temporary storage if the caller doesn't want to know ModuleObject address */
215         if (!ModuleObjectp)
216                 ModuleObjectp=&ModuleObject_tmp;
217
218         /* load the module */
219         err=LdrLoadModule(ModuleName,ModuleObjectp);
220         g_return_val_if_fail(NT_SUCCESS(err),err);
221
222         /* initialize the module */
223         DriverEntry=(PDRIVER_INITIALIZE)(*ModuleObjectp)->EntryPoint;
224         err=(*DriverEntry)(DriverEntry_DriverObject,DriverEntry_RegistryPath);
225         if (!NT_SUCCESS(err))
226                 goto err_LdrUnloadModule;
227
228         /* assumed STATUS_SUCCESS */
229         return err;
230
231 err_LdrUnloadModule:
232         /* errors ignored */
233         LdrUnloadModule(*ModuleObjectp);
234         *ModuleObjectp=NULL;
235 /* err: */
236         g_return_val_if_reached(err);
237 }
238
239
240 /**
241  * captive_ModuleList_add_builtin:
242  * @FullName_utf8: String to fill in #PMODULE_OBJECT->FullName.
243  * @...: (const gchar *sym_name,void *sym_val) symbol list terminated by %NULL.
244  *
245  * Adds simulated built-in module to #ModuleListHead module list.
246  * It can be used for the functionality of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement().
247  * libcaptive does not support Ordinals - we just pretend liner (%0-based)
248  * Ordinal numbers of the functions in given @... stdargs order.
249  *
250  * Returns: %TRUE if the module was successfuly added.
251  */
252 gboolean captive_ModuleList_add_builtin(const gchar *FullName_utf8,...)
253 {
254 MODULE_OBJECT *ModuleObject;
255 BOOLEAN errbool;
256 va_list ap;
257 const gchar *sym_name;
258 const void *sym_val;
259 gchar *basename_utf8;
260 IMAGE_DOS_HEADER *DosHeader;
261 IMAGE_NT_HEADERS *NTHeaders;
262 IMAGE_EXPORT_DIRECTORY *ExportDir;
263 guint symi,symN;        /* index,number of passed stdarg sym_name/sym_val pairs */
264 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
265 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
266 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
267
268         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
269
270         captive_new0(ModuleObject);
271         /* A lot of offsets in the structures are relative to ModuleObject->Base */
272 #define MODULEOBJECT_BASE_OFFSET_MINUS(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
273
274         ModuleObject->Flags = MODULE_FLAG_PE;
275
276         /* fill ModuleObject->FullName
277          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
278          * we put there '/'-based UNIX filenames in libcaptive.
279          */
280         errbool=RtlCreateUnicodeString(&ModuleObject->FullName,captive_utf8_to_UnicodeString_alloca(FullName_utf8)->Buffer);
281         if (!errbool) {
282                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
283                 goto err_g_free_ModuleObject;
284                 }
285
286         /* fill ModuleObject->BaseName
287          * reactos/ntoskrnl/ldr/loader.c/LdrpBuildModuleBaseName() does this one
288          * but it is 'static' for that file and also it is stricly backslash based.
289          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
290          * we put there '/'-based UNIX filenames in libcaptive.
291          */
292         basename_utf8=g_path_get_basename(FullName_utf8);
293         errbool=RtlCreateUnicodeString(&ModuleObject->BaseName,captive_utf8_to_UnicodeString_alloca(basename_utf8)->Buffer);
294         g_free(basename_utf8);
295         if (!errbool) {
296                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
297                 goto err_g_free_ModuleObject;
298                 }
299
300         /* We must satisfy reactos/ntoskrnl/ldr/rtl.c/RtlImageDirectoryEntryToData(,,IMAGE_DIRECTORY_ENTRY_EXPORT)
301          * prepare module headers
302          */
303         captive_new0(DosHeader);
304         ModuleObject->Base=DosHeader;   /* link DosHeader to ModuleObject */
305         DosHeader->e_magic=IMAGE_DOS_MAGIC;
306         captive_new0(NTHeaders);
307         DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET_MINUS(NTHeaders);  /* link NTHeaders to DosHeader */
308         NTHeaders->Signature=IMAGE_PE_MAGIC;
309         NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
310         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
311                         =sizeof(*ExportDir);    /* in bytes; just prevent LdrPEFixupForward() invocation */
312         captive_new0(ExportDir);
313         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
314                         =MODULEOBJECT_BASE_OFFSET_MINUS(ExportDir);     /* link ExportDir to NTHeaders */
315         ExportDir->Base=0;      /* base Ordinal number; Ordinals are not supported by libcaptive */
316
317         /* count the number of symbols */
318         va_start(ap,FullName_utf8);
319         for (symN=0;captive_va_arg(sym_name,ap);symN++)
320                 captive_va_arg(sym_val,ap);
321         va_end(ap);
322         ExportDir->NumberOfNames=symN;
323         ExportDir->NumberOfFunctions=symN;
324
325         /* allocate and link the symbol tables */
326         captive_newn(NameList,symN);
327         ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(NameList);   /* link NameList to ExportDir */
328         captive_newn(OrdinalList,symN);
329         ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(OrdinalList);  /* link OrdinalList to ExportDir */
330         captive_newn(FunctionList,symN);
331         ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(FunctionList);   /* link FunctionList to ExportDir */
332
333         /* fill in symbol tables */
334         va_start(ap,FullName_utf8);
335         for (symi=0;symi<symN;symi++) {
336                 captive_va_arg(sym_name,ap);
337                 captive_va_arg(sym_val ,ap);
338                 NameList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_name);
339                 OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
340                 FunctionList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
341                 }
342         va_end(ap);
343
344         /* successful module info build */
345         InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
346         return TRUE;
347
348 err_g_free_ModuleObject:
349         g_free(ModuleObject);
350 /* err */
351         g_return_val_if_reached(FALSE);
352 }
353
354
355 static gsize instruction_length(const guint8 *instr)
356 {
357         if ((instr[0] & 0xF8)==0x50)    /* push %regular-register */
358                 return 1;
359         if ((instr[0] & 0xF8)==0x58)    /* pop  %regular-register */
360                 return 1;
361         if ((instr[0] & 0xE7)==0x06)    /* push %segment-register */
362                 return 1;
363         if ((instr[0] & 0xE7)==0x07 && instr[0]!=0x0F)  /* pop  %segment-register */
364                 return 1;
365         switch (instr[0]) {
366                 case 0x33:      /* xor GB,Ev */
367                         return 1+1;
368                 case 0x64:      /* prefix %fs */
369                         return instruction_length(instr+1);
370                 case 0x68:      /* push $quad-byte */
371                         return 1+4;
372                 case 0x6A:      /* push $single-byte */
373                         return 1+1;
374                 case 0x80:
375                         switch (instr[1]) {
376                                 case 0x3D:      /* cmpb $byte,immediate-quad-indirect */
377                                         return 1+1+4+1; /* 80 3D immediate-quad-indirect byte */
378                                 default: g_assert_not_reached();
379                                 }
380                 case 0x83:
381                         switch (instr[1]) {
382                                 case 0x01:      /* addl $byte,(%ecx) */
383                                         return 1+1+1;
384                                 case 0x3D:      /* cmpl $byte,immediate-quad-indirect */
385                                         return 1+1+4+1; /* 83 3D immediate-quad-indirect byte */
386                                 default: g_assert_not_reached();
387                                 }
388                 case 0x8A:      /* mov ??,?? */
389                 case 0x8B:      /* mov Gb,Eb */
390                         switch (instr[1]) {
391                                 case 0x0D:      /* mov immediate-quad-indirect,%ecx */
392                                         return 1+1+4;
393                                 case 0x15:      /* mov immediate-quad-indirect,%edx */
394                                         return 1+1+4;
395                                 case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
396                                 case 0x4C:      /* mov #offset(%reg,#mult),%reg */
397                                 case 0x54:      /* mov #offset(%reg,#mult),%reg */
398                                         return 1+1+1+1; /* 8B 44 address-mode offset */
399                                 default: g_assert_not_reached();
400                                 }
401                 case 0x8D:      /* lea Gb,M */
402                         switch (instr[1]) {
403                                 case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
404                                         return 4;       /* 8B 44 address-mode offset */
405                                 default: g_assert_not_reached();
406                                 }
407                 case 0x9C:      /* pushf */
408                         return 1;
409                 case 0x9D:      /* popf */
410                         return 1;
411                 case 0xA1:      /* mov immediate-quad-indirect,%eax */
412                         return 1+4;
413                 case 0xB8:      /* mov $immediate-quad,%eax */
414                         return 1+4;
415                 case 0xC2:      /* ret $immediate-double */
416                         return 1+2;
417                 case 0xCC:      /* int $0x3 */
418                         return 1;
419                 case 0xFA:      /* cli */
420                         return 1;
421                 case 0xFB:      /* sti */
422                         return 1;
423                 case 0xFF:      /* inc/dec Ev */
424                         return 2;
425                 default: g_assert_not_reached();
426                 }
427         g_assert_not_reached(); /* TODO:fill the table */
428         /* NOTREACHED */
429         return 0;
430 }
431
432
433         /* A lot of offsets in the structures are relative to ModuleObject->Base */
434 #define MODULEOBJECT_BASE_OFFSET_PLUS(addr) ((gpointer)((char *)addr+(ptrdiff_t)ModuleObject->Base))
435
436 static void captive_ModuleList_patch_function_disable
437                 (CHAR *funcname,PVOID *ExportAddressp,MODULE_OBJECT *ModuleObject /* user_data */)
438 {
439 PVOID ExportAddress;
440
441         g_return_if_fail(funcname!=NULL);
442         g_return_if_fail(ExportAddressp!=NULL);
443         g_return_if_fail(ModuleObject!=NULL);
444
445         ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
446         g_return_if_fail(ExportAddress!=NULL);
447
448         /* Some alised name; safe to ignore as we checked for possible W32 binary 0xF4
449          * hits during our initialization in captive_ModuleList_patch().
450          */
451         if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress)
452                 return;
453
454         captive_ModuleList_function_disable_hash_init();
455
456         *(guint8 *)ExportAddress=0xF4; /* hlt */
457         /* We may get aliased here; already existing entry is therefore allowed. */
458         g_hash_table_insert(captive_ModuleList_function_disable_hash,
459                         ExportAddress,  /* key */
460                         funcname);      /* value */
461 }
462
463
464 /**
465  * captive_ModuleList_patch:
466  * @FullName_utf8: String to find #PMODULE_OBJECT by #FullName.
467  * @...: (const gchar *sym_name,void (*sym_val)(void),struct captive_ModuleList_patchpoint *patchpoint) symbol list terminated by %NULL.
468  *
469  * Patches existing @FullName_utf8 module to use for function named #sym_name
470  * pointer to the handler #sym_val. If #patchpoint is not %NULL it gets assigned the original
471  * pointer value (used for %pass keyword in #exports.captivesym).
472  * 
473  * Put here 0xF4 'hlt' instead of 0xCC 'int $0x3; breakpoint'
474  * as 'hlt' will generate handled SIGSEGV instead of SIGTRAP which
475  * is used by gdb(1) during debugging.
476  * See also libcaptive/ps/signal.c/ sigaction_SIGSEGV().
477  *
478  * Returns: %TRUE if the module was successfuly added.
479  */
480 gboolean captive_ModuleList_patch(const gchar *FullName_utf8,...)
481 {
482 MODULE_OBJECT *ModuleObject;
483 va_list ap;
484 IMAGE_EXPORT_DIRECTORY *ExportDir;
485 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
486 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
487 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
488 ULONG ExportDirSize;
489 USHORT Idx;
490 PVOID ExportAddress,*ExportAddressp;
491 const gchar *sym_name;
492 void (*sym_val)(void);
493 struct captive_ModuleList_patchpoint *patchpoint;
494 GHashTable *exportdir_hash;
495 gboolean errbool;
496
497         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
498
499         captive_ModuleList_patchpoint_hash_init();
500
501         ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(FullName_utf8)));
502         g_return_val_if_fail(ModuleObject!=NULL,FALSE);
503
504         ExportDir=(PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(ModuleObject->Base,
505                         TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT,&ExportDirSize);
506         g_return_val_if_fail(ExportDir!=NULL,FALSE);
507
508         NameList    =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNames);
509         OrdinalList =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNameOrdinals);
510         FunctionList=MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfFunctions);
511
512         /* map (CHAR *)funcname->(PVOID *)ExportAddressp */
513         exportdir_hash=g_hash_table_new(g_str_hash,g_str_equal);
514
515         /* Initialize 'exportdir_hash' by all W32 exported functions. */
516         for (Idx=0;Idx<ExportDir->NumberOfNames;Idx++) {
517 CHAR *funcname=MODULEOBJECT_BASE_OFFSET_PLUS(NameList[Idx]);
518
519                 ExportAddressp=(PVOID *)(FunctionList+OrdinalList[Idx]);
520                 ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
521                 if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress) {
522                         /* FIXME: 'data' type symbols should be permitted to have 0xF4 byte */
523                         g_error("%s: Function already patched although we did not touch it yet: %s",G_STRLOC,funcname);
524                         g_assert_not_reached();
525                         }
526                 g_assert(NULL==g_hash_table_lookup(exportdir_hash,funcname));
527                 g_hash_table_insert(exportdir_hash,
528                                 funcname,       /* key */
529                                 ExportAddressp);        /* value */
530                 }
531
532         /* Patch wished functions and remove them from 'exportdir_hash'. */
533         va_start(ap,FullName_utf8);
534         while (captive_va_arg(sym_name,ap)) {
535                 captive_va_arg(sym_val ,ap);
536                 g_assert(sym_val!=NULL);
537                 captive_va_arg(patchpoint,ap);  /* 'data' type if ==NULL */
538                 ExportAddressp=g_hash_table_lookup(exportdir_hash,sym_name);
539                 if (ExportAddressp==NULL) {
540                         g_error("%s: Function not found for patchpoint: %s",G_STRLOC,sym_name);
541                         g_assert_not_reached();
542                         }
543                 errbool=g_hash_table_remove(exportdir_hash,sym_name);
544                 g_assert(errbool==TRUE);
545                 ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
546                 *ExportAddressp=(PVOID)MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
547                 if (((ULONG)ExportAddress >= (ULONG)ExportDir) &&
548                                 ((ULONG)ExportAddress <  (ULONG)ExportDir + ExportDirSize))
549                         g_assert_not_reached(); /* LdrPEFixupForward() needed */
550                 if (!patchpoint) /* 'data' type */
551                         continue;
552                 patchpoint->orig_w32_func=ExportAddress;
553                 if (0xF4 /* hlt */ ==*patchpoint->orig_w32_func)        /* Already patched by name-aliased function? */
554                         continue;
555                 g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_func);
556                 patchpoint->orig_w32_2ndinstr=patchpoint->orig_w32_func
557                                 +instruction_length((guint8 *)patchpoint->orig_w32_func);
558                 g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_2ndinstr);
559                 patchpoint->wrap_wrap_func=sym_val;
560                 patchpoint->orig_w32_func_byte=*patchpoint->orig_w32_func;
561                 patchpoint->orig_w32_2ndinstr_byte=*patchpoint->orig_w32_2ndinstr;
562                 patchpoint->through_w32_func=FALSE;
563                 g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_func));
564                 g_hash_table_insert(captive_ModuleList_patchpoint_hash,
565                                 patchpoint->orig_w32_func,      /* key */
566                                 patchpoint);    /* value */
567                 g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_2ndinstr));
568                 g_hash_table_insert(captive_ModuleList_patchpoint_hash,
569                                 patchpoint->orig_w32_2ndinstr,  /* key */
570                                 patchpoint);    /* value */
571                 *(guint8 *)ExportAddress=0xF4;  /* hlt */
572                 }
573         va_end(ap);
574
575         /* The remaining entries of 'exportdir_hash' are W32 native functions
576          * unspecified by .captivesym file; we patch them as not-implemented ones.
577          */
578         g_hash_table_foreach(exportdir_hash,
579                         (GHFunc)captive_ModuleList_patch_function_disable,      /* func */
580                         ModuleObject);  /* used_data; unused */
581
582         g_hash_table_destroy(exportdir_hash);
583
584 #undef MODULEOBJECT_BASE_OFFSET_PLUS    /* no longer valid */
585 #undef MODULEOBJECT_BASE_OFFSET_MINUS   /* no longer valid */
586         return TRUE;
587 }
588
589
590 struct captive_ModuleList_patchpoint *captive_ModuleList_patchpoint_find(gconstpointer ExportAddress)
591 {
592 struct captive_ModuleList_patchpoint *r;
593
594         g_return_val_if_fail(ExportAddress!=NULL,NULL);
595
596         captive_ModuleList_patchpoint_hash_init();
597
598         r=g_hash_table_lookup(captive_ModuleList_patchpoint_hash,ExportAddress);
599         g_return_val_if_fail(r!=NULL,NULL);
600         g_assert(r->orig_w32_func==ExportAddress || r->orig_w32_2ndinstr==ExportAddress);
601
602         return r;
603 }
604
605
606 G_CONST_RETURN gchar *captive_ModuleList_function_disable_find(gconstpointer ExportAddress)
607 {
608         g_return_val_if_fail(ExportAddress!=NULL,NULL);
609
610         return g_hash_table_lookup(captive_ModuleList_function_disable_hash,ExportAddress);     /* funcname */
611 }