Flexlm: The Flexible lies manager.
XprismPro 1.0
Written
by SiuL+Hacky
Introduction |
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:
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.
Essay |
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):
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 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, 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
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:
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 (support@globes.com) for a description of these techniques.
Easy
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:
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 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:
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]; 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 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 :-)
0806428aleal 0xfffff7b4(%ebp),%eax 08064290 pushl %eax; pushl $0x0;
call 08078710 08064298 addl $0x4,%esp 0806429b movl %eax,%eax 0806429d pushl %eax; movl 0x10(%ebp),%eax 080642a1 pushl %eax; movl 0x80aebac,%eax 080642a7 pushl %eax; call 0808c0a0 080642ad addl $0x10,%esp
then the parameter comes from a local variable, let's see now the whole routine:
Reference to function : l_svk 0806423bcall 08087e30 08064240 addl $0x8,%esp 08064243 movl %eax,%eax 08064245 movl %eax,local_A; pushl $0x28; movl 0x80ae910,%eax; pushl %eax 08064253 leal local_B,%eax 08064259 pushl %eax Reference to function : memcpy call 0804977c 0806425f addl $0xc,%esp 08064262 movl 0x80ae910,%eax; movl 0x4(%eax),%ecx; xorl local_A,%ecx; movl %ecx,local_B+4; movl 0x80ae910,%eax; movl 0x8(%eax),%ecx; xorl local_A,%ecx; movl %ecx,local_B+8; leal local_B,%eax; pushl %eax; 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 (m
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 !
#!/usr/bin/perl ;############ MODIFY THIS LINE WITH YOUR PERL LOCATION ############ push(@INC,"/usr/lib/perl5"); require("flush.pl"); ;################################################################## ;######## LINUX DISASSEMBLER 2.01 ;######## (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 ;################################################################## $f_input=$ARGV[0]; $f_output=$ARGV[1]; &printflush(STDOUT, "\nCreating disassembled file ..."); $return=system("objdump -d -T -x --prefix-addresses ".$f_input.">".$f_output."2"); 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 permissions\n"; exit(1); } open(INPUT, "; while (!/.rodata/){ $_=; } ($rubbish, $rest)=split(/.rodata/,$_,2); ($rubbish, $rest)=split(/0/,$rest,2); @numbers=split(/ /,$rest,5); $size=hex($numbers[0]); $starting_address=hex($numbers[1]); $end_address=$starting_address+$size; $offset=hex($numbers[3]); open(CODIGO, " ; while (!/SYMBOL TABLE/){ $_=; } &printflush(STDOUT, "\nProcessing symbol table ..."); $_=; while (!/^\n/){ @st_element=split(/ /, $_); $_=$st_element[$#st_element]; chop; $symbol_table{$st_element[0]}=$_; $_=; } 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; chomp($destino); $salto{$destino}.=($direccion." \; "); } elsif (/call/){ ($direccion,$inst,$destino)=split(/ /,$_,3); $destino=~s/ //g; chomp($destino); $call{$destino}.=($direccion." \; "); } } seek(INPUT,0,0); &printflush(STDOUT, "\nWritting references ...\n"); open(OUTPUT, ">".$f_output) || die print "\nError opening write file\n"; print OUTPUT "FILE REFERENCED\n\n"; while (!/Disassembly of section .text:/){ $_=; print OUTPUT; } $char="."; $counter=0; while(){ $counter++; if ( ($counter % 400)==0){ printflush(STDOUT,$char); if ( ($counter % 4000)==0){ printflush(STDOUT,"\r"); if ($char eq "."){ $char=" ";} else { $char=".";} } } $copia=$_; $_=~s//GE; $_=~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; chomp($destino); if ( defined( $symbol_table{$destino} )){ print OUTPUT "\n"; print OUTPUT "Reference to function : ".$symbol_table{$destino}."\n\n"; } } if ( defined( $salto{$direccion} )){ print OUTPUT "\n"; print OUTPUT "Referenced from jump at ".$salto{$direccion}."\n\n"; } if ( defined( $call{$direccion} )){ print OUTPUT "\n"; print OUTPUT "Referenced from call at ".$call{$direccion}."\n\n"; } if (/\$/){ ($instruccion, $operand)=split(/\$/,$_,2); if (!/push/){ ($operand, $rest)=split(/\,/,$operand,2); } chomp($operand); $offset=hex($operand); if ( ($offset <= $end_address) && ($offset >= $starting_address ) ){ $auxiliar=substr($cadena, $offset-$starting_address); $length=index($auxiliar, pack("x") ); $auxiliar=substr($auxiliar, 0, $length); $auxiliar=~s/\n//g; print OUTPUT "\n"; print OUTPUT "Possible reference to string:"; print OUTPUT "\n\"$auxiliar\"\n\n" } } print OUTPUT $copia; } close(INPUT); close(OUTPUT); 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: