HACKINFO

Navigator

FlexLM без проблем.

Что же это, FlexLM?

FlexLM популярный менэджер лицензий от Globetrotter Inc. programm Flexlm. Его используют многие ведущие разработчики программного обеспечения, такие как Sun Microsystems, Apple, Corel, Adobe, для защиты своих программных продуктов. Полный список фирм есть на Web сервере фирмы. Там не меньше 100 названий. Обычно это дорогие интересные продукты (дешевле $1000 за 1 пользователя я еще не встречал. ;-) Система защиты существует для большинства известных UNIX платформ, MS Windows. Globetrotter считает свой продукт ведущим в области и самым надежным.

FlexLM позволяет производить контроль за запуском программ в сети, используя сервер лицензий. Система позволяет контролировать количество запущенных в сети версий продукта, ограничивать срок действия продукта, используемые версии. Общая схема защиты состоит из четырех компонент:

На сервере запускается менеджер лицензий и считывает конфигурацию из файла лицензий. Согласно конфигурации запускаются демоны производителей программного обеспечения, проверяющие соответствующие строки лицензий. Программное обеспечение обращается к серверу лицензий каждый раз привыполнении ответственных операций, а также по таймеру примерно раз в 120сек. - так называемый heartbeat. Расположение сервера определяется по переменной LM_LICENSE_FILE. Ее значение - или путь до файла лицензий или вида port@servername.

Формат файла лицензий.

Обычный файл лицензий выглядит так:

SERVER    servername    hostid    [port]
DAEMON  vendord /path/to/daemon/vendord [/path/to/file/license.opt]
FEATURE featurename vendord version expirationDate #n_licenses KEY "vendor_string" [OPTIONS]

Файл начинается со строки SERVER. Их так же может быть три, при системе из 3х серверов. Строка кроме ключевого слова содержит имя сервера, его hostid и номер порта, на котором будет висеть демон. Имя сервера используется лишь для проверки соответствия при запуске и в проверке лицензии не учавствует. Hostid - уникальный номер характеризующий сервер. В разных системах он получается по разному. Для SUN этот номер прошит в eeprom, для DEC, PC это MAC-адрес сетевой карты(для его получения требуется установка протокола IPX), для PC это так же может быть серийный номер раздела на котором установлена Windows или номер предоставляемый аппаратной заглушкой(Sentinel), etc. Вместо hostid может быть слово ANY, тогда номер указывается в параметре HOSTID в разделе options строки лицензии. Значение порта может быть опущено, оно будет взято из базы системных сервисов.

Строка DAEMON определяет путь до демона производителя софта. Третий, необязательный параметр - указатель на файл license.opt - опции для демона производителя. Таких строк может быть любое количество.

Далее следуют строки лицензий. Они могут начинаться словами FEATURE, INCREMENT, FEATURESET, PACKAGE, UPGRADE.

FEATURE, INCREMENT используются для описания лицензий. Ключевое слово INCREMENT может использоваться вместо FEATURE или для добавления дополнительных лицензий. Cуществует очень простое правило: для каждого featurename может быть только одна строка FEATURE и она должна стоять до любой из строк INCREMENT этого же продукта. Поддерживаются следующие опции:

В получении ключа лицензии (криптовании) используются только опции написанные заглавными буквами.

FEATURESET vendord KEY

FEATURESET строка используется для блокировки от изменения FEATURE линий в файле лицензий.

PACKAGE suite vendord version #licenses KEY COMPONENTS='comp1 comp2' OPTIONS=SUITE
FEATURE suite vendord version expiration_date #licenses KEY ""

PACKAGE строка используется для описания пакета программ. Компоненты описываются в виде feature[:version[:count]]. Если задано OPTIONS=SUITE, то строка FEATURE c соответствующим именем checked out (Пардон, даже не знаю как это сказать по-русски, но я надеюсь понятно). Если OPTIONS=SUITE не задано, соответствующая строка FEATURE игнорируется.

