【破文标题】LaptopAlarm V2.20算法解析 +  MD5汇编注册机源码
【破文作者】Playboysen
【作者邮箱】playboysen@126.com
【破解工具】OD
【破解平台】Windows XP
【软件语言】英文
【原版下载】http://www.syfer.nl/
【保护方式】用户名 注册码
【软件简介】LaptopAlarm V2.20最新版,目前唯一一款笔记本防盗软件,一旦你锁定电脑,必须输入密码正常解锁,在未解锁期间任何人都不可以拔掉鼠标等外设,不能关机或重启、不能合上笔记本待机,否则电脑会大声呼救(呼救声音可自定义),即使本本处于静音状态。强烈推荐常蜗居宿舍的学生族使用!
【破解声明】一点心得,愿与大家分享o(∩_∩)o 版权所有,转载注明作者!
【破解内容】

   主程序VC++ 8语言编写,无壳,注册验证无提示,打开对话框有试用提示。输入假码后搜索注册表发现以下痕迹:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Syfer\LaptopAlarm]
"email"="playboysen@126.com"
"serial"="1234567890abcdef"

   但是尝试下注册表类断点、文件访问断点、对话框类断点均无果而终,直接查找字符无重大发现。最后偶然发现一处重大疑点,即先输入假码然后点击“OK”按钮,这时打开OD,Alt+E双击进入LaptopAlarm.exe模块,搜索字符串后发现几处假码字符,逐处排查后找到注册验证处。

