Flexlm, the Flexible lies manager.
XprismPro 1.0

our tools
Our tools

06 September 1998

by SiuL+Hacky


Courtesy of Fravia's page of reverse engineering

Well, well, well... while some of us have been sleeping in the sun, other (more serious) crackers have worked a lot. SiuL+Hacky's Linux essays are legendary, and here you have another VERY interesting paper. Since everyone and his dog is now switching to Linux (and the megaflop of Windows'98 contributes to this... man, anyone that went trough the ordeal of upgrading that idiotical Windoze's OS will switch to Linux, I believe) these essays are more and more important. Actually, d'you want an advice? Throw your windoze away, start working (was about time) as you should in Linux and start cracking the hell out of it following Siul+Hacky's steps!
Enjoy this great work, but be warned: this stuff is not for beginners, not at all.


There is a crack, a crack in everything That's how the light gets in


()Beginner ()Intermediate ( )Advanced ( )Expert

Flexlm: The Flexible lies manager.
XprismPro 1.0
Written by SiuL+Hacky


One of the most popular commercial schemes in the unix worl is Flexlm. It has been working for several years in the market, but I've never seen attempts to break it, probably, because it was not so popular in the win-world, and anyway it is not practically used in a shareware basis. It's used with huge software package that has more to do with warez. If you succede in grasping the idea of FlexLM, you'll see that it may try to stop making (ilegal) copies of legal software. It fails.

Tools required

Ltrace search for ltrace_0.3.2.tar.gz
Dasm Here !
perl interpreter ( available in linux distributions)
gcc (the same apply)
Flexlm programer's kit: http://www.flexlm.com
DDD http://www.cs.tu-bs.de/softech/ddd

Target's URL/FTP

XprismPro: http://www.khoral.com

The demo was removed some months ago, but it is used here as an example and some other linux program protected with Flexlm, could be used. Moreover, you could try also with Windows NT versions (I encourage you not to do it, even just for aesthetic reason :-).

Tools Update

There's a growing number of tools available for developing, but they are quite specific and only a few of them are for general use. They should be know by you already, but for those of you off the world you could have a look to these variations of the known gdb:

GDB 4.17

This upgrade provides gdb multithread capabilites, that until this version had no support at all.


I must admit the idea of smartgdb developers is really interesting, but i have to warn you too, that it still needs a lot of work for not disappointing you when you try to use it. The main improvements are firstly the multithread support, and secondly and more important, it carries a Tcl/Tk interpreter that allows you to write code that ameliorates the interface the way you want. Moreover, it gives you the chance to write procedures that take care of breakpoints.


After cracking with ltrace XPrismPro, i was really interested in some of the internals of the comercial protection used: FlexLm (Flexible License Manager). Probably many of you know it, many hardware cad tools use that crap, and you find it no matter you run Solaris or Windows NT (sorry for the last ones). XPrismpro (hey don't look, they removed the demo time ago !), carried a complete user manual of Flexlm, well that's nice for motivation, but there were a lot of questions unanswered. The main details available in the user's manual are (excerpt from flexlm user's manual):

  1. The license module in the client application finds the license file, which includes the host name of the license server node and port number of the license manager daemon, lmgrd.
  2. The client establishes a connection with the license manager daemon (lmgrd) and tells it what vendor daemon it needs to talk to.
  3. lmgrd determines which machine and port correspond to the master vendor daemon and sends that information back to the client.
  4. The client establishes a connection with the specified vendor daemon and sends its request or a license.
  5. The vendor daemon checks in its memory to see if any licenses are available and sends a grant or denial back to the client.
  6. The license module in the application grants or denies use of the feature, as appropriate.

I must add that the license file, a text file, contains a key (ten bytes i think) that authenticates the license. This license file and the vendor daemon must be provided by the software company that sells you the program. This is an exaple of license file (censored :-).

SERVER localhost.localdomain ANY
VENDOR khoral /usr/local/flexlm/v6.0/i86_l1/khoral
FEATURE xprismpro khoral 1.0 permanent 4 XXXXXXXXXXXX

