新微赢技术网

标题: RecoverNT V3.0完全破解实战 [打印本页]

作者: lianeh    时间: 2009-11-23 01:21
标题: RecoverNT V3.0完全破解实战
RecoverNT是个文件/磁盘误删除恢复工具,如果你不小心误删了文件,甚至误对磁盘作了FORMAT(格式化)或FDISK(分区),只要相应的磁盘空间数据没有毁坏, RecoverNT就能帮你找回失去的数据。RecoverNT可以支持Windows 9x/NT/2000平台,FAT/FAT32/NTFS文件格式,3.0版本需要注册,未注册版只能对文件删除进行恢复,不提供对FORMAT(格式化)或FDISK(分区)误操作的恢复。
程序名 :RecoverNT
版本   :V3.0
大小   :375KB
运行平台:Windows 9x/NT/2000
保护方式:注册码
破解方式:注册码破解
破解难度:难
程序下载:recovernt.zip
附件  :注册机及其C语言源程序
作者   :ddcrack (2001/06/06)
笨冬瓜 :ddcrack.yeah.net
破解步骤:
1. 用softice载入windows(通过CTRL+D来检查softice是否已经准备好,按F5退出);
2. 运行RecoverNT;
3. 在“姓名:”中输入:ddcrack(随意),“公司:”中输入:RND(随意),“序号:”中输入:12345678(随意);
4. 用CTRL+D呼出softice,下万能断点:bpx hmemcpy,按F5返回RecoverNT;
5. 在RecoverNT中选择“注册”,很快程序就被softice拦截下来;
6. 用 bd * 暂停断点 bpx hmemcpy ;
7. 按F12键8次,返回到RecoverNT的领空,程序停留在下面的地方:
。。。
0167:0043A1F6 CALL [USER32!GetWindowTextA]    <-- 获取我们输入的信息
0167:0043A1FC PUSH FF               <-- 程序停在这里
0167:0043A1FE MOV ECX,[EBP+10]
0167:0043A201 CALL 0043208E
0167:0043A206 JMP 0043A213
。。。
8. 按F10多次(有耐心一点^_^,此处也可以按F12省点力气,效果一样,不过其它程序可不能保证)一直来到下面可疑的地方(也许你会问为什么可疑?——因为有很多JZ、JNZ的跳转指令嘛):
。。。
0167:00406372 PUSH 004580B0            <-- 程序停在这里
0167:00406377 MOV EAX,[ESI+64]
0167:0040637A PUSH EAX              <-- EAX指向输入的用户名“ddcrack”
0167:0040637B CALL 0041C5E0
0167:00406380 ADD ESP,08
0167:00406383 TEST EAX,EAX
0167:00406385 JZ 0040641B
0167:0040638B LEA EDI,[ESI+68]
0167:0040638E PUSH 004580B0
0167:00406393 MOV EAX,[EDI]
0167:00406395 PUSH EAX              <-- EAX指向输入的注册码“12345678”
0167:00406396 CALL 0041C5E0
0167:0040639B ADD ESP,08
0167:0040639E TEST EAX,EAX
0167:004063A0 JZ 0040641B
0167:004063A2 PUSH 11
0167:004063A4 MOV ECX,EDI
0167:004063A6 CALL 00432039
0167:004063AB PUSH EAX
0167:004063AC CALL 00402EC0
0167:004063B1 ADD ESP,04
0167:004063B4 TEST EAX,EAX
0167:004063B6 JZ 004063FA
。。。
9. 按F10两次,程序走到0167:0040637A PUSH EAX,用 D EAX 可以看到EAX指向的内存区域中存放着我们输入的用户名“ddcrack”;接着按F10走到下面的0167:00406385 JZ 0040641B处,你会发现零标志位为0(即EAX的值为非零),程序不会发生跳转。那么上面的CALL 0041C5E0有什么作用呢?如果你继续按F10往下走,还会发现0167:00406395 PUSH EAX处的EAX指向我们输入的注册码“12345678”,下面又有同样的CALL 0041C5E0,既然两次调用同样的一个子程序,子程序后又有同样的判断和跳转地址,说明这个子程序只是具有通用的功能,而并不是比较注册码的地方。
10. 为了验证子程序CALL 0041C5E0的作用,按F10走到0167:004063A0 JZ 0040641B停下,然后用指令 RFL Z 改变标志寄存器的值(使程序偏离正常的运行方向,跳到0040641B去),接着按F5让程序自己往下运行。你会看到RecoverNT弹出一个信息框,告诉你必须输入“姓名”和“序号”。由此我们可以知道子程序CALL 0041C5E0的作用是判断我们输入的信息是否是有效的字符串,因为前面正常输入了用户名和注册码,所以当执行完CALL 0041C5E0后后程序不会跳到0040641B去。
11. 重复步骤2到步骤8,重新来到0167:00406372 PUSH 004580B0处。因为已经知道子程序CALL 0041C5E0的作用只是判断输入的信息是否是有效的字符串,所以我们按F10大步流星的走到0167:004063AB PUSH EAX,用 D EAX 查看内存中的数据,发现EAX指向输入的注册码“12345678”(这个地方有点可疑),继续按F10走过它下面的子程序0167:004063AC CALL 00402EC0并停在 0167:004063B6 JZ 004063FA处,此时EAX为零,程序将跳到004063FA去。为了知道这个CALL后程序会有什么结果,暂且按F10跳到004063FA去看个究竟:
。。。
0167:004063FA PUSH FF
0167:004063FC PUSH 30
0167:004063FE PUSH 0000EF1F
0167:00406403 CALL 0043A52B           <-- 弹出错误框的子程序
0167:00406408 MOV ECX,ESI
0167:0040640A CALL 0042DAC8
0167:0040640F PUSH FF
0167:00406411 MOV ECX,EDI
0167:00406413 CALL 0043208E
0167:00406418 POP EDI
0167:00406419 POP ESI
0167:0040641A RET
。。。
12. 按F10走过0167:00406403处的CALL 0043A52B,你会看到RecoverNT弹出一个消息框告诉你“序列号码无效”,所以刚才0167:004063AC处的CALL 00402EC0肯定是判断注册码正确与否的地方。
13. 再次重复步骤2到步骤8,又一次来到0167:00406372 PUSH 004580B0处(不要厌烦喔!破解就是这样的,要有一点耐心啦^_^)。接着按F10走到0167:004063AC CALL 00402EC0,然后再按F8进入子程序CALL 00402EC0里面去:
。。。
0167:00402EC0 SUB ESP,24
0167:00402EC3 PUSH EBX
0167:00402EC4 PUSH ESI
0167:00402EC5 MOV ESI,[ESP+30]
0167:00402EC9 PUSH EDI
0167:00402ECA MOVSX EAX,BYTE PTR [ESI]     <-- ESI指向输入的注册码“12345678”
0167:00402ECD PUSH EAX              <-- EAX是输入注册码的第一个字符“1”
0167:00402ECE CALL 0041C370
0167:00402ED3 ADD ESP,04
0167:00402ED6 CMP EAX,52
0167:00402ED9 JNZ 0040302F
0167:00402EDF MOVSX EAX,BYTE PTR [ESI+01]
0167:00402EE3 PUSH EAX
0167:00402EE4 CALL 0041C370
0167:00402EE9 ADD ESP,04
0167:00402EEC CMP EAX,56
0167:00402EEF JNZ 0040302F
0167:00402EF5 CMP BYPE PTR [ESI+07],2D
0167:00402EF9 JNZ 0040302F
0167:00402EFF PUSH ESI
0167:00402F00 CALL [KERNEL32!lstrlen]
0167:00402F06 CMP EAX,0F
0167:00402F09 JNZ 0040302F
。。。
14. 按F10走到0167:00402ECA MOVSX EAX,BYTE PTR [ESI],然后用 D ESI 看看,发现ESI指向的内存区域中的内容是我们输入的注册码“12345678”,所以这条指令的作用就是取出输入注册码的第一个字符,即“1”。紧接着下面有个CALL 0041C370对这个字符进行处理,我们按F10走到这个CALL处停下,然后用F8进入这个子程序中:
。。。
0167:0041C370 CMP DWORD PTR [00459B50],00
0167:0041C377 PUSH ESI
0167:0041C378 PUSH EDI
0167:0041C379 JNZ 0041C38F
0167:0041C37B MOV EAX,[ESP+0C]
0167:0041C37F CMP EAX,61            <-- 小于“a”?
0167:0041C382 JL 0041C3DB
0167:0041C384 CMP EAX,7A            <-- 大于“z”?
0167:0041C387 JG 0041C3DB
0167:0041C389 SUB EAX,20            <-- 将小写转变为大写
0167:0041C38C POP EDI
0167:0041C38D POP ESI
0167:0041C38E RET
。。。
  上面的程序比较简单,作用就是判断字符是否是小写字母,如果是小写字母则转变成大写字母。