代码:
00405BFE  |.  8B0D 701A4100 mov     ecx, dword ptr [411A70]
00405C04  |.  8B35 0CC34000 mov     esi, dword ptr [<&USER32.SendMes>;  USER32.SendMessageW
00405C0A  |.  68 60194100   push    00411960                         ; /playboysen@126.com
00405C0F  |.  68 80000000   push    80                               ; |wParam = 80
00405C14  |.  6A 0D         push    0D                               ; |Message = WM_GETTEXT
00405C16  |.  51            push    ecx                              ; |hWnd => 301B2
00405C17  |.  FFD6          call    esi                              ; \SendMessageW
00405C19  |.  8B15 6C1A4100 mov     edx, dword ptr [411A6C]
00405C1F  |.  68 54194100   push    00411954                         ; /1234
00405C24  |.  6A 05         push    5                                ; |wParam = 5
00405C26  |.  6A 0D         push    0D                               ; |Message = WM_GETTEXT
00405C28  |.  52            push    edx                              ; |hWnd => 301FE
00405C29  |.  FFD6          call    esi                              ; \SendMessageW
00405C2B  |.  A1 681A4100   mov     eax, dword ptr [411A68]
00405C30  |.  68 48194100   push    00411948                         ; /5678
00405C35  |.  6A 05         push    5                                ; |wParam = 5
00405C37  |.  6A 0D         push    0D                               ; |Message = WM_GETTEXT
00405C39  |.  50            push    eax                              ; |hWnd => 401B0
00405C3A  |.  FFD6          call    esi                              ; \SendMessageW
00405C3C  |.  8B0D 641A4100 mov     ecx, dword ptr [411A64]
00405C42  |.  68 3C194100   push    0041193C                         ; /90ab
00405C47  |.  6A 05         push    5                                ; |wParam = 5
00405C49  |.  6A 0D         push    0D                               ; |Message = WM_GETTEXT
00405C4B  |.  51            push    ecx                              ; |hWnd => 40200
00405C4C  |.  FFD6          call    esi                              ; \SendMessageW
00405C4E  |.  8B15 601A4100 mov     edx, dword ptr [411A60]
00405C54  |.  68 30194100   push    00411930                         ; /cdef
00405C59  |.  6A 05         push    5                                ; |wParam = 5
00405C5B  |.  6A 0D         push    0D                               ; |Message = WM_GETTEXT
00405C5D  |.  52            push    edx                              ; |hWnd => 80230
00405C5E  |.  FFD6          call    esi                              ; \SendMessageW
00405C60  |.  68 54194100   push    00411954                         ;  1234
00405C65  |.  8D8C24 380100>lea     ecx, dword ptr [esp+138]
00405C6C  |.  FF15 30C14000 call    dword ptr [<&MSVCP80.std::basic_>  
00405C72  |.  68 48194100   push    00411948                         ;  5678
00405C77  |.  8D8C24 380100>lea     ecx, dword ptr [esp+138]
00405C7E  |.  FF15 A8C14000 call    dword ptr [<&MSVCP80.std::basic_>  
00405C84  |.  68 3C194100   push    0041193C                         ;  90ab
00405C89  |.  8D8C24 380100>lea     ecx, dword ptr [esp+138]
00405C90  |.  FF15 A8C14000 call    dword ptr [<&MSVCP80.std::basic_>  
00405C96  |.  68 30194100   push    00411930                         ;  cdef
00405C9B  |.  8D8C24 380100>lea     ecx, dword ptr [esp+138]
00405CA2  |.  FF15 A8C14000 call    dword ptr [<&MSVCP80.std::basic_>  
00405CA8  |.  68 60194100   push    00411960                         ;  playboysen@126.com
00405CAD  |.  8D8C24 540100>lea     ecx, dword ptr [esp+154]
00405CB4  |.  FF15 30C14000 call    dword ptr [<&MSVCP80.std::basic_>  
00405CBA  |.  83EC 1C       sub     esp, 1C
00405CBD  |.  8D8424 500100>lea     eax, dword ptr [esp+150]
00405CC4  |.  8BCC          mov     ecx, esp
00405CC6  |.  896424 30     mov     dword ptr [esp+30], esp
00405CCA  |.  50            push    eax
00405CCB  |.  FF15 24C14000 call    dword ptr [<&MSVCP80.std::basic_> 
00405CD1  |.  83EC 1C       sub     esp, 1C
00405CD4  |.  8D9424 880100>lea     edx, dword ptr [esp+188]
00405CDB  |.  8BCC          mov     ecx, esp
00405CDD  |.  896424 50     mov     dword ptr [esp+50], esp
00405CE1  |.  52            push    edx
00405CE2  |.  C68424 FC0100>mov     byte ptr [esp+1FC], 0B
00405CEA  |.  FF15 24C14000 call    dword ptr [<&MSVCP80.std::basic_>
00405CF0  |.  C68424 F80100>mov     byte ptr [esp+1F8], 1           
00405CF8  |.  E8 43100000   call    00406D40                        ; 注册码验证,关键                    
00405CFD  |.  85C0          test    eax, eax
00405CFF  |.  74 4F         je      short 00405D50
......
跟入00405CF8关键call

代码:
00406D40  /$  6A FF         push    -1
00406D42  |.  68 1CAB4000   push    0040AB1C
00406D47  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
00406D4D  |.  50            push    eax
......
00406D7A  |.  8B8424 980000>mov     eax, dword ptr [esp+98]
00406D81  |.  83F8 10       cmp     eax, 10                          ;  注册码应为16位,否则不再运算
00406D84  |.  0F85 CF010000 jnz     00406F59
00406D8A  |.  8B1D A8C24000 mov     ebx, dword ptr [<&MSVCR80.tolowe>;  MSVCR80.tolower
00406D90  |.  33F6          xor     esi, esi
00406D92  |.  3BF0          cmp     esi, eax
00406D94  |.  76 0D         jbe     short 00406DA3
00406D96  |.  FF15 9CC24000 call    dword ptr [<&MSVCR80._invalid_pa>
00406D9C  |.  8B8424 980000>mov     eax, dword ptr [esp+98]
00406DA3  |>  8BAC24 880000>/mov     ebp, dword ptr [esp+88]
00406DAA  |.  BF 08000000   |mov     edi, 8
00406DAF  |.  39BC24 9C0000>|cmp     dword ptr [esp+9C], edi
00406DB6  |.  73 07         |jnb     short 00406DBF
00406DB8  |.  8DAC24 880000>|lea     ebp, dword ptr [esp+88]
00406DBF  |>  3BF0          |cmp     esi, eax
00406DC1  |.  76 06         |jbe     short 00406DC9
00406DC3  |.  FF15 9CC24000 |call    dword ptr [<&MSVCR80._invalid_p>
00406DC9  |>  39BC24 9C0000>|cmp     dword ptr [esp+9C], edi
00406DD0  |.  8BBC24 880000>|mov     edi, dword ptr [esp+88]
00406DD7  |.  73 07         |jnb     short 00406DE0
00406DD9  |.  8DBC24 880000>|lea     edi, dword ptr [esp+88]
00406DE0  |>  0FB74475 00   |movzx   eax, word ptr [ebp+esi*2]       ;  注册码逐位放入EAX,进行转化操作(ebp中存放的是假码)
00406DE5  |.  50            |push    eax
00406DE6  |.  FFD3          |call    ebx                             ;  MSVCR80.tolower函数 转大写为小写,数字不变
00406DE8  |.  66:890477     |mov     word ptr [edi+esi*2], ax        ;  保存转换后的结果(由数字和小写字母组成)
00406DEC  |.  8B8424 9C0000>|mov     eax, dword ptr [esp+9C]         ;  注册码位数放入
00406DF3  |.  83C6 01       |add     esi, 1
00406DF6  |.  83C4 04       |add     esp, 4
00406DF9  |.  3BF0          |cmp     esi, eax                        ;  比较以便循环转换
00406DFB  |.^ 72 A6         \jb      short 00406DA3
00406DFD  |.  8D4C24 68     lea     ecx, dword ptr [esp+68]
00406E01  |.  51            push    ecx
00406E02  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
00406E06  |.  FF15 24C14000 call    dword ptr [<&MSVCP80.std::basic_>;  读取用户邮箱地址
00406E0C  |.  68 E0CE4000   push    0040CEE0                         ;  固定字符"yevjeprpsolsuis2001"
00406E11  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
00406E15  |.  C64424 64 02  mov     byte ptr [esp+64], 2
00406E1A  |.  FF15 A4C14000 call    dword ptr [<&MSVCP80.std::basic_>;  用户邮箱+固定字符串"yevjeprpsolsuis2001"
00406E20  |.  6A 01         push    1
00406E22  |.  E8 CD2C0000   call    <jmp.&MSVCR80.operator new>      ;  operator new
00406E27  |.  894424 18     mov     dword ptr [esp+18], eax
00406E2B  |.  83EC 18       sub     esp, 18
00406E2E  |.  8D5424 54     lea     edx, dword ptr [esp+54]
00406E32  |.  8BCC          mov     ecx, esp
00406E34  |.  896424 34     mov     dword ptr [esp+34], esp
00406E38  |.  B3 03         mov     bl, 3
00406E3A  |.  52            push    edx
00406E3B  |.  889C24 800000>mov     byte ptr [esp+80], bl
00406E42  |.  FF15 24C14000 call    dword ptr [<&MSVCP80.std::basic_>
00406E48  |.  8D4424 38     lea     eax, dword ptr [esp+38]          
00406E4C  |.  50            push    eax                             
00406E4D  |.  E8 DE1D0000   call    00408C30                         ; 此call过去,堆栈窗口出现一串可疑32位值,莫非……
00406E52  |.  8B8424 980000>mov     eax, dword ptr [esp+98]
我们的目的不是爆破(男人,对自己要严一点!),毕竟只有了解算法才能写出注册机,所以我们跟入00406E4D关键call

代码:
00408C30  /$  6A FF         push    -1
00408C32  |.  68 D4A94000   push    0040A9D4
00408C37  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
......
00408CBF  |.  8D7C24 54     lea     edi, dword ptr [esp+54]           ;  ECX中的值是"用户邮箱+yevjeprpsolsuis2001",参数之一
00408CC3  |.  E8 B8FDFFFF   call    00408A80                          ;  此call过后,出现了一串32位可疑数值,难道是……
00408CC8  |.  50            push    eax
00408CC9  |.  8D4C24 58     lea     ecx, dword ptr [esp+58]
00408CCD  |.  C68424 9C0000>mov     byte ptr [esp+9C], 3
......
到这里,我禁不住想大胆的猜测MD5算法。为了验证我们的猜测,跟入00408CC3处关键call

代码:
00408A80  /$  6A FF         push    -1
00408A82  |.  68 F9A54000   push    0040A5F9
00408A87  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
00408A8D  |.  50            push    eax
00408A8E  |.  83EC 78       sub     esp, 78
00408A91  |.  A1 18104100   mov     eax, dword ptr [411018]
00408A96  |.  33C4          xor     eax, esp
00408A98  |.  894424 70     mov     dword ptr [esp+70], eax
00408A9C  |.  53            push    ebx
00408A9D  |.  56            push    esi
00408A9E  |.  A1 18104100   mov     eax, dword ptr [411018]
00408AA3  |.  33C4          xor     eax, esp
00408AA5  |.  50            push    eax
00408AA6  |.  8D8424 840000>lea     eax, dword ptr [esp+84]
00408AAD  |.  64:A3 0000000>mov     dword ptr fs:[0], eax
00408AB3  |.  33DB          xor     ebx, ebx
00408AB5  |.  897C24 10     mov     dword ptr [esp+10], edi
00408AB9  |.  895C24 0C     mov     dword ptr [esp+C], ebx
00408ABD  |.  C78424 8C0000>mov     dword ptr [esp+8C], 1
00408AC8  |.  83BC24 AC0000>cmp     dword ptr [esp+AC], 10
00408AD0  |.  8B8424 980000>mov     eax, dword ptr [esp+98]
00408AD7  |.  895C24 28     mov     dword ptr [esp+28], ebx
00408ADB  |.  895C24 24     mov     dword ptr [esp+24], ebx
00408ADF  |.  C74424 14 012>mov     dword ptr [esp+14], 67452301     ;  看到这里的四串数值,我们基本可以确定我们的猜测
00408AE7  |.  C74424 18 89A>mov     dword ptr [esp+18], EFCDAB89     ;  标准MD5算法
00408AEF  |.  C74424 1C FED>mov     dword ptr [esp+1C], 98BADCFE
00408AF7  |.  C74424 20 765>mov     dword ptr [esp+20], 10325476
00408AFF  |.  73 07         jnb     short 00408B08
00408B01  |.  8D8424 980000>lea     eax, dword ptr [esp+98]
到这里我们基本已经了解此软件的算法,还算简单主算法使用了标准MD5,参数是用户邮箱+固定字符串"yevjeprpsolsuis2001"。单步回到以下地方接着分析

代码:
00406E59  |.  33F6          xor     esi, esi
00406E5B  |.  85C0          test    eax, eax
00406E5D  |.  76 5C         jbe     short 00406EBB
00406E5F  |.  3BF0          cmp     esi, eax
00406E61  |.  76 06         jbe     short 00406E69
00406E63  |.  FF15 9CC24000 call    dword ptr [<&MSVCR80._invalid_pa>
00406E69  |>  8BBC24 880000>/mov     edi, dword ptr [esp+88]
00406E70  |.  BD 08000000   |mov     ebp, 8
00406E75  |.  39AC24 9C0000>|cmp     dword ptr [esp+9C], ebp
00406E7C  |.  73 07         |jnb     short 00406E85
00406E7E  |.  8DBC24 880000>|lea     edi, dword ptr [esp+88]
00406E85  |>  3B7424 30     |cmp     esi, dword ptr [esp+30]
00406E89  |.  76 06         |jbe     short 00406E91
00406E8B  |.  FF15 9CC24000 |call    dword ptr [<&MSVCR80._invalid_p>
00406E91  |>  396C24 34     |cmp     dword ptr [esp+34], ebp
00406E95  |.  8B4424 20     |mov     eax, dword ptr [esp+20]         ;  00406E4D处的call所求出的MD5值放入
00406E99  |.  73 04         |jnb     short 00406E9F
00406E9B  |.  8D4424 20     |lea     eax, dword ptr [esp+20]
00406E9F  |>  66:8B0C77     |mov     cx, word ptr [edi+esi*2]        ;  假码逐位放入(edi中存放的是假码)
00406EA3  |.  66:3B0C70     |cmp     cx, word ptr [eax+esi*2]        ;  比较处我好像看到了曙光!(eax存放着MD5值)
00406EA7  |.  0F85 82000000 |jnz     00406F2F
00406EAD  |.  8B8424 980000>|mov     eax, dword ptr [esp+98]
00406EB4  |.  83C6 01       |add     esi, 1
00406EB7  |.  3BF0          |cmp     esi, eax
00406EB9  |.^ 72 AE         \jb      short 00406E69
00406EBB  |>  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00406EBF  |.  885C24 60     mov     byte ptr [esp+60], bl
00406EC3  |.  FF15 10C14000 call    dword ptr [<&MSVCP80.std::basic_>s<wchar_t>,std::allocator<wchar_t> >
00406EC9  |.  8B4424 14     mov     eax, dword ptr [esp+14]
00406ECD  |.  50            push    eax
00406ECE  |.  E8 272C0000   call    <jmp.&MSVCR80.operator delete>   ;  operator delete
......
00406F22  |.  33CC          xor     ecx, esp
00406F24  |.  E8 722B0000   call    00409A9B
00406F29  |.  83C4 50       add     esp, 50
00406F2C  |.  C2 3800       retn    38
从上面我们看到了,程序计算出的32位MD5值的前16位即为注册码。

附件中是MD5汇编注册机源码,其中主要代码如下:
注:原作者MiSSiNG iN ByTES,我只是针对本软件修改了部分代码,借花献佛了!
详情下载附件:
代码:
.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

include MD5_tut.inc
include md5_addin.asm

.code

start:

  invoke GetModuleHandle,NULL
  mov    hInstance,eax

    invoke InitCommonControls
  invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
  invoke ExitProcess,0

;########################################################################

DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

  mov    eax,uMsg
  .if eax==WM_INITDIALOG
    invoke DisplayBmp,hWin,10000,327,2,100
    
  .elseif eax==WM_COMMAND
    .if wParam == 1005
      invoke GetDlgItemText,hWin,1002,addr UserName,256    ;gets the user name
      invoke lstrcat,addr UserName,addr key                           
      invoke MD5Init              ;initialize MD5, here loads our magic numbers stored in md5_addin.asm source file
      invoke lstrlen,addr UserName          ;returns the user name lenght in eax
      invoke MD5Update,addr UserName,eax        ;MD5Update sets the text to be hashed -- the username which is eax bytes long
      invoke MD5Final              ;hash to MD5
      invoke max_ConvertBytesToAscii,addr Serial,eax,16    ;thanks to Maxtreme here we convert the returned from MD5Final bytes to ascii string
      invoke lstrcpyn,addr FinalSerial,addr Serial,17      ;please refer to Win32 API Reference for details
      invoke SetDlgItemText,hWin,1003,addr FinalSerial    ;show the serial
    .endif
  .elseif eax==WM_CLOSE
    invoke EndDialog,hWin,0
  .else
    mov    eax,FALSE
    ret
  .endif
  mov    eax,TRUE
  ret

DlgProc endp

end start
MD5关键算法部分(源码中md5_addin.asm)如下:
代码:
.code

max_ConvertBytesToAscii proc uses ebx esi edi lpDst:DWORD, lpSrc:DWORD, nSrcLen:DWORD
  
  mov esi,lpSrc
  mov edi,lpDst
  
@@:
  movzx eax,byte ptr[esi]
  invoke wsprintf,edi,chr$("%02x"),EAX
  add edi,2
  inc esi
  dec nSrcLen
  mov ecx,nSrcLen
  test ecx,ecx
  jne @b
  
  Ret
  
max_ConvertBytesToAscii EndP

MD5Init proc uses edi
  xor eax, eax
  mov MD5Len,eax
  MD5BURN
  mov eax,offset MD5Digest

  ;magic numbers
  mov dword ptr [eax+0*4],67452301h  ;magic 1
  mov dword ptr [eax+1*4],0EFCDAB89h  ;magic 2
  mov dword ptr [eax+2*4],98BADCFEh  ;magic 3
  mov dword ptr [eax+3*4],10325476h  ;magic 4
  
  ret
MD5Init endp

MD5Update proc uses esi edi ebx lpBuffer:dword, dwBufLen:dword
  mov ebx,dwBufLen
  mov esi,lpBuffer
  add MD5Len,ebx
  .while ebx
    mov eax,MD5Index
    mov ecx,64
    sub ecx,eax
    lea edi,[MD5HashBuf+eax]  
    .if ecx <= ebx
      sub ebx,ecx
      rep movsb
      call MD5Transform
      MD5BURN
    .else
      mov ecx,ebx
      rep movsb
      add MD5Index,ebx
      .break
    .endif
  .endw
  ret
MD5Update endp

......
我们来看看这次能学到什么:
1、此软件无注册错误对话框提示、尽量少的出现相关字符串提示(在解客面前尽可能少的漏出马脚)、避开常用API这些都值得我们借鉴,我相信世界上没有不能被破解的软件,但通过程序员的重视和努力,尽可能地延长我们软件的生存周期还是可行的。
2、软件出现了试用提示而且这些字符串并没有加密(OD可直接搜索到)、主程序没有加壳或VM、关键算法直接套用标准MD5、注册表中所保存的注册信息未经加密(解客可轻易查找到关键键值)这些都要引以为戒!一个很小的疏忽,可能就会导致整个公司所有软件全盘功亏一篑。
上传的附件 MD5_keygen_src.rar