I must admit i don't find the coherence. Do flexlm guys really think that network administrators are so worried with the lincensed/unlicensed software that the people run ? Is the access granted by the server, by the local license file, or by both ? I just can think the license server is located inside vendor's network, i mean, imagine i buy some crap to Adobe, and then everytime i run it, it connects with Adobe and grant me access. But that is fucking unflexible :-) !!!. In that case i was thinking it could not be so hard to make a fake server that grants access everytime.

Anyway, after reading that i had to visit flexlm web site and look for some enlightenment:


:-), firstly you can read there a ton of comercial shit about stop piracy, improve your sales and so on. Go to Flexlm product, and there is available an evaluation programmer's kit. Do you think flexlm programmer's kit is protected with flexlm :-DDDD ??? NO, of course, not. BTW, you can get that programmers kit from some mirrors (do ftpsearch, and look for install_flexlm.ftp).

Two of the files are provided encrypted, and of course you must begg for the key or start some reversing. A program is given to decrypt, and it nicely tells you if the code is wrong and what is the format of the code:

xxxx-xxxx-xxxx-xxxx-xx or 
                 xxxx-xxxx-xxxx-xxxx-xxx or 

One of the features of the decryption program is to provide the key in a text file. Perfect, don't forget it.

Run ltrace, of course, and you'll see that when you introduce a number in the format above, the last number (two or three ciphers) is converted to long (atol function) and the error message is constructed. Dasm the file and you'll see that the value returned by the function atol is compared with 0x100 (256), and you're fired if the value is greater. Then when you introduce the right format and the last number is less than 256, you receive a different error message. Looking at the new ltrace output, woowww, there's a call to sprintf that generates a string like xxxx-xxxx-xxxx-xxxx, hmmm, interesting. Now if you put this number you got from sprintf and keep the last number untouched, you'll get a good decryption key, but not good for the specific files. Fortunately, that sprintf is the only one present in the whole log.

The last task is to get the specific decryption key for flexlm files. I decided to use a funny approach and write a perl script that

  1. calls the decryption program through ltrace. Last number among [0-99]
  2. reads ltrace's log and builds a good key. Store it in a file and repeat.
  3. once you get all the good keys, extract the first one, put it in a file and check if it can decrypt flexlm files.
  4. if not, just try the next

in a few seconds you'll get the good decryption key that i'll not give you, but you can easily get with the power of perl :-).

Once you get the programmers kit (evaluation) available, let's start reading the manual, written in good html. I'll not punish you with the gory details but the conclusion i got was, how could it be patented ??? All the supposed security of the flexlm is based in keeping the methods secret. That is absurd, anyone could get this kit and you'll see how vendors are giving away in their daemons (or in their client programs) the information needed to build a license generator. The programmer's guide just gives you some clues of the encryption process, and some of the functions of Flexlm API (the ones used in the source code examples provided).

Now let's go to setup the whole thing and run an installation script. It will ask you the vendor daemon name, a pair of 8 bytes encryption seeds that MUST BE KEPT SECRET as they are the ones that are unique to your software, and 5 vendor keys supposedly provided by Flexlm guys. Of course, i had not that vendor keys. The script starts to compile your new daemon, but at the end you receive a message that the vendor keys provided are invalid or so.

If you now look at the Makefile and the software provided, this is a summary of what they supply in this kit:

  1. License manager: lmgrd
  2. Source examples for daemon and client program
  3. Some utilities to test lmgrd, build licenses and so on.
  4. Three libraries that must be linked and that contain Flexlm API, server and client side.
  5. Source code for the license generator: lmcrypt (remeber that name)

The last one is really surprising at the first glance, but as the interesting routines are in the libraries it's not that easy. Anyway it's impossible not to run lmcrypt just the second you see it. Run it and you'll get the stupid message about the vendor keys. Most of the compiled programs showed the same behaviour, so it is neccessary to:

May be any of you like the first option, i find it particularly boring. Hey, hey, one moment, that's what Flexlm guys says about the security of their product:

---------------------------------------------------------- Keeping Your Software Secure

No software is completely secure. FLEXlm is no exception. While GLOBEtrotter Software has made every effort to ensure the integrity of FLEXlm, all points of attack can never be anticipated. The following lists known points of vulnerability in FLEXlm in increasing order of difficulty to break. Globetrotter Software also maintains a list of techniques for making your implementation more secure - please contact technical support ([email protected]) for a description of these techniques.


Running the debugger on the application code if it is released with unstripped executables (on Unix) or as a debug version (on Windows).

Difficult, depending on application policy

Killing the daemons, since a majority of daemons must be up in order for anything to run, and a dead daemon is detected within the timer interval in a client. If, however, you do not use one of the built-in timers and you do not call HEARTBEAT(), then your software protection could be bypassed by someone who kills the daemons each time that the application reaches the maximum license limit, as the applications would never detect that the daemon went down.

Very Difficult

Guessing the license keys that belong in the license file. FLEXlm's standard authentication algorithm takes the user-visible data fields (number of licenses, expiration date, version number, vendor-defined string, feature name, host IDs of all servers, plus any optional authenticated fields) and combines them with the vendor's private encryption seeds to produce a license key. The algorithm used is a proprietary one-way block chaining encypherment of all the input data.

Writing a new daemon that emulates your vendor daemon. FLEXlm encrypts the traffic between client and vendor daemon to make this point of attack much more difficult.

Running the debugger on a stripped (Unix) or a non-debug (Windows) executable. This requires someone to find the FLEXlm calls without any symbol table knowledge.

-------------------------------------------- At the end of the essay you may set your own ratios :-).

I like this facility of english language to put qualifiers one after the other, even though is widely used in commercial stuff. Watch the pseudo-sophistication that it provides:

"The algorithm used is a proprietary one-way block chaining encypherment ..."

Back to the cracking, if you see the source code of lmcrypt.c, the error message is caused by a call to the function lc_init:

status = lc_init(prevjob, VENDOR_NAME, &code, &job);

if the function doesn't return 0, you know, beggar off. I felt really dissappointed patching the source code, so let's go back to our libraries and look for a global solution. There are three:


Usually you find two types of libraries: .a libraries (static) and .so (dynamic). So in this case there's a collection of static libraries that are statically linked to your code. How they are made? Well, very simple these files are just "ar" archives (similar to tar archives), and inside them there's a collection of object (.o) files. With "ar" command you may extract files, add, and so on. The syntax is the same of the tar command. There's also a very useful command for investigating the contents of a library: nm. This command gives you information about how is organized the library's functions and variables. This is just some interesting part of running nm libmgr.a

                 00000000 d VERSION
                          U calloc
                          U errno
                 00000004 d first
                 00000000 t gcc2_compiled.
                          U getenv
                          U l_getattr
                          U l_getattr_init
                          U l_malloc
                          U l_more_featdata
                 00000014 D l_n36_buff
                          U l_set_error
                          U l_sg
                 00000af0 T lc_first_job
                          U lc_get_attr
                 00000000 T lc_init
                 00000b10 T lc_next_job
                          U lc_set_attr
                 00000008 D lm_bpi
                 0000000c D lm_max_masks
                 00000010 D lm_nofile
                          U localtime

the numbers on the right is the location of the data or the function inside the object code file. If it is empty then the symbol was declared as external. So here we can see that the code of lc_init is inside file lm_init.o. You know what to do, extract lm_init.o, patch the code in a way lc_init returns always 0 and then replace the old lm_init.o in the library, with your patched file. This way if you rebuild all the programs, they will give you no problems with the vendor keys (at least less :-).

Now lmcrypt runs fine. It gets a just-made license file as the input (the checksum key must be equal to 0), and gets out a perfect valid license file. My very first idea was to crack the daemon in order to grant access always, so i realized debuggin a daemon (memory resident) program could be too hard without ltrace. The problem is that, although may be supported in the future, now ltrace doesn't log calls to functions inside the executable (even if they are available in the symbol table), and all the cool functions from the API are statically linked. What i tried next, was to make them dynamically linked. I know that needs an explanation, ok.

We've got three libraries full of object files, now if you extract those object files in a directory and then use gcc (C compiler if you don't, but should, know) to create a shared .so library, it could be possible to recompile the programs, indicating the compiler to link dynamically to the new .so library. I was not quite sure that it would work, but in about an hour i had my brand new version of lmcrypt dynamically linked. The command line for creating shared libraries is something like this:

gcc -shared -Wl,-soname,libflex.so.1 -o libflex.so.1.0 *.o

