I'm a first traveler coding in WinApi, Hooks, COM objects. Then, I made a dll to inject (EasyHook) this in the target SIEMENS Step7 process and modify the software code at runtime:
Maybe I want some opinion about this kind of practices, hooking, injection, file system driver, kernel hook, modify .exe vtable before init, interprocess-communication, etc.
This code gets the DataSource
from a Grid GUI(COM Object) when the user do a determined "Force" action through menu.
using EasyHook;
using Octtos.S7ussapx.Injected.COM.Interfaces;
using Octtos.S7ussapx.Injected.Step7.COM.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace Octtos.S7ussapx.Injected
{
public class Main : IEntryPoint
{
private ClientCallbackHandler _callbackHandler;
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
unsafe delegate int IDispatchInvokeDelegate(IntPtr pthis, int dispIdMember, ref Guid riid, uint lcid, ushort wFlags,
ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, void* ppvResult,
ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, IntPtr[] pArgErr);
#region CoGetClassObject ole32.dll
private LocalHook _coGetClassObjectHook;
private LocalHook _S7StatusOCXIClassFacttoryCreateInstanceHook;
private LocalHook _S7VarServer50IClassFacttoryCreateInstanceHook2;
private bool _s7StatusOCXCOMHookCreated = false;
private bool _s7VarServer50COMHookCreated = false;
private IntPtr _S7StatusOCXIClassFacttoryCreateInstanceMethodPtr;
private IntPtr _S7VarServer50IClassFacttoryCreateInstanceMethodPtr;
private delegate uint CoGetClassObjectDelegate(ref Guid rclsid, int dwClsContext, IntPtr pServerInfo, ref Guid riid, out IntPtr ppv);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
private delegate int IClassFactoryCreateInstanceDelegate(IntPtr pThis, IntPtr pUnkOuter, ref Guid riid, out IntPtr ppv);
private static unsafe uint CoGetClassObject_Hook(ref Guid clsid, int dwClsContext, IntPtr pServerInfo, ref Guid iid, out IntPtr ppv)
{
var me = (Main)HookRuntimeInfo.Callback;
var classObjectInstance = CoGetClassObject(ref clsid, dwClsContext, pServerInfo, ref iid);
ppv = Marshal.GetIUnknownForObject(classObjectInstance);
var iclassFactory = classObjectInstance as IClassFactory;
if (!me._s7StatusOCXCOMHookCreated)
{
if (iclassFactory != null && clsid == typeof(IS7StatusOcx).GUID)
{
var method = typeof(IClassFactory).GetMethod("CreateInstance");
var methodVTableIdx = Marshal.GetComSlotForMethodInfo(method);
int** comVTable = *(int***)ppv.ToPointer();
int* hMethod = comVTable[methodVTableIdx];
me._S7StatusOCXIClassFacttoryCreateInstanceMethodPtr = new IntPtr(hMethod);
me._S7StatusOCXIClassFacttoryCreateInstanceHook = LocalHook.Create(me._S7StatusOCXIClassFacttoryCreateInstanceMethodPtr, new IClassFactoryCreateInstanceDelegate(S7StatusOCXCreateInstance_Hook), me);
me._S7StatusOCXIClassFacttoryCreateInstanceHook.ThreadACL.SetExclusiveACL(new int[0]);
me._s7StatusOCXCOMHookCreated = true;
}
}
if (!me._s7VarServer50COMHookCreated)
{
if (iclassFactory != null && clsid == typeof(IS7VarServer50).GUID)
{
var method = typeof(IClassFactory).GetMethod("CreateInstance");
var methodVTableIdx = Marshal.GetComSlotForMethodInfo(method);
int** comVTable = *(int***)ppv.ToPointer();
int* hMethod = comVTable[methodVTableIdx];
me._S7VarServer50IClassFacttoryCreateInstanceMethodPtr = new IntPtr(hMethod);
me._S7VarServer50IClassFacttoryCreateInstanceHook2 = LocalHook.Create(me._S7VarServer50IClassFacttoryCreateInstanceMethodPtr, new IClassFactoryCreateInstanceDelegate(S7VarServer50CreateInstance_Hook), me);
me._S7VarServer50IClassFacttoryCreateInstanceHook2.ThreadACL.SetExclusiveACL(new int[0]);
me._s7VarServer50COMHookCreated = true;
}
}
Marshal.ReleaseComObject(classObjectInstance);
return 0;
}
#endregion
#region S7StatusOCX IClassFactory.Create ole32.dll
private IS7StatusOcxDispatch _currentInstance;
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
private delegate int IClassFactoryCreateInstanceDelegate2(IntPtr pThis, IntPtr pUnkOuter, ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object obj);
private static unsafe int S7StatusOCXCreateInstance_Hook(IntPtr pThis, IntPtr pUnkOuter, ref Guid riid, out IntPtr ppv)
{
var me = (Main)HookRuntimeInfo.Callback;
var baseMethod = (IClassFactoryCreateInstanceDelegate2)Marshal.GetDelegateForFunctionPointer(me._S7StatusOCXIClassFacttoryCreateInstanceMethodPtr, typeof(IClassFactoryCreateInstanceDelegate2));
object objInstance;
var hr = baseMethod(pThis, pUnkOuter, ref riid, out objInstance);
me._currentInstance = (IS7StatusOcxDispatch)objInstance;
var ppv2 = Marshal.GetIUnknownForObject(objInstance);
Marshal.QueryInterface(ppv2, ref riid, out ppv);
return hr;
}
#endregion
#region S7VarServer50 IClassFactory.Create ole32.dll
private LocalHook _s7VarServer5IDispatchHook;
private IntPtr _s7VarServer50IDispatchInvokeMethodPtr;
private bool _S7VarServer50CreateInstanceHookCreated = false;
private IDictionary<IntPtr, IS7StatusOcxDispatch> _s7StatusOCXDispatchInstances = new Dictionary<IntPtr, IS7StatusOcxDispatch>();
private static unsafe int S7VarServer50CreateInstance_Hook(IntPtr pThis, IntPtr pUnkOuter, ref Guid riid, out IntPtr ppv)
{
var me = (Main)HookRuntimeInfo.Callback;
var baseMethod = (IClassFactoryCreateInstanceDelegate2)Marshal.GetDelegateForFunctionPointer(me._S7VarServer50IClassFacttoryCreateInstanceMethodPtr, typeof(IClassFactoryCreateInstanceDelegate2));
object objInstance;
var hr = baseMethod(pThis, pUnkOuter, ref riid, out objInstance);
var ppv2 = Marshal.GetIUnknownForObject(objInstance);
Marshal.QueryInterface(ppv2, ref riid, out ppv);
if (objInstance is IS7VarServer50Dispatch)
{
Guid iidIDispatch = typeof(IDispatch).GUID;
IntPtr ppv3;
Marshal.QueryInterface(ppv2, ref iidIDispatch, out ppv3);
if (me._s7StatusOCXDispatchInstances.ContainsKey(ppv3))
{
me._s7StatusOCXDispatchInstances[ppv3] = me._currentInstance;
}
else {
me._s7StatusOCXDispatchInstances.Add(ppv3, me._currentInstance);
}
me._currentInstance = null;
if (!me._S7VarServer50CreateInstanceHookCreated)
{
var methodVTableIdx = Marshal.GetComSlotForMethodInfo(typeof(IDispatch).GetMethod("Invoke"));
int** comVTable = *(int***)ppv3.ToPointer();
int* hMethod = comVTable[methodVTableIdx];
me._s7VarServer50IDispatchInvokeMethodPtr = new IntPtr(hMethod);
me._s7VarServer5IDispatchHook = LocalHook.Create(me._s7VarServer50IDispatchInvokeMethodPtr, new IDispatchInvokeDelegate(S7VarServer50IDispatch_Invoke_Hook), me);
me._s7VarServer5IDispatchHook.ThreadACL.SetExclusiveACL(new int[0]);
me._S7VarServer50CreateInstanceHookCreated = true;
}
}
return hr;
}
//Por que esse IDispatch tem IntPtr pThis como primeiro parâmetro? Se na DOC o primeiro parâmetro dessa função é o dispIdMember?!
//http://msdn.microsoft.com/en-us/library/aa912367.aspx
unsafe static int S7VarServer50IDispatch_Invoke_Hook(IntPtr pthis, int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, void* ppvResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, IntPtr[] pArgErr)
{
var me = (Main)HookRuntimeInfo.Callback;
switch (dispIdMember)
{
case 14:
me._callbackHandler.WriteLine("CreateForceJob() called");
break;
case 15:
me._callbackHandler.WriteLine("StartForceJob() called");
IS7StatusOcxDispatch instance;
if (me._s7StatusOCXDispatchInstances.TryGetValue(pthis, out instance))
{
string varList;
instance.GetVariableList(out varList, false);
var bytes = Encoding.Unicode.GetBytes(varList);
var str = Encoding.Default.GetString(bytes);
me._callbackHandler.WriteLine(str);
}
else
{
me._callbackHandler.WriteLine("ERROR!!! Tentou pegar uma instancia que não existia. Por que essa instância não existe?");
}
break;
default:
break;
}
var baseMethod = (IDispatchInvokeDelegate)Marshal.GetDelegateForFunctionPointer(me._s7VarServer50IDispatchInvokeMethodPtr, typeof(IDispatchInvokeDelegate));
return baseMethod(pthis, dispIdMember, ref riid, lcid, wFlags, ref pDispParams, ppvResult, ref pExcepInfo, pArgErr);
}
#endregion
public Main(RemoteHooking.IContext ctx, string channelId)
{
_callbackHandler = RemoteHooking.IpcConnectClient<ClientCallbackHandler>(channelId);
}
public unsafe void Run(RemoteHooking.IContext ctx, string arg1)
{
try
{
_coGetClassObjectHook = LocalHook.Create(LocalHook.GetProcAddress("ole32.dll", "CoGetClassObject"),
new CoGetClassObjectDelegate(CoGetClassObject_Hook), this);
_coGetClassObjectHook.ThreadACL.SetExclusiveACL(new int[0]);
RemoteHooking.WakeUpProcess();
_callbackHandler.InjectStart();
while (true)
{
_callbackHandler.SetIsAlive(true);
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
catch (Exception ex)
{
throw;
}
}
[DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
private static extern object CoGetClassObject(ref Guid rclsid, int dwClsContext, IntPtr pServerInfo, ref Guid riid);
}
}