FEATURE featurename vendord version expdate #lics KEY ""
UPGRADE featurename vendord version new_version expdate #lics KEY ""

UPGRADE строка преобразует указанное количество лицензий с версии version до new_version.

#lics = 0. Отдельный случай - когда количество лицензий в строке - ноль. Такие лицензии привязываются к конкретной машине. Для работы такой строки она содержит дополнительные опции, такие как DEMO, HOSTID=12345678, DOMAIN="uma.net", DISK_SERIAL_NUM=1234567. Такие строки игнорируются license демоном и проверяются самим продуктом. Сервер для таких лицензий не требуется.

KEY во всех описанных случаях, это 20 символьная строка из символов {0, 1, ..., E, F}. 64-bit значение, полученное криптованием поданной строки, представляется с помощью алфавита из 16 символов (4-bit на символ). К полученной строке добавляются еще 4 символа (позиции 2, 4, 6, 8 KEY). Эти символы выбираются по (?) закону для новых ключей или сохраняются старые значения при их регенерации.

Как это лечится.

Я думаю это самый интересный для всех вопрос. Можно ли сделать самому лицензии для этого "крутейшего и надежнейшего" диспетчера лицензий N1?

Правильный ответ - ДА! Вы будете смеяться, но хваленый продукт не имеет никакой защиты от изучения кода(!) и проходится любым дебаггером. Единственная "защита" - отрезанная символьная таблица имен. Это тем более смешно, что некоторые системы позволяют эту информацию восстановить (Solaris например). Более того, продукт имеет оригинальную схему проверки ключа - считается правильный ключ и сравнивается с данным. Это дает замечательную возможность создавать собственные ключи любого содержания.

Сначала немножко теории. Как получается ключ лицензии? Защищаемая строка и hostid сервера для которого она предназначена шифруются с двумя 32-битными числами - ENCRYPTION_SEEDs. Эти два числа являются секретными и выбираются производителем. Как показывает опыт, обычно продукты для разных платформ одного производителя имеют одинаковые ENCRYPTION_SEEDs, для возможности инсталляции license сервера на произвольной платформе.

	mov     	20, %o2
	mov     	%i0, %o1
	call    	strncmp
	mov     	%i4, %o0
	orcc    	%g0, %o0, %g0
	bne,a   	0x16ef0
	nop

Это фрагмент проверки ключа лицензии. Ключ просто сравнивается вызовом функции strncmp c ограничением длины 20 символами - длиной ключа. Сразу за этим идет сравнение возвращенного значения %o0 с нулем %g0 - равенство строки. (Версии 6.x используют внутреннюю процедуру для сравнения.)

lu_gethostid()
sethi %hi(0xfffffc00), %g1 add %g1, 0x330, %g1 save %sp, %g1, %sp add %fp, -101, %o1 mov 7, %o0 mov 100, %o2 call sysinfo // sysinfo, function=7: SI_HW_SERIAL nop add %fp, -101, %o0 add %fp, -108, %o2 sethi %hi(0x61c00), %o1 or %o1, 0x15c, %o1 call sscanf nop ld [%fp - 108], %o0 ba m001 nop m001: mov %o0, %i0 // host_id ret restore

lu_gethostid получает и возвращает hostid системы.

Для получения лицензии для произвольного хоста необходимо составить файл лицензий с необходимыми опциями, иметь vendor daemon производителя продукта и минимум времени.
Совет: при составлении файла лицензий располагайте линии с одинаковым именем продукта друг за другом. FEATURE line должна быть первой. Порядок версий значения не имеет. Одинаковые линии инкремента должны различаться строкой в кавычках ("0", "1", etc.). Впишите временные произвольные значения 20-символьных ключей. Главное - они не должны повторяться!

Далее можно действовать 2мя способами - дебаггером или с помощью своей библиотеки. Какой из них применять - решать вам. Для демонов версий 6.x приемлим только второй способ.

Если вы хотите использовать библиотеку - вам необходимо сделать две свои функции - strncmp() и lu_gethostid(). lu_gethostid() должна возвращать необходимый u_long hostid. strncmp() должна выдавать в stderr значения подаваемых строк если заданная длина = 20. Загрузите эту библиотеку перед штатными, например определив LD_PRELOAD, и получайте удовольствие (см. приложение A)

Второй способ использует дебаггер, например adb (Solaris). Необходимо дизассемблировать демон производителя и найти в нем описанные выше места - проверку ключа и получение hostid. Для демонов версий 6.x удобнее всего искать по XORу кодов производителя (см. раздел ниже). Далее vendord запускается в дебаггере. На найденные точки ставятся остановы и необходимая информация подменяется или просматривается.

:s -T server 4 -c license.dat	# Command line
ls_feat_validate+0x248 :b	# Check for correct code (suntechd)
lu_gethostid+0x3c :b		# Get hostid
:c							# Continue
12345678 >o0				# Hostid in o0	(lu_gethostid+0x3c)
:c							# Continue
<o0,20 ?c				# Check for correct code, o0 - correct, o1 - taken, (ls_feat_validate+0x248)
:k							# Kill
$q							# Quit

Оба способа легко автоматизируются для обработки больших списков или лицензий нескольких производителей одновременно.

Универсальный генератор лицензий

Bravissimo! Я думаю вы прочитали предыдущий раздел и поняли что создавать лицензии для FlexLM приятно и занимательно. Но что делать если вам нужно создать много лицензий, скажем 50? А для разных vendord? ;) Парни из Globetrotter предусмотрели и это. Спасибо!
Как я уже говорил выше, проверкой лицензий храняшихся в license.dat занимается vendord. Разумеется и программный продукт имеет в своем составе все необходимые процедуры для анализа лицензий и общения с сервером лицензий. Все vendord созданы по единому шаблону. Отличаются они именем, ключиками производителя vendorkeys) и (ну конечно!) версией "набора кубиков", из которого собраны. Производитель может переопределить некоторые функции демона, расширяя его функциональность.
Имя демона - его название которое фигурирует в файле лицензий. Проверяя этот файл, демон игнорирует "чужие" строки.
Ключики производителя. Это структура, которая содержит названные выше ENCRYPTION_SEEDs и защитные коды Globetrotter Inc. Существует несколько версий структуры:
Версия 1. Первая версия, не имеет защиты и уже не встречается.

{
	short type;              /* Type of structure = 1 */
	long data[2];            /* ENCRYPTION_SEEDs: x, y */
}

Версия 2. Старая версия. Используется в suntechd, licsrv, etc.

{
	short type;              /* Type of structure = 2 */
	long data[2];            /* ENCRYPTION_SEEDs: x, y */
	long keys[3];            /* VENDOR_CODE: k1, k2, k3 */
}

Версия 3. Похоже промежуточная версия. Ни разу не встречал. Не могу сказать, что находится в поле ENCRYPTION_SEEDs (см. тип 4).

{
	short type;              /* Type of structure = 3 */
	long data[2];            /* ENCRYPTION_SEEDs: ?, ? */
	long keys[3];            /* VENDOR_CODE: k1, k2, k3 */
	short flexlm_version;    /* DAEMON_VERSION */
	short flexlm_revision;   /* DAEMON_REVISION */
}

Версия 4. Текущая, "популярная", версия. Вы можете найти ее в большинстве в настоящее время работающих демонов. ENCRYPTION_SEEDs хранятся не в открытом виде, а XOR их с ключем k5. Восстановление x и y происходит непосредственно перед криптованием.

{
	short type;              /* Type of structure = 4 */
	unsigned long data[2];   /* F(ENCRYPTION_SEEDs): x^k5, y^k5 */
	unsigned long keys[4];   /* VENDOR_CODE: k1, k2, k3, k4 */
	short flexlm_version;    /* DAEMON_VERSION */
	short flexlm_revision;   /* DAEMON_REVISION */
}