if you want to repeat this process, i got two unresolved references that have to be solved:

  1. Add an aditional object file lm_new.o that is compiled when you build the whole crap.
  2. Before creating the .so library remove lm_sapi.o file.

If you don't want problems, put the library in /usr/local/lib for instance, run ldconfig and it's ready to be used:

gcc -o lmcrypt lmcrypt.c -lflex

As you'll know soon, i got nothing with all this, but i'm telling you because i got a really good time with the process (the best of the whole crack) and may be useful for you in the future.

Anyway, like it was quite successful, i repeated the process compiling the daemon. It was not so hard and i got a dynamically linked daemon, but soon i realized i could not use ltrace with it for two reasons:

I finally decided to build my fat statically linked daemon. When you run it, it gives you a message about vendor keys don't support daemon mode and then it shows your vendorkey1, vendorkey2, vendorkey3 and vendorkey4. I forgot to tell you the evaluation kit had the limitation that it didn't support daemon mode. I patched the file, and i received a message about my vendor keys were over. I patched it too and then, i got no message but my deamon died when it was booting, creating a segmentation fault. In that case a "core" file is generated and can be debugged with gdb ( i just got there the command line for the daemon).

Ok, may be the vendor keys are necessary ... Then i got the daemon provided by khoral guys from XPrismPro and it booted with no error. Ok, why don't getting khoral vendor keys ????

Now is the moment to give the information about the encryption process and some data structure used by the software. The supposed process is:

a) for running the software, you get vendor keys 1,2,3,4 and 5.

b) lmcrypt creates the license. For the job it uses your PERSONAL AND SUPERSECRET encryption seed1 and seed2.

c) the daemon checks the encryption using encryption seed1 and seed2 xored with vendorkey5, to keep them secret and improve security :-DDDDD.

typedef struct  {
                                             short type;   
                                             unsigned long data[2];
                                             unsigned long keys[4]; 
                                             short flexlm_version;
                                             short flexlm_revision;
                                             char flexlm_patch[2];
behavior_ver[LM_MAX_BEH_VER + 1];
                                           } codes;

where "data" array keeps encryption seeds (xored with vendorkey5), and "key" array keeps vendorkeys1,2,3 and 4. If you go back to the definition of function lc_init, a pointer to a structure like this is passed to the function.

You'll notice that vendorkey5 is not present in the data structure, you can do a binary search in the daemon and it is no present as a redable data. It's quite easy to run DDD and in the call to lc_init, for instance, read khoral vendor keys1,key2,key3,key4, xored_seed1 and xored_seed2. All of them are carried in a the structure declared above.

I tried to use those vendor keys (and make up the five one :-) building my home daemon, but the performance was the same i obtained with my patching: the daemon dies with no error message. Ok, let's admit that the evaluation kit doesn't support daemon mode (or at least is not too obvious to fool it). Well let's concentrate now in the license generation.

What lmcrypt finally does is to call the function lc_cryptstr

int lc_cryptstr(job, str, return_str, code, flag, filename, errors)

that takes the text line of the license with the sensitive information (and the checksum key=0) and replaces the checksum key with the good value. The parameter "code", is the structure that we saw before, BUT seed1 and seed2 have been previously unxored, i.e., the original value of seed1 and seed2. Vendorkey5 is available to the program lmcrypt, so the unxoring is no mistery. The difference is that vendorkey5 is not provided to the daemon that checks the license to be good or bad.

To be honest i didn't believe such an algorithm that takes the ckecksum and says if it's good or bad without generating it again. Obviously vendorkey5 should be hidden somehow in the code ( i made some tests and vendorkey5 was not introduced in the code of the daemon ). It was a little bit tricky, but either way as vendorkey5 is a 4 bytes value, a brute force attack would not be so hard and the system would be quite unsecure.

Of course, my home daemon had a perfect symbol table, and the function used by lmcrypt (lc_cryptstr) was not used. It would be too obvious. Reading the documentation of lc_cryptstr, Globber guys says that function is an easier choice than using directly lc_crypt, a function that is not documented BTW. The declaration is something like:

lc_crypt (no_care, no_care, no_care, code);

