[原创]国庆小玩具CM20091001破解分析及注册机(含源码)
标题:国庆小玩具CM20091001破解分析及注册机(含源码)作者:AloneWolf
时间:2009-10-01
好久没有写字了,导致很多字打不出来,所以虽然字数不多的这篇文章,也用了不少时间,惭愧呀....写得不好,大家将就着看吧....在此偶也祝大家节日快乐!
对于这个CrackMe,由于作者提供了一组注册码:
用户名:52 NewChina 60
注册码:MiBOZXdDaGluYS5A2MA==
一眼看到形如这样的注册码自然就会想到Base64,找个Base64工具放上去一解,得到"2 NewChina.@乩",虽然少了第一个字母,且后面有乱码,不过基本就是Base64没错!只是作者应该还加了别的校验,明显的那个用户名前那个5有别的用途,而注册码后部份也有些变换.有这样一个结果,给后面的分析带来了方便.
好了,开OD载入,输入作者提供的用户名,注册码,下断__vbaLenBstr,点"点击注册"后在__vbaLenBstr被断下,返回到CM领空后来到0041CD91这儿,之后一步步往下跟,(由于偶的OD有些VB函数看不到名字,所以后面的代码偶是在IDA中COPY下来的,看得清楚些)
后面的分析见代码中的注释,偶用C写下验证过程(这儿全用判断,原程序中的有些验证是OnError出错跳出验证的)
void Check(const CString & sName , const CString & sNo)
{
int lenName = sName.GetLength();
int lenNo = sNo.GetLength();
int offCheck;
int c;
if(lenName < 1){ //用户名长度少于1验证失败
return ;
}
c = sName;
if(c <= ' 0' || c > '9'){ //用户名第一个不是数字验证失败
return ;
}
offCheck = (c - '0') * 3;
if(offCheck <= 0 || offCheck > lenNo){ //用户名第一个数字*3超出注册码长度验证失败
return ;
}
CString sNoBase64 = sNo.Left(offCheck - 1) + sNo.Right(lenNo - offCheck); //去掉offCheck处的那个字符
if(DecodeBase64(sNoBase64) != sName.Right(lenName - 1)){ //Base64解码同去了第一个字符的用户名明码比较,不等就失败
return ;
}
CString sFalse = "f" + "als";
sFalse += char('a' + (sNo - '0'));
if(sFalse == "false"){
//隐藏注册码输入对话框,显示后面的背景,也即验证成功
}
}
由于Base64以"="结束,所以在正确的注册码的后面加"="或在正确的注册码最后面的"="后加任意字符,都不影响注册成功
.text:0041CD89 push
.text:0041CD8C call __vbaLenBstr ; 取用户名长度
.text:0041CD91 sub eax, 1
.text:0041CD94 jo loc_41D38E
.text:0041CD9A push eax
.text:0041CD9B lea eax,
.text:0041CD9E push eax
.text:0041CD9F lea eax,
.text:0041CDA2 push eax
.text:0041CDA3 call rtcRightCharVar ; 取右边 lenName-1 个字符(也就是去掉第一个字符)
.text:0041CDA8 lea eax,
.text:0041CDAB push eax
.text:0041CDAC call __vbaStrVarMove
.text:0041CDB1 mov edx, eax
.text:0041CDB3 lea ecx,
.text:0041CDB6 call __vbaStrMove
.text:0041CDBB lea ecx,
.text:0041CDBE call __vbaFreeStr
.text:0041CDC3 lea eax,
.text:0041CDC6 push eax
.text:0041CDC7 lea eax,
.text:0041CDCA push eax
.text:0041CDCB lea eax,
.text:0041CDCE push eax
.text:0041CDCF lea eax,
.text:0041CDD2 push eax
.text:0041CDD3 push 4
.text:0041CDD5 call __vbaFreeObjList
.text:0041CDDA lea eax,
.text:0041CDDD push eax
.text:0041CDDE lea eax,
.text:0041CDE1 push eax
.text:0041CDE2 push 2
.text:0041CDE4 call __vbaFreeVarList
.text:0041CDE9 add esp, 20h
.text:0041CDEC mov , 6
.text:0041CDF3 mov eax,
.text:0041CDF5 push esi
.text:0041CDF6 call dword ptr
.text:0041CDFC push eax
.text:0041CDFD lea eax,
.text:0041CE00 push eax
.text:0041CE01 call __vbaObjSet
.text:0041CE06 mov , eax
.text:0041CE0C mov ecx,
.text:0041CE0E lea edx,
.text:0041CE11 push edx
.text:0041CE12 push ebx
.text:0041CE13 push eax
.text:0041CE14 call dword ptr
.text:0041CE17 fnclex
.text:0041CE19 cmp eax, ebx
.text:0041CE1B jge short loc_41CE30
.text:0041CE1D push 40h
.text:0041CE1F push offset dword_416B88
.text:0041CE24 push
.text:0041CE2A push eax
.text:0041CE2B call __vbaHresultCheckObj
.text:0041CE30
.text:0041CE30 loc_41CE30: ; CODE XREF: sub_41CAEE+32Dj
.text:0041CE30 mov eax,
.text:0041CE33 mov , eax
.text:0041CE39 mov ecx,
.text:0041CE3B lea edx,
.text:0041CE3E push edx
.text:0041CE3F push eax
.text:0041CE40 call dword ptr
.text:0041CE46 fnclex
.text:0041CE48 cmp eax, ebx
.text:0041CE4A jge short loc_41CE62
.text:0041CE4C push 0A0h
.text:0041CE51 push offset dword_416B98
.text:0041CE56 push
.text:0041CE5C push eax
.text:0041CE5D call __vbaHresultCheckObj
.text:0041CE62
.text:0041CE62 loc_41CE62: ; CODE XREF: sub_41CAEE+35Cj
.text:0041CE62 mov eax,
.text:0041CE65 mov , ebx
.text:0041CE68 mov , eax
.text:0041CE6B mov , edi
.text:0041CE6E push 1
.text:0041CE70 lea eax,
.text:0041CE73 push eax
.text:0041CE74 lea eax,
.text:0041CE77 push eax
.text:0041CE78 call rtcLeftCharVar; 取用户名第一个字符
.text:0041CE7D lea eax,
.text:0041CE80 push eax
.text:0041CE81 lea eax,
.text:0041CE84 push eax
.text:0041CE85 call __vbaStrVarVal
.text:0041CE8A push eax
.text:0041CE8B call rtcR8ValFromBstr ; 第一个字符转数字再*3,设为 offCheck
.text:0041CE8B ; 非数字或数字超出串范围将产生异常,验证失败
.text:0041CE90 fmul ds:dbl_4012F0
.text:0041CE96 fnstswax
.text:0041CE98 test al, 0Dh
.text:0041CE9A jnz loc_41D389
.text:0041CEA0 call __vbaFpI2
.text:0041CEA5 mov , eax
.text:0041CEA8 lea ecx,
.text:0041CEAB call __vbaFreeStr
.text:0041CEB0 lea eax,
.text:0041CEB3 push eax
.text:0041CEB4 lea eax,
.text:0041CEB7 push eax
.text:0041CEB8 push 2
.text:0041CEBA call __vbaFreeObjList
.text:0041CEBF lea eax,
.text:0041CEC2 push eax
.text:0041CEC3 lea eax,
.text:0041CEC6 push eax
.text:0041CEC7 push 2
.text:0041CEC9 call __vbaFreeVarList
.text:0041CECE add esp, 18h
.text:0041CED1 mov , 7
.text:0041CED8 mov eax,
.text:0041CEDA push esi
.text:0041CEDB call dword ptr
.text:0041CEE1 push eax
.text:0041CEE2 lea eax,
.text:0041CEE5 push eax
.text:0041CEE6 call __vbaObjSet
.text:0041CEEB mov , eax
.text:0041CEF1 mov ecx,
.text:0041CEF3 lea edx,
.text:0041CEF6 push edx
.text:0041CEF7 push 1
.text:0041CEF9 push eax
.text:0041CEFA call dword ptr
.text:0041CEFD fnclex
.text:0041CEFF cmp eax, ebx
.text:0041CF01 jge short loc_41CF16
.text:0041CF03 push 40h
.text:0041CF05 push offset dword_416B88
.text:0041CF0A push
.text:0041CF10 push eax
.text:0041CF11 call __vbaHresultCheckObj
.text:0041CF16
.text:0041CF16 loc_41CF16: ; CODE XREF: sub_41CAEE+413j
.text:0041CF16 mov eax,
.text:0041CF19 mov , eax
.text:0041CF1F mov ecx,
.text:0041CF21 lea edx,
.text:0041CF24 push edx
.text:0041CF25 push eax
.text:0041CF26 call dword ptr
.text:0041CF2C fnclex
.text:0041CF2E cmp eax, ebx
.text:0041CF30 jge short loc_41CF48
.text:0041CF32 push 0A0h
.text:0041CF37 push offset dword_416B98
.text:0041CF3C push
.text:0041CF42 push eax
.text:0041CF43 call __vbaHresultCheckObj
.text:0041CF48
.text:0041CF48 loc_41CF48: ; CODE XREF: sub_41CAEE+442j
.text:0041CF48 mov eax,
.text:0041CF4A push esi
.text:0041CF4B call dword ptr
.text:0041CF51 push eax
.text:0041CF52 lea eax,
.text:0041CF55 push eax
.text:0041CF56 call __vbaObjSet
.text:0041CF5B mov , eax
.text:0041CF61 mov ecx,
.text:0041CF63 lea edx,
.text:0041CF66 push edx
.text:0041CF67 push 1
.text:0041CF69 push eax
.text:0041CF6A call dword ptr
.text:0041CF6D fnclex
.text:0041CF6F cmp eax, ebx
.text:0041CF71 jge short loc_41CF86
.text:0041CF73 push 40h
.text:0041CF75 push offset dword_416B88
.text:0041CF7A push
.text:0041CF80 push eax
.text:0041CF81 call __vbaHresultCheckObj
.text:0041CF86
.text:0041CF86 loc_41CF86: ; CODE XREF: sub_41CAEE+483j
.text:0041CF86 mov eax,
.text:0041CF89 mov , eax
.text:0041CF8F mov ecx,
.text:0041CF91 lea edx,
.text:0041CF94 push edx
.text:0041CF95 push eax
.text:0041CF96 call dword ptr
.text:0041CF9C fnclex
.text:0041CF9E cmp eax, ebx
.text:0041CFA0 jge short loc_41CFB8
.text:0041CFA2 push 0A0h
.text:0041CFA7 push offset dword_416B98
.text:0041CFAC push
.text:0041CFB2 push eax
.text:0041CFB3 call __vbaHresultCheckObj
.text:0041CFB8
.text:0041CFB8 loc_41CFB8: ; CODE XREF: sub_41CAEE+4B2j
.text:0041CFB8 mov eax,
.text:0041CFBB mov , ebx
.text:0041CFBE mov , eax
.text:0041CFC1 mov , edi
.text:0041CFC4 mov ax, word ptr
.text:0041CFC8 sub ax, 1
.text:0041CFCC jo loc_41D38E
.text:0041CFD2 movsx eax, ax
.text:0041CFD5 push eax
.text:0041CFD6 lea eax,
.text:0041CFD9 push eax
.text:0041CFDA lea eax,
.text:0041CFDD push eax
.text:0041CFDE call rtcLeftCharVar; 取注册码 offCheck 左边的串
.text:0041CFE3 mov eax,
.text:0041CFE5 push esi
.text:0041CFE6 call dword ptr
.text:0041CFEC push eax
.text:0041CFED lea eax,
.text:0041CFF0 push eax
.text:0041CFF1 call __vbaObjSet
.text:0041CFF6 mov , eax
.text:0041CFFC mov ecx,
.text:0041CFFE lea edx,
.text:0041D001 push edx
.text:0041D002 push 1
.text:0041D004 push eax
.text:0041D005 call dword ptr
.text:0041D008 fnclex
.text:0041D00A cmp eax, ebx
.text:0041D00C jge short loc_41D021
.text:0041D00E push 40h
.text:0041D010 push offset dword_416B88
.text:0041D015 push
.text:0041D01B push eax
.text:0041D01C call __vbaHresultCheckObj
.text:0041D021
.text:0041D021 loc_41D021: ; CODE XREF: sub_41CAEE+51Ej
.text:0041D021 mov eax,
.text:0041D024 mov , eax
.text:0041D02A mov ecx,
.text:0041D02C lea edx,
.text:0041D02F push edx
.text:0041D030 push eax
.text:0041D031 call dword ptr
.text:0041D037 fnclex
.text:0041D039 cmp eax, ebx
.text:0041D03B jge short loc_41D053
.text:0041D03D push 0A0h
.text:0041D042 push offset dword_416B98
.text:0041D047 push
.text:0041D04D push eax
.text:0041D04E call __vbaHresultCheckObj
.text:0041D053
.text:0041D053 loc_41D053: ; CODE XREF: sub_41CAEE+54Dj
.text:0041D053 mov eax,
.text:0041D056 mov , ebx
.text:0041D059 mov , eax
.text:0041D05F mov , edi
.text:0041D065 movsx eax, word ptr
.text:0041D069 mov , eax
.text:0041D06F push
.text:0041D072 call __vbaLenBstr
.text:0041D077 sub eax, ; offCheck 要小于注册码长度
.text:0041D07D jo loc_41D38E
.text:0041D083 push eax
.text:0041D084 lea eax,
.text:0041D08A push eax
.text:0041D08B lea eax,
.text:0041D091 push eax
.text:0041D092 call rtcRightCharVar ; 取注册码 offCheck 右边的串
.text:0041D097 lea eax,
.text:0041D09A push eax
.text:0041D09B lea eax,
.text:0041D0A1 push eax
.text:0041D0A2 lea eax,
.text:0041D0A8 push eax
.text:0041D0A9 call __vbaVarCat ; 重新连接左右二个串
.text:0041D0A9 ; 也就是相当于去掉注册码中位置为offCheck的那个字符
.text:0041D0AE push eax
.text:0041D0AF call __vbaStrVarMove
.text:0041D0B4 mov edx, eax
.text:0041D0B6 lea ecx,
.text:0041D0B9 call __vbaStrMove
.text:0041D0BE lea ecx,
.text:0041D0C1 call __vbaFreeStr
.text:0041D0C6 lea eax,
.text:0041D0C9 push eax
.text:0041D0CA lea eax,
.text:0041D0CD push eax
.text:0041D0CE lea eax,
.text:0041D0D1 push eax
.text:0041D0D2 lea eax,
.text:0041D0D5 push eax
.text:0041D0D6 lea eax,
.text:0041D0D9 push eax
.text:0041D0DA lea eax,
.text:0041D0DD push eax
.text:0041D0DE push 6
.text:0041D0E0 call __vbaFreeObjList
.text:0041D0E5 lea eax,
.text:0041D0EB push eax
.text:0041D0EC lea eax,
.text:0041D0F2 push eax
.text:0041D0F3 lea eax,
.text:0041D0F6 push eax
.text:0041D0F7 lea eax,
.text:0041D0FD push eax
.text:0041D0FE lea eax,
.text:0041D101 push eax
.text:0041D102 push 5
.text:0041D104 call __vbaFreeVarList
.text:0041D109 add esp, 34h
.text:0041D10C mov , edi
.text:0041D10F lea eax,
.text:0041D112 push eax
.text:0041D113 call sub_432F45 ; 关键的变换函数(其实就是Base64解码)
.text:0041D113 ; 这个偶是猜的, 由作者提供的那个注册码的形式看到好象是Base64
.text:0041D113 ; 就用标准的Base64算法一算,结果一样,就没去具体分析这个函数的实现了:)
.text:0041D118 mov edx, eax
.text:0041D11A lea ecx,
.text:0041D11D call __vbaStrMove
.text:0041D122 push eax
.text:0041D123 push
.text:0041D126 call __vbaStrCmp ; 解码后的串同去掉第一个字符的用户名比较
.text:0041D12B mov edi, eax
.text:0041D12D neg edi
.text:0041D12F sbb edi, edi
.text:0041D131 inc edi
.text:0041D132 neg edi
.text:0041D134 lea ecx,
.text:0041D137 call __vbaFreeStr
.text:0041D13C cmp di, bx ; 比较不等则错误的注册码
.text:0041D13F jz loc_41D2DD
.text:0041D145 mov , 9
.text:0041D14C mov eax,
.text:0041D14E push esi
.text:0041D14F call dword ptr
.text:0041D155 push eax
.text:0041D156 lea eax,
.text:0041D159 push eax
.text:0041D15A call __vbaObjSet
.text:0041D15F mov edi, eax
.text:0041D161 mov eax,
.text:0041D163 lea ecx,
.text:0041D166 push ecx
.text:0041D167 push 1
.text:0041D169 push edi
.text:0041D16A call dword ptr
.text:0041D16D fnclex
.text:0041D16F cmp eax, ebx
.text:0041D171 jge short loc_41D181
.text:0041D173 push 40h
.text:0041D175 push offset dword_416B88
.text:0041D17A push edi
.text:0041D17B push eax
.text:0041D17C call __vbaHresultCheckObj
.text:0041D181
.text:0041D181 loc_41D181: ; CODE XREF: sub_41CAEE+683j
.text:0041D181 mov eax,
.text:0041D184 mov edi, eax
.text:0041D186 mov ecx,
.text:0041D188 lea edx,
.text:0041D18B push edx
.text:0041D18C push eax
.text:0041D18D call dword ptr
.text:0041D193 fnclex
.text:0041D195 cmp eax, ebx
.text:0041D197 jge short loc_41D1AA
.text:0041D199 push 0A0h
.text:0041D19E push offset dword_416B98
.text:0041D1A3 push edi
.text:0041D1A4 push eax
.text:0041D1A5 call __vbaHresultCheckObj
.text:0041D1AA
.text:0041D1AA loc_41D1AA: ; CODE XREF: sub_41CAEE+6A9j
.text:0041D1AA mov eax,
.text:0041D1AC push esi
.text:0041D1AD call dword ptr
.text:0041D1B3 push eax
.text:0041D1B4 lea eax,
.text:0041D1B7 push eax
.text:0041D1B8 call __vbaObjSet
.text:0041D1BD mov esi, eax
.text:0041D1BF mov , 1
.text:0041D1C6 mov , 2
.text:0041D1CD mov eax,
.text:0041D1D0 mov , ebx
.text:0041D1D3 mov , eax
.text:0041D1D6 mov , 8
.text:0041D1DD lea eax,
.text:0041D1E0 push eax
.text:0041D1E1 push
.text:0041D1E7 lea eax,
.text:0041D1EA push eax
.text:0041D1EB lea eax,
.text:0041D1F1 push eax
.text:0041D1F2 call rtcMidCharVar ; 取注册码中 offCheck 位置的字符
.text:0041D1F7 mov edi,
.text:0041D1F9 push 6
.text:0041D1FB call __vbaStrI2 ; 得到串 "6"
.text:0041D200 mov edx, eax
.text:0041D202 lea ecx,
.text:0041D205 call __vbaStrMove
.text:0041D20A push eax ; 由串"6"变换得到"f"
.text:0041D20A ; 即串中每个数字N变为字母表中第N个字母
.text:0041D20B call sub_4338B0
.text:0041D210 mov edx, eax
.text:0041D212 lea ecx,
.text:0041D215 call __vbaStrMove
.text:0041D21A push eax
.text:0041D21B push offset aAls ; "als"
.text:0041D220 call __vbaStrCat ; "f"+"als" = "fals"
.text:0041D225 mov edx, eax
.text:0041D227 lea ecx,
.text:0041D22A call __vbaStrMove
.text:0041D22F push eax
.text:0041D230 lea eax,
.text:0041D236 push eax
.text:0041D237 call __vbaStrErrVarCopy
.text:0041D23C mov edx, eax
.text:0041D23E lea ecx,
.text:0041D241 call __vbaStrMove
.text:0041D246 push eax ; 转换得到注册码中第 offCheck 位置的数字对应的字符"e"
.text:0041D247 call sub_4338B0
.text:0041D24C mov edx, eax
.text:0041D24E lea ecx,
.text:0041D251 call __vbaStrMove
.text:0041D256 push eax
.text:0041D257 call __vbaStrCat ; "fals" + "e" = "false"
.text:0041D25C mov edx, eax
.text:0041D25E lea ecx,
.text:0041D261 call __vbaStrMove
.text:0041D266 push eax
.text:0041D267 call __vbaBoolStr ; 转换串"false"为布尔值
.text:0041D267 ; 若后面一个字母不为"e,即 offCheck 位置的数字字母不为"5",
.text:0041D267 ; 则不能构成单词"false",将产生异常,验证失败
.text:0041D26C push eax ; eax 为Bool型参数,为False时隐去注册窗口,注册成功
.text:0041D26D push esi
.text:0041D26E call dword ptr
由以上分析不难写出注册机,下面是注册码计算函数:
其中 m_sName , m_sNo 均是CString类型,分别是用户名,注册码
void CKeyGen_CM20091001Dlg::KeyGen(void)
{
int lenName = m_sName.GetLength();
int c , off;
//用户名第一个要为验证数字,且其值*3要不大于注册码的长度,且其指向注册码的位置的字符要为'5'
//用户名后面的除第一个字符之外的串EncodeBase64运算后再插入那个字符'5'就是注册码了.
m_sNo.Empty();
if(lenName < 1){ //用户名不能为空
return ;
}
srand(GetTickCount()); //后面可能用到随机数,初始化之
//这儿开始的一段代码计算和验证以用户名第一个字符为验证数字的注册码
c = m_sName; //取用户名第一个字符
if(c > '0' && c <= '9'){ //第一个字符是数字的情况则计算以此数字验证数字时的注册码
off = (c - '0') * 3 - 1;
m_sNo = EncodeBase64(m_sName.GetString() + 1 , lenName - 1);
while(m_sNo.GetLength() < off)
{
m_sNo += '=';
}
}else{ //不是数字则用整个用户名作EncodeBase64,再生成并加入一个验证数字到用户名前
m_sNo = EncodeBase64(m_sName.GetString() , lenName);
off = 0 % m_sNo.GetLength() / 3 * 3 + 2; //这儿的0可以改成 rand()随机找个合适的数字加到用户名前,而不是固定的1
m_sName = char(off / 3 + '1') + m_sName;
}
m_sNo.Insert(off , '5'); //在注册码合适的位置插入一个验证字符'5'
}
对了,偶这个注册机在Debug时要是用户名输入是中文,退出时总会有个错误,那位大虾帮找找原因...
再次祝大家两节快乐!玩得开心! 牛叉。膜拜一下大牛。 太强了 膜拜 我也来膜拜楼主的强大 我也是来膜拜的~~~~ 膜拜很强大~~~
555看到那个注册码我也猜是BASE64的算法的,就是没弄懂具体流程:wwqwq 太强了 膜拜
wgz001 发表于 2009-10-1 10:36 http://www.52pojie.cn/images/common/back.gif
先膜拜大牛ING。。
但是 我记得 好像分析了或者写注册机给CB啊;www
我替楼主收债。。 先膜拜大牛,不知爆破怎么改。 分析的非常透彻、准确
楼主强大啊... 好长 看不懂啊哈和