easyhook

EasyHook DrawTextW user32.dll Injection, Application crashing


I'm using EasyHook too hook calls to DrawTextW, while testing with notepad, if I open Help -> About, It captures all the text that appears on the screen as expected. However, if I open File -> Open, Notepad crashes. I don't expect it to capture any text, but I can't understand why notepad is crashing. Any help would be appreciated.

using EasyHook;
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using UI;

namespace MyClassLibrary
{
    public class Main : IEntryPoint
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct HDC__
        {
            public int unused;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct tagRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [DllImport("user32.dll", EntryPoint = "DrawTextW")]
        public static extern int DrawTextW([In()] IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpchText, int cchText, ref tagRECT lprc, uint format);


        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
        public delegate int TDrawTextW(
          [In()] IntPtr hdc,
          [MarshalAs(UnmanagedType.LPWStr)]
          StringBuilder lpchText,
          int cchText,
          ref tagRECT lprc,
          uint format);


        static string ChannelName;
        RemoteMon Interface;

        LocalHook DrawTextWHook;
        public Main(RemoteHooking.IContext InContext, String InChannelName)
        {
            try
            {
                Interface = RemoteHooking.IpcConnectClient<RemoteMon>(InChannelName);
                ChannelName = InChannelName;
                Interface.IsInstalled(RemoteHooking.GetCurrentProcessId());
            }
            catch (Exception ex)
            {
                Interface.ErrorHandler(ex);
            }
        }

        static int hkDrawTextW(
                  [In()] IntPtr hdc,
                  [MarshalAs(UnmanagedType.LPWStr)]
                  StringBuilder lpchText,
                  int cchText,
                  ref tagRECT lprc,
                  uint format)
        {
            try
            {
                ((Main)HookRuntimeInfo.Callback).Interface.GotDrawTextW(lpchText);
                return DrawTextW(hdc, lpchText, cchText, ref lprc, format);
            }
            catch (Exception ex)
            {
                ((Main)HookRuntimeInfo.Callback).Interface.ErrorHandler(ex);
                return 0;
            }
        }

        public void Run(RemoteHooking.IContext InContext, String InChannelName)
        {
            try
            {
                DrawTextWHook = LocalHook.Create(LocalHook.GetProcAddress("user32.dll", "DrawTextW"),
                                                new TDrawTextW(hkDrawTextW), this);
                DrawTextWHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
            }
            catch (Exception ex)
            {
                Interface.ErrorHandler(ex);
            }
            try
            {
                RemoteHooking.WakeUpProcess();
            }
            catch (Exception ex)
            {
                Interface.ErrorHandler(ex);
            }
            while (true)
            {
                Thread.Sleep(10000);
            }
        }
    }
}

Solution

  • There is a problem with marshalling the text to a StringBuilder when the Open dialog shows. I'm not 100% sure of the reason, but perhaps the difference between a COM initialised string and one initialised with malloc? I have experienced marshalling issues with strings in the past depending on how their underlying memory has been initialised.

    The solution is to use a String instead of a StringBuilder for the text parameter. Replace the extern and the delegate and other appropriate areas with a string parameter:

        [DllImport("user32.dll", EntryPoint = "DrawTextW")]
        public static extern int DrawTextW([In()] IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] String lpchText, int cchText, ref tagRECT lprc, uint format);
    
        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
        public delegate int TDrawTextW(
          [In()] IntPtr hdc,
          [MarshalAs(UnmanagedType.LPWStr)]
          String lpchText,
          int cchText,
          ref tagRECT lprc,
          uint format);
    
        static int hkDrawTextW(
                  [In()] IntPtr hdc,
                  [MarshalAs(UnmanagedType.LPWStr)]
                  String lpchText,
                  int cchText,
                  ref tagRECT lprc,
                  uint format)
        {
          ...
        }