首页
社区
课程
招聘
[原创]菜鸟必看破文(2)-追出注册码之简单算法分析(补充)
发表于: 2005-1-24 13:13 11213

[原创]菜鸟必看破文(2)-追出注册码之简单算法分析(补充)

2005-1-24 13:13
11213

【文章标题】菜鸟必看的破解文章(2)-追出软件注册码之简单算法分析(补充小弟的破文)

【软件名称】crackme

【下载地址】好像是破解工具2004大礼包里面的

【使用工具】peid0.92CHS,FLYOD1.1

【保护方式】序列号

【开发语言】MASM

【本文写于】2005-1-24  ytcswb [无组织,纯粹业余爱好者]

―――――――――――――――――――――――――――――――――
【破解分析】

破解分析
注: 前面小弟,已经追出注册码了,相关内容不在重复.希望小弟不要介意我发这篇破文哦,本来是应该小弟自己完成的.(不能怪我叫你小弟哦,是你起的名的问题,^_^)

1---od载入.停在程序入口处.
因为是masm编写的,故入口代码比较直接,如果是c编写的,od载入后,会停在c编译器自动添加的start函数处,经过一些初始化,
然后再进入用户程序入口main().c加载的start函数的大致格式如下:
start()
{
1-设置seh
2-call getversion       //取操作系统版本号:构件编号,主版本号,次版本号,完整版本号
3-call _heap_init       //堆初始化
4-call getcommandlinea  //取命令行参数:参数个数,参数字符串指针数组
5-getenvironmentdtringA //设置环境变量:环境变量字符串指针数组
6-call main             //用户程序入口
7-call exit             //结束进程
}
/*   start函数大致就是这样的流程,不同的编译器,里面的函数会有所不同    */
/*   但功能基本都是完成以上功能!这部分内容是抄来的,希望有助于大家理解 */

本crcakme的入口代码如下(从od中截取到的):
00401000 u>/$  6A 00             push 0                                                 ; /pModule = NULL
00401002   |.  E8 34010000       call <jmp.&KERNEL32.GetModuleHandleA>                  ; \GetModuleHandleA
00401007   |.  A3 EC234000       mov dword ptr ds:[4023EC],eax
0040100C   |.  6A 00             push 0                                                 ; /lParam = NULL
0040100E   |.  68 29104000       push unabexcm.00401029                                 ; |DlgProc = unabexcm.00401029
00401013   |.  6A 00             push 0                                                 ; |hOwner = NULL
00401015   |.  6A 01             push 1                                                 ; |pTemplate = 1
00401017   |.  FF35 EC234000     push dword ptr ds:[4023EC]                             ; |hInst = NULL
0040101D   |.  E8 3D010000       call <jmp.&USER32.DialogBoxParamA>                     ; \DialogBoxParamA
/*--------------------------------------------------------------------*/
/*   产生对话框 ,我们看到的那个让你输入注册码的对话框就是这个产生的   */
/*   The DialogBoxParam function creates a modal dialog box           */
/*   from a dialog box template resource                              */
/*  int DialogBoxParam(
    HINSTANCE hInstance,        // 程序实例句柄
    LPCTSTR lpTemplateName,        // identifies dialog box template
    HWND hWndParent,                // 父窗口句柄
    DLGPROC lpDialogFunc,        // pointer to dialog box procedure  
    LPARAM dwInitParam                 // initialization value
   );        函数原型
/*--------------------------------------------------------------------*/

00401022   |.  6A 00             push 0                                                 ; /ExitCode = 0
00401024   \.  E8 1E010000       call <jmp.&KERNEL32.ExitProcess>                       ; \ExitProcess
/*--------------------------------------------------------------*/
/*            结束进程及本身的所有线程                          */
/* The ExitProcess function ends a process and all its threads. */
/* VOID ExitProcess(
    UINT uExitCode         // exit code for all threads
   ); 函数原型
/*--------------------------------------------------------------*/

