【文章标题】菜鸟必看的破解文章(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 大侠了.^_^
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2021-4-25 17:06
被kanxue编辑
,原因: