|
作者: jrg1982 [jrg1982] 论坛用户 | 登录 |
Add/Remove 4Good Add/Remove是个小巧实用的软件反安装/删除工具,其安装文件才100多KB,界面简洁,使用起来比较简单,未注册版本程序启动时就会出现要求注册的画面,不过没有使用时间和次数等限制。 破解步骤: 1. 用softice载入windows(通过CTRL+D来检查softice是否已经准备好,按F5退出softice); 2. 运行Add/Remove,马上就会出现注册画面,也可以在程序中点击“Register”按钮进入注册框; 3. 在“User”中输入:xinheng(随意),“Regigtration”中输入:12345678(随意); 4. 用CTRL+D呼出softice,下万能断点:bpx hmemcpy,按F5返回到Add/Remove; 5. 在Add/Remove中点击“Unlock”,很快程序就被softice拦截下来; 6. 用 bd * 暂停断点 bpx hmemcpy ; 7. 按F12键20次,返回到Add/Remove的领空,程序停留在下面的地方: 。。。 0167:004018D5 PUSH 000000FF <-- 程序停在这里 0167:004018DA MOV ECX,[EBP-10] 0167:004018DD PUSH 00468460 0167:004018E2 ADD ECX,000001A0 0167:004018E8 CALL 004053B6 0167:004018ED PUSH 00408360 0167:004018F2 LEA ECX,[EBP-18] 0167:004018F5 CALL 00405386 0167:004018FA MOV DWORD PTR [EBP-04],00000000 0167:00401901 MOV EAX,[EAX] 0167:00401903 PUSH 004085A4 <-- 内存地址004085A4中是原有的用户名“UNREGISTERED USER” 0167:00401908 PUSH EAX <-- EAX指向我们输入的用户名“xinheng” 0167:00401909 CALL [0040A810] 0167:0040190F MOV DWORD PTR [EBP-04],FFFFFFFF 0167:00401916 ADD ESP,08 0167:00401919 CMP EAX,01 0167:0040191C MOV EAX,[EBP-14] 0167:0040191F SUB [EBP-14],EAX 0167:00401922 NEG DWORD PTR [EBP-14] 0167:00401925 CALL 00401A33 0167:0040192A CMP DWORD PTR [EBP-14],00 0167:0040192E JZ 00401949 。。。 8. 上面程序的最后一行有个JZ跳转指令,这是值得我们怀疑的地方(因为通常程序在子程序CALL中验证了注册码的正确性之后就会根据其结果用跳转指令来走到不同的地方:无效的注册码就弹出警告窗,正确的注册码则恭喜你已是注册用户)。让我们来看看跳转指令前的CALL其入口参数是什么?按F10键走到0167:00401908 PUSH EAX处停下,分别用 D EAX 和 D 004085A4,你会发现EAX指向的内存区域中是我们输入的用户名“xinheng”,内存004085A4中是注册画面中原本有的用户名“UNREGISTERED USER”。因为子程序CALL [0040A810]的入口参数中没有我们输入的注册码“12345678”,所以可以肯定这里不是验证注册码的地方; 9. 如果你有兴趣想知道上面程序段作用的话,按F10一直走到0167:0040192E JZ 00401949,此时程序原本将要跳到00401949去。用 RFL Z 改变零标志位的状态,让程序不跳到00401949去,继续往下走,看看会有什么结果?改变了标志位的状态后,我们按F5让程序自己往下跑,你会发现结果是程序关闭了注册框,进入主界面中,依然显示的是未注册版本的信息。选择“Register”,然后重新输入用户名“xinheng”和注册码“12345678”,点击“Unlock”,结果是出现警告窗,告诉你注册码无效(因为刚才我们用 RFL Z 改变了程序的正确走向,所以刚才直接进入了主界面);如果你在注册框中不输入用户名和注册码,而用程序原本的“UNREGISTERED USER”,再点击“Unlock”,结果是直接进入主界面。再回头想想刚才子程序的入口分别是参数是“xinheng”和“UNREGISTERED USER”,现在我们可以知道上面程序段的作用是判断用户名是否是“UNREGISTERED USER”,如果是则直接进入主界面;如果不是,哈哈。。。,肯定下面就是判断注册码是否正确喽!否则怎么会出现警告窗呢^_^! 10. 如果你按步骤9的方法做过了,就请先重复步骤2到步骤7。然后按F10多次走到00401949去,即走到上面程序段的下一句: 。。。 0167:00401949 CALL 00401620 0167:0040194E TEST EAX,EAX 0167:00401950 JZ 004019F8 。。。 11. 我们已经知道步骤7的程序段主要是判断是继续用“UNREGISTERED USER”的用户名继续试用Add/Remove还是输入注册码进行注册,下来就应该是判断注册码是否正确了,所以上面的CALL 00401620极有可能就是验证注册码的子程序,但是我们也不能就此肯定就是我们想像的那样。首先将鼠标移到0167:00401949 CALL 00401620这一行并点击一下,然后按F9在此设置断点,以便下次能立马走到这里而不用再重复前面的步骤。按F10走到0167:00401950 JZ 004019F8,你会发现此时EAX等于0,零标志位为真,所以程序将跳到004019F8去,再按一次F10走到004019F8去看看: 。。。 0167:004019F8 PUSH FF 0167:004019FA CALL [USER!MessageBeep] 0167:00401A00 CMP DWORD PTR [EBP-10],00 0167:00401A04 MOV EAX,00000000 0167:00401A09 JZ 00401A11 0167:00401A0B MOV EAX,[EBP-10] 0167:00401A0E MOV EAX,[EAX+20] 0167:00401A11 PUSH 30 0167:00401A13 PUSH 004087C8 0167:00401A18 PUSH 00408688 0167:00401A1D PUSH EAX 0167:0040191E CALL [USER!MessageBoxA] <-- 错误消息框 0167:00401924 JMP 0040193A 。。。 12. 上面的程序段一看就知道是走到了错误的地方,按F10走过0167:0040191E CALL [USER!MessageBoxA]这行,马上就能看到Add/Remove弹出消息框告诉你注册码非法,由此我们也知道了步骤10的CALL 00401620肯定就是验证注册码正确与否的子程序,其返回值EAX应该不等于0才表示注册码正确。 13. 重新在Add/Remove进行注册,按“Unlock”键后马上就被Softice拦截下来停在0167:00401949 CALL 00401620处(因为刚才我们在这里按F9设置了断点),然后按F8跟踪进入这个CALL里面去: 。。。 0167:00401620 MOV EAX,FS:[00000000] 0167:00401626 PUSH EBP 0167:00401627 MOV EBP,ESP 0167:00401629 PUSH FF 0167:0040162B PUSH 004017A5 0167:00401630 MOV ECX,FFFFFFFF 0167:00401635 PUSH EAX 0167:00401636 MOV FS:[00000000],EBP 0167:0040163D SUB ESP,0C 0167:00401640 SUB EAX,EAX 0167:00401642 PUSH EDI 0167:00401643 MOV EDI,00408360 0167:00401648 REPNZ SCASB <-- EDI指向我们输入的用户名“xinheng” 0167:0040164A NOT ECX 0167:0040164C DEC ECX 0167:0040164D CMP ECX,06 <-- 判断用户名字符数是否大于等于6 0167:00401650 JAE 00401663 0167:00401652 XOR EAX,EAX <-- 用户名字符数小于6则将EAX清零,子程序返回 0167:00401654 MOV ECX,[EBP-0C] 0167:00401657 POP EDI 0167:00401658 MOV FS:[00000000],ECX 0167:0040165F MOV ESP,EBP 0167:00401661 POP EBP 0167:00401662 RET 。。。 14. 按F10走到0167:00401648 REPNZ SCASB,用 D EDI 可以看到EDI指向的内存区域中是我们输入的用户名。这段程序的作用很明显,就是检测输入用户名的字符数, 0167:0040164D CMP ECX,06这条指令判断用户名的字符数是否大于等于6,因为我们输入的用户名是“xinheng”,所以这里ECX=7大于6,程序在0167:00401650 JAE 00401663时将跳到00401663去。假如输入用户名的字符数小于6,则0167:00401652 XOR EAX,EAX这条指令将EAX清零然后子程序返回到步骤10的0167:0040194E TEST EAX,EAX,因为EAX已经被清零了,所以其下一句0167:00401950 JZ 004019F8将会跳到错误框的地方去,这样的话就完蛋了; 15. 继续按F10直到下面的地方停下: 。。。 0167:00401741 PUSH 00408460 <-- 内存地址00408460中是输入的注册码“12345678” 0167:00401746 MOV EAX,[EBP-14] 0167:00401749 PUSH EAX <-- EAX指向字符串“6582-1RQPU” 0167:0040174A CALL [0040A810] 0167:00401750 ADD ESP,08 0167:00401753 MOV ECX,[EBP-10] 0167:00401756 MOV DWORD PTR [EBP-04],FFFFFFFF 0167:0040175D CMP EAX,01 0167:00401760 SBB [EBP-10],ECX 0167:00401763 NEG DWORD PTR [EBP-10] 0167:00401766 CALL 004017AF 0167:0040176B MOV EAX,[EBP-10] 0167:0040176E JMP 00401654 。。。 0167:00401654 MOV ECX,[EBP-0C] 0167:00401657 POP EDI 0167:00401658 MOV FS:[00000000],ECX 0167:0040165F MOV ESP,EBP 0167:00401661 POP EBP 0167:00401662 RET 。。。 16. 按F10走到0167:0040174A CALL [0040A810]停下,然后分别用 D 00408460 和 D EAX ,可以看到内存地址00408460中是输入的注册码“12345678”,而EAX指向的内存地址中则存放着字符串“6582-1RQPU”,不用说这个就是我们需要的注册码了,赶紧把它记在纸上; 17. 按F5返回Add/Remove,重新输入用户名“xinheng”和注册码“6582-1RQPU”,你将看到注册成功的画面,再次启动Add/Remove时主界面最上方的那个“UNREGISTERED VERSION”字样不见了,OK ,至此破解结束; 18. 也许有些朋友会说了:你怎么一下子就走到了这个地方,前面还有很多的程序段啊!你为什么不在前面停下来仔细看看,而是一下就跳到了这里,正好把注册码给抓了出来?首先我要说一点:通常在判断注册码的子程序中,程序开始都会根据我们输入的用户名进行一些处理,到了这个子程序的最后才会将正确的注册码和我们输入的注册码进行比较,紧接着将结果返回到寄存器中,子程序返回,然后程序再根据子程序的返回值判断注册码是否正确。所以,如果你不关心程序的算法,只想得到正确的注册码,那么子程序的中间程序大可不必仔细去跟踪,只要走到子程序快要结束的地方去抓注册码就行了^_^!那么刚才步骤15时如何知道到了子程序的最后了呢?如果你多往下面看看,到了0167:0040176E JMP 00401654时程序将跳到00401654去,而00401654程序段处已经是子程序马上返回的地方,所以在前面0167:00401741 PUSH 00408460 停了下来,因为这里有两个压栈操作PUSH 00408460和PUSH EAX,紧接着是子程序过程,比较可以嘛。实际跟踪时很可能没有那么顺利能很快找到这里,所以破解Add/Remove不是非常的容易,稍微要费点周折。 深入研究注册码算法。。。 19. 如果你还觉得不爽,想要彻底搞清楚程序的注册码算法,然后写个注册机的话,就需要具有一定的汇编基础了,因为你必须要看懂程序的几乎每一个步骤,搞明白程序的整个流程。要弄明白程序的注册码算法通常比获得程序的注册码难得多,所以必须要有耐心。从步骤13程序段最后开始到步骤15程序段以前之间的程序就是注册码的算法过程,Add/Remove的注册码算法本身并不是很复杂,但是程序中有关于注册码的操作很烦琐,所以注释中只列出要点部分,对于跟注册码算法关系不大的地方则不作解释: 。。。 0167:00401663 MOV EDI,00408360 <-- 内存地址00408360中是输入的用户名“xinheng” 0167:00401668 MOV ECX,FFFFFFFF 0167:0040166D SUB EAX,EAX 0167:0040166F REPNZ SCASB <-- 扫描用户名“xinheng”中的非0字符 0167:00401671 NOT ECX 0167:00401673 SUB EDX,EDX 0167:00401675 LEA EAX,[ECX-01] <-- 用户名“xinheng”的字符数ECX-01=07赋予EAX 0167:00401678 MOV ECX,0000000C <-- 将ECX赋值0000000C 0167:0040167D DIV ECX <-- 用户名“xinheng”的字符数07除以0C 0167:0040167F MOV EAX,[0040835C] <-- 内存地址0040835C中是码表“87ae2401my69” 0167:00401684 LEA ECX,[EBP-10] 0167:00401687 MOV AL,[EDX+EAX] <-- 将用户名“xinheng”的字符数07除以0C的余数07作为偏移地址 在码表“87ae2401my69”中提取相应的字符“1” 0167:0040168A PUSH EAX <-- 将查表结果字符“1”,即16进制值压栈 0167:0040168B PUSH 0040859C <-- 内存地址0040859C中是注册码前缀“6582-” 0167:00401690 CALL 00405386 0167:00401695 PUSH EAX 0167:00401696 LEA ECX,[EBP-14] 0167:00401699 MOV DWORD PTR [EBP-04],00000000 0167:004016A0 PUSH ECX 0167:004016A1 CALL 00405392 <-- 这个CALL将注册码前缀“6582-”和查表结果字符“1”合并成“6582-1”, 将字符串地址存放在EAX指向的内存地址的内容中。 0167:004016A6 MOV BYTE PTR [EBP-04],02 0167:004016AA CALL 0040179D 0167:004016AF MOV EDI,00408360 0167:004016B4 MOV ECX,FFFFFFFF 0167:004016B9 MOV DWORD PTR [EBP-10],00000000 <-- 将内存地址EBP-10的内容置0 0167:004016C0 SUB EAX,EAX 0167:004016C2 REPNZ SCASB <-- 扫描用户名“xinheng”中的非0字符 0167:004016C4 NOT ECX 0167:004016C6 DEC ECX <-- ECX为用户名字符数7 0167:004016C7 JZ 00401741 0167:004016C9 TEST BYTE PTR [EBP-10],01 <-- EBP-10为字符序数,这条指令测试是否是用户名“xinheng”的第偶数个字符 0167:004016CD JNZ 00401728 <-- 如果不是第偶数个字符(即不是字符“d”、“c”,“a”,“k”之一)则跳过此字符 0167:004016CF MOV EAX,[EBP-10] <-- 字符序数值赋予EAX 0167:004016D2 MOV AL,[EAX+00408360] <-- 取出第EAX个字符 0167:004016D8 CMP AL,7F <-- 判断字符ASCII码值是否大于7F 0167:004016DA JG 00401773 <-- 大于7F则显示错误框 0167:004016E0 CMP AL,20 <-- 判断字符ASCII码值是否小于20 0167:004016E2 JL 00401784 <-- 小于20则显示错误框 0167:004016E8 CBW <-- 将单字节字符值扩展成字 0167:004016EA MOV CL,02 <-- 将CL赋值02 0167:004016EC IDIV CL <-- 字符ASCII码值除以2 0167:004016EE ADD AL,20 <-- 商加上20 0167:004016F0 CMP AL,5A <-- 判断处理后的字符ASCII值是否小于等于5A,即字母“Z” 0167:004016F2 JLE 004016FA 0167:004016F4 CMP AL,61 <-- 判断处理后的字符ASCII值是否大于等于61,即字母“a” 0167:004016F6 JGE 004016FA 0167:004016F8 ADD AL,06 <-- 如果处理后的字符大于“Z”又小于“a”(即ASCII码值为5B、5C、5D、5E、5F和60的字符), 则将其值加06,作用是将其转换成相对应的字母“a”、“b”、“c”、“d”和“e” 0167:004016FA CMP AL,39 <-- 判断处理后的字符ASCII值是否小于等于39,即数字“9” 0167:004016FC JLE 00401704 0167:004016FE CMP AL,41 <-- 判断处理后的字符ASCII值是否大于等于41,即字母“A” 0167:00401700 JGE 00401704 0167:00401702 ADD AL,08 <-- 如果处理后的字符大于“9”又小于“A”(即ASCII码值为3A、3B、3C、3D、3E、3F和40的字符), 则将其值加08,作用是将其转换成相对应的字母“B”、“C”、“D”、“E”、“F”和“G” 0167:00401704 PUSH EAX <-- 将处理完的字符压栈 0167:00401705 LEA ECX,[EBP-18] 0167:00401708 LEA EAX,[EBP-14] 0167:0040170B PUSH EAX <-- EAX指向的内存地址中的内容是字符串“6582-1XXX”的地址值 0167:0040170C PUSH ECX 0167:0040170D CALL 00405392 <-- 这个CALL将注册码“6582-1”和处理完的字符“X”合并成“6582-1X”, 将字符串地址存放在EAX指向的内存地址的内容中。 0167:00401712 PUSH EAX 0167:00401713 LEA ECX,[EBP-14] 0167:00401716 MOV BYTE PTR [EBP-04],03 0167:0040171A CALL 0040538C 0167:0040171F MOV BYTE PTR [EBP-04],02 0167:00401723 CALL 00401795 0167:00401728 MOV EDI,00408360 <-- EDI指向用户名“xinheng” 0167:0040172D MOV ECX,FFFFFFFF 0167:00401732 INC DWORD PTR [EBP-10] <-- 字符序数值递增加1 0167:00401735 SUB EAX,EAX 0167:00401737 REPNZ SCASB 0167:00401739 NOT ECX 0167:0040173B DEC ECX <-- ECX为用户名“xinheng”的字符数7 0167:0040173C CMP ECX,[EBP-10] <-- 判断是否处理完最后一个用户名字符 0167:0040173F JA 004016C9 <-- 没有则继续处理其它字符 。。。 0167:00401773 MOV DWORD PTR [EBP-04],FFFFFFFF 0167:0040177A CALL 004017AF 0167:0040177F JMP 00401652 。。。 0167:00401784 MOV DWORD PTR [EBP-04],FFFFFFFF 0167:0040178B CALL 004017AF 0167:00401790 JMP 00401652 。。。 0167:00401652 XOR EAX,EAX <-- 将EAX清零,子程序返回 0167:00401654 MOV ECX,[EBP-0C] 0167:00401657 POP EDI 0167:00401658 MOV FS:[00000000],ECX 0167:0040165F MOV ESP,EBP 0167:00401661 POP EBP 0167:00401662 RET 。。。 注意:0167:00401675 LEA EAX,[ECX-01]这条指令实则是进行减法运算,不要被指令操作符LEA所蒙蔽!!! 20. 下面以我们输入的用户名“xinheng”为例具体讲解注册码的算法过程: 用户名限制:必须大于等于6个字符 用户名: d d c r a c k 16进制值: 64 64 63 72 61 63 6B 偶数位: 64 63 61 6B ⑴ 用户名字符数EAX=07,EAX= EAX mod 0C = 07,[EDX]=“87ae2401my69”,则[EDX+EAX]=“1” ⑵ 注册码前缀为“6582-”,和前一步查表结果合并得注册码前半部分“6582-1” ⑶ 字符“d”(ASCII码值为64)除以2得32,32+20=52,52<5A(不用加06)且52>41(不用加08),处理结果为52,即字母“R” ⑷ 字符“c”(ASCII码值为63)除以2得31,31+20=51,51<5A(不用加06)且51>41(不用加08),处理结果为51,即字母“Q” ⑸ 字符“a”(ASCII码值为61)除以2得30,30+20=50,50<5A(不用加06)且50>41(不用加08),处理结果为50,即字母“p” ⑹ 字符“k”(ASCII码值为6B)除以2得35,35+20=55,55<5A(不用加06)且55>41(不用加08),处理结果为55,即字母“U” ⑺ 和并字符处理结果得到注册码:6582-1RQPU 22. 补充:如果你在步骤17已经注册成功Add/Remove,然后又想要继续下面的注册码算法研究,你会发现此时Add/Remove中已经没有了注册选项。解决的办法是:不用重新安装Add/Remove,直接删除注册表子键“HKEY_LOCAL_MACHINE\Software\4Developers\AddRemove”中的键值“4D”就可以了。简单的反安装对它来说是没有用的,有关的具体介绍在“破解注意事项”中可以找到。 21. 好了,余下的事情你自己看着办吧^_^! |
地主 发表时间: 02/14 11:45 |
回复: jiantian [jiantian] 论坛用户 | 登录 |
万能断点:bpx hmemcpy 什么软件都可以用它么,如果不是,怎么区分该用什么断点? |
B1层 发表时间: 02/15 23:12 |
回复: jrg1982 [jrg1982] 论坛用户 | 登录 |
当然并不所有的东东都是用万能断点的,比如WINAMP里面就是不行的,不过在用SOFTICE时,我们一般的习惯性的先放一个万能断点,在万能断点无用时再想其它的办法,其实这不是我们学习的重点,重点是在于算法,呵呵 |
B2层 发表时间: 02/16 21:38 |
回复: rxd [rxd] 论坛用户 | 登录 |
怎么办啊 为什么我的xp里的softice driver suite 2.7里的softice不能用bmp hmemcpy这个名令阿 |
B3层 发表时间: 04/20 13:44 |
回复: bei [bei] 论坛用户 | 登录 |
我好像载那见过! |
B4层 发表时间: 04/20 14:51 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号