注:此文同时发表于BCG

【软件名称】:XnView - Free graphic viewer 1.74
【软件大小】:840 KB  
【下载地址】:http://www.xnview.com/
【软件简介】:图像浏览处理
【软件限制】:免费软件
【保护方式】:注册码
【破解声明】:绝对新手,第一次破解
【破解作者】:w.h.m
【调试环境】:WinXP、OllyDBG、PEiD、ImportREC

———————————————————————————————————————————

【破解过程】:

由于是第一次,所以找了一个免费软件,但是可以注册的,当然不注册也没有限制,心想免费的
破解一定容易些. 
PEid断定是"ASPack 2.12b -> Alexey Solodovnikov" 加壳. 
初期目标是找出一个注册码.于是也没有脱壳, 直接祭起OllyDBG,想找到一个注册码就OK.
但是初步发现不是明码比较,Oooops,我转向爆破,正好尝试一下脱壳,好像很容易的样子,找到
入口,dump,然后 ImportREC 修改,就OK了. 然后爆破也没有难度,由于不是本次主题,故省略.
心有不甘,决定破解其注册算法,于是...

代码:
用OllyDBG载入脱壳后的程序x_.exe (其实也没有必要脱壳,如果我一开始就想破算法)  bp GetDlgItemTextA  输入用户名whm,注册码454545 程序中断在 77D6AC1E U>  8BFF          mov edi,edi                                     ; USER32.GetDlgItemTextA Ctrl-F9,径直走到retn,然后返回到程序领空0047C707  0047C6F3    .  8B3D 743556>mov edi,dword ptr ds:[<&user32.GetDlgItemTextA>>;  USER32.GetDlgItemTextA 0047C6F9    .  68 00010000 push 100                                        ; /Count = 100 (256.) 0047C6FE    .  50          push eax                                        ; |Buffer 0047C6FF    .  68 D0070000 push 7D0                                        ; |ControlID = 7D0 (2000.) 0047C704    .  56          push esi                                        ; |hWnd 0047C705    .  FFD7        call edi                                        ; \GetDlgItemTextA 0047C707    .  8D4C24 10   lea ecx,dword ptr ss:[esp+10] 0047C70B    .  6A 20       push 20                                         ; /Count = 20 (32.) 0047C70D    .  51          push ecx                                        ; |Buffer 0047C70E    .  68 D1070000 push 7D1                                        ; |ControlID = 7D1 (2001.) 0047C713    .  56          push esi                                        ; |hWnd 0047C714    .  FFD7        call edi                                        ; \GetDlgItemTextA 0047C716    .  8A4424 70   mov al,byte ptr ss:[esp+70] 0047C71A    .  84C0        test al,al 0047C71C    .  0F84 3A0100>je x_.0047C85C 0047C722    .  8A4424 10   mov al,byte ptr ss:[esp+10] 0047C726    .  84C0        test al,al 0047C728    .  0F84 2E0100>je x_.0047C85C 0047C72E    .  8D5424 08   lea edx,dword ptr ss:[esp+8] 0047C732    .  8D4424 70   lea eax,dword ptr ss:[esp+70] 0047C736    .  52          push edx                                        ;  esp+8 0047C737    .  50          push eax                                        ;  "whm" 0047C738    .  E8 8309F9FF call x_.0040D0C0 0047C73D    .  8D4C24 18   lea ecx,dword ptr ss:[esp+18] 0047C741    .  51          push ecx                                        ;  "454545" 0047C742    .  E8 D7A40000 call x_.00486C1E 0047C747    .  8B4C24 14   mov ecx,dword ptr ss:[esp+14]                   ;  ecx=esp+14 0047C74B    .  83C4 0C     add esp,0C 0047C74E    .  3BC8        cmp ecx,eax                                     ;  比较ecx,eax 0047C750    .  74 5D       je short x_.0047C7AF                            ;  如果不等,往下走就提示注册失败 0047C752    .  A1 043C5C00 mov eax,dword ptr ds:[5C3C04] 0047C757    .  8D5424 30   lea edx,dword ptr ss:[esp+30] 0047C75B    .  6A 40       push 40                                         ; /Count = 40 (64.) 0047C75D    .  52          push edx                                        ; |Buffer 0047C75E    .  68 93130000 push 1393                                       ; |RsrcID = STRING "Invalid registration" 0047C763    .  50          push eax                                        ; |hInst => 00400000 0047C764    .  FF15 2C3656>call dword ptr ds:[<&user32.LoadStringA>]       ; \LoadStringA 0047C76A    .  6A 10       push 10                                         ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL 0047C76C    .  8D4C24 34   lea ecx,dword ptr ss:[esp+34]                   ; | 0047C770    .  68 20975B00 push x_.005B9720                                ; |Title = "" 0047C775    .  51          push ecx                                        ; |Text 0047C776    .  56          push esi                                        ; |hOwner 0047C777    .  FF15 B03556>call dword ptr ds:[<&user32.MessageBoxA>]       ; \MessageBoxA 0047C77D    .  68 D0070000 push 7D0                                        ; /ControlID = 7D0 (2000.) 0047C782    .  56          push esi                                        ; |hWnd 0047C783    .  FF15 4C3656>call dword ptr ds:[<&user32.GetDlgItem>]        ; \GetDlgItem 0047C789    .  50          push eax                                        ; /hWnd 0047C78A    .  FF15 8C3556>call dword ptr ds:[<&user32.SetFocus>]          ; \SetFocus 0047C790    .  68 20975B00 push x_.005B9720                                ; /Text = "" 0047C795    .  68 D1070000 push 7D1                                        ; |ControlID = 7D1 (2001.) 0047C79A    .  56          push esi                                        ; |hWnd 0047C79B    .  FF15 303656>call dword ptr ds:[<&user32.SetDlgItemTextA>]   ; \SetDlgItemTextA 0047C7A1    .  5F          pop edi 0047C7A2    .  B8 01000000 mov eax,1 0047C7A7    .  5E          pop esi 0047C7A8    .  81C4 680100>add esp,168 0047C7AE    .  C3          retn 初步分析,文章就在 call x_.0040D0C0 和 call x_.00486C1E 两个函数里面. 注意第一个函数 有两个参数,esp+8 和 "whm",第二个函数只有一个参数"454545" ,但是第二个函数返回后比较 的是EAX和ECX,ECX=esp+14,减去push进去三个参数,正好应该是第一个函数的参数一的地址. OK,我先看第一个函数,F7进去0040D0C0... 0040D0C0   /$  8B5424 04   mov edx,dword ptr ss:[esp+4]                    ;  "whm" 0040D0C4   |.  53          push ebx 0040D0C5   |.  55          push ebp 0040D0C6   |.  56          push esi 0040D0C7   |.  57          push edi 0040D0C8   |.  8BFA        mov edi,edx                                     ;  "whm" 0040D0CA   |.  83C9 FF     or ecx,FFFFFFFF 0040D0CD   |.  33C0        xor eax,eax 0040D0CF   |.  F2:AE       repne scas byte ptr es:[edi] 0040D0D1   |.  F7D1        not ecx 0040D0D3   |.  49          dec ecx                                         ;  ecx = strlen("whm") 0040D0D4   |.  BE 787F5900 mov esi,x_.00597F78 0040D0D9   |.  8BE9        mov ebp,ecx 0040D0DB   |.  B9 05000000 mov ecx,5 0040D0E0   |.  BF 2C975B00 mov edi,x_.005B972C 0040D0E5   |.  F3:A5       rep movs dword ptr es:[edi],dword ptr ds:[esi]  ;  复制了5个dword从00597F78到005B972C 0040D0E7   |.  8BF0        mov esi,eax 0040D0E9   |.  74 21       je short x_.0040D10C 0040D0EB   |>  8A0C16      /mov cl,byte ptr ds:[esi+edx] 0040D0EE   |.  8AD9        |mov bl,cl 0040D0F0   |.  3298 2C975B>|xor bl,byte ptr ds:[eax+5B972C] 0040D0F6   |.  40          |inc eax 0040D0F7   |.  83F8 05     |cmp eax,5 0040D0FA   |.  881C16      |mov byte ptr ds:[esi+edx],bl 0040D0FD   |.  8888 2B975B>|mov byte ptr ds:[eax+5B972B],cl 0040D103   |.  75 02       |jnz short x_.0040D107 0040D105   |.  33C0        |xor eax,eax 0040D107   |>  46          |inc esi 0040D108   |.  3BF5        |cmp esi,ebp 0040D10A   |.^ 72 DF       \jb short x_.0040D0EB                           ;  把"whm"逐个BYTE与5B972C内容做XOR,                                                                            ;  但是如果name超过5个BYTE,又要回头从5B972C开始XOR                                                                            ;  我这里没有超过5                                                                             ;  XOR结果放在name远处                                                                            ;  name原值替换 5B972C内容 0040D10C   |>  33FF        xor edi,edi 0040D10E   |.  33C9        xor ecx,ecx 0040D110   |.  85ED        test ebp,ebp 0040D112   |.  76 26       jbe short x_.0040D13A 0040D114   |>  8A9F 31975B>/mov bl,byte ptr ds:[edi+5B9731] 0040D11A   |.  8BF5        |mov esi,ebp 0040D11C   |.  2BF1        |sub esi,ecx 0040D11E   |.  4E          |dec esi 0040D11F   |.  8A0416      |mov al,byte ptr ds:[esi+edx] 0040D122   |.  32D8        |xor bl,al 0040D124   |.  47          |inc edi 0040D125   |.  881C16      |mov byte ptr ds:[esi+edx],bl 0040D128   |.  8887 30975B>|mov byte ptr ds:[edi+5B9730],al 0040D12E   |.  83FF 05     |cmp edi,5 0040D131   |.  75 02       |jnz short x_.0040D135 0040D133   |.  33FF        |xor edi,edi 0040D135   |>  41          |inc ecx 0040D136   |.  3BCD        |cmp ecx,ebp 0040D138   |.^ 72 DA       \jb short x_.0040D114                           ;  重复上面,只是换做与5B972C+5做XOR, 并且name倒过来操作  0040D13A   |>  33F6        xor esi,esi 0040D13C   |.  33FF        xor edi,edi 0040D13E   |.  85ED        test ebp,ebp 0040D140   |.  76 21       jbe short x_.0040D163 0040D142   |>  8A0417      /mov al,byte ptr ds:[edi+edx] 0040D145   |.  8A8E 36975B>|mov cl,byte ptr ds:[esi+5B9736] 0040D14B   |.  32C8        |xor cl,al 0040D14D   |.  46          |inc esi 0040D14E   |.  880C17      |mov byte ptr ds:[edi+edx],cl 0040D151   |.  8886 35975B>|mov byte ptr ds:[esi+5B9735],al 0040D157   |.  83FE 05     |cmp esi,5 0040D15A   |.  75 02       |jnz short x_.0040D15E 0040D15C   |.  33F6        |xor esi,esi 0040D15E   |>  47          |inc edi 0040D15F   |.  3BFD        |cmp edi,ebp 0040D161   |.^ 72 DF       \jb short x_.0040D142                          ;  重复,只是换做与5B972C+A做XOR 0040D163   |>  33FF        xor edi,edi 0040D165   |.  33C9        xor ecx,ecx 0040D167   |.  85ED        test ebp,ebp 0040D169   |.  76 26       jbe short x_.0040D191 0040D16B   |>  8A9F 3B975B>/mov bl,byte ptr ds:[edi+5B973B] 0040D171   |.  8BF5        |mov esi,ebp 0040D173   |.  2BF1        |sub esi,ecx 0040D175   |.  4E          |dec esi 0040D176   |.  8A0416      |mov al,byte ptr ds:[esi+edx] 0040D179   |.  32D8        |xor bl,al 0040D17B   |.  47          |inc edi 0040D17C   |.  881C16      |mov byte ptr ds:[esi+edx],bl 0040D17F   |.  8887 3A975B>|mov byte ptr ds:[edi+5B973A],al 0040D185   |.  83FF 05     |cmp edi,5 0040D188   |.  75 02       |jnz short x_.0040D18C 0040D18A   |.  33FF        |xor edi,edi 0040D18C   |>  41          |inc ecx 0040D18D   |.  3BCD        |cmp ecx,ebp 0040D18F   |.^ 72 DA       \jb short x_.0040D16B                         ;  重复,只是换做与5B972C+F做XOR, 并且name倒过来操作 0040D191   |>  8B7C24 18   mov edi,dword ptr ss:[esp+18]                 ;  这是参数一  0040D195   |.  33C0        xor eax,eax 0040D197   |.  85ED        test ebp,ebp 0040D199   |.  C707 000000>mov dword ptr ds:[edi],0                      ;  参数一清0  0040D19F   |.  76 17       jbe short x_.0040D1B8 0040D1A1   |>  8BC8        /mov ecx,eax 0040D1A3   |.  83E1 03     |and ecx,3 0040D1A6   |.  8A1C39      |mov bl,byte ptr ds:[ecx+edi] 0040D1A9   |.  8D3439      |lea esi,dword ptr ds:[ecx+edi] 0040D1AC   |.  8A0C10      |mov cl,byte ptr ds:[eax+edx] 0040D1AF   |.  02D9        |add bl,cl 0040D1B1   |.  40          |inc eax 0040D1B2   |.  3BC5        |cmp eax,ebp 0040D1B4   |.  881E        |mov byte ptr ds:[esi],bl 0040D1B6   |.^ 72 E9       \jb short x_.0040D1A1                         ;  把name里面的结果折叠相加进一个DWORD,存入参数一  0040D1B8   |>  5F          pop edi 0040D1B9   |.  5E          pop esi 0040D1BA   |.  5D          pop ebp 0040D1BB   |.  5B          pop ebx 0040D1BC   \.  C3          retn 看来函数一清楚了,针对name字符串和一个事先确定的5 BYTE 数组做异或,然后一些处理, 结果放在参数一指向的buffer里面. 我看看00597F78里面是什么...   00597F78  AA 89 C4 FE 46 78 F0 D0    00597F80  03 E7 F7 FD F4 E7 B9 B5    00597F88  1B C9 50 73  不管他,我看函数二,F7进去00486C1E 00486C1E   /$  53          push ebx 00486C1F   |.  55          push ebp 00486C20   |.  56          push esi 00486C21   |.  57          push edi 00486C22   |.  8B7C24 14   mov edi,dword ptr ss:[esp+14] 00486C26   |>  833D 7C4B5A>/cmp dword ptr ds:[5A4B7C],1 00486C2D   |.  7E 0F       |jle short x_.00486C3E 00486C2F   |.  0FB607      |movzx eax,byte ptr ds:[edi] 00486C32   |.  6A 08       |push 8 00486C34   |.  50          |push eax 00486C35   |.  E8 49040000 |call x_.00487083 00486C3A   |.  59          |pop ecx 00486C3B   |.  59          |pop ecx 00486C3C   |.  EB 0F       |jmp short x_.00486C4D 00486C3E   |>  0FB607      |movzx eax,byte ptr ds:[edi] 00486C41   |.  8B0D 70495A>|mov ecx,dword ptr ds:[5A4970]                  ;  x_.005A497A 00486C47   |.  8A0441      |mov al,byte ptr ds:[ecx+eax*2] 00486C4A   |.  83E0 08     |and eax,8 00486C4D   |>  85C0        |test eax,eax 00486C4F   |.  74 03       |je short x_.00486C54 00486C51   |.  47          |inc edi 00486C52   |.^ EB D2       \jmp short x_.00486C26 00486C54   |>  0FB637      movzx esi,byte ptr ds:[edi] 00486C57   |.  47          inc edi 00486C58   |.  83FE 2D     cmp esi,2D 00486C5B   |.  8BEE        mov ebp,esi 00486C5D   |.  74 05       je short x_.00486C64 00486C5F   |.  83FE 2B     cmp esi,2B 00486C62   |.  75 04       jnz short x_.00486C68 00486C64   |>  0FB637      movzx esi,byte ptr ds:[edi] 00486C67   |.  47          inc edi 00486C68   |>  33DB        xor ebx,ebx 00486C6A   |>  833D 7C4B5A>/cmp dword ptr ds:[5A4B7C],1 00486C71   |.  7E 0C       |jle short x_.00486C7F 00486C73   |.  6A 04       |push 4 00486C75   |.  56          |push esi 00486C76   |.  E8 08040000 |call x_.00487083 00486C7B   |.  59          |pop ecx 00486C7C   |.  59          |pop ecx 00486C7D   |.  EB 0B       |jmp short x_.00486C8A 00486C7F   |>  A1 70495A00 |mov eax,dword ptr ds:[5A4970] 00486C84   |.  8A0470      |mov al,byte ptr ds:[eax+esi*2] 00486C87   |.  83E0 04     |and eax,4 00486C8A   |>  85C0        |test eax,eax 00486C8C   |.  74 0D       |je short x_.00486C9B 00486C8E   |.  8D049B      |lea eax,dword ptr ds:[ebx+ebx*4]      ; eax=ebx+ebx*4=ebx*5 00486C91   |.  8D5C46 D0   |lea ebx,dword ptr ds:[esi+eax*2-30]   ; ebx=esi+eax*2-30=esi-30+ebx*10 00486C95   |.  0FB637      |movzx esi,byte ptr ds:[edi] 00486C98   |.  47          |inc edi 00486C99   |.^ EB CF       \jmp short x_.00486C6A 00486C9B   |>  83FD 2D     cmp ebp,2D 00486C9E   |.  8BC3        mov eax,ebx                            ; eax=ebx 00486CA0   |.  75 02       jnz short x_.00486CA4 00486CA2   |.  F7D8        neg eax 00486CA4   |.  5F          pop edi 00486CA5   |.  5E          pop esi 00486CA6   |.  5D          pop ebp 00486CA7   |.  5B          pop ebx 00486CA8   \.  C3          retn 这个函数有些绕,我看了不少时间,后来终于发现前面完全是迷魂阵,用了一个事先确定的大数组 做不少运算判断,实际如果你的注册码全部是数字,所有检查都会pass. 真正有用的是00486C8E 和00486C91两行程序而已,这里esi里面放的是输入的数字ASCII码,减去0x30真好是数值,加上 原来的值*10,ok,原来它在把我输入的注册码换算成十六进制. 结果存在EAX. 总结算法.函数一从用户名以及一个预先确定的数组计算注册码(十六进制,DOWRD), 函数二 把输入的字符串转化成十六进制数值,和函数一计算结果比较.  于是我写出keygen...     keygen(char *name) {  BYTE a[]={   0xAA, 0x89, 0xC4, 0xFE, 0x46, 0x78, 0xF0, 0xD0,   0x03, 0xE7, 0xF7, 0xFD, 0xF4, 0xE7, 0xB9, 0xB5,   0x1B, 0xC9, 0x50, 0x73  };  BYTE nm[100],temp;  union{   DWORD k1;   BYTE k2[4];  }key;  int n,i,j;  n=strlen(name);  strcpy((char*)nm,name);  for(j=0;j<4;j++)  {   for(i=0;i<n;i++)   {    if(j%2)    {     temp=nm[n-1-i];     nm[n-1-i]^=a[i%5+j*5];     a[i%5+j*5]=temp;    }    else    {     temp=nm[i];     nm[i]^=a[i%5+j*5];     a[i%5+j*5]=temp;    }   }  }  key.k1=0;  for(i=0;i<n;i++)  {   key.k2[i%4]+=nm[i];  }  printf("key is 0x%x,%uL\n",key.k1,key.k1); }