Данная статья является плодом моих собственных исследований и опыта, накопленного при создании компонентов ActiveX (используя Delphi 3.0 и Visual C++ 5.0 (как с помощью MFC, так и ATL)). Я не видел нигде в Сети (включая приснопамятную Fravia) сколько-нибудь внятного объяснения принципов строения компонентов ActiveX с точки зрения Reverse Engeneeringа, несмотря на то, что данная технология становится всё более распространённой - по моим наблюдениям, количество ActiveX компонентов уже давно превысило число компонентов Delphi и модулей для Perl'а, вместе взятых.

Ваши замечания и предложения можете присылать автору по адресу redplait@usa.net.

Данная статья предполагает знакомство читателя с моделью COM, знание ассемблера и умение использовать дизассемблер IDA Pro.

Вам понадобятся программы:
Дизассемблер IDA Pro;
Средство просмотра COM объектов - подойдет, например, инспектор объектов от Visual Basic, я использовал "OLE/COM Object Viewer", входящий в поставку Visual C++ 5.0;
Документация по Win 32 API и "OLE Programmers Reference", их можно взять из какого-либо серьёзного C++ компилятора для Win 32, я использую Visual C++ 5.0.

ShotGraph - это небольшой ActiveX компонент, позволяющий из любой скриптовой программы, являющейся контроллером OLE Automation (например, Visual Basic, Visual Basic for Applications, Active Server Pages etc) , в полёте строить и сохранять GIF и JPEG картинки. Взять можно:
http://download.proxy.ru/mike/shotgraph/
http://www.geocities.com/SiliconValley/Foothills/9219/shotgraph.

Однако, там лежит shareware версия с неполными функциональными возможностями, обладающая следующими ограничениями (взято из README файла):
максимальный размер картинки 320x240;
максимальное число цветов 8;
метод 'BuildPalette' не реализован;
метод 'WebPalette' недоступен;
метод 'ReadImage' не реализован;
метод 'ChangePaletteSize' не реализован;
метод 'TrackLoop' не реализован;
максимальный размер картинки для сохранения в JPEG формате 200x200.

Как видим, ограничения слишком существенны, чтобы можно было серьёзно использовать этот компонент. Хочется выяснить, действительно ли некоторые методы не реализованы, и, даже если они физически отсутствуют, есть шанс побороть ограничения.

Итак, загружаем shotgraph.dll (молча инсталлировавшуюся в системный каталог Windows) в IDA Pro (я использовал старую добрую версию 3.76), и пока она дизассемблируется, вспомним (кто знал, конечно), что представляют собой OLE Automation и компоненты ActiveX.

OLE Automation
это фирменная технология Microsoft прикладного уровня , представляющая собой механизм, с помощью которого различные модули (в том числе и написанные на разных языках программирования) могут использовать функциональные возможности друг друга.

Различают клиентов (или контроллеры) автоматизации - приложения, использующие функциональность других приложений, и сервера автоматизации - программы, предоставляющие другим приложениям свои функциональные возможности. Нужно отметить, что сервера автоматизации могут являться одновременно и контроллерами автоматизации, так же возможна и другая картина. Компоненты ActiveX являются серверами OLE Automation (но могут быть и контроллерами).

Компонент ActiveX
представляет собою реализацию одного или нескольких COM интерфейсов, хранимый в некотором двоичном формате.
COM интерфейс
(как и любой другой интерфейс) представляет из себя некоторую таблицу указателей на функции со строго определёнными списками аргументов и возвращаемых значений.

Любой COM объект должен реализовывать как минимум один такой интерфейс IUnknown, управляющий временем жизни COM объектов и предназначенный для запроса у COM объектов указателей на другие интерфейсы. Также, чтобы некий COM объект стал объектом OLE Automation, он должен реализовать ещё один интерфейс - IDispatch. Более подробно об этом, и о методах этих интерфейсов, Вы сможете прочесть в "OLE Programmers Reference".

Также кроме этих интерфейсов, дабы представлять собою нечто полезное, COM объект,как правило, реализует ещё один или несколько интерфейсов, которыми собственно и пользуются контроллеры OLE Automation. А раз так, то где-то в исследуемой программе должна иметься таблица, содержащая указатели на предоставляемые функции, в том числе и на якобы нереализованные методы BuildPalette and company. Звучит странно - указатель на нереализованный метод, не правда ли? Но Вы должны помнить - если интерфейс описан, его методы могут быть вызваны без всяких проблем. Если вместо указателя на такую нереализованную функцию помещён, скажем, NULL указатель - это значит, что программа-контроллер потерпит крах при попытке вызова такого метода! Однако этого не случается - значит, некий код всё-таки вызывается (другое дело, что он может не делать ничего, кроме возврата кода ошибки).

