标签:href eps client idm pos because rem export width
1.1 Recently, a friend asked me for advise on a very unusual requirement.
1.2 He needs to replace all UUIDs in a COM DLL with new ones.
1.3 He does not have the source codes to the original COM server so any modifications will have to be done on the binary code.
1.4 To be more accurate, the objective was to replace the UUIDs of COM Types defined in the COM DLL.
1.5 Ever enthusiastic for any opportunity to do unusual spelunking, I helped to research into this.
1.6 Using a sample COM in-proc server written in ATL, I managed to accomplish this and verified the results using a test program.
1.7 In this blog, I aim to demonstrate to the reader how this can be done.
2.1 As I advised my friend, performing such a feat is certainly possible but it depends on how the original COM DLL was created :
– What tool was used to create the DLL ? e.g. ATL, VB,
C#, etc.
– Was it hand-crafted instead in C++ ?
2.2 Assuming that the COM DLL server was developed using ATL, the process is quite straightforward but involves a number of steps :
2.3 For the purposes of demonstration, I created a simple COM in-proc server and a test console application that uses the COM Types in the DLL.
2.4 The test program will initially link with the original DLL.
2.5 After we have created a new DLL with all the replacement UUIDs, the test program will link to the new DLL and we shall see that the output of the test program will be the same.
3.1 The following are the main characteristics of the COM DLL (SimpleATLCOMServer.dll) which we will be working on :
// SimpleATLCOMServer.idl : IDL source for SimpleATLCOMServer // // This file will be processed by the MIDL tool to // produce the type library (SimpleATLCOMServer.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(4C1AE3E8-736E-4750-9D08-48CDC33E66FA), dual, nonextensible, pointer_default(unique) ] interface ISimpleCOMClass : IDispatch{ [id(1)] HRESULT TestMethod01([in] BSTR strParam); }; [ uuid(8A32BF84-3743-4AE5-A791-623F17C6E804), version(1.0), ] library SimpleATLCOMServerLib { importlib("stdole2.tlb"); [ uuid(60FD53D5-2D3E-4BCC-96E6-484F8D8A5119) ] dispinterface _ISimpleCOMClassEvents { properties: methods: [id(1)] HRESULT TestEvent01([in] BSTR strParam); }; [ uuid(68226D2E-8EE4-4B42-9B39-2D4ED9D578DD) ] coclass SimpleCOMClass { [default] interface ISimpleCOMClass; [default, source] dispinterface _ISimpleCOMClassEvents; }; };
3.3 The ISimpleCOMClass interface has a single method TestMethod01() which takes a BSTR as parameter.
3.4 The _ISimpleCOMClassEvents event interface has a single method TestEvent01() which also takes a BSTR parameter.
3.5 The actual implementation for ISimpleCOMClass is a C++ class named CSimpleCOMClass.
3.6 The definition for TestMethod01() is as follows :
STDMETHODIMP CSimpleCOMClass::TestMethod01(BSTR strParam) { // TODO: Add your implementation code here _bstr_t _bst(strParam, true); LPCTSTR lpszParam = (LPCTSTR)_bst; MessageBox(NULL, lpszParam, "CSimpleCOMClass", MB_OK); Fire_TestEvent01(strParam); return S_OK; }
It displays the contents of the BSTR parameter and then fires TestEvent01() using the same BSTR parameter to TestMethod01().
3.7 In the sections that follow, we will expound in greater detail the steps that we take to replace all COM UUIDs in the DLL with new ones.
4.1 Extracting the type library of the original DLL can be done using OleView. The following is a screenshot of how the IDL will be displayed :
4.2 Next, open SimpleATLCOMServerNew.IDL and perform the following changes :
The following is a sample modified SimpleATLCOMServerNew.IDL :
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: SimpleATLCOMServer.dll
[ uuid(F702C924-7CC9-4E26-BE36-E646222A057E), version(1.0), custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 134218331), custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1485538469), custom(DE77BA65-517C-11D1-A2DA-0000F8773CE9, "Created by MIDL version 8.00.0603 at Sat Jan 28 01:34:25 2017
")
]
library SimpleATLCOMServerNewLib
{ // TLib : // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole2.tlb"); // Forward declare all types defined in this typelib dispinterface _ISimpleCOMClassEvents; interface ISimpleCOMClass; [ uuid(E793B680-C0B2-4FB6-ADDD-7F66A30EF5DB) ] dispinterface _ISimpleCOMClassEvents { properties: methods: [id(0x00000001)] HRESULT TestEvent01([in] BSTR strParam); }; [ uuid(F7C6EE89-AA69-4BB6-8CA0-D7CC9B6A0473) ] coclass SimpleCOMClass { [default] interface ISimpleCOMClass; [default, source] dispinterface _ISimpleCOMClassEvents; }; [ odl, uuid(78A36043-6DAE-4457-95D8-A4425ECF84DA), dual, nonextensible, oleautomation ] interface ISimpleCOMClass : IDispatch { [id(0x00000001)] HRESULT TestMethod01([in] BSTR strParam); };
};
All new replacement UUIDs are displayed in bold. The new name for the library is also in bold.
4.3 Next we need to recompile the new IDL using MIDL.exe :
midl SimpleATLCOMServerNew.IDL /env win32 /W1 /char signed
set INCLUDE=%INCLUDE%;C:\Program Files (x86)\Windows Kits\8.1\Include\um;C:\Program Files (x86)\Windows Kits\8.1\Include\shared
4.4 Then, use Visual Studio to replace the original type library of the DLL with the new modified one :
5.1 The registration scripts are the .rgs resources commonly found in ATL projects.
5.2 It contains strings that are used in the COM registration process.
5.3 The objective is to extract the existing .rgs resources, modify them, and then re-insert them into the DLL.
5.4 Extracting the .rgs resource is done once again using Visual Studio by opening up the DLL as an executable file :
HKCR { NoRemove CLSID { ForceRemove {68226D2E-8EE4-4B42-9B39-2D4ED9D578DD} = s ‘SimpleCOMClass Class‘ { ForceRemove Programmable InprocServer32 = s ‘%MODULE%‘ { val ThreadingModel = s ‘Apartment‘ } TypeLib = s ‘{8A32BF84-3743-4AE5-A791-623F17C6E804}‘ Version = s ‘1.0‘ } } }
HKCR { NoRemove CLSID { ForceRemove {F7C6EE89-AA69-4BB6-8CA0-D7CC9B6A0473} = s ‘SimpleCOMClass Class‘ { ForceRemove Programmable InprocServer32 = s ‘%MODULE%‘ { val ThreadingModel = s ‘Apartment‘ } TypeLib = s ‘{F702C924-7CC9-4E26-BE36-E646222A057E}‘ Version = s ‘1.0‘ } } }
6.1 This is by far the most interesting part of the replacement process.
6.2 To do this, we need to write a program that performs the following :
6.3 It is important to note that a UUID is in actual fact a binary structure and not a string (see UUID structure).
spISimpleCOMClass->QueryInterface(IID_ISimpleCOMClass, (void**)&pISimpleCOMClass);
requires that IID_ISimpleCOMClass be expressed as a structure like the following :
{0x4c1ae3e8,0x736e,0x4750,{0x9d,0x08,0x48,0xcd,0xc3,0x3e,0x66,0xfa}
6.4 This program is best done in C# due to the rich functionality of the .NET class libraries.
6.5 The following is a listing of the C# program :
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace ConsoleGUIDReplacer { class Program { // Code for the following algorithm taken from : // Most efficient way to replace one sequence of the bytes with some other sequence // http://stackoverflow.com/questions/10702514/most-efficient-way-to-replace-one-sequence-of-the-bytes-with-some-other-sequence private static byte[] Replace(byte[] input, byte[] pattern, byte[] replacement) { if (pattern.Length == 0) { return input; } List<byte> result = new List<byte>(); int i; for (i = 0; i <= input.Length - pattern.Length; i++) { bool foundMatch = true; for (int j = 0; j < pattern.Length; j++) { if (input[i + j] != pattern[j]) { foundMatch = false; break; } } if (foundMatch) { result.AddRange(replacement); i += pattern.Length - 1; } else { result.Add(input[i]); } } for (; i < input.Length; i++) { result.Add(input[i]); } return result.ToArray(); } static void Main(string[] args) { string strCOMServerTargetPath = args[0]; string strCOMServerNewPath = args[1]; Guid guid_search = new Guid(args[2]); Guid guid_replace = new Guid(args[3]); byte[] byCOMServerBytesTarget = File.ReadAllBytes(strCOMServerTargetPath); byte[] byCOMServerBytesNew = Replace(byCOMServerBytesTarget, guid_search.ToByteArray(), guid_replace.ToByteArray()); File.WriteAllBytes(strCOMServerNewPath, byCOMServerBytesNew); } } }
Most efficient way to replace one sequence of the bytes with some other sequence.
6.6 The program expects 4 runtime arguments :
The following is a sample command line :
ConsoleGUIDReplacer.exe SimpleATLCOMServerNew.dll SimpleATLCOMServerNew.01.dll 4C1AE3E8-736E-4750-9D08-48CDC33E66FA 78A36043-6DAE-4457-95D8-A4425ECF84DA
6.7 The availability of structures like Guid in .NET goes a long way towards simplifying things :
7.1 Not to forget : we must also register the new SimpleATLCOMServerNew.dll.
7.2 This is necessary because it is, in the eyes of COM, completely distinct from the original SimpleATLCOMServer.dll.
7.3 Run regsvr32.exe as per normal.
8.1 The following is a general game plan for the test :
8.2 The listing for the test program is as follows :
// ConsoleClientApp.cpp : Defines the entry point for the console application. // #include "stdafx.h" // Comment in/out the import and using statement according // to which version of the DLL is to be used. #import "SimpleATLCOMServer.dll" raw_interfaces_only using namespace SimpleATLCOMServerLib; //#import "SimpleATLCOMServerNew.dll" raw_interfaces_only //using namespace SimpleATLCOMServerNewLib; #include "TEventHandler.h" using namespace TEventHandlerNamespace; class EventHandler; typedef TEventHandler<EventHandler, ISimpleCOMClass, _ISimpleCOMClassEvents> ISimpleCOMClassEventHandler; class EventHandler { public : EventHandler(ISimpleCOMClassPtr spISimpleCOMClass) { // ***** Create an instance of an object which implements IEventFiringObject. ***** m_spISimpleCOMClass = spISimpleCOMClass; // ***** Instantiate an IEventFiringObjectEventHandler object. ***** m_pISimpleCOMClassEventHandler = new ISimpleCOMClassEventHandler(*this, m_spISimpleCOMClass, &EventHandler::OnSimpleCOMClassInvoke); } ~EventHandler() { if (m_pISimpleCOMClassEventHandler) { m_pISimpleCOMClassEventHandler->ShutdownConnectionPoint(); m_pISimpleCOMClassEventHandler->Release(); m_pISimpleCOMClassEventHandler = NULL; } if (m_spISimpleCOMClass) { m_spISimpleCOMClass = NULL; } } ISimpleCOMClassPtr m_spISimpleCOMClass; // ***** Declare a pointer to a TEventHandler class which is specially tailored ***** // ***** to receiving events from the _IEventFiringObjectEvents events of an ***** // ***** IEventFiringObject object. ***** ISimpleCOMClassEventHandler* m_pISimpleCOMClassEventHandler; HRESULT EventHandler::OnSimpleCOMClassInvoke ( ISimpleCOMClassEventHandler* pEventHandler, DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr ) { if (dispidMember == 0x01) // Event1 event. { // 1st param : [in] BSTR strParam. VARIANT varValue; VariantInit(&varValue); varValue = (pdispparams->rgvarg)[0]; _bstr_t _bst(V_BSTR(&varValue), true); char szMessage[256]; sprintf_s(szMessage, sizeof(szMessage), "Event 1 is fired with value : %s.", (LPCTSTR)_bst); ::MessageBox(NULL, szMessage, "Event", MB_OK); } return S_OK; } }; void DoTest() { ISimpleCOMClassPtr spISimpleCOMClass = NULL; spISimpleCOMClass.CreateInstance(__uuidof(SimpleCOMClass)); EventHandler event_handler(spISimpleCOMClass); BSTR bstr = ::SysAllocString(L"Hello World"); if (bstr) { spISimpleCOMClass->TestMethod01(bstr); ::SysFreeString(bstr); bstr = NULL; } } int main() { ::CoInitialize(NULL); DoTest(); ::CoUninitialize(); return 0; }
8.3 For the purposes of the test, I have copied the original and new DLLs into the same folder as the project of the test program.
8.4 Notice that when we import SimpleATLCOMServerNew.dll, we use the SimpleATLCOMServerNewLib namespace.
8.5 This is because of what we did in point 4.2 where we changed the name of the library statement in the new IDL from SimpleATLCOMServerLib to SimpleATLCOMServerNewLib.
8.6 For handling COM events, I have created the EventHandler class and have used the TEventHandler class (see Understanding COM Event Handling) to simplify handling IDispatch-based event interfaces.
8.7 When we run the above code, the following will occur :
8.8 Next, comment out the import of the SimpleATLCOMServer.dll and comment in the import of SimpleATLCOMServerNew.dll :
// Comment in/out the import and using statement according // to which version of the DLL is to be used. //#import "SimpleATLCOMServer.dll" raw_interfaces_only //using namespace SimpleATLCOMServerLib; #import "SimpleATLCOMServerNew.dll" raw_interfaces_only using namespace SimpleATLCOMServerNewLib;
You will see that the same dialog boxes will be displayed.
8.9 As an additional step that we can take to ensure that these 2 DLLs are truly distinct, we can modify the original SimpleATLCOMServer.dllby changing the code for TestMethod01() as follows :
STDMETHODIMP CSimpleCOMClass::TestMethod01(BSTR strParam) { // TODO: Add your implementation code here _bstr_t _bst(strParam, true); LPCTSTR lpszParam = (LPCTSTR)_bst; MessageBox(NULL, lpszParam, "CSimpleCOMClass Original", MB_OK); Fire_TestEvent01(strParam); return S_OK; }
We simply change the dialog box title to “CSimpleCOMClass Original”.
8.10 Thereafter, modify ConsoleClientApp.cpp once again to import SimpleATLCOMServer.dll, compile the test program and run it :
8.11 Now, once again import SimpleATLCOMServerNew.dll, re-compile and run the program :
9.1 In summary, we can see that complete replacement of UUIDs in a COM DLL server is possible. However, note well :
9.2 Note well that, with the exception of COM servers written in a managed language, regardless of whatever tool was used to generate a COM DLL server, the following are common requirements of the DLL :
Hence at minimum, it should be no problem to at least modify the embedded type libraries contained inside a COM DLL. Modifying the self-registration code will require some further research.
9.3 The techniques for modification of UUIDs in runtime code will also likely be varied.
9.4 I hope to research into these in the future.
10.1 The source codes for this blog can be downloaded from here.
[转]Replace all UUIDs in an ATL COM DLL.
标签:href eps client idm pos because rem export width
原文地址:http://www.cnblogs.com/czytcn/p/7928173.html