15. 按F10走出上面的子程序,我们将会返回到0167:00402ECE CALL 0041C370的下一句,即程序停在0167:00402ED3 ADD ESP,04上。因为“1”不是小写字符,所以 EAX的返回值仍然是16进制数31H,即字符“1”:
。。。
0167:00402ECA MOVSX EAX,BYTE PTR [ESI]    <-- ESI指向输入的注册码“12345678”
0167:00402ECD PUSH EAX             <-- EAX是输入注册码的第1个字符“1”
0167:00402ECE CALL 0041C370
0167:00402ED3 ADD ESP,04            <-- 程序停在这里
0167:00402ED6 CMP EAX,52            <-- 注册码的第1个字符“1”是否等于16进制数52,即字母“R”?
0167:00402ED9 JNZ 0040302F
0167:00402EDF MOVSX EAX,BYTE PTR [ESI+01]
0167:00402EE3 PUSH EAX             <-- EAX是输入注册码的第2个字符“2”
0167:00402EE4 CALL 0041C370
0167:00402EE9 ADD ESP,04
0167:00402EEC CMP EAX,56            <-- 注册码的第2个字符“2”是否等于16进制数56,即字母“V”?
0167:00402EEF JNZ 0040302F
0167:00402EF5 CMP BYPE PTR [ESI+07],2D    <-- 注册码的第8个字符“8”是否等于16进制数2D,即字符“-”?
0167:00402EF9 JNZ 0040302F
0167:00402EFF PUSH ESI             <-- ESI指向输入的注册码“12345678”
0167:00402F00 CALL [KERNEL32!lstrlen]     <-- 调用API函数计算输入注册码的字符个数
0167:00402F06 CMP EAX,0F            <-- 输入注册码的字符个数是否为15位
0167:00402F09 JNZ 0040302F
。。。
  上面这段程序的作用比较容易搞懂,就是确定输入注册码的第1、2、8个字符是否分别是“R”、“V”、“-”,即正确注册码的格式应该是类似:RVXXXXX-XXXXXXX,其中X代表未知数,不过肯定的是“RV”后应该紧跟5个字符,然后再跟个连字符“-”,连字符后面应该还有7个字符,总共加起来字符个数正好是15位。因为我们输入的注册码是“12345678”,和正确的注册码格式不一致,所以如果你想单步跟踪这段程序的话,在遇到JNZ指令时用命令 RFL Z 改变程序的运行方向,使其继续往下走。否则在开始的0167:00402ED9 JNZ 0040302F是因为输入注册码的第1个字符是“1”而不等于“R”,如果不用 RFL Z 改变标志位的状态,程序马上会跳到0040302F去,这样就完蛋喽^_^。
