今天在DFCG论坛上闲逛时无意中看到一个国产C/C++编译器,C-Free V3.5.2,随手下载只是想看看注 册算法,没有使用,不知道功能如何。 [Cracker] : prince [时间] : 2005.01.28 [声明] : 只做技术交流,不做商业用途,如果你手头宽裕并喜欢这个软件的话请支持正版软件。 [E-mail] : [email protected] [软件说明] : C-Free是一款基于Windows的C/C++集成化开发软件(IDE)。利用本软件,使用者可以轻松地编辑、编 译、连接、运行、调试C/C++程序。特别地,这款软件对于C/C++的学习者非常容易使用,是迅速提高C/C++水平 的好帮手。当然,C/C++高手也会在其中找到许多惊喜的功能。 C-Free包含下列主要特征: 支持MinGW编译器和C/C++解释器Ch 集成化的调试环境 工程管理 可定制的语法加亮编辑器 智能输入(大大地提高代码输入效率) 函数列表 集成化的C/C++库函数帮助 快速创建控制台、窗口和DLL应用 借助工程模板创建自己想要的工程类型 完善的查找替换功能 可定制的代码模板 支持Window图形界面应用的开发 对话框资源编辑器 [保护方式] : 机器码 + 序列号 [限制方式] : 次数限制 (10 次试用 ) [外壳保护] : ASPack 2.12 [编译器/语言]: Borland C++ 1999 / C++ [下载地址] :http://www.programarts.com/cfree_ch/index.htm ASPack 2.12 脱壳很简单,ASPackDie也可以轻松对付。无自校验,脱壳后可直接运行。注册情况:机 器码给出,输入用户名prince和序列号8764321,确定,提示重启验证。 说到重启验证,最简单最直接的就想到注册表,确认一下,打开注册表搜索用户名prince,果然找到 在\HKEY_LOCAL_MACHINE\SOFTWARE\C-Free\3.5下,同样列在其中的还有我们输入的假码87654321和我机器上的 机器码(MachineCode)2781318776。这下我们就可以确定了软件确实是通过注册表来进行重启验证的。目标如此 明确,载入脱壳后的程序,下断点RegQueryValueA,恩?没有,再下RegQueryValueExA,呵呵,可以了。F9运 行,马上被断下,看堆栈,ValueName = "layout text",不是我们想要的,继续F9,注意断点不能取消,因为 后面的对注册表的读取还是要靠这个函数,再次断下,还不是,再运行...,大约81次,堆栈中显示ValueName = "MachineCode",这就是要读取机器码了,注意。再次F9,断在读取RegistryCode也就是假码的地方,呵呵, 敏感。再接下来是读取UserName,即用户名。到这里,计算注册码的准备工作就做完了,可是在那里计算的呢 ?作者在软件启动的时候将所有的配置信息连同机器码,用户名和注册码一起读出,而且也没有读出后立即计 算注册码继续比较,这就给我们定位注册码计算造成了困难。这个时候我们该怎么办?两个办法,一个就是下 面都进行单步跟踪,直到找到关键函数为止,毕竟软件在启动前肯定会计算注册码的;另外一个办法就是在内 存中搜索假码然后下内存断点,这个方法倒是即快又方便,但是要掌握时机,具体什么时候搜内存要看代码的 动作。我通常都是先搜内存,不行的话只好一步一步的单跟了,做Cracker要有耐心。当你在堆栈中看到 ValueName = "EditorTabWidth"的时候,小心,呵呵,我们到了藏有宝藏的秘密入口了。 ---------------------------------------------------------------------------------------- 00419654 |.>MOV WORD PTR DS:[EBX+10],4B8 0041965A |.>MOV EDX,unpacked.005DAA4C ; ASCII "EditorTabWidth" 0041965F |.>LEA EAX,DWORD PTR SS:[EBP-620] 00419665 |.>CALL unpacked.0058D308 0041966A |.>INC DWORD PTR DS:[EBX+1C] 0041966D |.>MOV EDX,DWORD PTR DS:[EAX] 0041966F |.>MOV EAX,ESI 00419671 |.>CALL unpacked.004E936C 00419676 |.>MOV ECX,DWORD PTR DS:[EDI] 00419678 |.>MOV EDX,2 0041967D |.>MOV DWORD PTR DS:[ECX+A0C],EAX 00419683 |.>LEA EAX,DWORD PTR SS:[EBP-620] 00419689 |.>DEC DWORD PTR DS:[EBX+1C] 0041968C |.>CALL unpacked.0058D520 00419691 |.>MOV ECX,DWORD PTR DS:[EDI] 00419693 |.>MOV BYTE PTR DS:[ECX+8F4],0 0041969A |.>MOV EAX,DWORD PTR DS:[EDI] 0041969C |.>INC DWORD PTR DS:[EAX+8F0] 004196A2 |.>CALL unpacked.00462F18 ; 取机器码送EAX 004196A7 |.>MOV DWORD PTR SS:[EBP-764],EAX 004196AD |.>MOV WORD PTR DS:[EBX+10],98 004196B3 |.>MOV EDX,DWORD PTR DS:[EDI] 004196B5 |.>MOV ECX,DWORD PTR DS:[EDX+8E4] 004196BB |.>CMP ECX,DWORD PTR SS:[EBP-764] 004196C1 |.>JNZ unpacked.00419772 004196C7 |.>LEA EAX,DWORD PTR SS:[EBP-884] 004196CD |.>PUSH EAX ; /Arg2 004196CE |.>MOV EDX,DWORD PTR SS:[EBP-764] ; |[EBP-764]==机器码 004196D4 |.>PUSH EDX ; |Arg1 004196D5 |.>CALL unpacked.00462F70 ; \关键CALL,跟进 004196DA |.>MOV WORD PTR DS:[EBX+10],4C4 ; 上面的这个CALL计算真码,存放在EAX中(哎!又是明文 ) 004196E0 |.>ADD ESP,8 004196E3 |.>LEA EDX,DWORD PTR SS:[EBP-884] ; 真码地址送入EDX 004196E9 |.>LEA EAX,DWORD PTR SS:[EBP-624] 004196EF |.>CALL unpacked.0058D308 004196F4 |.>INC DWORD PTR DS:[EBX+1C] 004196F7 |.>MOV EDX,DWORD PTR DS:[EAX] 004196F9 |.>MOV EAX,DWORD PTR DS:[EDI] 004196FB |.>MOV EAX,DWORD PTR DS:[EAX+8E8] ; 假码送入EAX,呵呵,准备比较了哦 00419701 |.>CALL unpacked.004ECED4 ; 比较函数,嘿嘿。 00419706 |.>TEST EAX,EAX 00419708 |.>LEA EAX,DWORD PTR SS:[EBP-624] ---------------------------------------------------------------------------------------- 我们要找算法的计算过程,所以004196D5 处跟进: ---------------------------------------------------------------------------------------- 00462F70 /$>PUSH EBP 00462F71 |.>MOV EBP,ESP 00462F73 |.>ADD ESP,-0C 00462F76 |.>XOR EDX,EDX 00462F78 |.>PUSH EBX 00462F79 |.>PUSH ESI 00462F7A |.>PUSH EDI 00462F7B |.>MOV EBX,25 ; EBX=0x25 00462F80 |.>MOV ECX,DWORD PTR SS:[EBP+8] ; [EBP+8]==机器码 00462F83 |.>XOR ECX,90909090 ; 机器码异或90909090,ECX==35571EE8 00462F89 |.>MOV EAX,ECX 00462F8B |.>DIV EBX ; 上面异或的结果除以25 00462F8D |.>MOV EAX,EDX ; 余数送EAX 00462F8F |.>CMP EAX,11 ; 余数同0x11比较 00462F92 |.>JGE SHORT unpacked.00462F97 ; 大于等于就直接压栈准备函数调用 00462F94 |.>ADD EAX,11 ; 否则余数+0x11,然后再入栈 00462F97 |>>PUSH EAX ; /Arg3 余数入栈 00462F98 |.>LEA EDX,DWORD PTR SS:[EBP-C] ; | 00462F9B |.>PUSH EDX ; |Arg2 00462F9C |.>PUSH ECX ; |Arg1 上面异或结果入栈 00462F9D |.>CALL unpacked.005861F0 ; \跟进 00462FA2 |.>MOV ECX,DWORD PTR SS:[EBP+C] 00462FA5 |.>ADD ESP,0C 00462FA8 |.>MOV ESI,ECX 00462FAA |.>XOR EAX,EAX 00462FAC |.>LEA EDI,DWORD PTR SS:[EBP-C] ---------------------------------------------------------------------------------------- 00462F9D 处继续跟进: ---------------------------------------------------------------------------------------- 005861F0 /$>PUSH EBP 005861F1 |.>MOV EBP,ESP 005861F3 |.>MOV EAX,DWORD PTR SS:[EBP+10] 005861F6 |.>MOV EDX,DWORD PTR SS:[EBP+8] 005861F9 |.>CMP EAX,0A ; 余数同0A比较 005861FC |.>PUSH 61 005861FE |.>SETE CL ; 条件为假,所以CL清零 00586201 |.>AND ECX,1 00586204 |.>CMP EAX,0A ; 仍然同0A比较 00586207 |.>PUSH ECX 00586208 |.>PUSH EAX 00586209 |.>MOV ECX,DWORD PTR SS:[EBP+C] 0058620C |.>PUSH ECX 0058620D |.>JNZ SHORT unpacked.00586213 0058620F |.>MOV EAX,EDX 00586211 |.>JMP SHORT unpacked.00586215 00586213 |>>MOV EAX,EDX 00586215 |>>PUSH EAX ; |Arg1 00586216 |.>CALL unpacked.00586160 ; \跟进 0058621B |.>ADD ESP,14 0058621E |.>POP EBP 0058621F \.>RETN ---------------------------------------------------------------------------------------- 没有结果,00586216处继续跟进: ---------------------------------------------------------------------------------------- 00586160 /$>PUSH EBP 00586161 |.>MOV EBP,ESP 00586163 |.>ADD ESP,-24 00586166 |.>PUSH EBX 00586167 |.>PUSH ESI 00586168 |.>PUSH EDI 00586169 |.>MOV EDI,DWORD PTR SS:[EBP+10] ; [EBP+10]为前面压栈的余数 0058616C |.>MOV ESI,DWORD PTR SS:[EBP+8] ; [EBP+8]为机器码异或90909090的结果 0058616F |.>MOV EBX,DWORD PTR SS:[EBP+C] 00586172 |.>CMP EDI,2 ; 余数同0x2比较 00586175 |.>JL SHORT unpacked.005861C4 ; 小于跳 00586177 |.>CMP EDI,24 ; 同0x24比较 0058617A |.>JG SHORT unpacked.005861C4 ; 大于则跳 0058617C |.>TEST ESI,ESI 0058617E |.>JGE SHORT unpacked.0058618C 00586180 |.>CMP BYTE PTR SS:[EBP+14],0 00586184 |.>JE SHORT unpacked.0058618C 00586186 |.>MOV BYTE PTR DS:[EBX],2D 00586189 |.>INC EBX 0058618A |.>NEG ESI 0058618C |>>LEA ECX,DWORD PTR SS:[EBP-24] ; 下面为关键循环 0058618F |>>/MOV EAX,ESI ; ESI==机器码异或结果 00586191 |.>|XOR EDX,EDX ; EDX清零 00586193 |.>|DIV EDI ; 将上面异或结果除以压栈的余数 00586195 |.>|MOV BYTE PTR DS:[ECX],DL ; 上面计算的余数的一个字节写入内存 00586197 |.>|INC ECX 00586198 |.>|MOV EAX,ESI 0058619A |.>|XOR EDX,EDX 0058619C |.>|DIV EDI 0058619E |.>|MOV ESI,EAX ; 又做了一次相同的计算,商送入ESI 005861A0 |.>|TEST EAX,EAX ; 直到EAX==0为止 005861A2 |.>\JNZ SHORT unpacked.0058618F ; 不为0则继续循环 005861A4 |.>JMP SHORT unpacked.005861BD 005861A6 |>>/DEC ECX 005861A7 |.>|MOV AL,BYTE PTR DS:[ECX] ; 内存[ECX]的值送入AL 005861A9 |.>|CMP AL,0A ; 同0A比较 005861AB |.>|JGE SHORT unpacked.005861B5 ; 大于等于跳到下面进行另外的计算 005861AD |.>|ADD EAX,30 ; 该值加上0x30 005861B0 |.>|MOV BYTE PTR DS:[EBX],AL ; 这个值就是注册码的第i个值,写入内存保存起来 005861B2 |.>|INC EBX ; 继续下一步 005861B3 |.>|JMP SHORT unpacked.005861BD 005861B5 |>>|ADD AL,BYTE PTR SS:[EBP+18] ; 上面如果大于0A,则加上[EBP+18]==61, 005861B8 |.>|ADD AL,0F6 ; 再加上0F6 005861BA |.>|MOV BYTE PTR DS:[EBX],AL ; 作为注册码的第i个值写入内存 005861BC |.>|INC EBX 005861BD |>> LEA EDX,DWORD PTR SS:[EBP-24] ; 取地址[EBP-24] 005861C0 |.>|CMP ECX,EDX ; 比较是否结束循环 005861C2 |.>\JNZ SHORT unpacked.005861A6 ; 没有结束则继续 005861C4 |>>MOV BYTE PTR DS:[EBX],0 005861C7 |.>MOV EAX,DWORD PTR SS:[EBP+C] 005861CA |.>POP EDI 005861CB |.>POP ESI 005861CC |.>POP EBX 005861CD |.>MOV ESP,EBP 005861CF |.>POP EBP 005861D0 \.>RETN ---------------------------------------------------------------------------------------- 呵呵,过程清晰明了吧?第一次循环:机器码异或0x90909090的结果除以前面求得的压栈的余数,然后这个过 程的余数写入内存保留,商作为下一次循环的变量继续循环。第二次循环:将第一次循环中写入内存的值逆序 读取出来,同0xA比较,小于就直接加上0x30,作为注册码的第i个字符写入内存;大于等于则加上61,再加 0xF6,取低字节作为注册码的第i个字符写入内存。用户名没有参与计算。也不知道我说明白了没有,还是看程 序来得直接,C源码的注册机: ----------------------------------------------------------------------------------------- //#include "stdafx.h" #include "stdlib.h" #include "stdio.h" int main(int argc, char* argv[]) { char chKey[128] = {0}; unsigned int unXORCode, unRemainder, unQuotient, unTmp, unMachineCode; printf("Please Key in the Machine Code:\n"); scanf("%d", &unMachineCode); unXORCode = unMachineCode ^ 0x90909090; unRemainder = unXORCode % 0x25; unQuotient = unXORCode; if (unRemainder < 0x11) { unRemainder += 0x11; } int i; i = 0; while (unQuotient != 0) { unTmp = unQuotient % unRemainder; unQuotient /= unRemainder; if (unTmp >= 0xa) { unTmp = unTmp + 0x61 + 0xf6; unTmp &= 0x0ff; chKey[i] = unTmp; } else { chKey[i] = unTmp + 0x30; } i++; } printf("Key is: \n"); while (i >= 0) { printf("%c", chKey[i]); i--; } printf("\n"); return 0; } |