На самом деле всё вышеописанное (как и всё в исполнении фирмы Microsoft) представляет собою более сложные вещи, например, возможно объединение двух и более интерфейсов (точнее, двух и более таблиц указателей на функции) в одну таблицу (так называемый дуальный интерфейс), однако предоставленной информации должно быть достаточно для понимания того, с чем мы будем иметь дело.

Кроме того, Вы должны задать мне вопрос - откуда контроллер автоматизации знает, какие функции есть для него у сервера автоматизации? Это хороший вопрос. Дело в том, что такая информация хранится в двоичном виде в специальном формате - в библиотеке типов. Библиотека типов может быть либо отдельным файлом, поставляемым вместе с компонентом ActiveX, либо быть подлинкованным к самому компоненту в виде двоичного ресурса. Самое важное - то, что Вы можете её просматривать так же, как это делает любой контроллер OLE Automation. Для этого нам потребуется какой-нибудь инструмент просмотра библиотек типов - например, программа oleview.exe из Visual C++ 5.0.

Здесь мы имеем дело с файлом .dll (также может иметь расширение .ocx) - так называемый внутризадачный COM компонент; его выполнение происходит в контексте вызывающего процесса. Эта .dll должна соответствовать некоторым требованиям, налагаемым на двоичный формат компонентов ActiveX, а именно - экспортировать несколько предопределенных функций:

  1. DllCanUnloadNow - определяет, занята ли в настоящий момент .dll, и нельзя ли её выгрузить.
  2. DllGetClassObject - функция, возвращающая ещё один интерфейс - IClassFactory для создания компонентов запрошенного типа. Также может возвращаться интерфейс IClassFactory2 - если компонент ActiveX поддерживает лицензирование (тоже обширный предмет исследования)
  3. DllRegisterServer - используется для автоматической регистрации элементов ActiveX (помещает в реестр Windows информацию о местонахождении компонента, а также его библиотеки типов и характеристики самого компонента, например, может ли ActiveX компонент быть помещён на форму Visual Basic etc)
  4. DllUnregisterServer - используется для автоматической дерегистрации компонента (скажем, при его удалении из системы)

Вы можете проверить другие установленные у Вас ActiveX компоненты - все они имеют вышеназванные функции.

Для начала я советую посмотреть на файл - поискать в нём строки "C++", "Delphi", "Borland" etc - можно увидеть много интересного. В нашем файле нашлась строчка "Microsoft Visual C++ Runtime Library" - значит, этот ActiveX написан на Visual C++, и, поскольку не содержит строчек с упоминанием MFC и экспортируемых функций, начинающихся с Afx, скорее всего без использования MFC (почти наверняка на Active Template Library).

Запустите oleview.exe - ведь мы хотим знать, какие методы предоставляет нам shotgraph. Чтобы просмотреть библиотеку типов, выберите поддерево "Unclassified Objects", и в нём, среди многих сотен различных объектов, упорядоченных по алфавиту, найдите подраздел, озаглавленный "shotgraph Class". С правой части окна появится несколько вкладок с характеристиками нашего ActiveX'а. Здесь нас интересует так называемый CLSID: строка {B9F7D335-AC62-11D1-8558-0000C050C497} = shotgraph Class. Дело в том, что все интерфейсы идентифицируются по некоему глобально-уникальному ключу. Структура этого ключа находится в файле заголовков BASETYPS.H, и выглядит так:

typedef struct _GUID

{

    unsigned long Data1;

    unsigned short Data2;

    unsigned short Data3;

    unsigned char Data4[8];

} GUID;

При стандарной записи CLSID первые его восемь символов означают unsigned long Data1 в шестнадцатеричной форме, последующие две пары по четыре символа - соответственно Data2 & Data3, а оставшиеся символы (неизвестно по какой причине первые два байта отделяют от остальных тире) - восемь байт массива Data4. Для чего нам нужен GUID? Дело в том, что GUIDы используются в методе QueryInterface обязательного для всех COM объектов интерфейса IUnknown, так что мы можем узнать по ним, какой именно интерфейс запрашивается, и какой класс программа создаёт для его реализации!