16. 重新返回RecoverNT(因为输入的注册码不对,程序早晚还是会出错的,继续下去也不利于跟踪),在“姓名:”中输入:ddcrack,“公司:”中输入:RND,“序号:”中输入:RV12345-6789000(12345和6789000是随意输入的),让程序程序走到步骤13的地方,同时因为我们输入了正确的注册码格式,所以可以一直按F10大步流星走到0167:00402F09 JNZ 0040302F的下一句:
。。。
0167:00402EFF PUSH ESI
0167:00402F00 CALL [KERNEL32!lstrlen]
0167:00402F06 CMP EAX,0F
0167:00402F09 JNZ 0040302F
0167:00402F0F MOV EDI,00000002         <-- 程序停在这里
0167:00402F14 MOVSX EAX,BYTE PTR [ESI+EDI]  <-- ESI+EDI指向输入注册码第3位开始的字符,即“12345”
0167:00402F18 PUSH EAX              <-- 取出一个字符
0167:00402F19 CALL 0041C340           <-- 处理字符
0167:00402F1E ADD ESP,04
0167:00402F21 TEST EAX,EAX           <-- 根据返回值判断是否合乎要求?
0167:00402F23 JZ 00403014
0167:00402F29 INC EDI              <-- 地址指针加1
0167:00402F2A CMP EDI,07            <-- 判断是否已经处理完字符“5”
0167:00402F2D JL 00402F14
。。。
17. 上面的程序段其作用是判断输入注册码“RV12345-6789000”中的“12345”是否合乎要求,如果不符合要求,程序就跳到00403014去(死掉了)。不过你将发现我们碰巧可以一直按F10走过这段程序而不会跳到00403014去,那究竟什么样的字符符合要求呢?如果你有兴趣,可以在程序走到0167:00402F19 CALL 0041C340时按F8进去看看:
。。。
0167:0041C340 CMP DWORD PTR [0045990C],01
0167:0041C347 JLE 0041C359
。。。
。。。
0167:0041C359 MOV EDX,[00459700]       <-- 将某一基表地址赋予EDX
0167:0041C35F XOR EAX,EAX
0167:0041C361 MOV ECX,[ESP+04]        <-- ESP+04中是字符“12345”之一,即上面的0167:00402F18 PUSH EAX指令压进堆栈的
0167:0041C365 MOV AX,[ECX*2+EDX]       <-- 用字符的16进制值作为地址偏移查表
0167:0041C369 AND EAX,04           <-- 将查表结果和04进行逻辑与运算
0167:0041C36C RET
。。。
  这个子程序的作用咋看起来不容易明白,只知道程序根据字符值查表,然后将查表结果和04相与,由此来判断字符是否符合要求。前面说过我们输入的“RV12345-6789000”可以平安的走过这段程序,说明数字是符合要求的。如果你有兴趣可以用大小写字母及其它字符试一下,你会发现除了数字以外的输入都不能安全走过这段程序,使得步骤8的0167:004063B4 TEST EAX,EAX时EAX返回值为零,从而其下一句程序0167:004063B6 JZ 004063FA的结果是程序跳到004063FA去,出现“序列号无效”的错误信息。我们输入的字符是“12345”,刚好碰巧全部合乎要求^_^!
18. 连续按F10跑到步骤16程序段的下面一行停下(看清楚噢!不要太着急,否则又要从头再来一遍):
。。。
0167:00402F2F MOV EDI,00000008        <-- 程序停在这里
0167:00402F34 MOVSX EAX,BYTE PTR [ESI+EDI]  <-- ESI+EDI指向输入注册码第9位开始的字符,即“6789000”
0167:00402F38 PUSH EAX             <-- 取出一个字符
0167:00402F39 CALL 0041C340          <-- 处理字符
0167:00402F3E ADD ESP,04
0167:00402F41 TEST EAX,EAX          <-- 根据返回值判断是否合乎要求?
0167:00402F43 JZ 0040301D
0167:00402F49 INC EDI             <-- 地址指针加1
0167:00402F4A CMP EDI,0F           <-- 判断是否已经处理完最后一个字符“0”
0167:00402F4D JL 00402F34
。。。
  这段程序实际上和上面一样,只不过是对输入注册码第9位开始的字符串“6789000”进行检测,判断其是否都为数字。显然我们输入的注册码依然能够走过这里,是不是比较幸运啊^_^(假如输入的不全是数字,结果会麻烦很多的)!
19. 按F10跑到上面程序的下一行停下:
。。。
0167:00402F4F MOV AX,[ESI+02]         <-- ESI指向输入的注册码,即“RV12345-6789000”
0167:00402F53 MOV [ESP+10],AX         <-- AX=3231,即字符“12”
0167:00402F58 LEA EAX,[ESP+10]        <-- EAX指向字符串“12”
0167:00402F5C MOV BYTE PTR [ESP+12],00    <-- 字符“12”后面补0,表示字符串结束
0167:00402F61 PUSH EAX
0167:00402F62 CALL 0041C330           <-- 将10进制数“12”转换成对应的16进制数“C”,结果返回EAX
0167:00402F67 MOV CX,[ESI+05]         <-- CX=3534,即字符“45”
0167:00402F6B ADD ESP,04
0167:00402F6E MOV [ESP+10],CX         <-- 将字符“45”放进地址ESP+10中
0167:00402F73 SUB AL,13            <-- 将“C”减去“13”,结果AL=F9
0167:00402F75 LEA ECX,[ESP+10]         <-- ECX指向字符串“45”
0167:00402F79 MOV [ESP+0C],AL         <-- AL=F9的值保存在地址ESP+14中
0167:00402F7D MOV BYTE PTR [ESP+12],00    <-- 字符“45”后面补0,表示字符串结束
0167:00402F82 PUSH ECX
0167:00402F83 CALL 0041C330          <-- 将10进制数“45”转换成对应的16进制数“2D”,结果返回EAX
0167:00402F88 LEA EDX,[ESP+14]        <-- 将字符串“45”的地址赋予EDX
0167:00402F8C ADD ESP,04
0167:00402F8F LEA EBX,[EAX-25]        <-- 将“2D”减去“25”,结果EBX=00000008
0167:00402F92 LEA ECX,[ESI+0A]        <-- ECX指向字符串“89000”
0167:00402F95 PUSH EDX
0167:00402F96 MOV EAX,[ECX]          <-- EAX=30303938,即字符“8900”
0167:00402F98 MOV [EDX],EAX          <-- 将字符“8900”放进EDX指向的内存中
0167:00402F9A MOV CL,[ECX+04]         <-- CL=30,即字符“0”
0167:00402F9D MOV [EDX+04],CL         <-- 将字符“0”放进内存地址EDX+04中
0167:00402FAO MOV BYTE PTR [ESP+19],00    <-- 此时EDX指向的内存地址中是字符串“89000”
0167:00402FA5 CALL 0041C330          <-- 将10进制数“89000”转换成对应的16进制数“15BA8”
0167:00402FAA ADD ESP,04
0167:00402FAD MOV EDI,EAX           <-- EDI=EAX=00015BA8
0167:00402FAF XOR DI,5468           <-- DI=5BA8 XOR 5468=0FC0,EDI=00010FC0
0167:00402FB4 MOV AX,[ESI+08]         <-- AX=3736,即字符“67”
0167:00402FB8 MOV [ESP+10],AX         <-- 将字符“45”放进地址ESP+10中
0167:00402FBD LEA EAX, [ESP+10]        <-- EAX指向字符串“67”
0167:00402FC1 MOV BYTE PTR [ESP+12],00    <-- 字符“67”后面补0,表示字符串结束
0167:00402FC6 MOVZX EDI,DI          <-- EDI=00000FC0
0167:00402FC9 PUSH EAX
0167:00402FCA CALL 0041C330          <-- 将10进制数“67”转换成对应的16进制数“43”,结果返回EAX
0167:00402FCF MOV [ESP+14],AL         <-- AL=43的值保存在地址ESP+14中
0167:00402FD3 ADD ESP,04
0167:00402FD6 XOR EAX,EAX           <-- EAX清零
0167:00402FD8 MOV ECX,00000064        <-- 给ECX赋值00000064
0167:00402FDD MOV AL,BL            <-- 在0167:00402F8F处BL=08,所以AL=08
0167:00402FDF MOV EBX,0000000A        <-- 给EBX赋值0000000A
0167:00402FE4 LEA EAX,[EDI+EAX+03]      <-- EAX=EDI+EAX+03=00000FC0+00000008+03=00000FCB
0167:00402FE8 CDQ                <-- 将EAX的值扩展到EDX中
0167:00402FE9 IDIV ECX             <-- EAX除以ECX,即00000FCB/00000064
0167:00402FEB MOV CL,DL            <-- 余数DL=2B,则CL=DL=2B
0167:00402FED XOR EAX,EAX           <-- EAX清零
0167:00402FEF MOV AL,[ESP+0C]         <-- AL=F9,即0167:00402F79 MOV [ESP+0C],AL处的结果
0167:00402FF3 LEA EAX,[EDI+EAX+03]      <-- EAX=EDI+EAX+03=00000FC0+000000F9+03=000010BC
0167:00402FF7 CDQ                <-- 将EAX的值扩展到EDX中
0167:00402FF8 IDIV EBX             <-- EAX除以EBX,即000010BC/0000000A
0167:00402FFA SUB DL, [ESI+04]        <-- 余数DL=04减去注册码第5位数“3”的16进制值33
0167:00402FFD CMP DL,D0            <-- 减法结果DL=D1,显然不等于D0
0167:00403000 JNZ 00403026           <-- 如果相等则继续往下比较
0167:00403002 CMP CL,[ESP+10]         <-- 上一次的除法余数CL=2B,显然也不等于“67”的16进制值“43”
0167:00403006 JNZ 00403026
0167:00403008 MOV EAX,00000001        <-- 如果比较都成功通过,则将EAX置1,表示注册码正确
0167:0040300D POP EDI
0167:0040300E POP ESI
0167:0040300F POP EBX
0167:00403010 ADD ESP,24
0167:00403013 RET
。。。
  上面这段程序是核心所在,比较复杂一些,不过注解已经比较详细,这里就不细讲了,只是大概提一些重点之处:
  1. 如何知道CALL 0041C330的作用呢?按F10走到0167:00402F61 PUSH EAX时我们知道EAX指向的内存地址中是字符串“12”,当F10走过这个CALL后,我们发现EAX的值发生了变化等于C,用 D EAX 可知16进制数C的十进制数即为12,也就是说程序将输入的字符“12”转换成相应的16进制数。如果按通常的办法跟踪到CALL 0041C330里面去,你会发觉程序不是很好懂的。所以通常破解时都需要靠感觉去猜子程序的作用,如果只是一味的去仔细跟踪每一步程序,效率是很低的,况且也完全没有必要。
  2. 0167:00402F8F LEA EBX,[EAX-25]、0167:00402FE4 LEA EAX,[EDI+EAX+03]和0167:00402FF3 LEA EAX,[EDI+EAX+03]这3条指令的作用是将源表达式EAX-25、EDI+EAX+03和EDI+EAX+03的值分别赋予其左边的寄存器EBX、EAX和EAX源操作数的地址赋予目的操作数。千万不要被LEA这个指令代码所蒙蔽,以为是将源操作数的地址赋予目的操作数,这一点要很小心。