00401029   /.  C8 000000         enter 0,0
0040102D   |.  817D 0C 11010000  cmp dword ptr ss:[ebp+C],111
00401034   |.  75 07             jnz short unabexcm.0040103D
00401036   |.  E8 1B000000       call unabexcm.00401056
0040103B   |.  EB 13             jmp short unabexcm.00401050
0040103D   |>  837D 0C 02        cmp dword ptr ss:[ebp+C],2
00401041   |.  75 07             jnz short unabexcm.0040104A
00401043   |.  E8 E5000000       call unabexcm.0040112D
00401048   |.  EB 06             jmp short unabexcm.00401050
0040104A   |>  33C0              xor eax,eax
0040104C   |.  C9                leave
0040104D   |.  C2 1000           retn 10

2----继续分析.不设任何断点,运行起来,输入试练码123456789(随便输入的),点确定,出现出错提示:
"The serial you entered is not correct!",然后我们重新载入 crtl+f12,再利用罗聪大侠给我们提供的
查字符串的工具(注:大家可以再论坛里下载,自己找找吧),查找上面的这个字符串,来到这里.

0040106C   |>  6A 25             push 25                                      ; /Count = 25 (37.)
/*------------------------------------------------------------------------------------*/
/*  我们在这里设个断点,重新载入 crtl+f12,f9运行起来,输入试练码,确定后,就会停在这里    */
/*  设断点方法:[利用命令行插件,输入bp 0040106C] 或 [把光标点到此行,按f2或双击]        */
/*------------------------------------------------------------------------------------*/
0040106E   |.  68 24234000       push unabexcm.00402324                       ; |Buffer = unabexcm.00402324
00401073   |.  6A 68             push 68                                      ; |ControlID = 68 (104.)
00401075   |.  FF75 08           push dword ptr ss:[ebp+8]                    ; |hWnd
00401078   |.  E8 F4000000       call <jmp.&USER32.GetDlgItemTextA>           ; \GetDlgItemTextA
/*------------------------------------------------------------------------------------*/
   The GetDlgItemText function retrieves the title or text associated                 
   with a control in a dialog box. 从对话框的一个控件中(这里是文本框),得到文本串,
   实际就是,我们输入的试练码123456789.
  UINT GetDlgItemText(
    HWND hDlg,                // handle of dialog box  对话框句柄
    int nIDDlgItem,        // identifier of control  控件id号
    LPTSTR lpString,        // address of buffer for text 文本缓冲区地址,存放要得到的文本
    int nMaxCount         // maximum size of string  文本串的长度
   );函数原型
函数执行完后,我们到od界面的左下内存窗口中,找到文本缓冲区地址00402324,就可以看到试练码了.
找到位置的方法: 先在内存窗口中任意点一下,使其成为当前窗口,在CRTL+G,输入00402324,回车,就到了
这就是你看到的试练码了:
00402324  31 32 33 34 35 36 37 38  12345678
0040232C  39 00 00 00 00 00 00 00  9.......
00402334  00 00 00 00 00 00 00 00  ........
/*------------------------------------------------------------------------------------*/
0040107D   |.  6A 00             push 0                               ; /pFileSystemNameSize = NULL
0040107F   |.  6A 00             push 0                               ; |pFileSystemNameBuffer = NULL
00401081   |.  68 C8204000       push unabexcm.004020C8               ; |pFileSystemFlags = unabexcm.004020C8
00401086   |.  68 90214000       push unabexcm.00402190               ; |pMaxFilenameLength = unabexcm.00402190
0040108B   |.  68 94214000       push unabexcm.00402194               ; |pVolumeSerialNumber = unabexcm.00402194
00401090   |.  6A 32             push 32                              ; |MaxVolumeNameSize = 32 (50.)
00401092   |.  68 5C224000       push unabexcm.0040225C               ; |VolumeNameBuffer = unabexcm.0040225C
00401097   |.  6A 00             push 0                               ; |RootPathName = NULL
00401099   |.  E8 B5000000       call <jmp.&KERNEL32.GetVolumeInformationA> ; \GetVolumeInformationA
/*------------------------------------------------------------------------------------*/
    The GetVolumeInformation function returns information about a file system
    and volume whose root directory is specified.返回文件系统及当前磁盘卷标信息
