首页
社区
课程
招聘
[旧帖] [原创]对“GIF Movie Gear 4.2算法分析”一文的补充 0.00雪花
发表于: 2009-1-11 23:36 2833

[旧帖] [原创]对“GIF Movie Gear 4.2算法分析”一文的补充 0.00雪花

2009-1-11 23:36
2833
【破文标题】对“GIF Movie Gear 4.2算法分析”一文的补充
【破文作者】真Λ安全
【作者邮箱】[EMAIL="sharkxu2008@126.com"]sharkxu2008@126.com[/EMAIL]
【作者主页】http://hi.baidu.com/sharkxu2008
【破解工具】OD
【破解平台】Windows XP
【软件名称】GIF Movie Gear 4.2 汉化版
【软件大小】1.88MB
【软件类别】国外软件/动画制作
【软件授权】共享版
【软件语言】中文
【原版下载】http://www.skycn.com/soft/2419.html
【保护方式】注册码
【软件简介】
GIF Movie Gear是一款实用的 GIF 文件制作、编辑、优化、转换软件。您可以用它打开 BMP/GIF/JPG/PNG/PSD/AVI/CUR/ICO 等格式并将它们转换或混合为 GIF 格式,并保存为 BMP/GIF/JPG/PNG/PSD/AVI/CUR/ICO 甚至 SWF 格式。您还可以用它剪切、缩放、旋转导入的图像文件,调整帧的顺序和延迟时间,更改循环次数,并用多种方法对其进行优化,以减小文件体积。此外,GIF Movie Gear 还能调用任意应用程序对帧进行实时编辑,为 GIF 文件添加注释以及输出 HTML 代码方便您在网页中调用图像。
【破解声明】拜读了tianxj的“GIF Movie Gear 4.2算法分析”一文,地址http://bbs.pediy.com/showthread.php?t=65049&highlight=GIF+Movie.觉得很好,但有缺憾,特此补充。文章中的注册机算法得出的结果局限于下面的格式:
int sum = DoCalc(用户名);
printf("your register code = mg37***%04d or mg37s***%04d", sum, sum);
而我补充的内容是tianxj漏掉的部分,即地址为00433520H的注册码计算函数。这个函数可以说是整个软件注册流程中很重要的部分。
这是我破解的第一个软件,不足之处希望大家指正。
--------------------------------------------------------------
【破解内容】
--------------------------------------------------------------
1.通过OllyICE打开GIF Movie Gear v4.2, 点击运行,软件界面显示使用期30天。在软件的注册界面中输入用户名和注册码。
2.在命令行中输入“bp GetWindowTextA”,然后运行注册,程序停在GetWindowTextA函数地址处,ALT+F9返回主线程。
3.跟踪运行到达00433C1Dh,此处上下的两个call GetWindowTextA用于分别获取用户名和注册码。下面有两个“PUSH 字串地址”以及一个“call 00433840H”,这个就是注册码判别函数了。
4.跟进这个判别函数。此函数首先运行的就是tianxj兄分析的注册码判别函数了。如果输入的注册码不满足要求的条件则进入另一个注册码计算函数,这个函数的起始地址是00433520h。一直跟踪程序向下运行,程序对输入的用户名进行了比较麻烦的运算,直到00433670 >LEA ESI, DWORD PTR SS:[ESP+84]这一句,ESI指向了合法的注册码。成功!
==============================================================
下面就是序列号计算函数的主体:
/*433520*/  MOV     EAX, DWORD PTR SS:[ESP+4]   //输入的用户名地址
/*433524*/  LEA     EDX, DWORD PTR SS:[ESP-C8]  //存储空间buffer的地址
/*43352B*/  SUB     ESP, 0D8                    //分配buffer空间
/*433531*/  SUB     EDX, EAX    
  //将用户名存到buffer
