Marshaling unmanaged pointer from hooked function to managed object and calling its member functions using managed wrapper class

1

I have hooked QtGui4.dll's unmanaged function QPainter::drawTextItem(const QPointF &p, const QTextItem &ti); using EasyHook in C#. I have wrapped the QPointF and QTextItem classes into managed structs and can get the data from these parameters by marshaling them. However, I would like to call member functions of the unmanaged QPainter class using managed code, so I am trying to wrap the QPainter class into a managed class. I have access to the unmanaged code for testing purposes, but will not have access to it in production. I have looked at the memory layout of the unmanaged classes QPainter and QPaintDevice. The QPainter class is just a single pointer and the QPaintDevice class is just two pointers (one for the virtual function table). I tried following the techniques in this blog, but when I call the wrapped QPainter::device() method just to see if I can get a pointer, my target (unmanaged, hooked) application crashes with either an access violation error (Access violation reading location 0x00000078) or an argument exception error (EEArgumentException at memory location 0x003786f4), depending on whether my wrapped QPainter class uses a sequential layout or not, respectively. I don’t expect someone to correct all the source code, but I was hoping if someone could point out some of the mistakes I am making in my approach. By the way, this is the first time I've ever posted a question on Stack Overflow because I usually find the answers to my problems from one or more existing questions. I apologize for the long question, but I tried to simplify the problem as much as possible without leaving out potentially relevant information. Thanks in advance for your help.

Memory Dump:

class QPainter (size = 4):
  (0) d_ptr

class QPaintDevice (size = 8):
  (0) {vfptr}
  (4) painters

Unmanaged Code (qpainter.h):

class QPainter
{
    QPainter();
    explicit QPainter(QPaintDevice *);
    ~QPainter();

    QPaintDevice *device() const; // trying to call this function

    void drawTextItem(const QPointF &p, const QTextItem &ti); // hooked function

    // class has additional functions, but left out for brevity
}

Managed Hook (hook.cs):

[DllImport("QtGui4.dll", CharSet = CharSet.Unicode, SetLastError = true,
    CallingConvention = CallingConvention.ThisCall,
    EntryPoint = "?drawTextItem@QPainter@@QAEXABVQPointF@@ABVQTextItem@@@Z")]
public static extern void QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);

[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode,
    SetLastError = true)]
delegate void D_QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);

static unsafe void QPainter_drawTextItem_Hooked(IntPtr obj, IntPtr pt, IntPtr ti)
{
    QTextItem qti = (QTextItem)Marshal.PtrToStructure(ti, typeof(QTextItem));
    QPointF qpt = (QPointF)Marshal.PtrToStructure(pt, typeof(QPointF));
    QPainter painter = (QPainter)Marshal.PtrToStructure(obj, typeof(QPainter));

    __QPaintDevice* pd = painter.device(); // ERROR

    QPainter_drawTextItem(obj, pt, ti);
}

Managed Wrapper (wrap.cs):

[StructLayout(LayoutKind.Sequential, Size=8)]
public unsafe struct __QPaintDevice
{
    public IntPtr *vfptr;
    public IntPtr painters;
};

[StructLayout(LayoutKind.Sequential, Size=4)]
public unsafe struct __QPainter
{
    public IntPtr d_ptr;
};

// When the following line is included, I get this error:
//     Access violation reading location 0x00000078.
// When the following line is excluded, I get this error:
//     EEArgumentException at memory location 0x003786f4.

[StructLayout(LayoutKind.Sequential)] // is this useful?
public unsafe class QPainter : IDisposable
{
    private __QPainter* _this;
    private __QPaintDevice* _pd;

    [DllImport("QtGui4.dll", EntryPoint = "??0QPainter@@QAE@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Constructor(__QPainter* ths);

    [DllImport("QtGui4.dll",
        EntryPoint = "??0QPainter@@QAE@PAVQPaintDevice@@@Z",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Constructor(__QPainter* ths,
        __QPaintDevice* pd);

    [DllImport("QtGui4.dll", EntryPoint = "??1QPainter@@QAE@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern int _QPainter_Destructor(__QPainter* ths);

    [DllImport("QtGui4.dll",
        EntryPoint = "?device@QPainter@@QBEPAVQPaintDevice@@XZ",
        CallingConvention = CallingConvention.ThisCall)]
    private static extern __QPaintDevice* _device(__QPainter* ths);

    public QPainter()
    {
        _this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
        _QPainter_Constructor(_this);
    }

    public QPainter(__QPaintDevice* pd)
    {
        _this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
        _pd = (__QPaintDevice*)Marshal.AllocHGlobal(sizeof(__QPaintDevice));
        _QPainter_Constructor(_this, pd);
    }

    public void Dispose()
    {
        _QPainter_Destructor(_this);
        Marshal.FreeHGlobal((IntPtr)_this);
        Marshal.FreeHGlobal((IntPtr)_pd);
        _this = null;
        _pd = null;
    }

    public __QPaintDevice* device()
    {
        return _device(_this);
    }
};
c#
c++
hook
marshalling
wrapper
asked on Stack Overflow Jan 14, 2014 by donk • edited Jan 14, 2014 by donk

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0