我正在开发一个应用程序,它使用 EasyHook 库将代码注入所需的进程并拦截来自特定 dll 的调用。在我的例子中,库是 Oracle Call Interface,OCI.dll。我想拦截执行的 sql 语句,以便在客户端创建 sql 查询日志。以前我用过微软的detours(2.1版),但是它的license不允许商业使用,3.0版的成本很高。我开始使用 EasyHook 库。我在交付的示例中更改了从 kernel32.dll 拦截函数 CreateFileW 的代码,并将其调整为与 oci.dll 中的函数 OCIStmtFetch2 一起使用。
我有头文件或 oci 库,我确切地知道函数参数和返回类型。根据头文件签名是:
剑 OCIStmtFetch2 ( OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2orientation, ub4 scrollOffset, ub4 mode);
根据 Oracle 提供的其他头文件,OCIStmt 是一个结构,而 OCIError 是错误函数的句柄。ub2 和 ub4 是 unsigned short(16 位)和 unsigned int(32 位)的类型定义。Sword 是 typedef 到有符号整数(也是 32 位) 我的 EasyHook 库注入代码如下所示(一些函数名称与示例 FileMonInject 相同):
using System;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;
using EasyHook;
namespace FileMonInject
{
public class Main : EasyHook.IEntryPoint
{
FileMon.FileMonInterface Interface;
LocalHook CreateFileHook;
Stack<String> Queue = new Stack<String>();
public Main(
RemoteHooking.IContext InContext,
String InChannelName)
{
// connect to host...
Interface = RemoteHooking.IpcConnectClient<FileMon.FileMonInterface>(InChannelName);
Interface.Ping();
}
unsafe public void Run(
RemoteHooking.IContext InContext,
String InChannelName)
{
// install hook...
try
{
CreateFileHook = LocalHook.Create(
LocalHook.GetProcAddress("oci.dll", "OCIStmtFetch2"),
new DOCIStmtFetch2(DOCIStmtFetch2_Hooked),
this);
CreateFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}
catch (Exception ExtInfo)
{
Interface.ReportException(ExtInfo);
return;
}
Interface.IsInstalled(RemoteHooking.GetCurrentProcessId());
RemoteHooking.WakeUpProcess();
// wait for host process termination...
try
{
while (true)
{
Thread.Sleep(500);
// transmit newly monitored file accesses...
if (Queue.Count > 0)
{
String[] Package = null;
lock (Queue)
{
Package = Queue.ToArray();
Queue.Clear();
}
Interface.OnOCIStmtFetch2(RemoteHooking.GetCurrentProcessId(), Package);
}
else
Interface.Ping();
}
}
catch
{
}
}
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Ansi,
SetLastError = true)]
unsafe delegate int DOCIStmtFetch2(
void* stmtp,
void* errhp,
UInt32 nrows,
UInt16 orientation,
UInt32 scroll,
UInt32 mode);
// just use a P-Invoke implementation to get native API access from C# (this step is not necessary for C++.NET)
[DllImport("oci.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
// [return: MarshalAs(UnmanagedType.I4)]
unsafe static extern Int32 OCIStmtFetch2(
void* stmtp,
void* errhp,
UInt32 nrows,
UInt16 orientation,
UInt32 scroll,
UInt32 mode);
// this is where we are intercepting all file accesses!
unsafe static Int32 DOCIStmtFetch2_Hooked(
void* stmtp,
void* errhp,
UInt32 nrows,
UInt16 orientation,
UInt32 scroll,
UInt32 mode)
{
try
{
Main This = (Main)HookRuntimeInfo.Callback;
This.Queue.Push("[" + RemoteHooking.GetCurrentProcessId() + ":" +
RemoteHooking.GetCurrentThreadId() + "]: \"" + nrows + "\"");
}
catch (Exception ee)
{
}
// call original API...
int E = OCIStmtFetch2(
stmtp,
errhp,
nrows,
orientation,
scroll,
mode);
return E;
}
}
}
如您所见,我将 ub4 与 UInt32 映射,将 ub2 与 UInt16 映射,将剑与 Int32 映射。第一次我使用 IntPtr 作为指针(两个第一个参数),但代码不能正常工作。注入的dll完美地拦截了函数调用,我可以在原始函数之前运行我的代码,我可以调用原始函数并返回预期值,但是当执行return E时,目标应用程序会导致内存冲突异常并退出。正如您在代码中看到的那样,然后我尝试使用 void* 指针和 unsafe 关键字来启用在 C# 中使用指针,结果相同。与我使用 Detours 库的代码相比,我可以使用调试器检查的参数和指针值对于两个库都是相同的,因此类型映射看起来不错。然而,当我从 DOCIStmtFetch2_Hooked 返回时,代码会中断。
有谁知道可能出了什么问题?即使我认为类型映射没问题,我也将错误归咎于他们。
问候。
我删除了锁定部分以缩短源。无论我是否锁定队列,问题仍然存在