/*433533*/  MOV     CL, BYTE PTR DS:[EAX]   
/*433535*/  MOV     BYTE PTR DS:[EDX+EAX], CL  
/*433538*/  INC     EAX
/*433539*/  TEST    CL, CL
/*43353B*/  JNZ     SHORT movgear.00433533
  //保存各寄存器的值
/*43353D*/  PUSH    EBX  
/*43353E*/  PUSH    EBP  
/*43353F*/  PUSH    ESI  
/*433540*/  PUSH    EDI  
  //将用户名转为大写
/*433541*/  LEA     EAX, DWORD PTR SS:[ESP+20]  //buffer中用户名
/*433545*/  PUSH    EAX
/*433546*/  CALL    NEAR DWORD PTR DS:[47F31C]  //USER32.CharUpperA
  //初始化一些变量
/*43354C*/  MOV     AL, BYTE PTR SS:[ESP+20]    //取用户名的第一个字符
/*433550*/  TEST    AL, AL
/*433552*/  LEA     ESI, DWORD PTR SS:[ESP+20]  //ESI指向buffer
/*433556*/  LEA     EDI, DWORD PTR SS:[ESP+20]  //EDI指向buffer
/*43355A*/  JE      SHORT movgear.00433582      //如果字符是0,跳
/*43355C*/  LEA     ESP, DWORD PTR SS:[ESP]     //
  //在软件定义的字串中查找用户名中的字符,并返回字符的地址 
/*433560*/  MOVSX   ECX, BYTE PTR DS:[ESI]      //取用户名中的字符
/*433563*/  PUSH    ECX                           ///字符
/*433564*/  PUSH    movgear.0048F478              //|字符串ASCII "HKRWQV2958DWNTQRGNSCFSXAZPYK"
/*433569*/  CALL    <movgear.LOCATE FIRST MATCH>  //\在字符串中找匹配字符,并返回字符地址
  //把在字串中找到的匹配的字符依次保存到buffer
/*43356E*/  ADD     ESP, 8
/*433571*/  TEST    EAX, EAX                   //在上面字符串中匹配字符的地址
/*433573*/  JE      SHORT movgear.0043357A     //如果地址为0,则字符没有匹配
/*433575*/  MOV     DL, BYTE PTR DS:[ESI]      //存在匹配的字符
/*433577*/  MOV     BYTE PTR DS:[EDI], DL      //将其写入buffer
/*433579*/  INC     EDI
/*43357A*/  MOV     AL, BYTE PTR DS:[ESI+1]    //读出下一个用户名字符
/*43357D*/  INC     ESI
/*43357E*/  TEST    AL, AL
/*433580*/  JNZ     SHORT movgear.00433560     //非零即跳转到循环开始
/*433582*/  LEA     EAX, DWORD PTR SS:[ESP+20] //buffer地址
/*433586*/  MOV     BYTE PTR DS:[EDI], 0       //buffer以零结束
  //计算strlen(buffer)
/*433589*/  LEA     EDX, DWORD PTR DS:[EAX+1]
/*43358C*/  LEA     ESP, DWORD PTR SS:[ESP]
/*433590*/  MOV     CL, BYTE PTR DS:[EAX]
/*433592*/  INC     EAX
/*433593*/  TEST    CL, CL
/*433595*/  JNZ     SHORT movgear.00433590
/*433597*/  SUB     EAX, EDX                    //len of result
 //判断buffer是否大于24d位,如果小于则用"HKRWQV2958DWNTQRGNSCFSXAZPYK"补足24d位