code ? quite interesting. Let's look at the assembly listing of the daemon, read it backwords: ( i worked most of the time with khoral daemon, the one with the intersting seeds and no symbols, but it's more educational if you see the symbol names :-)

0806428a  leal   0xfffff7b4(%ebp),%eax
08064290  pushl  %eax;   <-- code "parameter.class" 
08064291  pushl  $0x0;   <-- paramter for

Reference to function : l_bin_date

08064293  call   08078710
08064298  addl   $0x4,%esp
0806429b  movl   %eax,%eax
0806429d  pushl  %eax;    <--- no care parameter3
0806429e  movl   0x10(%ebp),%eax
080642a1  pushl  %eax;    <--- no care parameter2
080642a2  movl   0x80aebac,%eax
080642a7  pushl  %eax;    <--- no care parameter1

Reference to function : lc_crypt

080642a8  call   0808c0a0 <<<< calling
080642ad addl $0x10,%esp

then the parameter comes from a local variable, let's see now the whole routine:

Reference to function : l_svk

0806423b  call   08087e30 
08064240  addl   $0x8,%esp
08064243  movl   %eax,%eax
08064245  movl   %eax,local_A; <--- the returned value
0806424b  pushl  $0x28;     <------- size of structure
0806424d  movl   0x80ae910,%eax; <-- pointer to "code" structure
08064252  pushl  %eax
08064253  leal   local_B,%eax
08064259  pushl  %eax

Reference to function : memcpy  <------ copy code "structure.class" to local_2

0806425a  call   0804977c
0806425f  addl   $0xc,%esp
08064262  movl   0x80ae910,%eax; <-- pointer to code "struc.class" 
08064267  movl   0x4(%eax),%ecx; <-- load seed1
0806426a  xorl   local_A,%ecx;   <-- some xoring
08064270  movl   %ecx,local_B+4; <-- update seed1
08064276  movl   0x80ae910,%eax; <-- pointer to code "struc.class" 
0806427b  movl   0x8(%eax),%ecx; <-- load seed2
0806427e  xorl   local_A,%ecx;   <-- more xoring
08064284  movl   %ecx,local_B+8; <-- update seed2
0806428a  leal   local_B,%eax;   <-- corrected code
08064290  pushl  %eax;   <---------- our parameter
08064291  pushl  $0x0

Reference to function : l_bin_date

08064293  call   08078710 
08064298  addl   $0x4,%esp
0806429b  movl   %eax,%eax
0806429d  pushl  %eax
0806429e  movl   0x10(%ebp),%eax
080642a1  pushl  %eax
080642a2  movl   0x80aebac,%eax
080642a7  pushl  %eax

Reference to function : lc_crypt

080642a8  call   0808c0a0 
080642ad  addl   $0x10,%esp

I put again the code structure to ease the analysis:

typedef struct  {
                           short type;
                           unsigned long data[2];
                           unsigned long keys[4];
                           short flexlm_version;
                           short flexlm_revision;
                           char flexlm_patch[2];
                           char behavior_ver[LM_MAX_BEH_VER + 1];
                         } codes;

OK, so vendorkey5 is supplied by a call to function l_svk (of course, not documented)

int l_svk(char*,codes*)

And my dear friends, the first parameter is the daemon name !!! I didn't analysed it deeply, but apparently the function uses vendorkeys2 and 3, together with vendor name, and builds on the fly vendorkey5, used to unxor seed1 and seed2.

Now we get all the data to rebuild the whole crap, and build all kind, types, sort of licenses, server redundants, node locked, permanent, blah, blah. HEY COMPANIES FROM THE WORLD, THIS IS YOUR SECURITY !!! I don't understand quite well what does a company as Lockeed Martin using Flexlm. Once you know the process, it's quite easy to look at function signatures and locate the sensible functions quickly. The whole security is based on secrecy, and that uses to be no security.

The license generation works perfectly if you want to try it. Anyway my doubts about the whole crap remains. Apparently XPrismPro doesn't use the daemon thing at all. Turn it on, turn it off, it only needs the license file, so i'll gotta wait to test the daemon weakness ( or not ). If anyone understands perfectly the daemon working, please tell me. What now I grasp it's that the idea is to spread n (identic) licenses in a company, and you can use just m (mn. The information for "m" is got from the license that has to be located in the local host and the server too.

