说明:
1。此方法不适用于使用ECC加密的程序。
2。本文是以Flexlm9.2加密的程序为例,其他版本大致相同
3。需要事先知道feature、vendor、version
4。对于没有daemon程序的软件则直接对主程序动手即可

步骤(共5步):


===============>  第一步,反汇编目标程序daemon.exe <==========================
这一步没什么好多说的  
===============>        第二步,关键函数定位      s <==========================

搜索常数66D8B337 (在源代码里定义为L_SECLEN_SHORT)

第一次搜索到66D8B337 
.text:00421253 81 7D 18 37 B3 D8+                cmp     [ebp+arg_10], 66D8B337h <---第一次引用
.text:0042125A 0F 85 96 00 00 00                 jnz     loc_4212F6
.text:00421260 33 D2                             xor     edx, edx

在第二次出现66D8B337 之前
.text:004212D9 25 FF 00 00 00                    and     eax, 0FFh
.text:004212DE A2 25 5E 4B 00                    mov     byte_4B5E25, al <---会出现类似的内存访问,随后的代码均访问该地址附近(这是进行函数判断的关键所在!!)
.text:004212E3 C6 05 2B 5E 4B 00+                mov     byte_4B5E2B, 0
.text:004212EA 8A 15 2B 5E 4B 00                 mov     dl, byte_4B5E2B
.text:004212F0 88 15 2A 5E 4B 00                 mov     byte_4B5E2A, dl
.text:004212F6
.text:004212F6                   loc_4212F6:                             ; CODE XREF: sub_420416+E44j
.text:004212F6 C7 85 74 FE FF FF+                mov     [ebp+var_18C], 8
.text:00421300 81 7D 18 37 B3 D8+                cmp     [ebp+arg_10], 66D8B337h <---第二次引用
.text:00421307 75 0F                             jnz     short loc_421318

继续往后走,出现调用_l_isdigit
.text:004213AE 51                                push    ecx
.text:004213AF E8 6C 40 01 00                    call    _l_isdigit
.text:004213B4 83 C4 04                          add     esp, 4


继续往后走,在靠近函数结尾处
.text:00421420                   loc_421420:                             ; CODE XREF: sub_420416+FDEj
.text:00421420 8B 95 30 FE FF FF                 mov     edx, [ebp+var_1D0]
.text:00421426 81 E2 FF 00 00 00                 and     edx, 0FFh
.text:0042142C 8B 85 78 FE FF FF                 mov     eax, [ebp+var_188]
.text:00421432 33 C9                             xor     ecx, ecx
.text:00421434 8A 88 24 5E 4B 00                 mov     cl, byte_4B5E24[eax] <---byte_4B5E24为正确的SIGN开始地址
.text:0042143A 3B D1                             cmp     edx, ecx <--明文逐位明文比较
.text:0042143C 74 04                             jz      short loc_421442
.text:0042143E 33 C0                             xor     eax, eax
.text:00421440 EB 26                             jmp     short loc_421468

===============>   第三步 进行程序拦截,添加自定义代码   <==========================

事实上在第二次引用66D8B337h之前就已经生成了正确的SIGN
.text:0042126E 83 C4 04                          add     esp, 4
.text:00421271 25 FF 00 00 00                    and     eax, 0FFh
.text:00421276 25 FF 00 00 00                    and     eax, 0FFh
.text:0042127B A2 2B 5E 4B 00                    mov     byte_4B5E2B, al
.text:00421280 33 C0                             xor     eax, eax
.text:00421282 A0 2A 5E 4B 00                    mov     al, byte_4B5E2A
.text:00421287 50                                push    eax
.text:00421288 E8 87 03 00 00                    call    sub_421614
.text:0042128D 83 C4 04                          add     esp, 4
.text:00421290 25 FF 00 00 00                    and     eax, 0FFh
.text:00421295 25 FF 00 00 00                    and     eax, 0FFh
.text:0042129A A2 2A 5E 4B 00                    mov     byte_4B5E2A, al
.text:0042129F 33 C9                             xor     ecx, ecx
.text:004212A1 8A 0D 2B 5E 4B 00                 mov     cl, byte_4B5E2B
.text:004212A7 33 D2                             xor     edx, edx
.text:004212A9 8A 15 24 5E 4B 00                 mov     dl, byte_4B5E24 <--正确SIGN的地址
.text:004212AF 03 CA                             add     ecx, edx
.text:004212B1 81 E1 FF 00 00 00                 and     ecx, 0FFh
.text:004212B7 81 E1 FF 00 00 00                 and     ecx, 0FFh
.text:004212BD 88 0D 24 5E 4B 00                 mov     byte_4B5E24, cl
.text:004212C3 33 C0                             xor     eax, eax
.text:004212C5 A0 2A 5E 4B 00                    mov     al, byte_4B5E2A
.text:004212CA 33 C9                             xor     ecx, ecx
.text:004212CC 8A 0D 25 5E 4B 00                 mov     cl, byte_4B5E25
.text:004212D2 03 C1                             add     eax, ecx
.text:004212D4 25 FF 00 00 00                    and     eax, 0FFh
.text:004212D9 25 FF 00 00 00                    and     eax, 0FFh
.text:004212DE A2 25 5E 4B 00                    mov     byte_4B5E25, al
.text:004212E3 C6 05 2B 5E 4B 00+                mov     byte_4B5E2B, 0
.text:004212EA 8A 15 2B 5E 4B 00                 mov     dl, byte_4B5E2B
.text:004212F0 88 15 2A 5E 4B 00                 mov     byte_4B5E2A, dl <--正确SIGN的生成完毕
.text:004212F6
.text:004212F6                   loc_4212F6:                             ; CODE XREF: sub_420416+E44j
.text:004212F6 C7 85 74 FE FF FF+                mov     [ebp+var_18C], 8
.text:00421300 81 7D 18 37 B3 D8+                cmp     [ebp+arg_10], 66D8B337h <---第二次引用,在此处拦截使之跳转执行到我们自己的代码
.text:00421307 75 0F                             jnz     short loc_421318

