While completing challenge 9 of Flare-On 7, I found a lack of information out there on reverse engineering COM objects. Most tutorials are written from a developer point of view where the Interface Definition Language (IDL) file is available. Reverse engineers don't usually have the luxury and may have to write COM clients without any clue as to what functions are exposed and what arguments to pass to the functions. The code snippet below should allow you to load a COM InprocServer object. Do remember to use regsvr32
to register the COM object and to change the CLSID in the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
After loading the COM object successfully, we can now step through it in the Visual Studio debugger. We see that the vtable contains 3 entries, these are actually functions exposed by the InprocServer object and information about the memory address where the function is located. In order to call these functions, we can just define our own IDL. I have created my own IDL with 3 functions named func1
, func2
and func3
. The first function defined will automatically map to the first function in the vtable located at 0x7ffc389214b4
, the second defined function will map to the second function in the vtable and so on.
We will then have to debug the binary in a debugger like x64dbg or windbg, breakpoint at the first instruction of the function, 0x7ffc389214b4
in the example, and slowly step through the instructions. If the function expects an argument, we will likely encounter an access violation. We will then have to figure out what type of argument it expects. If the access violation occurs because it is unable to write to the memory address, the function may be expecting an argument containing address of writable memory space. In the example above, I added const uint64_t y
to the IDL for func1
and passed in the address of writable memory. You could also initialise a buffer and pass in the address of the buffer, char* buffer = new char[100]; func1(&buffer);
After re-running the program, we can finally see what the function is trying to write out.