hook two or more APIs.... the first one stops after a bit...

Sep 29, 2008 at 4:58 PM
Hello,
Thank you cris for developing easyhook... i'm learning a lot from yor code.
My problem is that I'm extending the FileMon project and ovveriding 2 Win API calls, and not just one like FileMon does.

The code is almost the same but I'm trying to install 2 hooks:
  • WriteFile from Kernel32
  • DrawText from User32
Here is the code:

 public void Run(RemoteHooking.IContext InContext, String InChannelName)
        {
            // install hook...
            try
            {
                    CreateFileHook = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "WriteFile"), new D_WriteFile(WriteFile_Hooked), this);
                    CreateFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
                    CreateFileHook = LocalHook.Create(LocalHook.GetProcAddress("User32.dll", "DrawTextW"), new D_DrawText(DrawText_Hooked), this);
                     CreateFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
            }
      .......
      }

Everything works fine, except that the the first  hook (WriteFile_Hooked) stops beeing called a little bit later.... without apparently reason.
If i use just one of them (WriteFile_Hooked or DrawText_Hooked), everything works fine, but when i try to use them both, the first one, always stops after a while.

what am i doing wrong?
Thanks.




Sep 29, 2008 at 6:32 PM
EasyHook is taking advantage of the garbage collector. You are overwriting the first hook by the time you set the second one. This way the first hook is being GCed after an undefined period of time...

You need to keep track of every hook handle separately as long as you want it to stay installed...

regards
chris
Sep 30, 2008 at 2:54 PM
Hello again Cristoph,

As you told me, I have splited the assembly and created 2 different dll to inject into a process:
  • a first one which hooks the WriteFile API
  • the second one hooks DrawText API
Those assemblies works well if they are injected one by one but when i try to inject them both using:


// All assemblies are correctly installed into GAC
Config.Register("FusionBot Dll neccessarie", newStringArray);

//The Remote Server is created correctly and a random ChannelName is created
System.Runtime.Remoting.Channels.Ipc.IpcServerChannel srvChan = EasyHook.RemoteHooking.IpcCreateServer(ref ChannelName, WellKnownObjectMode.SingleCall);

//works!
EasyHook.RemoteHooking.Inject(TargetPID, "myHookDrawText.dll", "myHookDrawText.dll", ChannelName);

//fails
EasyHook.RemoteHooking.Inject(TargetPID, "Stella2.dll", "Stella2.dll", ChannelName);


the second one fails raising this exceprion:

Unknown error code (-1073741502): The user defined managed entry point failed in the target process. Make sure that EasyHook is registered in the GAC. Refer to event logs for more information. (Code: 13)

and the Event Viewer displays:

[error]: System.Runtime.Remoting.RemotingException: An error occurred while processing the request on the server: System.Runtime.Remoting.RemotingException: Expecting tcp chunked or content-length header: 43776
at System.Runtime.Remoting.Channels.Tcp.TcpSocketHandler.ReadContentLength(Boolean& chunked, Int32& contentLength)
at System.Runtime.Remoting.Channels.Ipc.IpcServerHandler.ReadHeaders()
at System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(Object state)
at System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()

Server stack trace:
at System.Runtime.Remoting.Channels.Ipc.IpcServerHandler.ReadToEndOfHeaders(BaseTransportHeaders headers)
at System.Runtime.Remoting.Channels.Ipc.IpcClientHandler.ReadHeaders()
at System.Runtime.Remoting.Channels.Ipc.IpcClientTransportSink.ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, ITransportHeaders& responseHeaders, Stream& responseStream)
at System.Runtime.Remoting.Channels.BinaryClientFormatterSink.SyncProcessMessage(IMessage msg)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at EasyHook.HelperServiceInterface.Ping()
at EasyHook.InjectionLoader.Main(String InParam)


I understand that the problem is releated with remoting, but even creating a new Remote Server ( IpcCreateServer ) for each injected assembly gives me the same exception...

I have googled a while but i didn' t find a solution for this exception....

I see that everytime you inject an assembly using EasyHook.RemoteHooking.Inject you create an another Remote Server (in EasyHook.PrepareInjection() ) with a random ChannelName, and you pass to the IntPtr pointer of the RemoteInfo with the new random ChannelName to:

[DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet=CharSet.Unicode)]
public static extern Int32 RhInjectLibrary(Int32 InTargetPID,Int32 InWakeUpTID,Int32 InInjectionOptions,String InLibraryPath_x86,String InLibraryPath_x64,IntPtr InPassThruBuffer,Int32 InPassThruSize);

Why a new Remote Server is needed?
Can you please tell me if I'm getting close to the solution of my problem or i didn't understand anything (like i suppose... :( )

I really have no idea on how can i keep track of every hook handle separately.
Thank you very much Cristoph




Oct 11, 2008 at 12:27 PM
i have tried many clean ways to solve this problem, but none of them worked: even creating a new ipc server for each injected dll i had strange issues.
anyway... here is the dirty way:

keep a refernce to each LocalHook object for injecting more then one api:

in the fields section:

LocalHook CreateFileHook;
LocalHook CreateFileHook2;
List lList;


public void Run(RemoteHooking.IContext InContext, String InChannelName)
{
// install hook...
try
{
//first reference
CreateFileHook = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "WriteFile"), new D_WriteFile(WriteFile_Hooked), this);
CreateFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });

// second reference..
CreateFileHook2 = LocalHook.Create(LocalHook.GetProcAddress("User32.dll", "DrawTextW"), new D_DrawText(DrawText_Hooked), this);
CreateFileHook2.ThreadACL.SetExclusiveACL(new Int32[] { 0 });


lList = new List();
lList.Add(CreateFileHook);
lList.Add(CreateFileHook2);

}
catch (Exception ExtInfo)
{
Interface.ReportException(ExtInfo);
return;
}

.........


}


i know it's not the right way.... but if some got a better snippet, i'll be greatfull if he posts it.
anyway, thx cristoph for developing easyhook.





Oct 11, 2008 at 6:10 PM
Seperate remote servers are created to isolate different injections. But maybe there is another way and possibly one would be enough. Just try it...

The error truely seems to be related to remoting which I don't know well ^^. I was happy to get it working... So maybe someone with more experiences in remoting could improve this connection style and get it working... I never tried to inject two libraries simultaneously...

To put the handles into a list is quite a way of doing what I told you. It is just the anonymous way if you don't need to access the handles later on, your solution seems to be the best...  But of course you could use a temp var like "LocalHook Tmp = LocalHook.Create(); GlobalList.Add(Tmp); Tmp = LocalHook.Create(); GlobalList.Add(Tmp);" The next thing is that you might type the list like "List<LocalHook> GlobalList = new List<LocalHook>()". But an untyped list works as well if you don't need convenient access to its entries...


But I don't understand why you inject two different libraries at all?! Just put them together... Even if you don't have the source code there is a tool out there which combines two NET assemblies...

regards
chris