Здесь нужно сделать одно важное замечание: для реализации COM объектов не обязательно должен использоваться язык C++. Вы должны просто предоставить указатель на таблицу, состоящую из указателей на функции, реализующие некоторый интерфейс. Но ведь компилятор C++ (как и почти всех объектно-ориентированных языков программирования) создаёт такую таблицу за Вас для представления виртуальных функций некоторого класса. Поэтому все реализации COM объектов пишут на языке C++ в виде классов, наследуемых от абстрактных классов - собственно интерфейсов.

Под разделом "shotgrapth Class" есть ещё два раздела: IDispatch & Igifsgr. Как я уже упоминал, интерфейс IDispatch COM объекты должны реализовывать для поддержки OLE Automation. А вот интерфейс Igifsgr является собственно заявленными функциональными возможностями нашего ActiveX компонента. Запишите его GUID: {B9F7D333-AC62-11D1-8558-0000C050C497} - он нам ещё потребуется. Теперь посмотрим, какие функции (методы) составляют этот интерфейс: для этого нажмите на нём правой кнопкой мыши, выберите в контекстном меню пункт View... и нажмите кнопку View Type Info - в правой части появившегося окна и находится информация из библиотеки типов. Она пишется обычно на языке описания интерфейсов - IDL (Interface Definition Language), по синтаксису он напоминает C++ - те же объявления классов и методов классов, только можно добавлять некоторые специфические характеристики классов и их методов, называемых атрибутами. Атрибуты описываются в квадратных скобках:

[

  uuid(B9F7D333-AC62-11D1-8558-0000C050C497),

  helpstring("Igifsgr Interface"),

  dual

]

dispinterface Igifsgr {

    properties:

    methods:

        [id(0x00000001), helpstring("method CreateImage")]

        VARIANT CreateImage(

                        [in] VARIANT* x, 

                        [in] VARIANT* y, 

                        [in] VARIANT* nc);

        [id(0x00000002), helpstring("method SetColor")]

        VARIANT SetColor(

                        [in] VARIANT* ind, 

                        [in] VARIANT* r, 

                        [in] VARIANT* g, 

                        [in] VARIANT* b);



...

uuid - это уже знакомый нам GUID идентификатор этого интерфейса. Обратите внимание на ключевое слово dual - оно означает, что в данном случае происходит объединение интерфейсов (в терминах C++ это можно описать как "класс Igifsgr наследует от класса IDispatch (который, как и все COM интерфейсы, в свою очередь наследован от класса IUnknown) "). Для нас же это означает, что в найденной нами таблице указателей на функции первых три метода принадлежат реализации интерфейса IUnknown, следующие четыре - IDispatch, и только с седьмого (считая с нуля) начнутся методы собственно Igifsgr.

Далее, в описаниях методов ключевое слово id (номер) значает идентификатор метода в интерфейсе. Для нас он является индексом в таблице указателей на функции на нужный нам метод.1)

Итак, запомним id нереализованных методов:
BuildPalette id=17
WebPalette id=1c
ReadImage id=2b
ChangePaletteSize id=2d
TrackLoop id=30

Ну вот, а автор уверял нас нас, что некоторые методы совсем не реализованы... Итак, мы многое узнали из библиотеки типов, Вы можете закрыть OLE/COM Object Viewer - он нам более не понадобится.

Наконец, погрузимся в исследование кода ActiveX компонента. Вы всегда должны представлять себе, что именно Вы хотите найти. В данном случае, конечно, можно бы ухватиться за проверку размера картинки, и поискать некоторый код, в котором происходит сравнение с числами 320 (0140h) и 240 (F0h) - однако, это плохой подход. Во-первых, мы не знаем точную инструкцию. Во-вторых, файл достаточно большой (184 Kb), так что на эти поиски Вы можете потратить может и не большую, но точно лучшую часть своей жизни. В-третьх, достоверно неизвестно, что сравнение должно происходить с константами - возможно, они зашифрованы или динамически вычисляются (что одно и то же). Поэтому мы пойдём другим путём. Мы будем искать GUID интерфейса Igifsgr. В IDA Pro нажмите F4, затем Alt + <B>, и наберите образец "B9F7D333". IDA достаточно интеллектуальна, чтобы понять, что её просят найти DWORD, учитывая обратный порядок расположения байт etc. (может, конечно, случиться, что и все GUIDы зашифрованы - в таком случае пришлось бы использовать SoftIce). Образец найден по адресу 100200C:

10021008 shortGraf_CLSID dd 0B9F7D333h           

1002100C                 dw 0AC62h

1002100E                 dw 11D1h

10021010                 db 85h, 58h, 0, 0, 0C0h, 50h

10021010                 db 0C4h, 97h

Узнаёте? Это GUID интерфейса Igifsgr, я обозвал его поприличнее и настроил размерность данных (понажимайте клавишу D до нужной кондиции). Для массива Data4 нажмите кнопку * (Array) и заполните поле Array Size: 8.

Теперь посмотрим, откуда на него ссылаются. В первой же ссылке по адресу 10021190 мы видим кое-что интересное - по адресам выше явно расположена таблица VTBL - таблица указателей на виртуальные методы. Но является ли это таблица VTBL нужного нам класса? Проверить это можно довольно просто - первые три метода в этой таблице принадлежат интерфейсу IUnknown, в котором первый метод QueryInterface предназначен для возврата ссылок на все прочие интерфейсы, реализованные в данном классе. Описание этого метода выглядит так (взято из файла заголовков UNKNWN.H):

   virtual HRESULT STDMETHODCALLTYPE QueryInterface(

       /* [in] */ REFIID riid,

       /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) = 0;

REFIID - указатель на CLSID и GUID. Как видим, эта функция принимает указатель на GUID в качестве входного параметра, и возвращает указатель на указатель на любой интерфейс (или NULL в случае неудачи). Соответственно, где-то в недрах этой функции должны быть манипуляции с GUIDами всех реализованных данным классом интерфейсов. Тело функции

10001C91                 push    dword ptr [esp+0Ch]

10001C95                 push    dword ptr [esp+0Ch]

10001C99                 push    offset off_10021190

10001C9E                 push    dword ptr [esp+10h]

10001CA2                 call    sub_10017A67

10001CA7                 retn    0Ch

По адресу 10021190 находится адрес уже знакомого нам GUIDа - это определённо то, что мы искали!

Вернёмся к исходной таблице VTBL, теперь мы точно знаем, какой метод как называется - мы можем пронумеровать их на основе имён и индексов методов, полученных при разглядывании библиотеки типов.

Сейчас нас интересует метод по индексу [3 + 4 + 0x17 - 1] (BuildPalette) - по адресу 10021104:

100068C3                 mov     eax, offset loc_10020B4A

100068C8                 call    __EH_prolog

100068CD                 sub     esp, 858h

100068D3                 push    ebx

100068D4                 xor     ebx, ebx

100068D6                 cmp     IsFull, ebx	; не напоминает ли это

100068DC                 push    esi            ; нечто знакомое?

100068DD                 push    edi

100068DE                 jnz     short loc_100068EA

100068E0                 mov     eax, 80020003h

100068E5                 jmp     loc_100069B4

...

100069B4                 mov     ecx, [ebp-0Ch]

100069B7                 pop     edi

100069B8                 pop     esi

100069B9                 mov     large fs:0, ecx

100069C0                 pop     ebx

100069C1                 leave

100069C2                 retn    8

100069C2 BuildPalette    endp ; sp =  4

В начале расположена стандартная для C++ инициализация exception frame handlerа, в регистр eax помещается адрес кода очистки стека, которому будет передано управление при возникновении неперехваченного исключения и вызывается функция инициализации нового фрейма обработки исключений.

А вот далее идёт некий код, сильно напоминающий проверку "если меня использует плохой парень, пойду-ка я к выходу" (я обозвал эту переменную IsFull). Проверим ещё один метод (скажем, ChangePaletteSize):

10006BA0 ChangePaletteSize proc near             

10006BA0

10006BA0 arg_0           = dword ptr  8

10006BA0 arg_4           = dword ptr  0Ch

10006BA0

10006BA0                 push    esi

10006BA1                 mov     esi, [esp+arg_0]

10006BA5                 cmp     dword ptr [esi+368h], 0

10006BAC                 jz      short loc_10006BF3

10006BAE                 cmp     IsFull, 0

10006BB5                 jnz     short loc_10006BBE

10006BB7                 mov     eax, 80020003h

10006BBC                 jmp     short loc_10006BF5

...

10006BF3 loc_10006BF3:

10006BF3                 xor     eax, eax

10006BF5

10006BF5 loc_10006BF5:

10006BF5                 pop     esi

10006BF6                 retn    8

10006BF6 ChangePaletteSize endp

Здесь снова проверяется переменная IsFull и ещё член данного класса по смещению 0x368. Если Вы проверите другие функции данного класса, Вы можете убедиться, что переменная по смещению 0x368 используется в логике работы данного компонента, в частности, она сбрасывется в 0 после вызова функции DeleteObject - скорее всего, это флаг наличия memory (compatible) bitmap - растрового изображения в памяти. А вот переменная IsFull устанавливается в функции по адресу 10001F4A, которая вызывается из функции DllMain.

Я не буду давать конкретных рецептов, как вылечить данную конкретную программу - во-первых, я не cracker, во-вторых, данный site занимается не взломом программ, а Reverse Engeneering, в третьих, автора этой программы зовут Mikhail Tchikalov, а ведь мы должны проявлять заботу об отечественном товаропроизводителе. И, наконец, в-четвертых, я дал достаточно информации - если Вам действительно нужен этот ActiveX компонент - Вы сами с ним справитесь.

Вместо этого я объясню ещё некоторые тонкости внутреннего строения компонентов ActiveX.

Итак, в этом компоненте нам повезло, и мы быстро нашли нужную VTBL. Но ведь это не единственный способ её нахождения. Мы могли бы изучить экспортируемую функцию DllGetClassObject (см. её описание выше). Она должна возвращать указатель на интерфейс IClassFactory, имеющий, кроме обязательных трёх методов интерфейса IUnknown пару своих, среди которых есть метод CreateInstance. Последний описан так (взято из файла заголовков UNKNWN.H):

HRESULT CreateInstance(

IUnknown * pUnkOuter, // этот аргумент используется для агрегации

REFIID riid,          // указатель на GUID требуемого интерфейса

void ** ppvObject     // возвращаемый указатель на интерфейс

);

Внимательным анализом этого метода можно достичь многого...

Также может помочь отслеживание ссылок на GUIDы прочих предопределённых интерфейсов. Их значения можно посмотреть с помощью oleview.exe в разделе "Interfaces". Некоторые значения перечислены здесь:
IDispatch: {00020400-0000-0000-C000-000000000046} - реализуется для OLE Automaton
IClassFactory2: {B196B28F-BAB4-101A-B69C-00AA00341D07} - реализуется для компонентов, поддерживающих механизмы лицензирования
IErrorInfo: {1CF2B120-547D-101B-8E65-08002B2BD119} - реализует подробное текстовое описание ошибок OLE Automation
IOleControl: {B196B288-BAB4-101A-B69C-00AA00341D07} - должен реализовываться компонентами ActiveX, которые можно поместить на форму Visual Basic или Delphi
ITypeInfo: {00020401-0000-0000-C000-000000000046}
ITypeLib: {00020402-0000-0000-C000-000000000046} - используется совместно с ITypeInfo в одной из предоставляемых фирмой Microsoft реализаций механизма IDispatch

Об этих и множестве других OLE интерфейсах Вы сможете прочесть в "OLE Programmers Reference".

Можно посоветовать ещё один способ нахождения нужной таблицы VTBL, основывающийся на факте, что фирма Microsoft предоставляет готовую реализацию механизма IDispatch, доступную при вызове функции CreateStdDispatch (находится в файле oleaut32.dll) и описанную так (взято из файла заголовков oleauto.h):

HRESULT CreateStdDispatch(

 IUnknown FAR* punkOuter, 

 void FAR* pvThis, 

 ITypeInfo FAR* ptinfo, 

 IUnknown FAR* FAR* ppunkStdDisp 

); 

Второй параметр представляет собой как раз указатель на VTBL таблицу реализуемого интерфейса. Третий параметр этой функции является указателем на реализацию интерфейса ITypeInfo (интерфейс, используемый для чтения библиотеки типов объекта). Для этого интерфейса Microsoft также имеет готовую реализацию, доступную при вызове функции CreateDispTypeInfo. Она находится в файле oleaut32.dll и определена так (взято из файла заголовков oleauto.h):

HRESULT CreateDispTypeInfo(

 INTERFACEDATA pidata, 

 LCID lcid, 

 ITypeInfo FAR* FAR* pptinfo 

);