Версия 5. Это самая последняя версия, встречается только в демонах версии 6.x (sunlicd, rational). Структура является расширением структуры версии 4 и имеет тип структуры = 4.

{
	short type;                   /* Type of structure = 4 */
	unsigned long data[2];        /* F(ENCRYPTION_SEEDs): x^k5, y^k5 */
	unsigned long keys[4];        /* VENDOR_CODE: k1, k2, k3, k4 */
	short flexlm_version;         /* DAEMON_VERSION */
	short flexlm_revision;        /* DAEMON_REVISION */
	char flexlm_patch[2];         /* DAEMON_PATCH */
	char behavior_ver[5];         /* DAEMON_VER_BEHAVIOR */
}
Важное замечание Эти структуры имеют выравнивание(align) по 4 байта - DWORD. После первого поля в каждой из структур - Type(short, 2 byte) - следуют 2 нулевых байта выравнивания.

Структура содержит:

Поиск ключей в демоне. Для Solaris структура находится по символьному имени vendorkeys в дебаггере. Для Windows и OS в которых невозможно восстановить таблицу имен это очень просто делается в Hex-редакторе (например HIEW - PC). Ищется заголовок структуры с учетом порядка следования байт (little-endian, big-endian) и выравниванием - 4 байта. За заголовком должны следовать соответствующее количество ненулевых байт кода и версия библиотеки (хотя возможно у вас версия 2? ;-)).
Пример: PC, gdlm.exe, структура v4, библиотека v5.12:

04 00 00 00   65 7a e0 ba   1f 6c d0 ae   f0 9d 04 23
16 0d 84 9e   d8 aa b1 c5   bc 31 16 9d   05 00 0c 00
Искомая структура: 0x00040000, 0xbae07a65, 0xaed06c1f, 0x23049df0, 0x9e840d16, 0xc5b1aad8, 0x9d1631bc, 0x0005000c
Для получения k5 и значений x, y приходится использовать дебаггер. Значения x и у востанавливаются непосредственно перед шифрованием. Вы легко найдете вызов lc_crypt или lc_crypt_private перед вызовом strncmp при проверке ключиков лицензий и соответствующие ХOR до него:
Solaris, демоны до v5.x:
ld           [%i5 + 4], %o3
ld           [%i5 + 8], %o5
xor          %o3, %o0, %o3   ! vendorkeys
xor          %o5, %o0, %o5
st           %o5, [%fp - 24]
Solaris, демоны v5.x, 6.x:
ld           [%fp - 60], %o0
sethi        %hi(0x9e000), %o1
or           %o1, 0x244, %o1 ! vendorkeys
call
nop
st           %o0, [%fp - 52]
ld           [%fp - 48], %i2
ld           [%i2 + 4], %i3
ld           [%fp - 52], %i4
xor          %i3, %i4, %i5
st           %i5, [%fp - 40]
ld           [%fp - 48], %o0
ld           [%o0 + 8], %o1
ld           [%fp - 52], %o2
xor          %o1, %o2, %o3
st           %o3, [%fp - 36]
Windows, v5.c:
call    sub_414D40                  ; Здесь возвращается k5 в ax
lea     edi, [esp+864h+var_824]
add     esp, 8
mov     ecx, 8
rep movsd
mov     ecx, eax
xor     eax, ds:dword_4251A0        ; xor k5, x
xor     ecx, ds:dword_42519C        ; xor k5, y
mov     [esp+85Ch+var_81C], eax
mov     [esp+85Ch+var_820], ecx
================ Или так:
call    sub_411660                  ; Здесь возвращается k5 в ax
add     esp, 8
mov     ecx, [ebx+4]                ; x
xor     ecx, eax
lea     edx, [ebp+48h]
mov     [esp+3Ch+var_1C], ecx
mov     ecx, [ebx+8]                ; y
xor     ecx, eax
lea     eax, [esp+3Ch+var_20]
mov     [esp+3Ch+var_18], ecx

