说明:
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明码!你知道是哪里吗?
******************************************************************
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!