I was developing a custom input format for the LogParser 2.2. This COM plug-in reads binary files that can be several Gigabytes big. Scanning a file this size can take several minutes. My C# Windows Forms application that hosted the LogParser COM server and my custom input format COM server needed to stay responsive to user input during the processing of the file. I considered two approaches:
- Add a Message pump to my custom input format or
- Launch my LogParser query from a BackgroundWorkerThread component in my C# application.
I wanted to make an educated decision based on how LogParser.exe is initializing its COM runtime. This article describes how I debugged LogParser.exe to determine the parameters it used in the CoInitializeEx() function call.
CoInitializeEx()
Knowing that the function CoInitializeEx() determines the threading model used by your threads is the key information to start the reverse engineering investigation. According to MSDN: "CoInitializeEx() initializes the COM library for use by the calling thread, sets the thread's concurrency model, and creates a new apartment for the thread if one is required." Detailed information can be found when you search for CoInitializeEx on MSDN.
It Depends
If you have the source code and the debugging symbols it is very easy to set a breakpoint at a line of your source code in Visual Studio. But in this case we I don't have the source code to the DLL that includes the code for CoInitializeEx(). The DLL is OLO32.DLL. WinDBG, which is part of the Debugging Tools For Windows is able to inject breakpoint instructions (int 3) into the code of OLE32.DLL based on a code offset supplied by a Set Breakpoint command.
To calculate the memory offset where the debugger should inject the breakpoint instruction requires 2 pieces of information. First we need to know at which address LogParser.exe loads OLE32.DLL. This can be done by attaching LogParser.exe to WinDBG, forcing a debug break and listing the modules. The other piece of information is the offset of the CoInitializeEx() function within the OLE32.DLL. This offset can be gathered using the Dependency Walker utility "Depends.exe". This is part of the Visual Studio tool box. Opening OLE32.DLL with depends.exe will show us the memory offset of the function within the DLL.
The Dependency Walker tells us that the function entry point of CoInitializeEx() in OLE32.DLL is at 0x0001EF6B.
Early Attachment
The function CoInitializeEx usually gets called very early in the game. Attaching the debugger to the process manually would be to late. The function has most likely already be called and the break point will never get hit again. Fortunately there is a way to automatically attach a debugger to a process during startup. The following registry entry will configure Windows to attach WinDBG to LogParser.exe at startup.
1 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\logparser.exe]
2 "Debugger"="c:\\debuggers\\windbg.exe"
Query the Event Logs with LogParser
Before we can put it all together and finally know the answer of our initial question we need to come up with a simple query for LogParser to execute. The following command reads the Windows Application Event Log and exports some events into a text file:
1 LogParser "SELECT TimeGenerated, SourceName, EventCategoryName, Message INTO report.txt FROM Application WHERE EventID = 1800" -o:CSV
Start your Engines
Once the "Image File Execution Options" for LogParser.exe have been set and the LogParser query has been kicked off, we finally should get a closer look at OLE32.DLL in the WinDBG debugger. WinDBG launched automatically and the LogParser.exe process is stopped in the debugger.
Right after WinDBG launches it lists the loaded modules automatically. OLE32.DLL is loaded at 0x774E0000. Doing a little math and adding the function entry point and the module load address of OLE32.DLL gives us the absolute address of the CoInitializeEx() function in the Virtual Memory of LogParser.exe:
0x774E0000 + 0x0001EF6B = 0x774FEF6B.
To set a break point at a specific address we are going to use the following WinDBG command:
Before you continue, make sure that the symbol file path has been set correctly. It needs to point to the Microsoft Symbol Server. As a refresher here is a commonly used path:
1 srv*c:\symbols*http://msdl.microsoft.com/download/symbols
Ready! Set! g
The next command is a simple g, which lets the debuggee run until it hits the breakpoint at CoInitializeEx().
At this point we finally hit the breakpoint at CoInitializeEx(). Now we can look in the Call Stack Window and see what value the second parameter is that LogParser passed into this function.
The second parameter is an enumeration of type COINIT and its value is two. The enumeration is defined as:
1 typedef enum tagCOINIT {
2 COINIT_MULTITHREADED = 0x0,
3 COINIT_APARTMENTTHREADED = 0x2,
4 COINIT_DISABLE_OLE1DDE = 0x4,
5 COINIT_SPEED_OVER_MEMORY = 0x8,
6 } COINIT;
The value of the COINIT parameter is 2 according to the debugger. This means that LogParser.exe has been initialized as Apartment Threaded application.
Summary
This article describes a simple way to automatically attach a debugger to a process and set a breakpoint based on the memory address of a function entry point. In this case I found that the LogParser launched my custom LogParser input format in a single threaded apartment. Based on this knowledge I decided to use a simple message pump in my input plug-in COM server to keep my Windows Forms application responsive to user input.