Описанное выше дает две возможности:

1. Случай который был у меня совсем недавно: Необходимо было сделать лицензии для демона insignia старой версии под AIX, coff формат. Практически не было возможности ни дизассемблировать демон, ни применить свою библиотеку.
Решение: В демоне в Hiew была найдена структура VENDOR_CODE версии 2. В дебаггере был запущен suntechd (VENDOR_CODE 2). Останов на процедуре инициализации FlexLM - lc_init, здесь я поменял имя демона и значения структуры VENDOR_CODES. Далее, следуя способу описанному в предыдущей главе, я получил необходимую лицензию - Softwindows 1.0 AIX.

2. Написать свою программу для обработки списка лицензий. Простейший пример приведен в приложении B. Его можно модифицировать, например, для установки на web-сервер. Для "красивой" генерации лицензий можно использовать утилиту genlic32.exe для Windows из FlexLM SDK.

=================================

Ну на этот раз вроде все ;-).

Если я что-то забыл написать или написал не ясно, пишите, я буду рад.

Vyacheslav.

e-mail: vinny@nix.ru



Приложение А

Образец дополнительной библиотеки для vendord. По многочисленным просьбам помещаю этот фрагмент. Образец написан под Solaris и SunPro C-compiler. Для других платформ/компиляторов команды могут меняться. (Например, GNU gcc использует -fpic вместо -Kpic, etc...).

/*========== libcrack.c ===========*/
#include <stdio.h>
int strncmp(char* s1, char* s2, int len) {
	int i;
	if(len == 20 && *s1)
		fprintf(stderr, "CMP: given: %s, correct: %s\n", s2, s1);
	for(i = 0; i < len; i++) {
		if(!s1[i]) return (-1);
		if(!s2[i]) return 1;
		if(s1[i] < s2[i]) return (-1);
		if(s1[i] > s2[i]) return 1;
	}
	return 0;
}
/*=================================*/
Компилируем:
# cc -Kpic -G -o libcrack.so libcrack.c
Добавляем текущую директорию в LD_LIBRARY_PATH, если его там нет.
# setenv LD_LIBRARY_PATH .:/lib:/usr/lib
Определяем библиотеку для предварительной загрузки.
# setenv LD_PRELOAD libcrack.so
Запускаем демон производителя.
# vendord -T hostname 4 -c license.dat
Смотрим результаты, корректируем license.dat, проверяем. ;)

Приложение B Простейший пример программы для генерации списка лицензий. Borland C++ 5.02. Выключена генерация underscores ("_"). Программа использует библиотеку lmgr325c.dll. IMPORTS: lc_init, lc_perror, lc_cryptstr.
Аргументы: входной файл и выходной файл. input_file - необходимый вам файл лицензий, ключи могут быть заменены на 0.
// This program is to be compilled with Borland C++

// Turn off underscore generation!
#define puts      _puts
#define exit      _exit
#define perror    _perror
#define fopen     _fopen
#define stat      _stat
#define malloc    _malloc
#define strlen    _strlen
#define fgets     _fgets
#define printf    _printf
#define fputs     _fputs
#define main      _main
//----------------------------
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <malloc.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <dos.h>
#include <conio.h>

// FlexLM definitions
#include "lmclient.h"

LM_HANDLE *lm_job;

// GDlm daemon parameters
char* vname = "GDlm";
char site_code[] = {	   0x04, 0x00, 0x00, 0x00,
   0x12, 0x11, 0x57, 0x13, 0x68, 0x07, 0x67, 0x07,
   0xf0, 0x9d, 0x04, 0x23, 0x16, 0x0d, 0x84, 0x9e,
   0xd8, 0xaa, 0xb1, 0xc5, 0xbc, 0x31, 0x16, 0x9d,
   0x05, 0x00, 0x0c, 0x00};

void die(char* str) {
   perror(str);
   exit(1);
}

