NDR INSPECTOR v.1.00
NDR Inspector is an unique tool by Vito Plantamura .COM that allows to peek at the format of COM interfaces even in the case when a Type Library is not available.
NDR Inspector looking at the NDR-derived interface format of the interface IMapGen4x3Matrix (from the automation model of the Vito Plantamura's MapGen CSG editor)...
INTRODUCTION
Some time ago, in a reverse engineering work I was doing for discovering the reason of a very nasty crash in an application I was debugging, I needed a way to know the schemas of some COM interfaces that the application in question was calling for making out what was happening. Infact the COM object that the application was calling was generating an access violation exception and the source code of both the application and the server component was not available to me for reference. Obviously the Type Libraries of the aforementioned interfaces were not packaged inside/along with the component binaries, so an other method had to be devised to resolve the problem. Obviously I could disassemble the application exactly in the positions in code where the calls to the component were being made or, in the case of the component, try to guess the positions of the entry points of the methods and then run the disassembler over them. As you can guess, this is a very hard work to do: in most cases, you have to debug the application itself in assembly mode (disassembling it offline is not enough) and then try to guess what the code in question is doing in order to make an idea of its real purpose... I was not prepared to all this pain (and consider that I love debugging and disassembling works of others), so I came out with an idea that could resolve or at least simplify my problem.
I remembered that the DCOM marshaling engine transmits parameter data in what is known as NDR format. After some research and several tests, I noticed that when you compile an IDL source with the MIDL tool, the resulting proxy/stub code that is automatically generated may contain some interesting bytecode data that is intended to be passed as parameter to the well-known-to-debug-people NdrClientCall2 function. As it turned out reading the MSDN, you can compile your IDL source files specifying exactly to the compiler the format and working of the resulting proxy/stub function code: infact, the MIDL tool provides two methods for marshaling data: fully-interpreted and mixed-mode. The two methods differ a lot both in terms of approach to data marshaling and in terms of memory footprint of the resulting P/S modules. Basically what differs is really the presence or not and the format of the aforemetioned bytecode data: doing a simple test compiling a simple IDL source, I noticed that applying the old mixed-mode approach, MIDL was generating a 3Mb source file for the proxy code and that the bytecode data in question (although present in that source file) was pretty poor in format and richness for our purposes (it was in what the documentation calls the -Oi format). Infact, in this case, the stub code of the interface methods marshals the parameter data "online", i.e. by the means of actual source code that on a case by case basis handles the job of getting the parameters, preparing the buffers, converting the data and so on... The -Oi data present in this code can still come in handy for guessing the format of the related interfaces, but, after a quick investigation in internet and after several tests with MIDL I realized that the vast majority of the P/S modules registered on registries of today machines was compiled with the -Oif MIDL parameter: this flag instructs MIDL to use the fully-interpreted method in the generated P/S code. This means that the format of all the interface methods is represented in a special bytecode (in what the documentation calls the -Oif format) and no code is generated (in most cases) for the stub methods. In all cases the P/S code will call the NdrClientCall2 function simply specifying the offset in this bytecode table of the method being called and the NDR engine will do the rest (handling the buffers, converting the parameters data etc.). As you can guess, an all-purposes engine like this requires data in a very rich format: getting this data and trying to interpret it will surely come in handy in my reverse engineering work... !
GETTING "-OIF" DATA FROM P/S MODULES
After realizing the potentialities of the NDR bytecode data, I was faced with an other serious problem: how to get a pointer to the correct record in the bytecode table for a given interface method? I thought to various solutions to this problem, but the easiest one can be deduced looking at the source files that the MIDL compiler generates. Infact it turned out that MIDL prepares and fills in code an interesting structure (of type "ProxyFileInfo") that seems to be the starting point for getting pointers to other data structures that give out a wealth of information on the proxy and the stub code itself.
Since we are searching for the byte pointers to the NDR bytecode records of our interface methods, we have to follow the pointers in the various structs as in the graph above. Infact the pStubVtblList pointer in the ProxyFileInfo structure will point to an array of CInterfaceStubVtbl: each item in this collection refers to a single interface in the P/S module. From this point on, it is only a matter of following the indicated pointers for knowing the pointer to the global format string table and to the array of method_index-to-format_string_table_offset values for the specified interface.
The next problem, however, is how to obtain a pointer to the ProxyFileInfo data structure itself, our starting point in knowing the secrets of our proxy/stubs: from a quick research, you can discover that a good part of the P/S modules that are registered in your system export a function with name "GetProxyDllInfo". The prototype of this function can be extracted with ease from the Platform SDK header files relative to the RPC PROXY APIs:
void RPC_ENTRY GetProxyDllInfo(
const ProxyFileInfo*** pInfo,
const CLSID ** pId
);
This function returns a pointer to a pointer to the ProxyFileInfo structure and in the second output parameter tipically a pointer to the well-known CLSID_PSFactoryBuffer clsid identifier. However, further researching with utilities such as DEPENDS, can lead you to an unpleasant discovery: a very not-small portion of the P/S dlls registered in the registry of a typical computer, doesn't come with this function exported. For resolving this problem, I was forced to think to an other solution for obtaining this pointer. After further research in the Platform SDK header files, I discovered that this same pointer is also handled internally by the well-known DllGetClassObject function: this function needs this pointer in order to call the low-level NdrDllGetClassObject function in order to process the application requests for class objects that tipically come from calls to the CoGetClassObject function (note however that the mechanism described here for returning a class object interface pointer to the user in result of a CoGetClassObject call is the "standard" one that MIDL implements in all its automatically generated stub files: a custom implementation can override this behaviour and use other means for returning this same information). Then the idea that I have implemented in the NDR Inspector is to force a call to the DllGetClassObject function and then to intercept all the calls that are made in that same thread (in the scope of that function call) to the low-level function NdrDllGetClassObject. This has been proved to lead to the expected results.
This is the implementation of the class that takes care of this nasty task:
//
// NDR class.
//
DETOUR_TRAMPOLINE( HRESULT RPC_ENTRY Trampoline_NdrDllGetClassObject( IN REFCLSID rclsid, IN REFIID riid, OUT void **ppv, IN const ProxyFileInfo **pProxyFileList, IN const CLSID *pclsid, IN CStdPSFactoryBuffer* pPSFactoryBuffer ), NdrDllGetClassObject )
HRESULT RPC_ENTRY Hooked_NdrDllGetClassObject( IN REFCLSID rclsid, IN REFIID riid, OUT void **ppv, IN const ProxyFileInfo **pProxyFileList, IN const CLSID *pclsid, IN CStdPSFactoryBuffer* pPSFactoryBuffer );
typedef HRESULT ( STDAPICALLTYPE * PFNDllGetClassObject ) ( REFCLSID rclsid, REFIID riid, LPVOID * ppv );
typedef void ( RPC_ENTRY * PFNGetProxyDllInfo )( const ProxyFileInfo*** pInfo, const CLSID ** pId );
typedef std::vector< ProxyFileInfo* > VPProxyFileInfos;
struct SClsIdModName
{
CLSID clsid;
charstring modname;
HMODULE hmod;
CHAR szPSDllName[ MAX_PATH ];
PFNDllGetClassObject pfnDllGetClassObject;
PFNGetProxyDllInfo pfnGetProxyDllInfo;
BOOL getclsCalled;
VPProxyFileInfos vpPFIs;
};
typedef std::vector< SClsIdModName > VClsIdModName;
class CProxyFileInfoRetriever
{
public:
//
// Construction.
//
CProxyFileInfoRetriever ()
{
if ( pINSTANCE )
throw;
else
pINSTANCE = this;
::InitializeCriticalSection( & csGetCs );
|