BOOL GetVolumeInformation(
    LPCTSTR lpRootPathName,                // address of root directory of the file system
    LPTSTR lpVolumeNameBuffer,                //  当前磁盘卷标的地址,里面就是读到的卷标,我的机器是CCC-1
    DWORD nVolumeNameSize,                // length of lpVolumeNameBuffer
    LPDWORD lpVolumeSerialNumber,        // address of volume serial number
    LPDWORD lpMaximumComponentLength,        // address of system's maximum filename length
    LPDWORD lpFileSystemFlags,                // address of file system flags
    LPTSTR lpFileSystemNameBuffer,        // address of name of file system
    DWORD nFileSystemNameSize                 // length of lpFileSystemNameBuffer
   );        函数原型
/*------------------------------------------------------------------------------------*/

0040109E   |.  68 F3234000       push unabexcm.004023F3                  ; /StringToAdd = "4562-ABEX"
004010A3   |.  68 5C224000       push unabexcm.0040225C                  ; |ConcatString = "CCC-1"
                                                                            我c盘的卷标!
004010A8   |.  E8 94000000       call <jmp.&KERNEL32.lstrcatA>           ; \lstrcatA
/*------------------------------------------------------------------------------------*/
  The lstrcat function appends one string to another. 连接字符串
  LPTSTR lstrcat(
    LPTSTR lpString1,        // 要追加的字符串的内存地址,这里就是"4562-ABEX"
    LPCTSTR lpString2         // 要连接的原字符串内存地址,这里就是我的c盘的卷标
   );函数原型
我们看一下内存区0040225C(原字符串内存地址):
0040225C  43 43 43 2D 31 34 35 36  CCC-1456   //看到了吧,字符串已经连接起来了.
00402264  32 2D 41 42 45 58 00 00  2-ABEX..
0040226C  00 00 00 00 00 00 00 00  ........  
/*------------------------------------------------------------------------------------*/
004010AD   |.  B2 02             mov dl,2
004010AF   |>  8305 5C224000 01  /add dword ptr ds:[40225C],1
004010B6   |.  8305 5D224000 01  |add dword ptr ds:[40225D],1
004010BD   |.  8305 5E224000 01  |add dword ptr ds:[40225E],1
004010C4   |.  8305 5F224000 01  |add dword ptr ds:[40225F],1
004010CB   |.  FECA              |dec dl
004010CD   |.^ 75 E0             \jnz short unabexcm.004010AF //循环
/*------------------------------------------------------------------------------------*/
这个循环,把上面连接好的字符串的前4位的acsii,加2.我们可以在内存区看到结果:
0040225C  45 45 45 2F 31 34 35 36  EEE/1456 //结果为:"EEE/14562-ABEX" ------记为 str1
00402264  32 2D 41 42 45 58 00 00  2-ABEX..
0040226C  00 00 00 00 00 00 00 00  ........
/*------------------------------------------------------------------------------------*/
004010CF   |.  68 FD234000       push unabexcm.004023FD                    ; /StringToAdd = "L2C-5781"
004010D4   |.  68 00204000       push unabexcm.00402000                    ; |ConcatString = ""
004010D9   |.  E8 63000000       call <jmp.&KERNEL32.lstrcatA>             ; \lstrcatA
/*------------------------------------------------------------------------------------*/
这里还是连接字符串,结果: "L2C-5781"-------记为 str2
/*------------------------------------------------------------------------------------*/
004010DE   |.  68 5C224000       push unabexcm.0040225C                     ; /StringToAdd = "EEE/14562-ABEX"
004010E3   |.  68 00204000       push unabexcm.00402000                     ;|ConcatString = "L2C-5781EEE/14562-ABEX"
004010E8   |.  E8 54000000       call <jmp.&KERNEL32.lstrcatA>              ; \lstrcatA
/*------------------------------------------------------------------------------------*/
这里还是连接字符串,结果: "L2C-5781EEE/14562-ABEX" 实际就是 str2 + str1
/*------------------------------------------------------------------------------------*/
004010ED   |.  68 24234000       push unabexcm.00402324                     ; /String2 = "123456789"
004010F2   |.  68 00204000       push unabexcm.00402000                     ; |String1 = "L2C-5781EEE/14562-ABEX"
004010F7   |.  E8 51000000       call <jmp.&KERNEL32.lstrcmpiA>             ; \lstrcmpiA
/*------------------------------------------------------------------------------------*/
The lstrcmpi function compares two character strings. The comparison is not case sensitive.
比较2个字符串是否相等.这里是比较 : 试练码 (123456789) 和 真注册码 (L2C-5781EEE/14562-ABEX)
int lstrcmpi(

    LPCTSTR lpString1,        // address of first string  第1个字符串
    LPCTSTR lpString2         // address of second string 第2个字符串
   );函数原型
If the strings are equal, the return value is zero. 如果相等则返回0
/*------------------------------------------------------------------------------------*/
004010FC   |.  83F8 00           cmp eax,0
004010FF   |.  74 16             je short unabexcm.00401117
/*------------------------------------------------------------------------------------*/
    相等,就显示"Yep, you entered a correct serial!" 是使用MessageBoxA来显示的
    因此,你也可以设 bp MessageBoxA 断点
/*------------------------------------------------------------------------------------*/
00401101   |.  6A 00             push 0                               ; /Style = MB_OK|MB_APPLMODAL
00401103   |.  68 34244000       push unabexcm.00402434               ; |Title = "Error!"
00401108   |.  68 3B244000       push unabexcm.0040243B               ; |Text = "The serial you entered is not correct!"
0040110D   |.  FF75 08           push dword ptr ss:[ebp+8]            ; |hOwner
00401110   |.  E8 56000000       call <jmp.&USER32.MessageBoxA>       ; \MessageBoxA
00401115   |.  EB 16             jmp short unabexcm.0040112D

00401117   |>  6A 00             push 0                               ; /Style = MB_OK|MB_APPLMODAL
00401119   |.  68 06244000       push unabexcm.00402406               ; |Title = "Well Done!"
0040111E   |.  68 11244000       push unabexcm.00402411               ; |Text = "Yep, you entered a correct serial!"
00401123   |.  FF75 08           push dword ptr ss:[ebp+8]            ; |hOwner
00401126   |.  E8 40000000       call <jmp.&USER32.MessageBoxA>       ; \MessageBoxA //显示正确的提示,表示你成功了.
0040112B   |.  EB 00             jmp short unabexcm.0040112D

0040112D   |$  6A 00             push 0                                                 ; /Result = 0
0040112F   |.  FF75 08           push dword ptr ss:[ebp+8]                              ; |hWnd
00401132   |.  E8 22000000       call <jmp.&USER32.EndDialog>                           ; \EndDialog
00401137   |.  C9                leave
00401138   \.  C2 1000           retn 10
/*----------------------------------------------------------------------------------------------------------------*/
下面就是引入函数表(这样说不知道是否正确,请大侠们指点一下!),程序中使用什么函数,就来到这里获得函数的入口地址,这些函数的入口地址是由操作系统帮我们自动计算的,我们不用理会.现在我们看到的就是一堆跳转指令.
例如:
call 0040113b ==相当于== call [<&KERNEL32.GetModuleHandleA>],即调用这个api函数.函数的具体结构及功能大家可以查msdn!
/*----------------------------------------------------------------------------------------------------------------*/
0040113B    $- FF25 6C304000     jmp dword ptr ds:[<&KERNEL32.GetModuleHandleA>]        ;  kernel32.GetModuleHandleA
00401141    $- FF25 70304000     jmp dword ptr ds:[<&KERNEL32.lstrcatA>]                ;  kernel32.lstrcatA
00401147    .- FF25 74304000     jmp dword ptr ds:[<&KERNEL32.ExitProcess>]             ;  kernel32.ExitProcess
0040114D    $- FF25 78304000     jmp dword ptr ds:[<&KERNEL32.lstrcmpiA>]               ;  kernel32.lstrcmpiA
00401153    $- FF25 7C304000     jmp dword ptr ds:[<&KERNEL32.GetVolumeInformationA>]   ;  kernel32.GetVolumeInformationA
00401159    $- FF25 84304000     jmp dword ptr ds:[<&USER32.EndDialog>]                 ;  USER32.EndDialog
0040115F    $- FF25 88304000     jmp dword ptr ds:[<&USER32.DialogBoxParamA>]           ;  USER32.DialogBoxParamA
00401165    .- FF25 8C304000     jmp dword ptr ds:[<&USER32.wsprintfA>]                 ;  USER32.wsprintfA
0040116B    $- FF25 90304000     jmp dword ptr ds:[<&USER32.MessageBoxA>]               ;  USER32.MessageBoxA
00401171    $- FF25 94304000     jmp dword ptr ds:[<&USER32.GetDlgItemTextA>]           ;  USER32.GetDlgItemTextA
00401177       00                db 00

