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

Introduction

One of the most popular commercial schemes in the unix world 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 on a shareware basis. It's used with huge software packages that have more to do with warez. If you succeed in grasping the idea of FLEXlm, you'll see that it may try to stop making (illegal) 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 (as above)
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 known to you already, but 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.

SMARTGDB 0.9

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.

Essay

After cracking with ltrace XPrismPro, I was really interested in some of the internals of the commercial protection used: FLEXlm (Flexible License Manager). Probably many of you know it, many hardware CAD tools use it, and you find it no matter you run Solaris or Windows NT (sorry for the last ones). XPrismpro carried a complete user manual of FLEXlm, well that's nice for motivation, but there were a lot of questions unanswered. 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 example of a 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't think the license server is located inside vendor's network, 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's web site and look for some enlightenment.

Firstly you can read there a ton of commercials about stopping piracy, improving 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. 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 beg 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 nnnn-xxxx-xxxx-xxxx-xxxx-xx

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, 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), 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 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 no 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 its 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. I like this facility of english language to put qualifiers one after the other, even though it 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 :-

liblmgr.a
liblmgr_as.a
liblmgr_s.a

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

lm_init.o: 
                 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 are the locations 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. 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 realised debugging a daemon (memory resident) program could be too hard without ltrace. The problem is that, although it 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, U 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, maybe the vendor keys are necessary ... Then I got the daemon provided by khoral guys from XPrismPro and it booted with no error. 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.

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;

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 not present as readable 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 the structure declared above.

I tried to use those vendor keys (and make up the fifth 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 mystery. 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, Globe 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 backwards : (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; <-- paramater for l_bin_date

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 lc_crypt
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 show 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 analyse 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 all types of licenses, node locked, permanent. HEY! COMPANIES OF THE WORLD, THIS IS YOUR SECURITY!. I don't understand why a company such as Lockheed Martin is 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.

The license generation works perfectly if you want to try it. Apparently XPrismPro doesn't use the daemon thing at all. Turn it on, turn it off, it only needs the license file. Because people keeping asking me, I repeat here the code for dasm.

Download Dasm in a zip here.

Final Notes

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