s/;.*//s;
next if /^\s*$/s;
next if /^(?:LIBRARY|EXPORTS)\b/s;
- my($atsign,$symbol,$args,$isdata)=(/^\s*(\@)?(\S+?)(?:\@(\d+))?(\s+DATA)?\s*$/s);
+ my($atsign,$symbol,$args,$argscdecl,$isdata)=(/^\s*(\@)?(\S+?)(?:\@(\d+))?(?::(\d+))?(\s+DATA)?\s*$/s);
die "Invalid line" if !defined $symbol;
- # popped args doc *.def
- # cdecl no stack _f f
+ # popped args doc *.def *.def2
+ # cdecl no stack _f f f:4
# stdcall yes stack _f@4 f@4
# fastcall yes ecx,edx,stack @f@4 @f@4
- die "Invalid attributes for data symbol" if $isdata && ($atsign || defined $args);
- die "\@funcname without \@4 suffix not recognized" if $atsign && !defined $args;
- die "Invalid \@$args number" if defined $args && ($args<0 || ($args%4));
- die "Duplicate symbol: $symbol" if exists $def{$symbol};
+ die "Invalid attributes for data symbol: $symbol" if $isdata && ($atsign || defined $args);
+ die "\@funcname without \@4 suffix not recognized: $symbol" if $atsign && !defined $args;
+ die "Invalid \@$args number: $symbol" if defined $args && ($args<0 || ($args%4));
+ if (!$argscdecl) {
+ die "Duplicate symbol: $symbol" if exists $def{$symbol};
+ }
+ else {
+ die "cdecl-fixup without previous declaration: $symbol" if !$def{$symbol};
+ die "cdecl-fixup with non-cdecl previous declaration: $symbol" if $def{$symbol}{"type"} ne "cdecl";
+ die "cdecl-fixup for already fix-uped cdecl: $symbol" if exists $def{$symbol}{"args4"};
+ $args=$argscdecl;
+ }
$def{$symbol}={
"type"=>($isdata ? "data" : (!defined $args ? "cdecl" : (!$atsign ? "stdcall" : "fastcall"))),
(!defined $args ? () : ("args4"=>$args/4)),
# read source
my %module; # $module{'module'}{'symbol'}=1/""
-my %symbol; # $symbol{'symbol'}=1/""
+my %symbol; # $symbol{'symbol'}='module'
+my %patch; # $patch{'module'}=1/undef
while (<>) {
chomp;
next if /^\s*$/; # empty
next if /^\s*#.*/; # comment
- my($module,$symbol,$isundef)=(/^\s*(\S+)\s+(\S+)(\s+undef)?\s*$/);
+ my($module,$symbol,$iswhat)=(/^\s*(\S+)\s+(\S+)(?:\s+(undef|pass|wrap))?\s*$/);
+ $iswhat="" if !defined $iswhat;
die "Invalid line" if !defined $symbol;
+ if ($symbol eq "<patch>") {
+ die "Invalid line" if $iswhat;
+ die "Symbols already present during <patch> for: $module" if $module{$module}{$symbol};
+ $patch{$module}=1;
+ next;
+ }
die "Symbol already exists: $symbol" if exists $symbol{$symbol};
- if ($isundef) {
+ if ($iswhat eq "undef") {
warn "Undefined symbol not in *.def files; 'data' type risk imminent: $symbol" if !$def{$symbol};
die "Undefined 'data' type symbols are not safe: $symbol" if $def{$symbol} && $def{$symbol}{"type"} eq "data";
delete $def{$symbol};
}
- die "Symbol not in *.def files: $symbol" if !$isundef && !$def{$symbol};
- $module{$module}{$symbol}=!$isundef;
- $symbol{$symbol}=!$isundef;
+ if ($iswhat eq "pass" || $iswhat eq "wrap") {
+ die "args count not fixed up for '$iswhat' type: ".$symbol."[".$def{$symbol}{"type"}."]" if !$def{$symbol}{"args4"};
+ die "'$iswhat' not permitted if <patch> not specified for module on symbol: $symbol" if !$patch{$module};
+ $def{$symbol}{$iswhat}=1;
+ }
+ die "Symbol not in *.def files: $symbol" if $iswhat ne "undef" && !$def{$symbol};
+ $module{$module}{$symbol}=$iswhat ne "undef";
+ $symbol{$symbol}=$module;
}
# file header
}
HERE
$symbols_undef++;
+ next;
+ }
+ if ($patch{$symbol{$symbol}} && "data" ne $def->{"type"}) {
+ print "static struct captive_ModuleList_patchpoint ${symbol}_patchpoint;\n";
}
- elsif ("data" eq $def->{"type"}) {
+ if ("data" eq $def->{"type"}) {
+ die "'data' type not pass-able: $symbol" if $def->{"pass"} || $def->{"wrap"};
print "extern void/* ==unknown */ ${symbol};\n",
"#define ${symbol}_".$def->{"type"}." ${symbol}\n";
+ next;
}
- elsif ("cdecl" eq $def->{"type"}) {
- # FIXME: g_log(,G_LOG_LEVEL_DEBUG,...) not implemented
+ if ("cdecl" eq $def->{"type"} && !defined $def->{"args4"} && !$def->{"pass"} && !$def->{"wrap"}) {
+ # g_log(,G_LOG_LEVEL_DEBUG,...) not possible if we do not know the arguments count
print "void/* ==unknown */ ${symbol}(void/* ==unknown */);\n",
"#define ${symbol}_".$def->{"type"}." ${symbol}\n";
+ next;
+ }
+
+ die "Needed argument count for: $symbol" if !defined $def->{"args4"};
+ my @args_out=map("arg$_",0..($def->{"args4"}-1));
+ my @args_in=($def->{"type"} ne "fastcall" ? @args_out
+ : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
+ my $attrib=""
+ ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
+ .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3)))");
+ for my $type ("clean","attrib") {
+ print
+ "typedef guint64"
+ ." ".($type eq "clean" ? "" : $attrib)
+ ." (${symbol}_t_".$type.")(".(join(",",map("guint32 $_",
+ ($type eq "clean" ? @args_out : @args_in))) || "void").");\n";
}
- else {
- my @args_out=map("arg$_",0..($def->{"args4"}-1));
- my @args_in=($def->{"type"} ne "fastcall" ? @args_out
- : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
- print "guint64 ${symbol}(".(join(",",map("guint32 $_",@args_out)) || "void").");\n",
- "static guint64 "
- ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
- .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3))) ")
- ."${symbol}_".$def->{"type"}."(".(join(",",map("guint32 $_",@args_in)) || "void").")\n",
+ if ($def->{"wrap"}) {
+ print "${symbol}_t_clean ${symbol}_wrap;\n";
+ }
+ elsif (!$def->{"pass"}) { # direct
+ print "${symbol}_t_clean $symbol;\n";
+ }
+ my @args_print=@args_out;
+ for my $pass ("outer","clean","inner") {
+ if ($pass eq "inner") {
+ next if !$def->{"wrap"}; # use two passes only for $def->{"wrap"}
+ # swap the roles of @args_in and @args_out
+ my @args_xchg=@args_in;
+ @args_in=@args_out;
+ @args_out=@args_xchg;
+ }
+ next if $pass eq "clean" && !$def->{"pass"} && !$def->{"wrap"};
+ print(
+ "guint64".($pass eq "outer" ? " $attrib" : "")
+ ." ${symbol}"
+ .($pass eq "clean" ? "" :
+ ($pass eq "inner" ? "_orig" : "_".$def->{"type"}))
+ ."(".(join(",",map("guint32 $_",($pass eq "clean" ? @args_out : @args_in))) || "void").")\n",
"{\n",
"guint64 r;\n",
+ ($pass ne "outer" && $def->{"type"} eq "fastcall" ? "guint32 "
+ .join(",",map("$_=0xDEADF00D","stub_eax",
+ ($def->{"args4"}<=0 ? "arg0" : ()),
+ ($def->{"args4"}<=1 ? "arg1" : ())))
+ .";\n" : ""),
"\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
- ."\"%s(".join(",",map("0x%08x",@args_out)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_out))
+ ."\"%s".($def->{"wrap"} ? ";$pass" : "")
+ ."(".join(",",map("0x%08x",@args_print)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print))
.");\n",
- "\tr=${symbol}(".join(",",@args_out).");\n",
+ "");
+ if ($def->{"pass"} || ($def->{"wrap"} && $pass eq "inner")) {
+ print
+ "\tg_return_val_if_fail(${symbol}_patchpoint.orig_w32_func!=NULL,0);\n",
+ "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n",
+ "\t${symbol}_patchpoint.through_w32_func=TRUE;\n",
+ "\tr=(*(${symbol}_t_attrib *)${symbol}_patchpoint.orig_w32_func)(".join(",",@args_in).");\n",
+ "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n";
+ }
+ else {
+ print
+ "\tr=${symbol}".($def->{"wrap"} ? "_wrap" : "")."(".join(",",@args_out).");\n";
+ }
+ print
# We diplay just the lower 32-bit of the result EDX:EAX as it is usually not used,
# the only exception are _all{mul,div,*}(); EDX:EAX convention is always compatible.
"\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
- ."\"... %s(".join(",",map("0x%08x",@args_out)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_out)).",(guint32)r"
+ ."\"... %s".($def->{"wrap"} ? ";$pass" : "")
+ ."(".join(",",map("0x%08x",@args_print)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print)).",(guint32)r"
.");\n",
"\treturn r;\n",
"}\n";
}
}
-# export function captive_kernel_exports()
-print <<"HERE";
+# write function captive_kernel_{exports,patches}()
+for my $functype ("exports","patches") {
+ print <<"HERE";
-gboolean captive_kernel_exports(void)
+gboolean captive_kernel_$functype(void)
{
gboolean errbool;
HERE
-for my $module (sort keys(%module)) {
- my $moduleref=$module{$module};
- print <<"HERE";
- errbool=captive_ModuleList_add_builtin("$module",
-HERE
- for my $symbol (sort keys(%$moduleref)) {
- my $value=${$moduleref}{$symbol};
- print "\t\t\t\"$symbol\",&${symbol}_",
- ($def{$symbol}{"type"} || "undef"),
- ",\n";
- }
- print <<"HERE";
+ for my $module (sort keys(%module)) {
+ my $moduleref=$module{$module};
+ next if ($functype eq "patches") != defined $patch{$module};
+ print "\t\terrbool="
+ .($functype eq "patches" ? "captive_ModuleList_patch" : "captive_ModuleList_add_builtin")
+ ."(\"$module\",\n";
+ for my $symbol (sort keys(%$moduleref)) {
+ next if $functype eq "patches" && (!$def{$symbol} || "data" eq $def{$symbol}{"type"});
+ print "\t\t\t\"$symbol\",&${symbol}_",
+ ($def{$symbol}{"type"} || "undef"),
+ (($functype ne "patches") ? () : (",&${symbol}_patchpoint")),
+ ",\n";
+ }
+ print <<"HERE";
NULL);
g_return_val_if_fail(errbool,FALSE);
HERE
- }
-print <<"HERE";
+ }
+ print <<"HERE";
return TRUE;
}
HERE
+ }
# exit
print STDERR "$0: Processed ".scalar(keys(%module))." modules, ".scalar(keys(%symbol))." symbols",
Source files with symbol call type definitions are identified by matching
pattern I<*.def>. The remaining files (I<.captivesym> ones) must
-consist of lines with whitespace-separated
-(B<module>,B<symbol>,[undef]) items.
+consist of lines with whitespace-separated lines as described below.
+
+=over
+
+=item (B<module>,E<lt>patchE<gt>)
+
+Declare B<module> as mandatory W32 binary file to be patched by libcaptive.
+Currently being used only for C<ntoskrnl.exe>.
+
+Any function call even inside such module is trapped and redirected for
+libcaptive processing even if it is just for debug-dumping of B<pass> type.
+
+=item (B<module>,B<symbol>,[undef|pass|wrap])
=over
+=item ""
+
+Name without special attribute declares function fully implemented by GNU/Linux
+code. Original W32 binary function will never be called.
+
+You may fully implement function for both E<lt>patchE<gt>ed and
+unE<lt>patchE<gt>ed modules.
+
=item undef
Optional "undef" specifies invocation of a generated stub function displaying
C<g_error()> message.
+For "unpatched" modules you have to specify all the referenced symbols at least
+as this "undef" symbol. For "patched" modules it is not needed for native
+W32-PE binary modules importing such symbol but it is still required for W32
+.so files to satisfy .so dynamic linker.
+
+It is forbidden to "undef" C<DATA> type of items; you have to cope with it.
+
+=item pass
+
+Calls of this function are debug-dumped on its entry/exit but they are fully
+left to be solved by W32 binary file being E<lt>patchE<gt>ed.
+
+It is forbidden to specify "pass" for unE<lt>patchE<gt>ed modules.
+
+=item wrap
+
+Calls of this function are debug-dumped on its entry/exit. Execution is left
+to be solved by your GNU/Linux implementation called B<functionname_wrap>.
+You are allowed to call the original W32 binary function named
+B<functionname_orig> but you have to use your own prototype declaration for it.
+Both B<functionname_wrap> and B<functionname_orig> should be used with standard
+GNU/Linux C compiler function call type notwithstanding any real W32
+implementation details.
+
+It is forbidden to specify "wrap" for unE<lt>patchE<gt>ed modules.
+
+=back
+
=back
=begin comment
=item fastcall: @functionname@argslength
+=item cdecl fixup: functionname:argslength
+
+This item must follow (even in some other I<*.def> file) previous B<cdecl>
+specification to specify the number of arguments as it is required for B<pass>
+or B<wrap> type of B<cdecl> function calls.
+
=back
=head1 COPYRIGHT
-Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
+Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "config.h"
#include "captive/ldr.h" /* self */
+#include "captive/ldr_exports.h" /* self */
#include "reactos/internal/ldr.h" /* self */
#include "captive/unicode.h"
#include "captive/rtl-file.h"
#include <gmodule.h>
+/* map: ExportAddress -> (struct captive_ModuleList_patchpoint *) */
+static GHashTable *captive_ModuleList_patchpoint_hash;
+
+/* initialize 'captive_ModuleList_patchpoint_hash' */
+static void captive_ModuleList_patchpoint_hash_init(void)
+{
+ if (captive_ModuleList_patchpoint_hash)
+ return;
+ captive_ModuleList_patchpoint_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
+}
+
+
+/* map: ExportAddress -> (CHAR *)funcname */
+static GHashTable *captive_ModuleList_function_disable_hash;
+
+/* initialize 'captive_ModuleList_function_disable_hash' */
+static void captive_ModuleList_function_disable_hash_init(void)
+{
+ if (captive_ModuleList_function_disable_hash)
+ return;
+ captive_ModuleList_function_disable_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
+}
+
+
/* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
-static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8)
+static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8,const gchar *Filename_bslash_utf8)
{
MODULE_OBJECT *r;
GModule *gmodule;
gboolean errbool;
g_return_val_if_fail(Filename_utf8!=NULL,NULL);
+ g_return_val_if_fail(Filename_bslash_utf8!=NULL,NULL);
g_return_val_if_fail(TRUE==g_module_supported(),NULL);
gmodule=g_module_open(Filename_utf8,
r->Base=(PVOID)gmodule;
r->Flags=MODULE_FLAG_COFF; /* some weird value - reactos uses MODULE_FLAG_PE */
- RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_utf8)->Buffer);
+ RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8)->Buffer);
LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
/* r->Length=0; */
PVOID ModuleLoadBase;
PMODULE_OBJECT Module;
NTSTATUS err;
-const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
+gchar *Filename_utf8=(/* de-const */ gchar *)captive_UnicodeString_to_utf8_alloca(Filename);
+gchar *Filename_bslash_utf8,*s;
+UNICODE_STRING *Filename_bslash;
*ModuleObjectp=NULL;
/* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
+ /* Filename=~s#/#\\#g -> Filename_bslash to satisfy basename-derivations by reactos. */
+ Filename_bslash_utf8=g_strdup(Filename_utf8);
+ for (s=Filename_bslash_utf8;(s=strchr(s,'/'));s++)
+ *s='\\';
+ Filename_bslash=captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8);
+
if ((('M'<<8U)|('Z'<<0U))==GUINT16_FROM_BE(*(const guint16 *)ModuleLoadBase)) {
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"MZ/PE-32");
/* examine/relocate the module */
- err=LdrProcessModule(ModuleLoadBase,Filename,&Module);
+ err=LdrProcessModule(ModuleLoadBase,Filename_bslash,&Module);
if (!NT_SUCCESS(err)) {
g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
goto err_captive_rtl_file_munmap;
}
else {
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
- Module=captive_LdrLoadModule_gmodule(Filename_utf8);
+ Module=captive_LdrLoadModule_gmodule(Filename_utf8,Filename_bslash_utf8);
g_assert(Module!=NULL);
}
*ModuleObjectp=Module;
/* Hook for KDB on loading a driver. */
- KDB_LOADDRIVER_HOOK(Filename,Module);
+ KDB_LOADDRIVER_HOOK(Filename_bslash,Module);
return STATUS_SUCCESS;
IMAGE_NT_HEADERS *NTHeaders;
IMAGE_EXPORT_DIRECTORY *ExportDir;
guint symi,symN; /* index,number of passed stdarg sym_name/sym_val pairs */
-ptrdiff_t *NameList; /* filled by sym_name; MODULEOBJECT_BASE_OFFSET()ed */
+ptrdiff_t *NameList; /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
WORD *OrdinalList; /* linear list - libcaptive doesn't support Ordinals */
-ptrdiff_t *FunctionList; /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
+ptrdiff_t *FunctionList; /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
captive_new0(ModuleObject);
/* A lot of offsets in the structures are relative to ModuleObject->Base */
-#define MODULEOBJECT_BASE_OFFSET(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
+#define MODULEOBJECT_BASE_OFFSET_MINUS(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
ModuleObject->Flags = MODULE_FLAG_PE;
ModuleObject->Base=DosHeader; /* link DosHeader to ModuleObject */
DosHeader->e_magic=IMAGE_DOS_MAGIC;
captive_new0(NTHeaders);
- DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET(NTHeaders); /* link NTHeaders to DosHeader */
+ DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET_MINUS(NTHeaders); /* link NTHeaders to DosHeader */
NTHeaders->Signature=IMAGE_PE_MAGIC;
NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
=sizeof(*ExportDir); /* in bytes; just prevent LdrPEFixupForward() invocation */
captive_new0(ExportDir);
NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
- =MODULEOBJECT_BASE_OFFSET(ExportDir); /* link ExportDir to NTHeaders */
+ =MODULEOBJECT_BASE_OFFSET_MINUS(ExportDir); /* link ExportDir to NTHeaders */
ExportDir->Base=0; /* base Ordinal number; Ordinals are not supported by libcaptive */
/* count the number of symbols */
/* allocate and link the symbol tables */
captive_newn(NameList,symN);
- ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET(NameList); /* link NameList to ExportDir */
+ ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(NameList); /* link NameList to ExportDir */
captive_newn(OrdinalList,symN);
- ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET(OrdinalList); /* link OrdinalList to ExportDir */
+ ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(OrdinalList); /* link OrdinalList to ExportDir */
captive_newn(FunctionList,symN);
- ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET(FunctionList); /* link FunctionList to ExportDir */
+ ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(FunctionList); /* link FunctionList to ExportDir */
/* fill in symbol tables */
va_start(ap,FullName_utf8);
for (symi=0;symi<symN;symi++) {
captive_va_arg(sym_name,ap);
captive_va_arg(sym_val ,ap);
- NameList[symi]=MODULEOBJECT_BASE_OFFSET(sym_name);
+ NameList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_name);
OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
- FunctionList[symi]=MODULEOBJECT_BASE_OFFSET(sym_val);
+ FunctionList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
}
va_end(ap);
/* successful module info build */
InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
-#undef MODULEOBJECT_BASE_OFFSET /* no longer valid */
return TRUE;
err_g_free_ModuleObject:
/* err */
g_return_val_if_reached(FALSE);
}
+
+
+static gsize instruction_length(const guint8 *instr)
+{
+ if ((instr[0] & 0xF8)==0x50) /* push %regular-register */
+ return 1;
+ if ((instr[0] & 0xF8)==0x58) /* pop %regular-register */
+ return 1;
+ if ((instr[0] & 0xE7)==0x06) /* push %segment-register */
+ return 1;
+ if ((instr[0] & 0xE7)==0x07 && instr[0]!=0x0F) /* pop %segment-register */
+ return 1;
+ switch (instr[0]) {
+ case 0x33: /* xor GB,Ev */
+ return 1+1;
+ case 0x64: /* prefix %fs */
+ return instruction_length(instr+1);
+ case 0x68: /* push $quad-byte */
+ return 1+4;
+ case 0x6A: /* push $single-byte */
+ return 1+1;
+ case 0x83:
+ switch (instr[1]) {
+ case 0x01: /* addl $byte,(%ecx) */
+ return 1+1+1;
+ case 0x3D: /* cmpl $byte,immediate-quad-indirect */
+ return 1+1+4+1; /* 83 3D immediate-quad-indirect byte */
+ default: g_assert_not_reached();
+ }
+ case 0x8A: /* mov ??,?? */
+ case 0x8B: /* mov Gb,Eb */
+ switch (instr[1]) {
+ case 0x0D: /* mov immediate-quad-indirect,%ecx */
+ return 1+1+4;
+ case 0x44: /* mov #offset(%reg,%reg,#mult),%reg */
+ case 0x4C: /* mov #offset(%reg,#mult),%reg */
+ case 0x54: /* mov #offset(%reg,#mult),%reg */
+ return 1+1+1+1; /* 8B 44 address-mode offset */
+ default: g_assert_not_reached();
+ }
+ case 0x8D: /* lea Gb,M */
+ switch (instr[1]) {
+ case 0x44: /* mov #offset(%reg,%reg,#mult),%reg */
+ return 4; /* 8B 44 address-mode offset */
+ default: g_assert_not_reached();
+ }
+ case 0x9C: /* pushf */
+ return 1;
+ case 0x9D: /* popf */
+ return 1;
+ case 0xA1: /* mov immediate-quad-indirect,%eax */
+ return 1+4;
+ case 0xB8: /* mov $immediate-quad,%eax */
+ return 1+4;
+ case 0xC2: /* ret $immediate-double */
+ return 1+2;
+ case 0xCC: /* int $0x3 */
+ return 1;
+ case 0xFA: /* cli */
+ return 1;
+ case 0xFB: /* sti */
+ return 1;
+ case 0xFF: /* inc/dec Ev */
+ return 2;
+ default: g_assert_not_reached();
+ }
+ g_assert_not_reached(); /* TODO:fill the table */
+ /* NOTREACHED */
+ return 0;
+}
+
+
+ /* A lot of offsets in the structures are relative to ModuleObject->Base */
+#define MODULEOBJECT_BASE_OFFSET_PLUS(addr) ((gpointer)((char *)addr+(ptrdiff_t)ModuleObject->Base))
+
+static void captive_ModuleList_patch_function_disable
+ (CHAR *funcname,PVOID *ExportAddressp,MODULE_OBJECT *ModuleObject /* user_data */)
+{
+PVOID ExportAddress;
+
+ g_return_if_fail(funcname!=NULL);
+ g_return_if_fail(ExportAddressp!=NULL);
+ g_return_if_fail(ModuleObject!=NULL);
+
+ ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+ g_return_if_fail(ExportAddress!=NULL);
+
+ /* Some alised name; safe to ignore as we checked for possible W32 binary 0xF4
+ * hits during our initialization in captive_ModuleList_patch().
+ */
+ if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress)
+ return;
+
+ captive_ModuleList_function_disable_hash_init();
+
+ *(guint8 *)ExportAddress=0xF4; /* hlt */
+ /* We may get aliased here; already existing entry is therefore allowed. */
+ g_hash_table_insert(captive_ModuleList_function_disable_hash,
+ ExportAddress, /* key */
+ funcname); /* value */
+}
+
+
+/**
+ * captive_ModuleList_patch:
+ * @FullName_utf8: String to find #PMODULE_OBJECT by #FullName.
+ * @...: (const gchar *sym_name,void (*sym_val)(void),struct captive_ModuleList_patchpoint *patchpoint) symbol list terminated by %NULL.
+ *
+ * Patches existing @FullName_utf8 module to use for function named #sym_name
+ * pointer to the handler #sym_val. If #patchpoint is not %NULL it gets assigned the original
+ * pointer value (used for %pass keyword in #exports.captivesym).
+ *
+ * Put here 0xF4 'hlt' instead of 0xCC 'int $0x3; breakpoint'
+ * as 'hlt' will generate handled SIGSEGV instead of SIGTRAP which
+ * is used by gdb(1) during debugging.
+ * See also libcaptive/ps/signal.c/ sigaction_SIGSEGV().
+ *
+ * Returns: %TRUE if the module was successfuly added.
+ */
+gboolean captive_ModuleList_patch(const gchar *FullName_utf8,...)
+{
+MODULE_OBJECT *ModuleObject;
+va_list ap;
+IMAGE_EXPORT_DIRECTORY *ExportDir;
+ptrdiff_t *NameList; /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
+WORD *OrdinalList; /* linear list - libcaptive doesn't support Ordinals */
+ptrdiff_t *FunctionList; /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
+ULONG ExportDirSize;
+USHORT Idx;
+PVOID ExportAddress,*ExportAddressp;
+const gchar *sym_name;
+void (*sym_val)(void);
+struct captive_ModuleList_patchpoint *patchpoint;
+GHashTable *exportdir_hash;
+gboolean errbool;
+
+ g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
+
+ captive_ModuleList_patchpoint_hash_init();
+
+ ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(FullName_utf8)));
+ g_return_val_if_fail(ModuleObject!=NULL,FALSE);
+
+ ExportDir=(PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(ModuleObject->Base,
+ TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT,&ExportDirSize);
+ g_return_val_if_fail(ExportDir!=NULL,FALSE);
+
+ NameList =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNames);
+ OrdinalList =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNameOrdinals);
+ FunctionList=MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfFunctions);
+
+ /* map (CHAR *)funcname->(PVOID *)ExportAddressp */
+ exportdir_hash=g_hash_table_new(g_str_hash,g_str_equal);
+
+ /* Initialize 'exportdir_hash' by all W32 exported functions. */
+ for (Idx=0;Idx<ExportDir->NumberOfNames;Idx++) {
+CHAR *funcname=MODULEOBJECT_BASE_OFFSET_PLUS(NameList[Idx]);
+
+ ExportAddressp=(PVOID *)(FunctionList+OrdinalList[Idx]);
+ ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+ if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress) {
+ g_error("%s: Function already patched although we did not touch it yet: %s",G_STRLOC,funcname);
+ g_assert_not_reached();
+ }
+ g_assert(NULL==g_hash_table_lookup(exportdir_hash,funcname));
+ g_hash_table_insert(exportdir_hash,
+ funcname, /* key */
+ ExportAddressp); /* value */
+ }
+
+ /* Patch wished functions and remove them from 'exportdir_hash'. */
+ va_start(ap,FullName_utf8);
+ while (captive_va_arg(sym_name,ap)) {
+ captive_va_arg(sym_val ,ap);
+ g_assert(sym_val!=NULL);
+ captive_va_arg(patchpoint,ap);
+ g_assert(patchpoint!=NULL);
+ ExportAddressp=g_hash_table_lookup(exportdir_hash,sym_name);
+ if (ExportAddressp==NULL) {
+ g_error("%s: Function not found for patchpoint: %s",G_STRLOC,sym_name);
+ g_assert_not_reached();
+ }
+ errbool=g_hash_table_remove(exportdir_hash,sym_name);
+ g_assert(errbool==TRUE);
+ ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+ if (((ULONG)ExportAddress >= (ULONG)ExportDir) &&
+ ((ULONG)ExportAddress < (ULONG)ExportDir + ExportDirSize))
+ g_assert_not_reached(); /* LdrPEFixupForward() needed */
+ patchpoint->orig_w32_func=ExportAddress;
+ g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_func);
+ patchpoint->orig_w32_2ndinstr=patchpoint->orig_w32_func
+ +instruction_length((guint8 *)patchpoint->orig_w32_func);
+ g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_2ndinstr);
+ patchpoint->wrap_wrap_func=sym_val;
+ patchpoint->orig_w32_func_byte=*patchpoint->orig_w32_func;
+ patchpoint->orig_w32_2ndinstr_byte=*patchpoint->orig_w32_2ndinstr;
+ patchpoint->through_w32_func=FALSE;
+ g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_func));
+ g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+ patchpoint->orig_w32_func, /* key */
+ patchpoint); /* value */
+ g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_2ndinstr));
+ g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+ patchpoint->orig_w32_2ndinstr, /* key */
+ patchpoint); /* value */
+ *(guint8 *)ExportAddress=0xF4; /* hlt */
+ *ExportAddressp=(PVOID)MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
+ }
+ va_end(ap);
+
+ /* The remaining entries of 'exportdir_hash' are W32 native functions
+ * unspecified by .captivesym file; we patch them as not-implemented ones.
+ */
+ g_hash_table_foreach(exportdir_hash,
+ (GHFunc)captive_ModuleList_patch_function_disable, /* func */
+ ModuleObject); /* used_data; unused */
+
+ g_hash_table_destroy(exportdir_hash);
+
+#undef MODULEOBJECT_BASE_OFFSET_PLUS /* no longer valid */
+#undef MODULEOBJECT_BASE_OFFSET_MINUS /* no longer valid */
+ return TRUE;
+}
+
+
+struct captive_ModuleList_patchpoint *captive_ModuleList_patchpoint_find(gconstpointer ExportAddress)
+{
+struct captive_ModuleList_patchpoint *r;
+
+ g_return_val_if_fail(ExportAddress!=NULL,NULL);
+
+ captive_ModuleList_patchpoint_hash_init();
+
+ r=g_hash_table_lookup(captive_ModuleList_patchpoint_hash,ExportAddress);
+ g_return_val_if_fail(r!=NULL,NULL);
+ g_assert(r->orig_w32_func==ExportAddress || r->orig_w32_2ndinstr==ExportAddress);
+
+ return r;
+}
+
+
+G_CONST_RETURN gchar *captive_ModuleList_function_disable_find(gconstpointer ExportAddress)
+{
+ g_return_val_if_fail(ExportAddress!=NULL,NULL);
+
+ return g_hash_table_lookup(captive_ModuleList_function_disable_hash,ExportAddress); /* funcname */
+}