more stuff on FlexLm
Contents ======== 1. Code signatures 2. How key 5 is generated and how to get it fast 3. Useful tools 4. More notes on license generation 5. Fast 32 bit Cryptwin decryption 1. Code signatures ================== The license manager DLLs are useful - they've got export tables for _most_ functions. However, in Globetrotters own utilities, and some third party code, the DLLs aren't used. Functions are called within the target EXE, and rarely have export tables. So it's useful to look at a desired function in the DLL, find some identifying features, and look for these in our target EXE. A few examples from lmgr326a.dll: a) XOR of seeds 1 and 2 with key 5: mov eax, dword ptr [edi+04] <- get seed 1 xor eax, ebx <- xor with key 5 mov dword ptr [ebp-24], eax <- and store mov eax, dword ptr [edi+08] <- get seed 2 xor eax, ebx <- xor with key 5 A little above this code is the call to generate key 5. So search for code which looks a little like this, maybe just xor, , , xor to start with. b) lc_set_attr() If we look at lmgr326a.dll, we see the attribute values such as 0x41 = 65 LM_A_BEHAVIOR_VER are pushed before a call to lc_set_attr() So search for a push 00000041 in your target and find likely candidates. Then repeat with something like push 00000038, LM_A_LICENSE_DEFAULT Reduce the number of candidates and find lc_set_attr(). 2. How key 5 is generated and how to get it fast! ================================================= Key 5 is generated exactly the same for flexlm V5.12 and V6.10. The exact address seems to vary with various versions of DLLs. You pass the vendor string and the 4 keys and key 5 is generated for you. For example, in lmgr326a.dll, we can see how the key 5 generator is called: Exported fn(): l_cksum - Ord:007Eh :10003723 push ebp ... :1000372F mov edi, dword ptr [ebp+14] <- start of the key structure :10003732 lea eax, dword ptr [esi+0000008C] <- VENDOR_STRING :10003738 push edi <- start of the key structure :10003739 push eax <- VENDOR_STRING :1000373A call 10011415 <- generate key 5 :1000373F add esp, 00000008 <- re-adjust stack :10003742 mov ebx, eax <- store key 5 for later use then... :10003753 mov eax, dword ptr [edi+04] <- seed 1 :10003756 xor eax, ebx <- XOR with key 5 :10003758 mov dword ptr [ebp-24], eax <- store XORed seed 1 :1000375B mov eax, dword ptr [edi+08] <- seed 2 :1000375E xor eax, ebx <- XOR with key 5 :10003760 mov edi, dword ptr [ebp+0C] <- store XORed seed 2 Feel free to analyse the generation, it applies various shifts, XORs and bit manipulation to the vendor string and the four keys. ( It uses the magic number 1504C935 noted by Vox ). So we _could_ rip it apart and write our own key 5 gen, but why bother when the function's in a DLL for us? What I did was patch the lc_init() function to call generate key 5 and display it. For example, using lmgr326a.dll, apply a patch to the start of the lc_init function: Exported fn(): lc_init - Ord:0034h mov eax, esp add eax, 0000000C mov ebx, dword ptr [eax] <- the vendor string push ebx sub eax, 00000004 mov ebx, dword ptr [eax] <- start of key structure push ebx call 10011415 <- generate key 5 in eax add esp, 00000008 <- re-adjust the stack push eax <- key 5 push 100381F8 <- "%lx" push 1003F1C0 <- empty RAM for workspace Call dword ptr [100414EC]<- USER32.wsprintfA add esp, 0000000C <- re-adjust the stack push 00000000 <- style of message box push 10038C50 <- "5.0" message box title push 1003F1C0 <- the ascii text in the workspace push 00000000 <- NULL window handle Call dword ptr [100414F0]<- USER32.MessageBoxA ret Now when your target calls lc_init, voila! a message box with key 5 appears. 3. Useful tools =============== You can apply a similar idea and patch the lmgr*.DLL to give you all the info you need, with no dissassembly of the target required. I patched lc_init to display the vendor string, the 2 seeds and all 5 keys. Unfortunately the targets crashed after this patch, but hey! we got the keys! You can patch lc_checkout and lc_get_config to display feature names. This can be done invisibly to the target which keeps on running. Very useful. Another useful patch is to get lc_set_attr to display attribute and value. This shows you when the target is implementing some of the trickier flexlm features, such as vendor-defined hostids. A few hints: lmgr325c.dll uses relocating addresses, so watch it! lmgr325c.dll has no call to wsprintf(), but has an internal function at 1002FB20 which does the same job. I used the 'obsolete' function lc_baddate as spare space for my code patches. A snippet of example for lmgr325c.dll: Exported fn(): lc_init - Ord:0034h mov eax, esp add eax, 00000008 <- pointer to the vendor string push 00000000 <- style of message box push 0093448C <- "VENDOR_STRING" push dword ptr [eax] <- the vendor string push 00000000 <- NULL window handle call dword ptr [0093A40C] <- USER32.MessageBoxA mov eax, esp add eax, 0000000C <- pointer to the key struct mov ebx, dword ptr [eax] <- start of key block add ebx, 00000004 mov eax, dword ptr [ebx] <- seed 1 push eax push 00931968 <- "%lx" push 009367F8 <- empty RAM for workspace call 1002FB20 <- sprintfA add esp, 0000000C <- re-adjust the stack push 00000000 <- style of message box jmp 100145A2 <- jump over code change due to relocation push 009313BC <- "0" message box title push 009367F8 <- the ascii text in the workspace push 00000000 <- NULL window handle call dword ptr [0093A40C] <- USER32.MessageBoxA ...etc...etc 4. More notes on license generation =================================== Use the genlic.exe program that comes with the 6.1 SDK if you've got a target using the basic 6.1 DLL. But if you've got 5.12, or your target implements enhanced flexlm features, then it's time to write your own licence generator. Vox shows you how. Now if your target applies something like vendor-specific IDs then you need to put some calls to lc_set_attr() in your license generator, mimicking the calls made by the target. A deeper analysis of lc_set_attr() shows that the final checksum in the license.dat file is not affected by date of license generation, or by calling lc_set_attr(). However, lc_init() returns error codes if the license file input is not as expected, so if you've got a vendor-specific ID in the input license file, you need to call lc_set_attr() so that lc_init() knows how to parse the input license file. 5. Fast 32 bit Cryptwin decryption ================================== Find the key by setting a breakpoint on the "%.4d-%.4d-%.4d-%.4d" Add a -00 to the end to get something like 1111-2222-3333-4444-00 Run this through cryptwin.exe. It will bomb out with an error message. The problem is the extra checksum stored at the start of the *.FC file. So patch cryptwin.exe to by-pass the checksum check: * Possible StringData Ref from Data Obj ->"FLEXcrypt Copyright (C) 1990-1997," ->"Globetrotter Software, Inc." | :00402D66 6870AE4200 push 0042AE70 :00402D6B 51 push ecx :00402D6C E8CFC40100 call 0041F240 <- string compare :00402D71 83C40C add esp, 0000000C :00402D74 85C0 test eax, eax :00402D76 7579 jne 00402DF1 <- patch to always jump :00402D78 FF7508 push [ebp+08] Now it'll by-pass the checksum check and decrypt (albeit with the wrong start value ).
As Vox says, there's still more to do on FlexLm. Vendor-defined checkouts, encryption etc, and then there's FlexLock... pilgrim