Ещё один приём идентификации методов, участвующих в OLE Automation: большинство методов пользуются в качестве параметров специальным типом переменных Variant. Misrosoft предоставляет несколько функций для манипуляций с этой структурой (находящихся в файле oleaut32.dll): VariantInit, VariantClear, VariantChangeType, VariantI4FromStr и другие. Соответственно, можно найти ссылки на эти функции и с некоторой вероятностью утверждать, что если в некоем методе используется несколько этих функций, то либо этот метод, либо вызвавший его участвуют в механизме OLE Automation. Если какой-либо метод OLE Automation требует в качестве аргумента массива, его можно идентифицировать по вызовам функций, начинающихся с префикса SafeArray.

Помните: все строковые переменные, используемые в OLE Automation, являются строками UNICODE, как под Windows NT, так и под Windows 95/98!

Если компонент написан на Active Template Library (наиболее эффективно работающие и имеющие наименьший размер), то эта библиотека шаблонов применяет несколько иную реализацию интерфейса IDispatch (исходный код ATL, равно как и MFC, поставляются вместе с Visual C++). ATL использует интерфейс ITypeInfo, который извлекается вызовом метода ITypeLib::GetTypeInfoOfGuid. Сам же интерфейс ITypeLib можно получить при вызове библиотечной функции LoadRegTypeLib, содержащейся в oleaut32.dll. Прототип этой функции таков (взято из файла заголовков oleauto.h):

HRESULT LoadRegTypeLib(

 REFGUID rguid,             // GUID библиотеки типов

 unsigned short wVerMajor,  // старшая версия

 unsigned short wVerMinor,  // младшая версия

 LCID lcid,                 // код национального языка

 ITypeLib FAR* FAR* pptlib  // указатель на возвращаемое значение

); 

После столь сложного алгоритма получения интерфейса ITypeInfo используется его метод Invoke, который имеет такой прототип:

HRESULT Invoke(

 VOID FAR* pvInstance, 

 MEMBERID memid, 

 unsigned short wFlags, 

 DISPPARAMS FAR* pDispParams, 

 VARIANT FAR* pVarResult, 

 EXCEPINFO FAR* pExcepInfo, 

 unsigned int FAR* puArgErr 

);

Из всего множества его аргументов нас интересует только первый - pvInstance; это как раз и есть искомая таблица VTBL реализации интерфейса, чья библиотека типов была загружена в вызове LoadRegTypeLib. Если же библиотека типов находится в отдельном файле, для её извлечения может применяться другая функция - LoadTypeLib, также расположенная в oleaut32.dll

Как видите, исследование компонентов ActiveX, при наличии достаточных знаний, не представляет собою сложной задачи - мне даже не потребовалось запускать SoftIce. Reverse Engeneer'ы должны просто поблагодарить замечательную фирму Microsoft за изобретение и проталкивание столь легко идентифицируемого и незащищённого двоичного формата.

Несколько советов авторам ActiveX

Я, вроде бы как, сам отношусь к авторам, так что могу позволить себе давать советы. Помнится, на fravia было несколько дельных статей, касающихся общей защиты программ, но ведь их почти никто и никогда не читает, поэтому я кое-что повторю и оттуда тоже.

Итак, главная ошибка Mikhailа Tchikalovа заключается в том, что если Вы декларируете, что программа НЕ РЕАЛИЗУЕТ какую-либо функциональность, то Вы сами НЕ ДОЛЖНЫ помещать в её shareware версию код, их реализующий. Иначе кто-нибудь сможет без особых проблем получить полнофункциональную версию. Поэтому создавайте две версии - одну для продажи, другую для free evaluation. Хотя это тоже не спасёт надолго - стоит кому-либо купить у Вас одну полную версию и выложить её на какой-нибудь анонимный ftp сервер...

НЕ кодируйте жестко константы, представляющие собой ограничения вашей evaluation версии. В этой же программе, в методе CreateImage, я обнаружил следующий замечательный код:

                                                        ; нижеследующие проверки 

1000371D                 cmp     IsFull, 0		; производятся только 

10003724                 jnz     short loc_10003741	; для неполной версии

10003726                 mov     ecx, 140h

1000372B                 cmp     edi, ecx

1000372D                 jle     short loc_10003731

1000372F                 mov     edi, ecx

10003731

10003731 loc_10003731:                          

10003731                 mov     ecx, 0F0h

10003736                 cmp     esi, ecx