修改拦截的代码
    .text:00421300 cmp     [ebp+arg_10], 66D8B337h
改为 
00421300   .- E9 FB2C0B00   jmp     004D4000 <--- 跳转到补丁位置,其余nop填充,切记!!
00421305      90            nop
00421306      90            nop
00421307      90            nop
00421308      90            nop
    
将正确sign输出的方式有两种,一个是在exe里直接进行文件读写操作,另一个是采用外部dll调用的方式。前者对汇编语言及win32API编程的功力要求较高,后者相对低一些。我选择了后者, 采用C语言编写相应的操作函数,在exe里仅需要添加调用dll的代码

将目标程序添加一个空区段(也可在以有区段的空白处直接添加代码),使用OD输入以下指令
004D4000    60              pushad  <--保存所有的寄存器
004D4001    68 17414D00     push    004D4117   ; ASCII "c:\flexlm_lic.dll",指向生成License文件的连接库
004D4006    FF15 40124A00   call    dword ptr [<&KERNEL32.LoadLibrar>; kernel32.LoadLibraryA
004D400C    FF75 1C         push    dword ptr [ebp+1C] <-- 输入的错误SIGN
004D400F    68 245E4B00     push    004B5E24  <-- 正确SIGN地址
004D4014    FF75 3C         push    dword ptr [ebp+3C] <-- feature地址,不同的程序可能稍微有所不同,应该以动态调试时找到为准
004D4017    05 30100000     add     eax, 1030 <--eax为加载连接库的基准地址,1030 是所调用函数的偏移地址(用peid直接察看获知)
004D401C    FFD0            call    eax  <--eax为对应的导出函数入口地址,也可用GetProcAddress获得导出函数入口地址
004D401E    83C4 0C         add     esp, 0C <--堆栈平衡
004D4021    61              popad  <--恢复所有寄存器
004D4022    90              nop
...........中间这么多空间全部是nop,可以将下面的指令全部移到4d4021之后...........
004D4100    817D 18 37B3D86>cmp     dword ptr [ebp+18], 66D8B337 <--继续运行原有的指令
004D4107  - 0F85 0BD2F4FF   jnz     00421318
004D410D  - E9 F7D1F4FF     jmp     00421309

不要忘了输入动态库路径名称,编辑二进制数据
004D4117  63 3A 5C 66 6C 65 78 6C 6D 5F 6C 69 63 2E 64 6C  c:\flexlm_lic.dl
004D4127  6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  l...............


===============>  第四步 实现文件读写的连接库    <==========================

// FlexlmDll.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "FlexlmDll.h"
#include <stdio.h>
#include <stdlib.h>
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
           )
{
    switch (ul_reason_for_call)
  {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
}

char cv(int c) 将16进制数字转换为字符
{
  if(c>=0 && c<=9)
    return c+'0';
  else
    return c-0xA+'A';
}

// feat为feature,sign0为正确的SIGN,在内存中以16进行数字存放,需要转换为相应的字符串
// 例如,内存数据为0x11 0x23 0x45 0xAB 0xCD 0xEF,则对应的SIGN为“112345ABCDEF”
// sign1为输入的错误SIGN
FLEXLMDLL_API void FlexlmDll(char * feat,char *sign0,char *sign1)
{
  char s0[20];
  int i;
  for(i=0;i<6;i++)
  {
    s0[i*2]=cv((sign0[i]&0xF0)>>4);
    s0[i*2+1]=cv(sign0[i]&0x0F);
  }

  s0[12]='\0';
  FILE *fp=fopen("c:\\flexlm_lic.txt","a+");
  if(fp==NULL)
    return;
  fprintf(fp,"%s %s %s\n", feat,s0,sign1);
  
  // 为了方便还可以直接生成license文件
  // fprintf(fp,"FEATURE %s xxxx 1.0 permanent uncounted %s HOSTID=ANY\n", feat,s0);
  // 要注意上面的格式化字符串应与假license文件的保持一致

  fclose(fp);
}


===============>  第五步 应用前面的劳动成果生成正确的license   <==========================

编辑一个伪造的license文件,要求feature version vendor 均正确,sign随便输入即可(字符串长度要够数)

SERVER localhost ANY
VENDOR xxxx
FEATURE f1 xxxx 1.0 permanent uncounted 123456ABCDEF HOSTID=ANY
FEATURE f2 xxxx 1.0 permanent uncounted 123456ABCDEF HOSTID=ANY

在c:\flexlm里放置daemon程序和lmgrd.exe, 在控制台运行lmgrd.exe后就会发现c:\flexlm_lic.txt里给出了正确的SIGN

再补充说明一点:

在第二步里确定的关键函数,可能会执行两次,也即c:\flexlm_lic.txt会相邻两行给出同样的输出;有时第一次生成的SIGN不能使用(还没搞清楚原因),将这些SIGN填充到license里,再一次运行lmgrd.exe就会生成正确的SIGN了

******************************************************************

其实在这个函数的两个不同位置处出现了正确的seed明码!你知道是哪里吗?   

******************************************************************