3---通过以上的分析,我们已经得到正确的注册码了: L2C-5781EEE/14562-ABEX
算法的原理如下:
   读卷标,连上串"4562-ABEX",  得到: "CCC-1"+"4562-ABEX"="CCC-14562-ABEX"
   然后前4位的acsii, 加2,     得到: "EEE/14562-ABEX"
   最后, 连接到串"L2C-5781"上.得到: "L2C-5781EEE/14562-ABEX"  //也就是真正的注册码.

―――――――――――――――――――――――――――――――――
【总结】

windows程序,都频繁调用api函数,因此装个msdn或我现在用的win32 programmer's refernce,很有必要,
你可以随时查看一下,弄明白了函数的功能,要弄清楚算法,就很简单了.

注: 我是个超级菜鸟,只能分析这样简单的算法,希望大侠们多点鼓励,也希望和我一样的菜鸟们,不要灰心,大家一起学习,共同提高.
    非常感谢您能耐着性子看完我的第一篇简单得不能在简单的破文.有错误之处,请大侠们多指点.
另: 因为文章看起来有点乱,本来想着色,可惜不会,那位大侠能指点一下.或给个好用的工具,先谢谢了!
   
   
―――――――――――――――――――――――――――――――――
本文由 <流行时代破文写手> 生成          //  这里要感谢一下破文写手的作者 aqata 大侠了.^_^


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

最后于 2021-4-25 17:06 被kanxue编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
2
支持一下,不错。
2005-1-24 14:28
0
雪    币: 225
活跃值: (146)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
3
^_^谢谢laoqian兄捧场啊^_^
因为是菜文,所以尽可能写的详细点,但能力有限,请大侠你们多指点了。。
2005-1-24 15:01
0
雪    币: 214
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
支持一下!btw:楼主,你现在用的win32 programmer's refernce能发给我一份吗?我的E-MAIL:ljy3282393@126.com 。谢谢!
2005-1-24 15:12
0
雪    币: 225
活跃值: (146)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
5
win32 programmer's refernce只是win32的,你最好装msdn,那里很全,还可以查到vc++ mfc的函数。向这样只使用win32api的程序几乎没有了。
win32 programmer's refernce下载地址
http://www.pediy.com/Document.htm 到这个页面下载吧
2005-1-24 15:18
0
雪    币: 214
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
最初由 ytcswb 发布
win32 programmer's refernce只是win32的,你最好装msdn,那里很全,还可以查到vc++ mfc的函数。向这样只使用win32api的程序几乎没有了。
win32 programmer's refernce下载地址
http://www.pediy.com/Document.htm 到这个页面下载吧

Thank You !
2005-1-24 16:35
0
雪    币: 2054
活跃值: (292)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
7
呵呵,我不会在意的.这样的文章也是越多越好
2005-1-24 19:16
0
雪    币: 209
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好文!我对API函数原型看不懂,是否只要知道此函数干什么的就可以了?
2005-1-24 20:12
0
雪    币: 98729
活跃值: (201034)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
9
我支持!!!
2005-1-24 21:46
0
游客
登录 | 注册 方可回帖
返回
//