flexlm after two years again...
published by tsehp
Solidedge - a well designed mid range CAD system. I'm not lying, kidding or something like this... It's truly good CAD application and I don't know why, EDS (the manufacturer) is releasing this for free. Again I found it on a magazine's cover CD. Not bad, I thought, but my license generator screw up this time...
Let's go on. After some initial sniffing (Bounschecker may help you) you can discover the dynamic link library which opens and reads your license file (selicense.dat). You said you didn't created a license file? Well, I think there's in your directory almost empty license file. Of course is the license key missing.... It looks like this: FEATURE SOLIDEDGECLASSIC sedemon 11.0 permanent uncounted \ 123456789ABC HOSTID=ANY ISSUER="I don't know" Let's look around. Our ugflex.dll is missing and was replaced by another, less suspicious name. Of course I'm speaking about jutil.dll, but what: I've discovered a MAP file of this library. The format is following: Address Publics by Value Rva+Base Lib:Object 0001:00000010 _JSetVACallback@4 5a001010 f auto.obj 0001:00000040 ?IsAutomationEnabled@@YAHXZ 5a001040 f auto.obj 0001:00000050 ?IsLicenseValidForAutomation@@YAHXZ 5a001050 f auto.obj In order to use this MAP file with idasym.exe remove the Rva+Base and Lib:Object columns. Then use the created sym file with symbol loader (it will be easier to set up breakpoints and of course understanding what's going on). From the past we know that the actual checking of the license is inside the LC_CHECKOUT routine (extracted from flexlm reference manual): lc_checkout() SYNTAX status = lc_checkout( job , feature , version , num_lic , flag ,code , dup_group ) DESCRIPTION Checks out one (or more) license(s) of the specified feature. If the process that calls lc_checkout() exits in any manner, then the checked-out license will be returned for re-use by another user. For TCP clients, the resulting checkin is immediate. Place the call to lc_checkout() in an executable that is active whenever the user is using the feature. If flag is specified as LM_CO_WAIT, then the process will wait until the number of licenses requested for this feature are available. The license file must specify a version that is greater than or equal to the version in the lc_checkout() call. If the license file is counted, that is, if the number of users specified on the FEATURE line is non-zero, lc_checkout() will request the license from a license server. If the number of users on the FEATURE line is uncounted, it will grant permission based on the contents of the license file onlyŠhostid, version, expiration date, etc. The _lc_checkout at 5a04cf70 is present. Good place to break. Looking for feature names we can bulid up our license file using above format. For correctness I will provide all feature names: SOLIDEDGECLASSIC, SOLIDEDGEADVANCEDPAR, SOLIDEDGEXPRESROUTE, SOLIDEDGEFEATURERECO, SOLIDEDGEXPAND3D, SEWEBPUBLISHER, SOLIDEDGEHANDBOOK, SOLIDEDGEMANAGER (note: some features like expand3d are checked out in another executable - we speak about it later). Looking further you might pass the l_good_lic_key routine. The _lc_checkout hits three times and the _l_good_lic_key just once. Suspicious? It is. Here are some code snippets from IDA: 5A04DA6C loc_5A04DA6C: ; CODE XREF: _lm_start_real+343j 5A04DA6C mov edx, [ebp+arg_14] 5A04DA6F push edx ; the vendorcode struct (this time improved - encrypted) 5A04DA70 mov eax, [ebp+var_20] 5A04DA73 push eax 5A04DA74 mov ecx, [ebp+arg_0] ; our license information 5A04DA77 push ecx 5A04DA78 call _l_good_lic_key ; go and check it 5A04DA7D add esp, 0Ch 5A04DA80 test eax, eax 5A04DA82 jnz short loc_5A04DA89 ;this should be familiar to you :-) You ran the part.exe and nothing happens? Well, that's because you didn't run the seiges.exe as I did. They places some antidebug features into a part.exe. They've learned something. But just to make intrigues. An idea got by a brainstorming? It's funny. And are we interested in this way? I don't believe it. Let us run seiges.exe. It seems, they left it behind. Running seiges.exe and setting eax to nonzero you'll get status = ok. Patching the executable isn't very effective, so we look further: 5A04E995 add edx, 54h . . 5A04E99C push eax 5A04E99D call _l_extract_date . . 5A04E9B1 push ecx ;our vendorcode structure 5A04E9B2 mov edx, [ebp+var_CC] 5A04E9B8 push edx ;feature name 5A04E9B9 mov eax, [ebp+arg_4] 5A04E9BC push eax 5A04E9BD mov ecx, [ebp+arg_0] 5A04E9C0 push ecx 5A04E9C1 call _l_ckout_crypt Tracing this routine we see that _l_ckout_crypt does some important work. There are some new keys too. Here are changing only the seed values. Seeing that the keys weren't changed, we may suppose that these keys are now decrypted, but the seed values are still hidden in some way. Further tracing shows us the _real_crypt routine (scary name, isn't it?) 5A050B44 mov eax, [ebp+arg_0] 5A050B47 push eax 5A050B48 call _real_crypt 5A050B4D add esp, 10h After some time of tracing I guessed that _real_crypt does the work. I felt it like a heartbeat. This name sounds like invitation: " Come and discover. " Some snippets from real crypt: . . 5A050ECB push ecx 5A050ECC call _l_getattr ; get license attributes . . 5A05106A push eax 5A05106B call _l_good_bin_date ; date . . 5A0510DA mov edx, [ebp+arg_0] 5A0510DD push edx 5A0510DE call _move_in_hostid ; move in hostid from license file . . 5A051456 push offset aDup_group ; "DUP_GROUP" ;DUP_GROUP=UHD means the duplicate grouping is (DUP_USER|DUP_HOST|DUP_DISPLAY), so for a user on the same host and display, additional uses of a feature do not consume additional licenses. 5A05145B mov eax, [ebp+arg_4] 5A05145E mov ecx, [eax+94h] 5A051464 push ecx 5A051465 call _addi ;add that to license . . 5A051855 push ecx 5A051856 call _l_ckout_string_key ;SO WHAT ABOUT THIS? 5A05185B add esp, 18h The _l_ckout_string_key took my attention. The local variables are filled each after other (loc.5A052CFF and below), instead to be filled from table. This could prevent an attacker from building an automatic key extactor (it's just my hypothesis). Anyway, it is a pretty unefficient way to store information. And of course, the only call reference to this routine did it too. 5A052902 _l_ckout_string_key proc near ; CODE XREF: _real_crypt+AD1p 5A052902 push ebp 5A052903 mov ebp, esp 5A052905 sub esp, 1E4h . . . 5A053755 call _our_encrypt2 ;some new scary names (james bond would be better...) 5A05375A add esp, 4 5A05375D jmp short loc_5A05376C 5A05375F ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ 5A05375F 5A05375F loc_5A05375F: ; CODE XREF: _l_ckout_string_key+E43j 5A05375F ; _l_ckout_string_key+E4Cj 5A05375F push offset byte_5A0C5820 5A053764 call _our_encrypt ;the second encrypt . . . 5A05384B mov ecx, [ebp+var_188] 5A053851 add ecx, 1 5A053854 mov [ebp+var_188], ecx 5A05385A 5A05385A loc_5A05385A: ; CODE XREF: _l_ckout_string_key+F47j 5A05385A mov edx, [ebp+var_188] 5A053860 cmp edx, [ebp+var_18C] 5A053866 jge loc_5A0539E0 5A05386C mov eax, [ebp+var_188] 5A053872 mov cl, [ebp+eax*2+var_168] 5A053879 mov [ebp+var_1CC], cl 5A05387F call ds:__p___mb_cur_max 5A053885 cmp dword ptr [eax], 1 . . . 5A0539CB xor eax, eax 5A0539CD mov al, byte_5A0C5820[edx] ; finally - compare license 5A0539D3 cmp ecx, eax 5A0539D5 jz short loc_5A0539DB ;go and compare the next byte 5A0539D7 xor eax, eax ;bad key 5A0539D9 jmp short loc_5A053A01 5A0539DB ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ 5A0539DB 5A0539DB loc_5A0539DB: ; CODE XREF: _l_ckout_string_key+10D3j 5A0539DB jmp loc_5A05384B ;loop back It is a similar loop like in the version seven. We can now get our key for this and remaining features and go asleep. The location at 5A0C5820 is the loc where the key is actually generated by program. At this point I would like to explain the key generation. Our license (when it has proper format) is scrambled in some way without the license key (remember the addi function?) and stored in memory. Now the program has to compute the correct vendor seeds (getting through lm_job and i don't know what else) and mix it up with our scrambled license information with the help of the four vendor keys. This will be done at location 5A0C5820. Unfortunately there, where is the license compared. We would like to extract the seeds. Doing a bpm 5A0C5820 w we see these locations: . . 5A053334 mov byte_5A0C5820[ecx], al ;here we go 5A05333A jmp short loc_5A0532F9 5A05333C loc_5A05333C: ; CODE XREF: _l_ckout_string_key+A0Dj 5A05333C cmp [ebp+var_188], 0 Tracing this code more than once from loc 5A05333C we discover the actual seed computing: 5A0533EA xor eax, ebx ;compute the right seed 5A0533EC push eax ;push it to stack 5A0533ED call sub_5A053AD3 5A0533F2 add esp, 4 5A0533F5 mov [ebp+seed_one], eax 5A0533FB mov edx, [ebp+seed_one] 5A053401 and edx, 0FFh . . 5A0534C9 add eax, 1 5A0534CC mov [ebp+var_10], eax 5A0534CF mov [ebp+seed_one], 3D4DA1D6h ;hiding the seed . . 5A053548 push eax ;push the second seed 5A053549 call sub_5A053AD3 5A05354E add esp, 4 5A053551 mov [ebp+seed_two], eax 5A053557 mov edx, [ebp+seed_two] . . 5A053628 mov [ebp+var_10], eax 5A05362B mov [ebp+seed_two], 3D4DA1D6h ;hide it! 5A053635 jmp loc_5A053735 Hiding the seed prevents the attacker from extracting the seeds with tools like memory dumpers. The seed extraction must be done at these locations and at a runtime (in flexlm 6 it was just simple xor with vendorkey 5 and seeds stored within the executable). So these are new improvements of ver 7.2 versus 6: The keys with seeds are now encrypted and stored in the executable. Both, the keys and the seeds have to be extracted at a runtime (the easiest way). Let's summarize: LC_CHECKOUT --- | | _l_good_lic_key--- /*encrypted keys*/ | | _l_ckout_crypt--- /*deciphered keys*/ | | _real_crypt--- | | _l_ckout_string_key--- | | 5A0533EA /*compute first seed*/ | 5A053547 /*compute second seed*/ | 5A0539DB <- | | 5A0539CD--- /*license compare*/ | -- Now we have to feed our lm_code.h and compile lmcrypt to get the damn thing work. Using the 3D4DA1D6h byte pattern we look now for similar location within the AddInlm.dll. We need actually only the LC_CHECKOUT for feature names. I will let it out as an exercise for you. Corresponding locations were found using this and some other patterns for these executables (useful for those who don't have the flexlm SDK): jutil.dll AddInlm.dll Selicwiz.exe 5A0533EC 046CAEDC 00428C6C . 5A0539CD 046CB4BD 0042924D The origin of the above code is the object lm_ckout.obj winthin the lmgr.lib library.
"But they have witnesses who will swear you did. They can get all the witnesses they need simply by persuading them that destroying you is for the good of the country. And in a way, it would be for the good of the country."