EasyHook с оригинальным вызовом функции
Я занимаюсь разработкой приложения, которое использует библиотеку EasyHook для внедрения кода в нужный процесс и перехвата вызовов из определенной библиотеки DLL. В моем случае это библиотека Oracle Call Interface, OCI.dll. Я хочу перехватить выполненные операторы SQL для создания журналов SQL запросов на стороне клиента. Раньше я пользовался обходными путями Microsoft (версия 2.1), но лицензия не разрешает коммерческое использование, а версия 3.0 стоит дорого. Я начал использовать библиотеку EasyHook. Я изменил код в приведенном примере, который перехватывает функцию CreateFileW из kernel32.dll и настроил ее для работы с функцией OCIStmtFetch2 в oci.dll.
У меня есть заголовочные файлы или библиотека oci, но я точно знаю параметры функции и тип возвращаемого значения. Согласно заголовочному файлу подпись:
меч OCIStmtFetch2 ( OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ориентация ub2, ub4 scrollOffset, режим ub4);
Согласно другим заголовочным файлам, поставляемым из Oracle, OCIStmt является структурой, а OCIError - дескриптором ошибки. ub2 и ub4 являются typedefs для unsigned short (16 бит) и unsigned int (32 бит). Sword - это typedef to sign int (также 32-битный). Мой код для библиотеки Injected by 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 перехватил вызов функции отлично, я могу запустить свой код перед исходной функцией, я могу вызвать исходную функцию, и она возвращает ожидаемое значение, но при выполнении возврата E целевые приложения вызывают исключение нарушения памяти и выходят. Как вы можете видеть в коде, я попытался использовать указатели void* и ключевое слово unsafe, чтобы разрешить работу с указателями в C# с тем же результатом. По сравнению с моим кодом с использованием библиотеки Detours, параметры и значения указателей, которые я могу проверить с помощью отладчика, одинаковы для обеих библиотек, поэтому сопоставление типов выглядит хорошо. Тем не менее код ломается, когда я возвращаюсь из моего DOCIStmtFetch2_Hooked.
Кто-нибудь знает, что может быть не так? Даже я думаю, что сопоставления типов в порядке, я виню их за ошибку.
С уважением.
Я удалил часть замка, чтобы сократить источник. Независимо от того, заблокирую я очередь или нет, проблема остается
2 ответа
Oracle(oci.dll) использует соглашение о вызовах "Cdecl", вы используете StdCall. Попробуйте изменить на "CallingConvention = CallingConvention.Cdecl"
Вы забыли заблокировать This.Queue в своем хуке, но я не уверен, решит ли это эту проблему.