Hook gdi32.dll [all process]

May 26, 2009 at 11:37 PM

Hi ppl.

I started to use EasyHook today. I made my first app and its working great! But, i need a little help.

I created a DLL (PrintMonInject.dll) to hook the CreateDCA method from gdi32:

 CreateDCAHook = LocalHook.Create(
                    LocalHook.GetProcAddress("gdi32.dll", "CreateDCA"),
                    new DCCreateDCA(CreateDCA_Hooked),
                    this);

But only works if I use this method inside my application:

 RemoteHooking.Inject(
                    PID,
                    "PrintMonInject.dll", // 32-bit version (the same because AnyCPU)
                    "PrintMonInject.dll", // 64-bit version (the same because AnyCPU)
                    // the optional parameter list...
                    ChannelName);

I need to pass the PID to make a reference to a process.
How could I make to use this Inject to all process? Something like  RemoteHooking.Inject(ALL_PROCESS,...) :D

I'm asking because an user can print from a lot of applications, and 99% uses CreateDCA, CreateDCW, StartDocA, EndDoc, StartPage, EndPage from GDI32.dll.

tks in advance.

 

May 27, 2009 at 12:11 AM
> I need to pass the PID to make a reference to a process.
> How could I make to use this Inject to all process? Something like
> RemoteHooking.Inject(ALL_PROCESS,...) :D

Have you tried just hooking all PIDs and monitoring new process
creation? If that is a feasible solution for you, it's probably the
easiest. If that doesn't work for you, more information as to why not
(performance, access restrictions?) would help.

Ben Schwehn
May 27, 2009 at 1:28 AM