/*433599*/  CMP     EAX, 18
/*43359C*/  JGE     SHORT movgear.004335BC
/*43359E*/  MOV     ECX, 18
/*4335A3*/  SUB     ECX, EAX                   //需要补足的位数
/*4335A5*/  LEA     EDI, DWORD PTR SS:[ESP+EAX+20] //地址buffer+strlen(buffer)
/*4335A9*/  MOV     EAX, ECX
/*4335AB*/  SHR     ECX, 2
/*4335AE*/  MOV     ESI, movgear.0048F478      //"HKRWQV2958DWNTQRGNSCFSXAZPYK"
/*4335B3*/  REP     MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]  //以四BYTE一组填充
/*4335B5*/  MOV     ECX, EAX
/*4335B7*/  AND     ECX, 3    //零头
/*4335BA*/  REP     MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI]    //填充剩下的
/*4335BC*/  MOV     EAX, movgear.0048F478
/*4335C1*/  MOV     BYTE PTR SS:[ESP+39], 0    //结束
/*4335C6*/  MOV     BYTE PTR SS:[ESP+84], 0
  //"HKRWQV2958DWNTQRGNSCFSXAZPYK"的长度
/*4335CE*/  XOR     EBP, EBP
/*4335D0*/  LEA     ECX, DWORD PTR DS:[EAX+1]
/*4335D3*/  MOV     DL, BYTE PTR DS:[EAX]
/*4335D5*/  INC     EAX
/*4335D6*/  TEST    DL, DL
/*4335D8*/  JNZ     SHORT movgear.004335D3
/*4335DA*/  SUB     EAX, ECX                   //len = 1CH
  //以下"HKRWQV2958DWNTQRGNSCFSXAZPYK"的地址用enc代替
  //计算序列号前初始化
/*4335DC*/  MOV     DWORD PTR SS:[ESP+10], EAX  //储存enc的长度
/*4335E0*/  MOV     EAX, 1
/*4335E5*/  SUB     EAX, movgear.0048F478       //1-enc
/*4335EA*/  MOV     DWORD PTR SS:[ESP+18], EAX
/*4335EE*/  XOR     EBX, EBX
/*4335F0*/  MOV     EAX, movgear.0048F478
/*4335F5*/  DEC     EAX                         //enc-1
/*4335F6*/  MOV     DWORD PTR SS:[ESP+14], EAX
/*4335FA*/  LEA     EAX, DWORD PTR SS:[ESP+20]  
/*4335FE*/  DEC     EAX                         //buffer-1
/*4335FF*/  LEA     EDI, DWORD PTR SS:[ESP+84]  //存储序列号的空间
/*433606*/  MOV     DWORD PTR SS:[ESP+1C], EAX
/*43360A*/  JMP     SHORT movgear.00433610
  //计算序列号
/*43360C*/  MOV     EAX, DWORD PTR SS:[ESP+1C]
/*433610*/  MOVSX   ECX, BYTE PTR DS:[EAX+EBX+1] //buffer[i]
/*433615*/  LEA     ESI, DWORD PTR DS:[EBX+1]    //i++
/*433618*/  PUSH    ECX                             ///字符
/*433619*/  PUSH    movgear.0048F478                //|enc
/*43361E*/  CALL    <movgear.LOCATE FIRST MATCH>    //\locate,返回匹配字符地址enc+offset
/*433623*/  MOV     EDX, DWORD PTR SS:[ESP+20]      //是1-enc
/*433627*/  MOV     ECX, EAX                        //ecx=enc+offset
/*433629*/  ADD     ECX, EDX                        //ecx=enc+offset+1-enc=offset+1
/*43362B*/  LEA     EAX, DWORD PTR DS:[ECX-1]       //eax=offset
/*43362E*/  IMUL    EAX, ECX                        //off*(off+1)
/*433631*/  ADD     EAX, EBP                        //EBP是上一轮除法的余数+1,假设在变量remainder中
/*433633*/  CDQ
/*433634*/  IDIV    DWORD PTR SS:[ESP+18]           //(off*(off+1)+remainder)div(strlen(enc))
/*433638*/  ADD     ESP, 8
/*43363B*/  MOV     ECX, 6                          //序列号的格式是每六个字母为一组,中间用‘-’分隔,一共四组
/*433640*/  INC     EDX                             //余数remainder++
/*433641*/  MOV     EBP, EDX
/*433643*/  MOV     EDX, DWORD PTR SS:[ESP+14]      //是enc-1
/*433647*/  MOV     AL, BYTE PTR DS:[EDX+EBP]       //enc[remainder]
/*43364A*/  MOV     BYTE PTR DS:[EDI], AL           //将enc[remainder]依次存入序列号存储空间
/*43364C*/  MOV     EAX, ESI                        //i
/*43364E*/  CDQ 
/*43364F*/  IDIV    ECX                             //i%6
/*433651*/  INC     EDI
/*433652*/  TEST    EDX, EDX  
/*433654*/  JNZ     SHORT movgear.0043365F
/*433656*/  CMP     EBX, 17
/*433659*/  JGE     SHORT movgear.0043365F
/*43365B*/  MOV     BYTE PTR DS:[EDI], 2D           //if(i%6==0) write '-'
/*43365E*/  INC     EDI
/*43365F*/  MOV     EBX, ESI
/*433661*/  CMP     EBX, 18                         //序列号24位字母
/*433664*/  JL      SHORT movgear.0043360C
/*433666*/  MOV     EAX, DWORD PTR SS:[ESP+F0]  
/*43366D*/  MOV     BYTE PTR DS:[EDI], 0            //结束sn
/*433670*/  LEA     ESI, DWORD PTR SS:[ESP+84]      //esi指向sn

==============================================================
【算法总结】
序列号共24个字符,都是通过上面函数的算法从"HKRWQV2958DWNTQRGNSCFSXAZPYK"中映射出来的。
--------------------------------------------------------------
【算法注册机】
    将这个过程做注册机,用c表示如下:
#include "stdafx.h"
//////////////////////////////
char str[] = "HKRWQV2958DWNTQRGNSCFSXAZPYK";
int LocSub(char*str,char c) //find c in str,and return offset
{
 for(int i=0;str[i];i++)
 {
  if(str[i]==c)
   return i;
 }
 
 return -1;
}
//////////////////////////////////
char * Calc_sn(char * name) 
{
 int i=0,j=0;
 int len;
 char snBuf[30];
   
 CharUpperA(name);
 
 while(name[i])
 {
  if(LocSub(str,name[i])<0)//not found
    i++;
  else
   {
    name[j]=name[i];   //只保留str中有的字符
    i++;
    j++;
   }
 }
 name[j]='\0';
 
 len = strlen(name);
 if(len<24)
  {
   for(i=0;i<24-len;i++)
    name[i+len]=str[i]; //不足24位用str补足
  }
 name[i+len]='\0';
 
 int offset,remainder=0;//余数
 len = strlen(str);
 for(i=0,j=0;i<24;i++)
 {
  offset = LocSub(str,name[i]);  //字符在str中的偏移
  offset = offset*offset+offset+remainder;
  remainder = offset%len;
  snBuf[j] = str[remainder];
  remainder++;
  j++;
  if((i+1)%6==0 && i<23)
   {
    snBuf[j]='-';
    j++;
   }
 }
 snBuf[j]='\0';
 strcpy(name,snBuf);
 return name;    
}
///////////////////////////////////////////////
int main(int argc, char* argv[])
{
 char name[30];
 cout<<"GIF Movie Gear v4.2注册机。作者:真Λ安全。邮箱:sharkxu2008@126.com\n";
 cout<<"name:";
 cin>>name;
 cout<<"sn:"<<Calc_sn(name)<<endl;
 return 0;
}

**********************************************************
【破解总结】
感觉写破文比破解都累
--------------------------------------------------------------
【注册信息】
用户名:sharkxu
注册码:29H9DT-QNZ8RV-FSDNGK-SVYVYR
保存在
[HKEY_LOCAL_MACHINE\SOFTWARE\gamani\GIFMovieGear\2.0]
--------------------------------------------------------------
借此机会感谢看雪BBS和《加密与解密》一书。
--------------------------------------------------------------
【版权声明】本破文纯属技术交流, 转载请注明作者并保持文章的完整,谢谢!

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//