【使用工具】: OD、Atlantis Word Processor --------------------------------------------------------- Atlantis Word Processor 一个独立的word处理器,支持读取并编辑*.rtf *.doc *.docx *.cod *.txt等数种格式的文档,支持命令行,软件小巧界面清爽,占用内存小,处理速度快,打开保存文件时支持全word格式的预览。(两个小小遗憾--word文档中的中文引号会显示乱码;竟不支持中文txt文件,汗。期待更新) 试运行得知软件以用户名、注册码方式保护(其实用户名不参与运算),有出错提示,UPX弱壳。 脱壳OD加载后,查找字符串“invalid”找到以下关键部位,以用户名playboysen,注册码HelloHenu试炼(算法分析一般都是随便输入假码后粗略跟踪几次注册码的验证流程,根据验证流程一次次地修正假码,最终得出一组可用注册码,弄清算法) 00495FC7 E8 9CC31600 call Atlantis.00602368 00495FCC 837D FC 00 cmp dword ptr ss:[ebp-4],0 00495FD0 75 17 jnz short Atlantis.00495FE9 00495FD2 B8 88604900 mov eax,Atlantis.00496088 ; ASCII "Please enter your registration code." 00495FD7 E8 28E31400 call Atlantis.005E4304 00495FDC 8B86 9C010000 mov eax,dword ptr ds:[esi+19C] 00495FE2 8B10 mov edx,dword ptr ds:[eax] 00495FE4 FF52 68 call dword ptr ds:[edx+68] 00495FE7 EB 45 jmp short Atlantis.0049602E 00495FE9 8D55 FC lea edx,dword ptr ss:[ebp-4] 00495FEC 8B86 9C010000 mov eax,dword ptr ds:[esi+19C] 00495FF2 E8 71C31600 call Atlantis.00602368 ; 算出注册码长度 00495FF7 8B45 FC mov eax,dword ptr ss:[ebp-4] ; 假码放入eax 00495FFA E8 0D060000 call Atlantis.0049660C ; 关键处,跟进 00495FFF 84C0 test al,al 00496001 75 17 jnz short Atlantis.0049601A 00496003 B8 B8604900 mov eax,Atlantis.004960B8 ; ASCII "The registration code you have specified is invalid..." 跟进00495FFA关键call ...... 00496640 8B45 FC mov eax,dword ptr ss:[ebp-4] 00496643 E8 4C5AF7FF call Atlantis.0040C094 ; 注册码第一次变形,把假码中的小写字母转换成大写 00496648 8B45 F0 mov eax,dword ptr ss:[ebp-10] ; 转换后的假码放入 0049664B B1 30 mov cl,30 ; 数字“0” 0049664D B2 4F mov dl,4F ; 大写字母“O” 0049664F E8 CC711400 call Atlantis.005DD820 ; 看上面入栈的参数,猜测这个函数里面有猫腻儿 00496654 8B55 F4 mov edx,dword ptr ss:[ebp-C] ; 上面的函数就是注册码的第二次变形了 数字“0”、大写字符“O”和假码同时入栈,葫芦里卖的什么药?进去遛遛 ...... 005DD83C E8 4B5DE2FF call Atlantis.0040358C ; 算出注册码长度 005DD841 83F8 01 cmp eax,1 005DD844 7C 1D jl short Atlantis.005DD863 005DD846 89C7 mov edi,eax ; 注册码长度放入 005DD848 8B06 mov eax,dword ptr ds:[esi] ; 注册码放入 005DD84A 3A5C38 FF cmp bl,byte ptr ds:[eax+edi-1] ; 假码从后往前逐位与大写字母“O”比较 005DD84E 75 0E jnz short Atlantis.005DD85E ; 一路小跑过去,发现这里是把注册码中出现的字母“O”替换成数字“0” 005DD850 8BC6 mov eax,esi ; 企图混淆视听啊 005DD852 E8 055FE2FF call Atlantis.0040375C 汗,这都行?至此,我们的假码经过了两次加工,记住 ...... 0049665F 8B45 FC mov eax,dword ptr ss:[ebp-4] 00496662 E8 25CFF6FF call Atlantis.0040358C ; 算出注册码长度 00496667 8BF0 mov esi,eax 00496669 83FE 01 cmp esi,1 0049666C 7C 3D jl short Atlantis.004966AB 0049666E 8B45 FC mov eax,dword ptr ss:[ebp-4] ; 经过以上两次加工后的变形假码放入EAX 00496671 8A4430 FF mov al,byte ptr ds:[eax+esi-1] ; 注册码从后往前依次参与运算 00496675 8BD0 mov edx,eax 00496677 80EA 20 sub dl,20 ; 逐位比较是不是空格 0049667A 74 05 je short Atlantis.00496681 0049667C 80EA 0D sub dl,0D ; 逐位比较是不是连字符“-” 0049667F 75 11 jnz short Atlantis.00496692 00496681 8D45 FC lea eax,dword ptr ss:[ebp-4] 00496684 B9 01000000 mov ecx,1 00496689 8BD6 mov edx,esi 0049668B E8 40D1F6FF call Atlantis.004037D0 ; 去除注册码中的“-” 00496690 EB 14 jmp short Atlantis.004966A6 00496692 8B55 FC mov edx,dword ptr ss:[ebp-4] ; 假码放入edx 00496695 25 FF000000 and eax,0FF 0049669A 8B15 4C7E6100 mov edx,dword ptr ds:[617E4C] ; Atlantis.0061CC30 004966A0 803C02 FF cmp byte ptr ds:[edx+eax],0FF ; 此时eax的值就是假码中每一位字母或数字的十六进制值 004966A4 74 77 je short Atlantis.0049671D ; 注册码中的每一位只能是 数字或a~f或A~F或字母o/O(不明白看下面的分析) 004966A6 4E dec esi 004966A7 85F6 test esi,esi 004966A9 ^ 75 C3 jnz short Atlantis.0049666E 004966AB 8B45 FC mov eax,dword ptr ss:[ebp-4] ; 无连字符“-”的大写注册码放入 004966AE E8 D9CEF6FF call Atlantis.0040358C ; 求经过三次变形后的假码长度 004966B3 83F8 10 cmp eax,10 ; 注册码16位啊 004966B6 75 65 jnz short Atlantis.0049671D 上述代码的后半部分理解有些费力 0049669A 8B15 4C7E6100 mov edx,dword ptr ds:[617E4C] ; Atlantis.0061CC30 [617E4C]指向的地址是0061CC30,我们看一下数据窗口对应位置的数值 醒目一点的估计看出来了,截图上的地址是从0061CC60开始的,为什么呢? 0049669A 8B15 4C7E6100 mov edx,dword ptr ds:[617E4C] ; Atlantis.0061CC30 004966A0 803C02 FF cmp byte ptr ds:[edx+eax],0FF ; 此时eax的值就是假码中每一位字母或数字对应的十六进制值 问题就出在第二句的[edx+eax]处,静态解释不易理解,动态跟踪一下就明白了 假设假码中其中一位字符是“8” 对应十六进制是 0x38 而edx == 0061CC30,所以[edx+eax] == 0061CC68 这个地址对应上图可知值为0x08,并不等于0xFF,证明假码中的这一位合格 好,跟踪至此,我们的假码可以进行修正了 HelloHenu --> Babe-5214-0987-acef 以上都是一些准备工作,下面就真的进入了算法核心了 …… 004966B8 BE 01000000 mov esi,1 ; 核心算法开始了 esi初始值为1 004966BD 8BC6 mov eax,esi ; 一个大的算法循环 开始处 004966BF 48 dec eax 004966C0 03C0 add eax,eax 004966C2 40 inc eax 004966C3 8B55 FC mov edx,dword ptr ss:[ebp-4] ; 几次变形后的注册码放入 004966C6 0FB65402 FF movzx edx,byte ptr ds:[edx+eax-1] ; 注册码从前往后依次运算 004966CB 8B0D 4C7E6100 mov ecx,dword ptr ds:[617E4C] ; Atlantis.0061CC30 004966D1 8A1411 mov dl,byte ptr ds:[ecx+edx] 004966D4 C1E2 04 shl edx,4 004966D7 8B4D FC mov ecx,dword ptr ss:[ebp-4] 004966DA 0FB60401 movzx eax,byte ptr ds:[ecx+eax] 004966DE 8B0D 4C7E6100 mov ecx,dword ptr ds:[617E4C] ; Atlantis.0061CC30 004966E4 021401 add dl,byte ptr ds:[ecx+eax] 004966E7 8855 FA mov byte ptr ss:[ebp-6],dl ; 可以看出,从前往后依次取出注册码的每两位进行下面的运算 004966EA B8 58674900 mov eax,Atlantis.00496758 ; 00496758地址处有段8位的密钥参与运算 004966EF 8A4430 FF mov al,byte ptr ds:[eax+esi-1] 004966F3 C645 F9 01 mov byte ptr ss:[ebp-7],1 ; (这里 标志位哦) 004966F7 B3 08 mov bl,8 004966F9 E8 0AFFFFFF call Atlantis.00496608 ; 把密钥的对应位数值做一次ror(循环右移)运算 004966FE 3A45 FA cmp al,byte ptr ss:[ebp-6] ; 比较置换后的对应位注册码值是否等于ror后的密钥值 00496701 75 06 jnz short Atlantis.00496709 ; 事实证明,如果不等于,最终就提示注册码错误 00496703 C645 F9 00 mov byte ptr ss:[ebp-7],0 00496707 EB 04 jmp short Atlantis.0049670D 00496709 FECB dec bl ; 对应密钥可以循环8次ror运算,共产生8个值 0049670B ^ 75 EC jnz short Atlantis.004966F9 ; 每次对应取出的两位注册码值必须等于上述8个结果的其中一个 其实,软件核心的算法就是: 1、 注册码应为16位,有没有“-”连接都可以 2、 注册码中的每一位只能是 0~9或a~f或A~F或字母o/O 3、 从前往后每次取出2位注册码值,转换成一个字节的十六进制值 如字符“a5c3” 就转换成 0xA5 0xC3 这样十六位的注册码,就变成了占8字节的一串十六进制值 4、 程序预置8位密钥,如下 5、 每位密钥都进行 至少一次的ror运算,把第一次ror后得出的值与相应位置的注册码值比较,如果不相等则在第一次算出值的基础上进行第二次的ror运算,依次循环,共可循环8次--如果还不相等,就说明是错误注册码了,不再继续运算 可以004966FE处为切入点,其中al的值就是密钥ror运算出的值--可用的注册码的其中两个值。在004966FE处下断,修改00496701处的jnz跳转为NOP,F9继续运算可以很轻易地跟出可用注册码 004966FE 3A45 FA cmp al,byte ptr ss:[ebp-6] ; 比较置换后的对应位注册码值是否等于ror后的密钥值 随便F9了几下 总结了几个类似A7B1-A6B4-2732-E1F2 7A1B6A4B72231E2F这样的值,不知道有什么用o_0 软件注册后,相关信息保存于注册表以下位置(软件没有对关键信息加密,也是一个败笔): Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\Rising Sun Solutions, Inc.\Atlantis.1_x\RegInfo] "RegCode"="XXXXXXXXXXXXXXXX" "RegTo"="playboysen" 总结: 看算法的分析有很多,但最主要的验证算法只有以下几行 Mov esi,1 mov eax,00496758 //这里是预置的一个8位数组,具体参考上图 mov al,byte ptr ds:[eax+esi-1] mov bl,8 004966F9: Ror al,1 cmp al,byte ptr ss:[ebp-6] //在这里添加相应代码取出al中的值转换后连接成16位的字符串即为一组注册码 dec bl jnz 004966F9 inc esi cmp esi,9 jz XXXXX 其实,上周就开始分析这个程序了,一路小跑几次觉得算法不难,但很繁琐,没有耐心看,搁置一周。偶尔看到kanxue十天前留下的信息,感动老大还记得我辈之余顿悟自己今年确实堕落了,莫说文章之类,就连论坛都鲜有登陆,惭愧!新入职,一切都不稳定,有些心浮气躁。 离开几个月回来发现大家都跑去搞VM、驱动之类去了,牛X新壳如雨后春笋层出不穷,自己竟成了古董。唏嘘不已,感慨IT业发展之迅猛。也鲜有大虾写些算法分析之流的东西,自己也惧怕贻笑大方。快过年了,写篇文章凑个数,算是给自己一个交代。 补充注册机代码: 代码: .data szFormat db '%s-%s-%s-%s',0 error db 'Please input your name!',0 Temp db 4Fh,63h,4Dh,69h,4Eh,64h,0C3h,0E5h .data? hInstance dd ? S1 db 8 dup(?) S2 db 8 dup(?) S3 db 8 dup(?) S4 db 8 dup(?) S5 db 8 dup(?) S6 db 8 dup(?) S7 db 8 dup(?) S8 db 8 dup(?) Serial db 8 dup(?) ;注意此处不能定义成4字节,必须留5字节以上的空间,因为字符最后有一个'\0' FinalSn1 db 5 dup(?) FinalSn2 db 5 dup(?) FinalSn3 db 5 dup(?) FinalSn4 db 5 dup(?) Sn db 20 dup(?) FinalSn db 20 dup(?) 算了,代码太长 直接发Radasm工程文件吧 |
|