That's all folks !!!


Because people use to ask it to me, i repeat here the code for dasm, but IT WAS AVAILABLE IN OLD ESSAYS TOO !


;######## (C) SiuL+Hacky Aug 1998
;######## You may copy, modify, distribute this program and
;######## is up you to keep this header here
;######## Usage: dasm exe_file dasm_file

&printflush(STDOUT, "\nCreating disassembled file ...");
$return=system("objdump -d -T -x --prefix-addresses
if ($return!=0){
  print "\nERROR OPENING OBJDUMP $return";
  print "\nUsage: dasm exe_file dasm_file";
  print "\nBe sure to get objdump in your path. Check also file

open(INPUT, "<".$f_output."2");

&printflush(STDOUT, "\nReading strings ...");
while (!/.rodata/){
($rubbish, $rest)=split(/.rodata/,$_,2);
($rubbish, $rest)=split(/0/,$rest,2);
@numbers=split(/  /,$rest,5);
open(CODIGO, "<".$f_input);

while (!/SYMBOL TABLE/){
&printflush(STDOUT, "\nProcessing symbol table ...");
while (!/^\n/){
 @st_element=split(/ /, $_);

while (!/\.text/){
&printflush(STDOUT,  "\nProcessing jmps and calls ...");

######### the regex gets rid of possible line information #############

while (){
  $_=~ s/<.*?>//g;
  $_=~s/  / /g;
  if (/j/){
    ($direccion,$inst,$destino)=split(/ /,$_,3);
    $destino=~s/ //g;
    $salto{$destino}.=($direccion." \; ");
  elsif (/call/){
    ($direccion,$inst,$destino)=split(/ /,$_,3);
    $destino=~s/ //g;
    $call{$destino}.=($direccion." \; ");

&printflush(STDOUT, "\nWritting references ...\n");
open(OUTPUT, ">".$f_output) || die print "\nError
opening write file\n";

while (!/Disassembly of section .text:/){
 print OUTPUT;
 if ( ($counter % 400)==0){
   if ( ($counter % 4000)==0){
     if ($char eq "."){ $char=" ";}
     else { $char=".";}
 $_=~s/  / /g;
 ($direccion, $inst, $destino)=split(/ /,$_,3);
 if ( defined( $symbol_table{$direccion} )){
   print OUTPUT "\n";
   print OUTPUT "---- Function :
".$symbol_table{$direccion}." ----\n";
 if (/call/){
   $destino=~s/ //g;
   if ( defined( $symbol_table{$destino} )){
     print OUTPUT "\n";
     print OUTPUT "Reference to function :
 if ( defined( $salto{$direccion} )){
   print OUTPUT "\n";
   print OUTPUT "Referenced from jump at
 if ( defined( $call{$direccion} )){
   print OUTPUT "\n";
   print OUTPUT "Referenced from call at
 if (/\$/){
        ($instruccion, $operand)=split(/\$/,$_,2);
        if (!/push/){
          ($operand, $rest)=split(/\,/,$operand,2);
        if ( ($offset <= $end_address) && ($offset >=
$starting_address ) ){
          $auxiliar=substr($cadena, $offset-$starting_address);
          $length=index($auxiliar, pack("x") );
          $auxiliar=substr($auxiliar, 0, $length);       
          print OUTPUT "\n";
          print OUTPUT "Possible reference to string:";
          print OUTPUT "\n\"$auxiliar\"\n\n"
 print OUTPUT $copia;
print "\n";
system("rm ".$f_output."2");

Final Notes

It's not my intention to harm any company that was fooled with this system, so as you could see no ready to use crack was released. I just wanted to show how poor is the protection scheme used in the license generation.

Ob Duh

I WILL bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one. Should you want to STEAL this software instead, you are a moron. This is the kind of software that WE NEED. Many people should register it and allow its Author to write even more interesting stuff!

You are deep inside Fravia's page of reverse engineering, choose your way out:

Fravia's main site
redhomepage red+ORC redanon redcounter measures redtools red+HCU Academy redstalking redreality cracking
redstudents' essays redcocktails redlinks redsearch_forms redmail_Fravia
redIs reverse engineering legal?