void main(int ac, char* av[])
{
   FILE *iff, *off;
   char *buf, *c, *res = NULL, *err;
   struct stat stt;

   // First we should init library.
   if(lc_init(NULL, vname, site_code, &lm_job)) {
       lc_perror(lm_job, "lc_init failed");
       exit(-1);
   }
   // Prepare files
   if(ac != 3) {
      printf("Usage: %s input_file output_file\n", av[0]);
      return;
   }
   iff = fopen(av[1], "rt");
   if(iff == NULL) {
       die("fopen infile");
   }
   off = fopen(av[2], "wt");
   if(off == NULL) {
       die("fopen outfile");
   }
   // Create buffer for input file and load it.
   if(stat(av[1], &stt)) {
       die("stat");
   }
   c = buf = (char*)malloc(stt.st_size);
   if(c == NULL) {
       die("malloc");
   }
   while(fgets(c, (int)stt.st_size, iff)) {
       c += strlen(c);
   }
   // Run crypt routine; Output buffer (res) will be allocated by routine;
   // Use LM_CRYPT_FORCE to force crypt all lines, even with keys are already set.
   if (lc_cryptstr(lm_job, buf, &res, site_code, LM_CRYPT_FORCE, av[1], &err)) {
       printf(err);
   }
   // If we have any output, lets write it.
   if(res) {
       fputs(res, off);
   }
}


Приложение C FlexLM FAQ

Q.

А вот э...
A.1. Прочитайте внимательно текст.
2. Обдумайте его.
3. Если все еще что-то не ясно, прочитайте еще раз и обдумайте.
4. Повторяйте пункты 1 и 2 до полного усваивания. ;)
Вы будуте смеяться, но ответы на многие заданные вопросы были прямо в тексте.

Q. Я дизассемблировал vendord и нашел 2, рядом расположенных, вызова strncmp с длинной строки 20. Какой из них нужный?

A. Да, во многих (но не во всех) демонах, в основном последних версий, 2 вызова strncmp. Я точно не могу сказать для чего это сделано. Демон использует оба, передавая в одном из случаев пустую строку. Приведенная в приложении А библиотека это учитывает. Если вы используете дебаггер, ловите оба вызова и проверяйте аргументы.

Q.

Влияют ли на что-нибудь значения в позициях 2, 4, 6, 8 ключа лицензии?
A. (Спасибо George G. за это наблюдение) Bandwidth Manager требует наличия в этих позициях определеных значений (сокращения от имени и номера версии?: B,A,0,1). Для остальных существующих продуктов никакого влияния не замечено (пока ;-)).

Q.

После инсталляции FlexLM, lmgrd запускается с привиллегиями root. Это необходимо?
A. Нет, необходимости в этом нет, более того это опасно. vendord создают lockfile для предотвращения запуска еще одной копии. Этот файл создается без проверки следования линкам, root -rw-rw-rw-. Проверьте свою версию, например suntechd:
Остановите сервер лицензий, владеющий suntechd.
Создайте линк на /.rhosts с именем lock-файла: $ ln -s /.rhosts /var/tmp/locksuntechd
Запустите сервер и проверьте наличее /.rhosts.

Q.

Как защититься от создания /.rhosts?
A. Запускать lmgrd от пользователя nobody или специально созданного:
su nobody -c "${lmgrd} -c ${license} > ${logfile} 2>&1 &"

Q.

А где бы мне раздобыть FlexLM SDK для моей платформы?
A. Ну это же просто! Регистрируетесь на http://www.globetrotter.com/ для получения ключа для расшифровки и скачиваете необходимые файлы из ftp://ftp.globes.com/flexlm.

Q.

Please in english!
A. Hello folks! This text has no english version. Sometime I'll write the one of the kind. But you should understand, that its not too easy to type-in several hundred lines of text. Please do not disturb me with such a questions. Take your favorite language convertor (in Russia we use Stylus for instance) and translate the article.
Сайт создан в системе uCoz