10003738                 jle     short loc_1000373C

1000373A                 mov     esi, ecx

1000373C

1000373C loc_1000373C:                          

1000373C                 cmp     eax, 8

1000373F                 jg      short loc_10003767

10003741

...

10003767 loc_10003767:

10003767

10003767                 pop     edi

10003768                 pop     esi

10003769                 xor     eax, eax

1000376B                 pop     ebx

1000376C                 pop     ebp

1000376D                 retn    14h

Константы 140h, 0F0h и 8h (после сравнения с которой, если значение больше 8, совершается возврат из функции) Вам ничего не напоминают? Лучше всего их вычислять динамически. Тогда пришлось бы запускать SoftIce и смотреть, что происходит.

Как ни странно, может помочь Visual Basic (подойдёт даже Component Creation Edition) - начиная с версии 5.0 он может не только использовать ActiveX компоненты, но и создавать их. А надёжно работающие декомпиляторы с VB 5 мне неизвестны (а с VB 6, кажется, ещё и в природе не существуют). С другой строны, я никогда не использую ActiveX компоненты, написанные на VB - они слишком ресурсоёмки.

Имеет смысл сделать несколько проверок "я работаю на машине плохого парня" - может быть, не просто выходить с гневным сообщением, а тихо и неправильно работать дальше

Поскольку в большинстве случаев VTBL Ваших классов лежит открытым текстом ( большинство инструментов, применяемых для создания компонентов ActiveX, поступают именно так), может иметь смысл сделать шифрование части программы (или сжать весь файл). Если я не сильно ошибаюсь, большинство инструментов для снятия дампа памяти с работающих программ (включая ProcDumper) не умеют корректно работать с отдельно лежащими DLL файлами. С другой стороны, экономически нецелосообразно писать защиту ценой несколько десятков долларов для защиты ActiveX компонента стоимостью 75 $

А главное - помните: развитию crackerов способствует низкий уровень жизни. Я не плохой парень, но я не могу позволить себе при среднемесячной зарплате чуть более 100 US $ покупать программы, стоящие немногим менее этой суммы. Таким образом, если, предположим, программа стоит 1000$, я могу позволить себе затратить на её взлом 1000 / 100 = 10 месяцев, т.е. если я взломаю её за меньшее время, это будет экономически целесообразно (мне сложно представить себе программу, которая не отдалась бы мне за десять месяцев...)! Поэтому бороться с crackingом иными мерами, кроме экономических, невозможно.

На этой печальной ноте позвольте откланяться.

Список литературы и on-line ресурсов

 
Том Армстронг "ActiveX: создание Web-приложений": Пер. с англ., издательство BHV, 1998, 592 стр.
У автора есть сайт по адресу www.WidgetWare.com
Adam Denning "OLE Controls Inside Out": Microsoft Press, 1995
Книга из разряда must be. Говорят, в 1998 году в Русской редакции вышел перевод, но я его в продаже не видел.
Джон Пьюполо "OLE: создание элементов управления": Пер. с англ., издательство BHV, 1997, 432 стр.
Учебник по написанию OCX компонентов с помощью библиотеки MFC. Весьма рекомендуется для настоящих (махровых таких) разработчиков OCXов
Д. Роджерсон "Основы COM": Пер. с англ., Русская редакция Microsoft Press, 1997, 376 стр.
Неплохой общеознакомительный курс "Что такое COM и с чем его едят"
Лоуренс Харрис "Программирование OLE. Освой самостоятельно за 21 день": Пер. с англ., издательство БИНОМ, 1995, 464 стр.
Вам потребуется явно больше трёх недель... :-)
"Справочник по Автоматизации": Пер. с англ., Русская редакция Microsoft Press, 1998, 440 стр.
Книга из разряда must be - наиболее подробный справочник по OLE Automation из всех мною виденных, да ещё и на русском языке!
Дэвид Чаппел "Технологии ActiveX и OLE": Пер. с англ., Русская редакция Microsoft Press, 1997, 320 стр.
Microsoft Development Network
На этом сайте можно найти множество информации о Win 32 и об OLE 2, в частности полный текст книги Крейга Брокшмидта "Inside OLE 2" (на английском, естественно), а также Win 32 SDK и OLE SDK. Требует небольшой регистрации, впрочем, бесплатно.
Рейтинг TOP 100 Бесплатные курсы английского языка в интернет