课程设计简单程序的密码破解.docx
《课程设计简单程序的密码破解.docx》由会员分享,可在线阅读,更多相关《课程设计简单程序的密码破解.docx(29页珍藏版)》请在冰点文库上搜索。
课程设计简单程序的密码破解
目录
1.题目要求2
2.使用工具2
3.破解正文2
3.1查看程序2
3.2查找关键点3
3.3逆向主要函数4
4.总结15
6.参考文献21
简单程序的密码破解
1.题目要求
自己写一个简单的Windows程序,要求输入密码,在密码不正确时,提示“passworderror”。
如果正确,则出现一个新的界面。
对于生成的执行文件进行反汇编,阅读和跟踪执行,暴力破解(生成新版本的执行程序)或找出密码。
2.使用工具
Olldbg,IDA,PEid,RadASM,VC++
3.破解正文
3.1查看程序
先使用PEid载入程序,如图3.1.1所示,显示界面为win32GUI,情况确实如此,此程序界面是用VC6++直接做出来的。
有最下面那个Nothingfound*可知程序并没有使用任何加壳手段。
图3.3.1
直接运行程序,输入任意字符如:
123456,123456点击注册,查看是否有任何提示信息,如图3.1.2所示
图3.1.2
3.2查找关键点
首先用OD载入程序,这里使用三种方式查找关键点,第一种方式是查找关键字符串passworderror,右键中文搜索引擎—>搜索ASCII码,即可查看所有字符串。
中文搜索引擎
地址反汇编文本字符串
0040113FpushKeShe.00402050汇编
00401144pushKeShe.00402038passworderror
00401172pushKeShe.00402050汇编
00401177pushKeShe.00402038passworderror
0040126FpushKeShe.00402050汇编
00401274pushKeShe.00402047注册正确
004012FBcall(InitialCPUselection)
0040133ApushKeShe.00402050汇编
0040133FpushKeShe.00402038passworderror
这里有三处出现关键字符passworderror,而且字符串——注册正确肯定也很重要。
分别双击跟进去,即有可能找到关键代码段。
第二种方式是给相关函数下断点,这里首先要输入字符串,则很有可能用到GetDlgItemText之类的API,所以给GetDlgItemText下断点,就能在输入用户名,密码后跟踪到程序,来分析关键代码。
第三种方式是根据程序的类型结构,直接分析。
004012FB>call;[InitCommonControls
00401300push0x0;/pModule=NULL
00401302call;\GetModuleHandleA
00401307movdwordptrds:
[0x40317C],eax
0040130Cpush0x0;/pThreadId=NULL
0040130Epush0x0;|CreationFlags=0
00401310push0x0;|pThreadParm=NULL
00401312pushKeShe.00401000;|ThreadFunction=KeShe.00401000
00401317push0x0;|StackSize=0
00401319push0x0;|pSecurity=NULL
0040131Bcall;\CreateThread
00401320push0x0;/lParam=NULL
00401322pushKeShe.0040128B;|DlgProc=KeShe.0040128B
00401327push0x0;|hOwner=NULL
00401329push0x65;|pTemplate=65
0040132Bpushdwordptrds:
[0x40317C];|hInst=NULL
00401331call;\DialogBoxParamA
00401336jmpXKeShe.0040134B
00401338push0x0;/Style=MB_OK|MB_APPLMODAL
0040133ApushKeShe.00402050;|汇编
0040133FpushKeShe.00402038;|passworderror
00401344push0x0;|hOwner=NULL
00401346call;\MessageBoxA
0040134Bpush0x0;/ExitCode=0
0040134Dcall;\ExitProcess
这是OD中刚载入时的代码,左边为内存地址,中间是汇编代码,右边为注释。
由上代码,很明显可以看出程序调用了GetModuleHandleA,DialogBoxParamA,ExitProcess。
则可以得出这里典型的win32程序,用DialogBoxParamA载入窗口资源,然后消息循环,ExitProcess为退出程序。
因此由DialogBoxParamA即可找到消息处理函数。
不过应注意一下这里使用了CreateThread函数来创建线程,肯定有什么目的需要关注一下。
3.3逆向主要函数
3.3.1逆向消息处理函数
由3.1的第三种方式中,DialogBoxParamA的第四个参数就可以找到消息处理函数的地址。
00401322pushKeShe.0040128B;|DlgProc=KeShe.0040128B
则消息处理函数的地址为0040128B,用OD来到此地址。
0040128B/.55pushebp
0040128C|.8BECmovebp,esp
0040128E|.53pushebx
0040128F|.57pushedi
00401290|.56pushesi
00401291|.8B450Cmoveax,[arg.2]
00401294|.83F810cmpeax,0x10;Switch(cases10..111)
00401297|.750CjnzXKeShe.004012A5
00401299|.6A00push0x0;/Result=0;Case10(WM_CLOSE)ofswitch00401294
0040129B|.FF7508push[arg.1];|hWnd
0040129E|.E8B5000000call;\EndDialog
004012A3|.EB4AjmpXKeShe.004012EF
004012A5|>3D10010000cmpeax,0x110
004012AA|.7502jnzXKeShe.004012AE
004012AC|.EB41jmpXKeShe.004012EF;Case110(WM_INITDIALOG)ofswitch00401294
004012AE|>3D11010000cmpeax,0x111
004012B3|.752EjnzXKeShe.004012E3
004012B5|.8B4510moveax,[arg.3];Case111(WM_COMMAND)ofswitch00401294
004012B8|.3DE8030000cmpeax,0x3E8
004012BD|.7513jnzXKeShe.004012D2
004012BF|.FF7514push[arg.4]
004012C2|.FF7510push[arg.3]
004012C5|.FF750Cpush[arg.2]
004012C8|.FF7508push[arg.1]
004012CB|.E84CFEFFFFcallKeShe.0040111C;关键注册响应函数
004012D0|.EB1DjmpXKeShe.004012EF
004012D2|>83F864cmpeax,0x64
004012D5|.7518jnzXKeShe.004012EF
004012D7|.6A00push0x0;/Result=0
004012D9|.FF7508push[arg.1];|hWnd
004012DC|.E877000000call;\EndDialog
004012E1|.EB0CjmpXKeShe.004012EF
004012E3|>B800000000moveax,0x0;Defaultcaseofswitch00401294
004012E8|.5Epopesi
004012E9|.5Fpopedi
004012EA|.5Bpopebx
004012EB|.C9leave
004012EC|.C21000retn0x10
004012EF|>B801000000moveax,0x1
004012F4|.5Epopesi
004012F5|.5Fpopedi
004012F6|.5Bpopebx
004012F7|.C9leave
004012F8\.C21000retn0x10
观察上面这个典型的消息处理函数,并有程序界面可以看出,这里使用了两次EndDialog,一次为点程序的叉的消息处理,还有一次为点取消键的消息处理。
则在两次EndDialog之间
004012CB|.E84CFEFFFFcallKeShe.0040111C
即是点注册键的消息处理函数。
3.3.2逆向线程函数
使用OD来到内存地址0040111C处,按F2设下断点如图3.3.2(A)
图3.3.2(A)
然后,按F9直接让程序运行,输入123456,123456,如下图:
不过,这里还没有输入用户名和注册码,就弹出了passworderror消息框,确定后就结束程序了。
没输入用户名和对话框就弹出了错误消息,是因为程序检测出了程序正在被调试器动态调试。
观察关于线程的窗口,如下图:
除了主线程,还有别的线程,这就和开始的CreateThread相对应,其中一个线程地址为00401000,在CreateThread函数中创建的线程函数也为该地址,用OD来到此处。
00401000movdwordptrds:
[0x4032D7],KeShe.004011>;入口地址
0040100Amovdwordptrds:
[0x4032DB],KeShe.004012>
00401014movebx,dwordptrds:
[0x4032D7]
0040101Axoredx,edx
0040101Cmovzxeax,byteptrcs:
[ebx]
00401020addedx,eax;累加
00401022incebx
00401023cmpebx,dwordptrds:
[0x4032DB]
00401029jnzXKeShe.0040101C
0040102Bcmpedx,0x777D;关键对比
00401031jnzXKeShe.00401049
00401033push0x0;/pThreadId=NULL
00401035push0x0;|CreationFlags=0
00401037push0x0;|pThreadParm=NULL
00401039pushKeShe.00401000;|ThreadFunction=KeShe.00401000
0040103Epush0x0;|StackSize=0
00401040push0x0;|pSecurity=NULL
00401042call;\CreateThread
00401047jmpXKeShe.0040104E
00401049jmpKeShe.00401338
0040104Eretn
分析这段程序的算法,作用为将地址0x40111C到地址0x40128B之间的机器码进行累加到EDX中,最后让EDX与0x777D比较,若不相等则跳转到00401249处,在401049处再跳转到401338h处,即弹出错误消息框然后结束程序。
如果不在40111C到40128B中设断点,测试得EDX刚好为777Dh,在线程结束之前又调用了CreateThread函数来创建线程继续进行校验和来防止设断点。
在动态调试下,设断点会改变机器码,即可检测出有调试器加载。
这里采用了多线程反动态调试技术,若不下断点,当输入用户名和注册码后,点注册程序会直接跑掉。
3.3.3分析注册函数
由于使用了多线程反调试技术,当然可以将线程挂起再调试,不过这里我使用静态分析这部分代码,使用IDA静态分析。
直接来到0x40111C处。
这里调用了GetDlgItemTextA函数,返回值为eax与6比较,如下图相等则跳转,不相等这出现错误提示信息,则用户名的长度必须为6。
在401157h处调用了第二个GetDlgItemTextA函数,返回值与0Bh比较,则密码长度必须为11,否则会显示失败,如下图:
当检验长度成功后,跳转到401188h处,这里压栈了五个参数,然后调用了sub_40104F函数,0040104F处不在地址0x40111C到地址0x40128B之间,因此可以下断点,这里现有OD调试这个函数,分析加密算法。
在0040104F处下断点,F9运行,用户名输:
123456密码:
12345678911
成功在此处断下,查看堆栈,如下图:
最上一行为返回地址,下面5行为参数,分别为1.2.3.4.5,这里用户名的第6个参数没有传进来。
0040104Fpushebp
00401050movebp,esp
00401052pushKeShe.0040328C;/pSystemInfo=KeShe.0040328C
00401057call;\GetSystemInfo
这里调用了GetSystemInfo函数,查MSDN得获取系统信息的结构体为
typedefstruct_SYSTEM_INFO{
union{
DWORDdwOemId;
struct{
WORDwProcessorArchitecture;
WORDwReserved;
};
};
DWORDdwPageSize;
LPVOIDlpMinimumApplicationAddress;
LPVOIDlpMaximumApplicationAddress;
DWORDdwActiveProcessorMask;
DWORDdwNumberOfProcessors;
DWORDdwProcessorType;
DWORDdwAllocationGranularity;
WORDwProcessorLevel;
WORDwProcessorRevision;
}SYSTEM_INFO,*LPSYSTEM_INFO;
则内存地址0040328C返回的是该结构体,如下图
0040105Cmovebx,dwordptrds:
[0x403290]
这里的403290刚好偏移4个字节,则是把DWORDdwPageSize给ebx,本电脑为00001000.
00401062orbl,byteptrss:
[ebp+0x8]
Ebp+8为第一个参数,与bl异或。
00401065andebx,0xFF
0040106Bmoval,byteptrds:
[ebx+0x403000]
这里ebx的值为31h,查看403000处发现填充了大量数据,如下图,这里将0x403000+31h处的字符给al,为’O’。
00401071cmpal,byteptrds:
[0x403180]
将0x403180处的字符与al做比对,查看该处的数据如下图,发现为密码数据,这里即为’1’
00401077jnzKeShe.00401338;不相等则跳转的结束,这里修改标志寄存器不让跳转
0040107Dmovebx,dwordptrds:
[0x4032A0]
这里偏移14h个字节,为dwNumberOfProcessors,本电脑为4
00401083orbl,byteptrss:
[ebp+0xC]
与第二个参数异或,的ebx为36h
00401086moval,byteptrds:
[ebx+0x403000]
0040108Ccmpal,byteptrds:
[0x403181]
403000h+36h处的字符这里为’F’,与密码的第二个字符’2’作比对
00401092jnzKeShe.00401338
00401098pushKeShe.004032B0;/pSystemTime=KeShe.004032B0
0040109Dcall;\GetSystemTime
获取系统时间,由MSDN,该结构为
typedefstruct_SYSTEMTIME{
WORDwYear;//07DD
WORDwMonth;//0006
WORDwDayOfWeek;//0006
WORDwDay;//000F
WORDwHour;//000F
WORDwMinute;//0002
WORDwSecond;//0009
WORDwMilliseconds;//0238
}SYSTEMTIME,
*PSYSTEMTIME;
返回的数据地址为004032B0h如下图,这为此时的系统时间:
004010A2movbx,wordptrds:
[0x4032B8]
这里相对于SYSTEMTIME偏移8字节,为wHour,即000F
004010A9orbl,byteptrss:
[ebp+0x10]
004010ACmoval,byteptrds:
[ebx+0x403000]
004010B2cmpal,byteptrds:
[0x403182]
Bl与第三个参数异或,则ebx为3Fh,将403000h+3Fh处的字符’h’给al,并与第三个密码字符’3’比对。
004010B8jnzKeShe.00401338
004010BEmovbx,wordptrds:
[0x4032BA]
004010C5orbl,byteptrss:
[ebp+0x14]
004010C8moval,byteptrds:
[ebx+0x403000]
004010CEcmpal,byteptrds:
[0x403183]
这里相对于SYSTEMTIME偏移10字节,为wMinute,即0002,Bl与第四个参数异或,则ebx为36h,将403000h+36h处的字符’F’给al,并与第四个密码字符’4’比对。
004010D4jnzKeShe.00401338
004010DApushKeShe.004032C0;/pSystemTime=KeShe.004032C0
004010DFcall;\GetSystemTime
这里又一次获取系统时间,
wMinute;wSecond;wMilliseconds发生变化
004010E4movax,wordptrds:
[0x4032BC]
004010EAmovbx,wordptrds:
[0x4032CC]
004010F1cmpax,bx
004010F4jnzKeShe.00401338
比较两次的wSecond,若不调试应该是同一个值,但这里调试了,所以时间不一样,所以跳转到结束,显示失败。
不过这里仍然要改变标志寄存器不使其跳转,这里根据时间差来反调试。
004010FAxorebx,ebx
004010FCmovbx,wordptrds:
[0x4032C4]
00401103orbl,byteptrss:
[ebp+0x18]
00401106moval,byteptrds:
[ebx+0x403000]
0040110Ccmpal,byteptrds:
[0x403184]
这里相对于SYSTEMTIME偏移4字节,为wDayOfWeek,即0006,Bl与第五个参数异或,则ebx为37h,将403000h+37h处的字符’E’给al,并与第五个密码字符’5’比对。
00401112jnzKeShe.00401338
00401118leave
00401119retn0x14
现在继续用IDA静态分析注册函数的代码:
Eax为3的平方9,将403000h+9的字符这里为’O’,赋值给String1
Eax为4的立方40h,将403000h+40h的字符’4’,赋值给String1_1
Eax为5的立方7Dh,将403000h+7Dh的字符’s’,赋值给String1_2
Eax为6的立方D8h,将403000h+D8h的字符’i’,赋值给String1_3
Eax为7的平方31h,将403000h+31h的字符’O’,赋值给String1_4,
将用户名的第六个字符给bl,这里输入的是6,则ebx为36h,则403000h+36h处的字符为’F’
比对密码的后六个字符,成功则显示成功。
则若用户名为:
123456密码为:
OF***O4siOF其中三个星号要根据具体的小时,分钟,所在星期的天数来觉得,具体看上