zaas 发表于 2011-6-5 20:43

DeblurMyImage 1.51算法分析及算法注册机

【破文标题】DeblurMyImage 1.51算法分析及算法注册机
【破文作者】zaas
【破解工具】OllyICE,PEiD v0.94
【破解平台】WinXP
【软件类别】:国外软件/图像处理
【软件授权】:共享版
【软件介绍】:针对拍照时晃动的图形修复工具。
【破解声明】我是一只小菜鸟,偶得一点心得,愿与大家分享
--------------------------------------------------------------
【破解内容】
--------------------------------------------------------------
从清明节一直忙到端午节。几乎没跟过什么软件了。此该公司的系列软件都不是通过输入注册码什么的来注册,而是通过读取注册文件license.txt来注册的。貌似不同其实也没啥大区别,软件作者写的也很规范,加密上也没啥技术含量,而且注册文件格式比较简单,所以跟了跟。

首先查壳:Microsoft Visual C++ ver. 8.0 / Visual Studio 2005,窃喜。。。

作者指明了注册文件为license.txt,自己伪造一个文本文件,发现有弹出窗口说注册失败云云。下messgebox断下,返回一看,txt文件的内容都列出来了,可叹啊!
0040211E|.50            push    eax                              ; /IniFileName
0040211F|.68 00020000   push    200                              ; |BufSize = 200 (512.)
00402124|.8D8C24 F02D00>lea   ecx, dword ptr       ; |
0040212B|.51            push    ecx                              ; |ReturnBuffer
0040212C|.68 C4F94200   push    0042F9C4                         ; |Default = ""
00402131|.68 C8F94200   push    0042F9C8                         ; |Key = "idcode1"
00402136|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
0040213B|.FFD6          call    esi                              ; \GetPrivateProfileStringW
0040213D|.8D9424 E80100>lea   edx, dword ptr
00402144|.52            push    edx                              ; /IniFileName
00402145|.68 00020000   push    200                              ; |BufSize = 200 (512.)
0040214A|.8D8424 F02500>lea   eax, dword ptr       ; |
00402151|.50            push    eax                              ; |ReturnBuffer
00402152|.68 C4F94200   push    0042F9C4                         ; |Default = ""
00402157|.68 E0F94200   push    0042F9E0                         ; |Key = "idcode2"
0040215C|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
00402161|.FFD6          call    esi                              ; \GetPrivateProfileStringW
00402163|.8D8C24 E80100>lea   ecx, dword ptr
0040216A|.51            push    ecx                              ; /IniFileName
0040216B|.68 00020000   push    200                              ; |BufSize = 200 (512.)
00402170|.8D9424 F01500>lea   edx, dword ptr       ; |
00402177|.52            push    edx                              ; |ReturnBuffer
00402178|.68 C4F94200   push    0042F9C4                         ; |Default = ""
0040217D|.68 F0F94200   push    0042F9F0                         ; |Key = "regkey"
00402182|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
00402187|.FFD6          call    esi                              ; \GetPrivateProfileStringW
00402189|.8D8424 E80100>lea   eax, dword ptr
00402190|.50            push    eax                              ; /IniFileName
00402191|.68 00020000   push    200                              ; |BufSize = 200 (512.)
00402196|.8D8C24 F02900>lea   ecx, dword ptr       ; |
0040219D|.51            push    ecx                              ; |ReturnBuffer
0040219E|.68 C4F94200   push    0042F9C4                         ; |Default = ""
004021A3|.68 00FA4200   push    0042FA00                         ; |Key = "username"
004021A8|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
004021AD|.FFD6          call    esi                              ; \GetPrivateProfileStringW
004021AF|.8D9424 E80100>lea   edx, dword ptr
004021B6|.52            push    edx                              ; /IniFileName
004021B7|.68 00020000   push    200                              ; |BufSize = 200 (512.)
004021BC|.8D8424 F02100>lea   eax, dword ptr       ; |
004021C3|.50            push    eax                              ; |ReturnBuffer
004021C4|.68 C4F94200   push    0042F9C4                         ; |Default = ""
004021C9|.68 14FA4200   push    0042FA14                         ; |Key = "email"
004021CE|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
004021D3|.FFD6          call    esi                              ; \GetPrivateProfileStringW
004021D5|.8D8C24 E80100>lea   ecx, dword ptr
004021DC|.51            push    ecx                              ; /IniFileName
004021DD|.68 00020000   push    200                              ; |BufSize = 200 (512.)
004021E2|.8D9424 F01900>lea   edx, dword ptr       ; |
004021E9|.52            push    edx                              ; |ReturnBuffer
004021EA|.68 C4F94200   push    0042F9C4                         ; |Default = ""
004021EF|.68 20FA4200   push    0042FA20                         ; |Key = "application"
004021F4|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
004021F9|.FFD6          call    esi                              ; \GetPrivateProfileStringW
004021FB|.8D8424 E80100>lea   eax, dword ptr
00402202|.50            push    eax                              ; /IniFileName
00402203|.68 00020000   push    200                              ; |BufSize = 200 (512.)
00402208|.8D8C24 F01D00>lea   ecx, dword ptr       ; |
0040220F|.51            push    ecx                              ; |ReturnBuffer
00402210|.68 C4F94200   push    0042F9C4                         ; |Default = ""
00402215|.68 38FA4200   push    0042FA38                         ; |Key = "usedSign"
0040221A|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
0040221F|.FFD6          call    esi                              ; \GetPrivateProfileStringW

下边紧接着不管你输入没输入,就把输入的内容按照格式写入了DeblurMyImage.txt,这个文件显然就是启动验证时要读取的注册文件。
004022BA|.50            push    eax                              ; /FileName
004022BB|.8D8C24 EC1900>lea   ecx, dword ptr       ; |
004022C2|.51            push    ecx                              ; |String
004022C3|.68 20FA4200   push    0042FA20                         ; |Key = "application"
004022C8|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
004022CD|.FFD6          call    esi                              ; \WritePrivateProfileStringW
004022CF|.8D9424 E80500>lea   edx, dword ptr
004022D6|.52            push    edx                              ; /FileName
004022D7|.8D8424 EC1D00>lea   eax, dword ptr       ; |
004022DE|.50            push    eax                              ; |String
004022DF|.68 38FA4200   push    0042FA38                         ; |Key = "usedSign"
004022E4|.68 D8F94200   push    0042F9D8                         ; |Section = "Key"
004022E9|.FFD6          call    esi                              ; \WritePrivateProfileStringW
004022EB|.E8 20F6FFFF   call    00401910                         ;关键call
004022F0|.84C0          test    al, al
004022F2|.6A 00         push    0                              ; /Style = MB_OK|MB_APPLMODAL
004022F4|.74 22         je      short 00402318                   ; |
004022F6|.68 A4244800   push    004824A4                         ; |Title = "Report"
004022FB|.68 A41C4800   push    00481CA4                         ; |Text = "Registration was successful"
00402300|.57            push    edi                              ; |hOwner
00402301|.FF15 68C24200 call    dword ptr [<&USER32.MessageBoxW>>; \MessageBoxW
00402307|.68 3A040000   push    43A                              ; /Result = 43A (1082.)
0040230C|.57            push    edi                              ; |hWnd
0040230D|.FF15 4CC24200 call    dword ptr [<&USER32.EndDialog>]; \EndDialog
00402313|.E9 7E010000   jmp   00402496
00402318|>68 A4344800   push    004834A4                         ; |Title = "Error"
0040231D|.68 A42C4800   push    00482CA4                         ; |Text = "Registration was not successful"
00402322|.57            push    edi                              ; |hOwner

没啥说的了,这里应该是一个struct,用c来描述下:
struct reg
{
    char idcode1;
    char idcode2;
    char username;
    char regkey;
    char email;
    char application;
    char usedSign;
}
直接按照格式伪造txt文件。


idcode1=123
idcode2=123
regkey=123
username=zaas
email=123
application=123
usedSign=123

重新读取license.txt。关键call也是明摆着的,直接跟进去找。
00401910/$51            push    ecx
00401911|.53            push    ebx
00401912|.6A 01         push    1
00401914|.E8 27F8FFFF   call    00401140                         ;算法1
00401919|.6A 00         push    0
0040191B|.8AD8          mov   bl, al                           ;标志位
0040191D|.E8 1EF8FFFF   call    00401140                         ;为了保险再来一遍?
00401922|.83C4 08       add   esp, 8
00401925|.884424 07   mov   byte ptr , al             ;标志位
00401929|.E8 B2FEFFFF   call    004017E0                         ;算法2
0040192E|.84DB          test    bl, bl
00401930|.5B            pop   ebx
00401931|.75 07         jnz   short 0040193A
00401933|.807C24 03 00cmp   byte ptr , 0
00401938|.74 0B         je      short 00401945
0040193A|>84C0          test    al, al
0040193C|.75 07         jnz   short 00401945
0040193E|.B8 01000000   mov   eax, 1                           ;需跳到这里
00401943|.59            pop   ecx
00401944|.C3            retn
00401945|>33C0          xor   eax, eax                         ;这里就完了
00401947|.59            pop   ecx
00401948\.C3            retn
没啥,继续跟进吧。。。
此处有一个对软件名称的判断,如果软件名称不对,直接跳死。
00401397|.BE A0F94200   mov   esi, 0042F9A0                  ;ASCII "DeblurMyImage"
0040139C|.B9 0E000000   mov   ecx, 0E
004013A1|.33D2          xor   edx, edx
004013A3|.F3:A6         repe    cmps byte ptr es:, byte ptr>;检查application是不是DeblurMyImage
004013A5|.74 19         je      short 004013C0                   ;不是跳死
004013A7|.5F            pop   edi
004013A8|.32C0          xor   al, al
004013AA|.5E            pop   esi
004013AB|.8B8C24 004900>mov   ecx, dword ptr
004013B2|.33CC          xor   ecx, esp
004013B4|.E8 C17E0100   call    0041927A
004013B9|.81C4 04490000 add   esp, 4904
004013BF|.C3            retn
004013C0|>53            push    ebx
004013C1|.68 00020000   push    200
004013C6|.8D8424 100100>lea   eax, dword ptr
004013CD|.6A 00         push    0
004013CF|.50            push    eax
004013D0|.E8 ABC80100   call    0041DC80                         ;new出空间
004013D5|.68 00020000   push    200
004013DA|.8D8C24 1C1300>lea   ecx, dword ptr
004013E1|.6A 00         push    0
004013E3|.51            push    ecx
004013E4|.E8 97C80100   call    0041DC80                         ;new出空间
004013E9|.8D9424 240100>lea   edx, dword ptr
004013F0|.52            push    edx
004013F1|.E8 FA4F0100   call    004163F0                         ;固定字符串"DS$58j((4hKdh40_u55tJ48ak49j"转为ascii码字符串 ,记为StrA
004013F6|.8D8424 281300>lea   eax, dword ptr

继续向下走,进入算法部分:
0040143C|.8D8424 181900>lea   eax, dword ptr       ;取idcode1
00401443|.83C4 0C       add   esp, 0C
00401446|.50            push    eax
00401447|.8D8C24 100100>lea   ecx, dword ptr
0040144E|.E8 4D520100   call    004166A0
00401453|.8D8C24 100B00>lea   ecx, dword ptr          ;取idcode2
0040145A|.51            push    ecx
0040145B|.8D8C24 140100>lea   ecx, dword ptr
00401462|.E8 39520100   call    004166A0
......
004014B7|.83C4 18       add   esp, 18
004014BA|.8D8C24 0C2300>lea   ecx, dword ptr       ;从以上算出的regkey假码
004014C1|.8D8424 0C0100>lea   eax, dword ptr          ;regkey的真码
004014C8|>8A10          /mov   dl, byte ptr             ;比较
004014CA|.3A11          |cmp   dl, byte ptr
004014CC|.75 1A         |jnz   short 004014E8
004014CE|.84D2          |test    dl, dl
004014D0|.74 12         |je      short 004014E4
004014D2|.8A50 01       |mov   dl, byte ptr
004014D5|.3A51 01       |cmp   dl, byte ptr
004014D8|.75 0E         |jnz   short 004014E8
004014DA|.83C0 02       |add   eax, 2
004014DD|.83C1 02       |add   ecx, 2
004014E0|.84D2          |test    dl, dl
004014E2|.^ 75 E4         \jnz   short 004014C8
004014E4|>33C0          xor   eax, eax                         ;成功
004014E6|.EB 05         jmp   short 004014ED
004014E8|>1BC0          sbb   eax, eax                         ;失败
004014EA|.83D8 FF       sbb   eax, -1
004014ED|>85C0          test    eax, eax
根据固定字符串StrA和注册文件的各项内容算出一个regkey,和注册文件中的regkey对比,如果不同就完蛋了。
如果不是研究算法,直接在这里把真码贴注册文件里就OK了。继续:
下边算法比较啰嗦,固定字符串拆了合,合了拆,没啥变化,现在电脑运算速度快,作者也不在乎了。
输入的各项字符串变化方式如下:
00416010/$33C9          xor   ecx, ecx
00416012|.85FF          test    edi, edi
00416014|.7E 31         jle   short 00416047
00416016|>8A5431 01   /mov   dl, byte ptr       ;取第二个字符
0041601A|.80FA 3A       |cmp   dl, 3A                        ;是不是数字以下ascii字符
0041601D|.73 05         |jnb   short 00416024                  ;不是+0xA9,是+0xD0
0041601F|.80C2 D0       |add   dl, 0D0
00416022|.EB 03         |jmp   short 00416027
00416024|>80C2 A9       |add   dl, 0A9
00416027|>8A0431      |mov   al, byte ptr           ;取第一个字符
0041602A|.3C 3A         |cmp   al, 3A                        ;如法炮制
0041602C|.73 04         |jnb   short 00416032
0041602E|.04 D0         |add   al, 0D0
00416030|.EB 02         |jmp   short 00416034
00416032|>04 A9         |add   al, 0A9
00416034|>C0E0 04       |shl   al, 4                           ;第一个字符左移4
00416037|.02C2          |add   al, dl                        ;+第二个字符,两个字符通过以上合并成一个值
00416039|.8BD1          |mov   edx, ecx
0041603B|.D1EA          |shr   edx, 1
0041603D|.83C1 02       |add   ecx, 2
00416040|.3BCF          |cmp   ecx, edi
00416042|.88041A      |mov   byte ptr , al          ;保存
00416045|.^ 7C CF         \jl      short 00416016
00416047\>C3            retn
字符串变完之后要和固定字符串运算了。
00416770|>33D2          /xor   edx, edx
00416772|. |8BC1          |mov   eax, ecx
00416774|. |F7F6          |div   esi
00416776|. |0FB6840C 1402>|movzx   eax, byte ptr    ;第一次:固定字符串DS$58j((4hKdh40_u55tJ48ak49j取字符
0041677E|. |0FB65414 14   |movzx   edx, byte ptr       ;和变换后的idcode1做加法
00416783|. |03D0          |add   edx, eax
00416785|. |81E2 FF000080 |and   edx, 800000FF                   ;取char长度
0041678B|. |79 08         |jns   short 00416795
0041678D|. |4A            |dec   edx
0041678E|. |81CA 00FFFFFF |or      edx, FFFFFF00
00416794|. |42            |inc   edx
00416795|> |88940C 140200>|mov   byte ptr , dl      ;写回固定字符串的地址
0041679C|. |83C1 01       |add   ecx, 1                        ;计数器
0041679F|. |3BCF          |cmp   ecx, edi
004167A1|.^\72 CD         \jb      short 00416770
004167A3|>8BCB          mov   ecx, ebx
004167A5|.8DB424 140200>lea   esi, dword ptr
004167AC|.E8 0FF8FFFF   call    00415FC0                         ;转回ascii字符串
本来以为就这样完了,接下去发现了新问题:idcode2不是风吹来的,是根据一个固定字符串和"computername","username"算出来的。计算方法和上边一致。悲剧的是,idcode2全部是固定字符串的来的,运算方法再复杂,最终会明码出现idcode2。真是多此一举啊。。。
懒得跟了,直接把idcode2抄写下来就可以写注册机了。

算法不复杂,也不再总结了。
以后不再写算法类文章了,好好学我的C吧。以此为总结。
注册机是按照denoisemyimage写的,算法一样,不同的是字符串。ok。
#include "key.h"

void ClearStr (char * str,int len)
{
    for (int i=0;i<len;i++)
    {
      *(str+i)=0;
    }
}

int StrToAscStr(char *pd,char *ps)
{
    char *temp;
    temp=(char *)malloc(sizeof(char)*4);
   
    ClearStr(pd,MAX);
    for (unsigned int i=0;i<strlen(ps);i++)
    {
      ClearStr(temp,4);
      sprintf(temp,"%02x",(unsigned char)*(ps+i));      
      strcat(pd,temp);
    }
    free(temp);
    return i;
}

int ProcessStr(char * dist,char * source)
{
    unsigned int len;
    char *temp1,*temp2;
    temp1=(char *)malloc(sizeof(char)*MAX);
    temp2=(char *)malloc(sizeof(char)*MAX);
    ClearStr(temp1,MAX);
    ClearStr(temp2,MAX);
    char attach={0xc,0xb,7,3,0};

    AscStrToStr(temp1,source);

    len=strlen(source)+4;
    strcat(temp2,attach);
    strcat(temp2,temp1);

    unsigned int k=0;
    for (unsigned int i=0;i<strlen(dist);i++)
    {
      if (len==k) k=0;
      *(dist+i)+=*(temp2+k);
      k++;
    }
    free(temp1);
    free(temp2);
    return 1;
}

void GetRandStr(char *str,char *index,int len)
{
    for (int i=0;i<len;i++)
    {
      *(str+i)=*(index+rand()%strlen(index));
    }
}

void AscStrToStr(char *pd,char *ps)
{
    char a,b;
    unsigned int len;
    if (strlen(ps)%2) len=strlen(ps)/2+1;
    else len=strlen(ps)/2;
    ClearStr(pd,MAX);
    for (unsigned int i=0;i<len;i++)
    {
      a=*(ps+2*i+1);
      if (a<0x3a) a +=0xd0;
      else a+=0xa9;
      b=*(ps+2*i);
      if (b<0x3a) b+=0xd0;
      else b+=0xa9;
      *(pd+i)=(b<<4)+a;
    }
}

void run(char *username)
{
    srand((int)time(0));
    char index[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    char path[]="C:\\license.txt";
    char *code1,*regkey,*usedSign,*email;
    char Soft[]="DenoiseMyImageGPU20";
    char code2[]="88878174847b79cbbfc0632d0be243c5d188b7d0bda8e59d8da9ada0";

    int len;
   
    regkey=(char *)malloc(sizeof(char)*MAX);
    code1=(char *)malloc(sizeof(char)*MAX);
    usedSign=(char *)malloc(sizeof(char)*MAX);
    email=(char *)malloc(sizeof(char)*MAX);
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    //有可能和为0,此时字符串长度有问题
    //所以要加一个while循环
    //此时把malloc都放在循环体外部
    //ref 不放在循环体内的话,每次修改都在原有基础上改,while循环就出错挑不出去了
    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    do
    {
      char ref[]="DS$58j((4hKdh40_u55tJ48ak49j";
      char temp;
      ClearStr(temp,10);
      ClearStr(regkey,MAX);   
      ClearStr(code1,MAX);
      ClearStr(usedSign,MAX);
      ClearStr(email,MAX);
      
      GetRandStr(code1,index,56);
      GetRandStr(usedSign,index,56);
      GetRandStr(temp,index,8);
      
      strcat(email,username);
      strcat(email,temp);
      
      ProcessStr(ref,code1);      
      ProcessStr(ref,code2);      
      ProcessStr(ref,username);
      ProcessStr(ref,email);      
      ProcessStr(ref,Soft);      
      ProcessStr(ref,usedSign);      
      
      len=StrToAscStr(regkey,ref);
      
    } while (len!=28);
   
    FILE *fp;
    fp=fopen(path,"w");
    fprintf(fp,"\n\n");
    fprintf(fp,"application=DenoiseMyImageGPU20\n");
    fprintf(fp,"username=%s\n",username);
    fprintf(fp,"email=%s\n",email);
    fprintf(fp,"idcode1=%s\n",code1);
    fprintf(fp,"idcode2=%s\n",code2);
    fprintf(fp,"usedSign=%s\n",usedSign);
    fprintf(fp,"regkey=%s\n",regkey);
   
    fclose(fp);
   
    free(email);
    free(regkey);
    free(code1);
    free(usedSign);
}


【版权声明】破文是学习的手记,兴趣是成功的源泉;本破文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!

chesion001 发表于 2011-6-5 20:55

楼主分析的真相近。

jixietui 发表于 2011-6-5 21:56

楼主是算法帝

gry8686 发表于 2011-6-5 21:56

支持楼主的分享,进来学习一下

HadgeROL 发表于 2011-6-5 22:40

开玩笑,,也不看看是哪里出来的人才{:1_921:}

wkxq 发表于 2011-6-5 22:43

支持楼主的分享,人才{:1_921:}

热火朝天 发表于 2011-6-5 23:35

好厉害哦,看着我眼花缭乱了

cnitren 发表于 2012-2-6 14:45

还达不到LZ的境界,看的迷糊啊,不过大概能了解一些。

cfc1680 发表于 2012-2-26 22:49

很强大,感谢分享了,学习

roccc 发表于 2012-3-21 21:43

啃了,最近我也碰到这个个问题,仔细学校了
页: [1] 2
查看完整版本: DeblurMyImage 1.51算法分析及算法注册机