Its a good solution, I tried and I found a big problem.
For example, if I hook AcrobatReader CreateDCA API, the application crashes :(
I need to find how to fix it or find a 'master api' in spoolsv.exe that all programs need to call to print.

 

tks Ben!

 

May 27, 2009 at 10:34 AM
> For example, if I hook AcrobatReader CreateDCA API, the application
> crashes :(


Can you post your code?
May 27, 2009 at 12:20 PM

Yes. I use the EasyHook solution. I added 2 new projects, PrinMonInject and PrintMonitor. Its a copy of ProcessMonitor example but i made some changes.

here we go,

PrinMonInject -> Main.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using EasyHook;
using System.Windows.Forms;


namespace PrintMonitor
{
    public struct DEVMODE // i need because CreateDCA structure
    {

        public const int CCHDEVICENAME = 32;
        public const int CCHFORMNAME = 32;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;

        public short dmOrientation;
        public short dmPaperSize;
        public short dmPaperLength;
        public short dmPaperWidth;

        public short dmScale;
        public short dmCopies;
        public short dmDefaultSource;
        public short dmPrintQuality;
        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
        public string dmFormName;
        public short dmLogPixels;
        public int dmBitsPerPel;    // Declared wrong in the full framework
        public int dmPelsWidth;
        public int dmPelsHeight;
        public int dmDisplayFlags;
        public int dmDisplayFrequency;

        public int dmICMMethod;
        public int dmICMIntent;
        public int dmMediaType;
        public int dmDitherType;
        public int dmReserved1;
        public int dmReserved2;
        public int dmPanningWidth;
        public int dmPanningHeight;

        public int dmPositionX; // Using a PointL Struct does not work
        public int dmPositionY;
    }


    public class DemoInjection : EasyHook.IEntryPoint
    {
        public DemoInterface Interface = null;
        public LocalHook CreateDCAHook = null;

        Stack<String> Queue = new Stack<string>();

        public DemoInjection(
            RemoteHooking.IContext InContext,
            String InChannelName)
        {
            Interface = RemoteHooking.IpcConnectClient<DemoInterface>(InChannelName);

            Interface.Ping(RemoteHooking.GetCurrentProcessId());
        }

        public void Run(
            RemoteHooking.IContext InContext,
            String InArg1)
        {
            try
            {
                CreateDCAHook = LocalHook.Create(
                    LocalHook.GetProcAddress("gdi32.dll", "CreateDCA"),
                    new DCCreateDCA(CreateDCA_Hooked),
                    this);

                /*
                 * Don't forget that all hooks will start deaktivated...
                 * The following ensures that all threads are intercepted:
                 */
                CreateDCAHook.ThreadACL.SetExclusiveACL(new Int32[1]);
            }
            catch (Exception e)
            {
                /*
                    Now we should notice our host process about this error...
                 */
                Interface.ReportError(RemoteHooking.GetCurrentProcessId(), e);

                return;
            }


            // wait for host process termination...
            try
            {
                while (Interface.Ping(RemoteHooking.GetCurrentProcessId()))
                {
                    Thread.Sleep(500);

                    // transmit newly monitored file accesses...
                    lock (Queue)
                    {
                        if (Queue.Count > 0)
                        {
                            String[] Package = null;
                            Package = Queue.ToArray();

                            Queue.Clear();

                            Interface.OnCreateFile(RemoteHooking.GetCurrentProcessId(), Package);
                        }
                    }
                }
            }
            catch
            {
                // NET Remoting will raise an exception if host is unreachable
            }
        }

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
        delegate IntPtr DCCreateDCA(
            String InDriver,
            String InDevice,
            String InOutput,
            DEVMODE InInitData);

        delegate void DCCreateDCAAsync(
            String InDriver,
            String InDevice,
            String InOutput,
            DEVMODE InInitData);

        // just use a P-Invoke implementation to get native API access from C# (this step is not necessary for C++.NET)
        [DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        static extern IntPtr CreateDCA(
            String InDriver,
            String InDevice,
            String InOutput,
            DEVMODE InInitData);

        // this is where we are intercepting all file accesses!
        // "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String,
        //              ByVal lpOutput As String, lpInitData As DEVMODE) As Long
        static IntPtr CreateDCA_Hooked(
            String InDriver,
            String InDevice,
            String InOutput,
            DEVMODE InInitData)
        {
            try
            {
                DemoInjection This = (DemoInjection)HookRuntimeInfo.Callback;
               
                lock (This.Queue)
                {
                    if (This.Queue.Count < 1000)
                        This.Queue.Push(InDevice);
                }
            }
            catch
            {
                This.Queue.Push("ops, erro")
            }

            // call original API...
            return CreateDCA(
                InDriver,
                InDevice,
                InOutput,
                InInitData);
        }
    }
}

 


 

PrintMonitor -> Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using EasyHook;

namespace PrintMonitor
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            try
            {
                Config.Register(
                    "um teste bacana!",
                    "PrintMonInject.dll",
                    "PrintMonitor.exe");
            }
            catch (ApplicationException)
            {
                MessageBox.Show("This is an administrative task!", "Permission denied...", MessageBoxButtons.OK);
                System.Diagnostics.Process.GetCurrentProcess().Kill();
            }

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

 


 

PrintMonitor -> DemoInterface.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace PrintMonitor
{
    internal class MonitorEntry
    {
        public readonly String Access;
        public readonly DateTime Timestamp = DateTime.Now;
        public readonly Int32 ClientPID;

        public MonitorEntry(
            Int32 InClientPID,
            String InAccess)
        {
            ClientPID = InClientPID;
            Access = InAccess;
        }
    }

    /*
     * This is the class where our clients will connect to!
     *
     * Please note that setting any breakpoint here will cause the related
     * thread in the client process to block until you continue execution!
     * So don't wonder if your browser (for example) hangs when you set a
     * breakpoint ;-)... Let's say you can debug a part of the code the client
     * is executing (that's not technically correct)
     *
     * In Windows 2000 debugging the following seems to cause problems.
     */
    public class DemoInterface : MarshalByRefObject
    {
        public void ReportError(
            Int32 InClientPID,
            Exception e)
        {
            MessageBox.Show(e.ToString(), "A client process (" + InClientPID + ") has reported an error...", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
        }
        public void Ei()
        {
            MessageBox.Show("test");

        }
        public bool Ping(Int32 InClientPID)
        {
            return true;
        }

        public void OnCreateFile(
            Int32 InClientPID,
            String[] data)
        {
            //something happened
            //MessageBox.Show("something happened");
        }
    }
}

 

tks!

May 27, 2009 at 12:37 PM

Firefox and Acrobat crashes. Word, notepad, excel doesn't works or they don't call CreateDCA.

this method returns the information:

public void OnCreateFile(
            Int32 InClientPID,
            String[] data)
        {

Maybe its a problem at PrinMonInjec, because the "String[] data" are in chinese :D

 

chinese??

May 27, 2009 at 4:30 PM

Hi,

BTW, Why do not try to do something like this? ( You will know what the original API Call passed or falls at least.. )

No exception here means what the original API Call get some unexpected and we have the problem with marshalling...

imho.

Regards, Igor

------------------------------------------------------------

            IntPtr ret = IntPtr.Zero;

            try
            {
                DemoInjection This = (DemoInjection)HookRuntimeInfo.Callback;
               
                lock (This.Queue)
                {
                    if (This.Queue.Count < 1000)
                        This.Queue.Push(InDevice);
                }

               // call original API...

                ret = CreateDCA(    InDriver,    InDevice,   InOutput,  InInitData);
            }
            catch (System.Exception ex)
            {
                This.Queue.Push("CreateDCA_Hooked Exception:"+ex.Message +" Stack: " +ex.StackTrace)
            }

           return ret;

May 27, 2009 at 5:05 PM
Edited May 27, 2009 at 5:14 PM

> Maybe its a problem at *PrinMonInjec*, because the "String[] data" are
> in chinese :D

Your interop specifies char set unicode for an ansi CreateDC*A*
method. That's probably not correct.

Benjamin Schwehn

May 27, 2009 at 5:50 PM
The Hook works if with an interop signature like this (devmode as IntPtr)

public static extern System.IntPtr
CreateDCA([System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
string pwszDriver, [System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
string pwszDevice, [System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
string pszPort, IntPtr pdm);

So it's quite definitely an interop problem.

Ben
May 27, 2009 at 6:24 PM

Tks Benjamin Schwehn and Igor!


I'll try your tip Ben about interop problem.

I found some differences between aplications:

Notepad.exe: CreateDCW only
Firefox.exe: CreateDCA first and then CreateDCW
AcroRd32.exe: CreateDCW first and then CreateDCA before I confirm the document to print.

I think maybe I have to hook DCA and DCW.

 

May 27, 2009 at 6:48 PM

Ben, it was an interop problem. Now its working!!!!

My next step is check the performance when I hook all the user process.

tks again Ben and Igor!

Apr 1, 2011 at 12:52 PM

Thanks a lot. I followed the procedure mentioned above to hook the CreateDCA on GDI32.dll. I face two issues namely;

1. Hook gets attached to a process successfully and when the print is issued for the first time, the callback gets fired. subsequent print commands the callback / hook is not geting invoked.

2. This needs to be hooked upon to all the process to identify which process is issuing print command. Any ideas on how this needs to be done.

thanks

Karthik

Apr 1, 2011 at 3:22 PM

Via some kind of windows hooks, I think. Like http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx - it loads your DLL to each process with window in system.

Apr 12, 2011 at 11:16 AM

We have tried to use Easyhook implementation for Dll Injection. Issue is, CreateDCA gets invoked only for the first time when print is executed. When we print for the second time the callback doesn't get invoked. Any areas that I need to specifically to look to resolve this problem.