20. 下面以我们输入的注册码“RV12345-6789000”为例讲解上面程序中注册码的算法:
注册码限制: 形式为RVXXXXX-XXXXXXX(总共15位,X为数字)
注册码:    RV  12  3  45 - 67  89000
16进制值:      0C  33  2D   43  15BA8
⑴  0C-13=F9
⑵  2D-25=08
⑶  5BA8 XOR 5468=0FC0
⑷  00000FC0+00000008+03=00000FCB
⑸  00000FCB/00000064,余数=2B
⑹  00000FC0+000000F9+03=000010BC
⑺  000010BC/0000000A,余数=04
⑻  04-33=D1
⑼  判断D1是否等于D0?
⑽  判断2B是否等于43?
21. 从上面的程序可以看出注册码和输入的姓名及公司名无关,而且其算法结果并是不唯一的,也就是有很多的注册码都可以满足条件。我们可以用试探的方法得到某一注册码,或者通过写程序来产生很多注册码,需要注意的是注册码应该从“89000”开始去试探(比如随便假设五位数“55555”),然后推算出其它部分的值。比如注册码“RV55255-2455555”就符合要求(具体的推算步骤就不用细讲了吧^_^):
注册码:    RV  55  2  55 - 24  55555
16进制值:      37  32  37   18  D903
⑴  37-13=24
⑵  37-25=12
⑶  D903 XOR 5468=8D6B
⑷  00008D6B+00000012+03=00008D80
⑸  00008D80/00000064,余数=18
⑹  00008D6B+00000024+03=00008D92
⑺  00008D92/0000000A,余数=02
⑻  02-32=D0
⑼  D0=D0
⑽  18=18
22. 好了,到此为止终于讲完,是不是有点晕啊^_^!没关系,静下心来仔细看看,不要着急,有空写个注册机




欢迎光临 新微赢技术网 (http://bbs.weiying.cn/) Powered by Discuz! X3.2