=============================
SWIFTDOG RamSmash 1.7.4.2005 KeyGen tutorial
=============================
破解说明
保护方式:
时间限制+注册码+网络认证
破解目的:
本软件是一款内存工具,界面友好。
该软件注册算法中使用了浮点数比较方式对注册码进行认证。
破解结果:
name = MrBeer[CCG]
sn = 196325168549558472+1+ 1个Licence
1.1857727812463e18+100+ 100个Licence
=============================
=============================
破解分析
1. 第一步是对程序进行查壳/脱壳,这个对于任何破解应该都是不可免除的一步。
昨天刚刚下载了一个FLY大侠整理的PEiD signatures,识别能力大增。^_^
检测主程序RamSmash.exe,发现PECompact 2.x -> Jeremy Collake。
直接使用PEiD Generic Unpacker插件进行脱壳失败,也没搜索到合适的自动脱壳机。
不得不用OD手动脱壳,这可是本人的弱项。*_&
参考peaceclub一年前写的《PeCompact 2.x 通用简单脱壳法》,轻松找到OPE,DUMP。
不用进行引入表修复,DUMP文件可以直接运行,BINGO,幸运啊,呵呵。
2. 第二步是侦测软件的加密算法,这一步大大减小了手动跟踪算法的强度。
这一步使用PEiD的插件Kanal,顺便还可以检测脱壳后的文件是用什么语言写的。^_^
没有发现已知的强力加密算法,看来作者自己写了注册算法。
嘿嘿,游戏,估计可以写出注册机。
本文件使用DELPHI编写,我没有用DEDE的习惯,所以DELPHI程序对我来说比较不爽。
因为不能用MessageBoxA/GetWindowsTestA等API设断,严重不爽!
3. 第三步是运行程序,进行注册,观察程序反应。
在Enter Serial Number界面输入FADE用户名和注册码,分别为:
Name = MrBeer[CCG]
Serial = 12345678900987654321。
提示"Invalid! ReEnter Name and Serial"。
直接验证型!还不能用MessageBoxA/GetWindowsTestA设断,怎么办!?
别急,看后面,嘿嘿。
4. 第四步是静态分析,这是我个人的习惯,有助于把握软件的整体构架,理清思路。
使用PLL621大侠的C32ASM,仔细查看字符串,寻找了"Invalid! ReEnter Name and Serial"。
没找到!更加郁闷。
不过郁闷中发现了几个令人注目的字符串:"SOFTWARE\SWIFTDOG\RamSmash"、"Name"、"Serial"。
很明显,这是注册表的键名。
抱着侥幸心里,打开注册表,
竟然在HKEY_LOCAL_MACHINE\SOFTWARE\SWIFTDOG\RamSmash底下赫然发现了俺输入的FADE Name,Serial。
OK,看来软件在启动时也可能会验证Name和Serial的。
那就让我们从RegQueryValueA/RegQueryValueExA函数入手吧!^_^
5. 第五步就是动态分析了。XP系统下,当然是OllyDbg了。
我使用的是DYK汉化、修改过的ODbyDYK,功能更强大。
通过对RegQueryValueA/RegQueryValueExA的API设断,很容易就跟踪到注册码验证部分。
验证过程是(逻辑过程,有程序实际的实现过程有出入):
a. name全部转换为小写字母;
b. 验证sn结构,必须为:"xxxxxxxxxxxxxxxxxx+xxxxxx+",xxx长度不定。
c. 以"+"为分隔的第2段Serial表示Licence的个数,即同一个Serial可以同时使用在几台机器上。
d. 根据第2段Serial,计算Name,结果与以"+"为分隔第1段Serial进行浮点数比较;
e. 两个值之差小于0x4000,即算验证成功。
Serial第1段的计算方法见KeyGen部分
另外,作者声明该程序会访问网络,发送Serial,OrderID信息。
如果该Serial不合法,此Serial会失效。
这一部分的就没有办法通过KeyGen破解了,只能修改文件做Crack了。
相关破解会另外发表。
=============================
=============================
源码分析
通过对RegQueryValueA/RegQueryValueExA设断,很容易跟踪到这里。
程序共有四处会调用这段验证函数:
一处是启动时;
一处是输入Serial Number时;
一处是打开About对话框时;
另外还有一处在何时会发生还不清楚,估计于前面提到的网络认证有关。
0048A5A8 $ 55 push ebp ; --->函数开始
0048A5A9 . 8BEC mov ebp,esp
0048A5AB . B9 0C000>mov ecx,0C
..............................................................
省略若干行
..............................................................
0048A612 . E8 9175F>call RamSmash.00421BA8 ; --->启动和About时得到Name/Serial
0048A617 . 8BD8 mov ebx,eax
0048A619 . BA 02000>mov edx,80000002
0048A61E . 8BC3 mov eax,ebx
0048A620 . E8 2376F>call RamSmash.00421C48
0048A625 . B1 01 mov cl,1
0048A627 . BA 78AA4>mov edx,RamSmash.0048AA78 ; ASCII "\Software\SWIFTDOG\RamSmash"
0048A62C . 8BC3 mov eax,ebx
0048A62E . E8 7976F>call RamSmash.00421CAC
0048A633 . 8B4D FC mov ecx,dword ptr ss:[ebp-4]
0048A636 . BA 9CAA4>mov edx,RamSmash.0048AA9C ; ASCII "Name"
0048A63B . 8BC3 mov eax,ebx
0048A63D . E8 2678F>call RamSmash.00421E68 ; --->从注册表得到Name
0048A642 . 8B4D F8 mov ecx,dword ptr ss:[ebp-8]
0048A645 . BA ACAA4>mov edx,RamSmash.0048AAAC ; ASCII "Serial"
0048A64A . 8BC3 mov eax,ebx
0048A64C . E8 1778F>call RamSmash.00421E68 ; --->从注册表得到Serial
0048A651 . 8BC3 mov eax,ebx
0048A653 . E8 C075F>call RamSmash.00421C18
0048A658 . 8BC3 mov eax,ebx
0048A65A . E8 2D8CF>call RamSmash.0040328C
0048A65F . 33C0 xor eax,eax
0048A661 . 5A pop edx
0048A662 . 59 pop ecx
0048A663 . 59 pop ecx
0048A664 . 64:8910 mov dword ptr fs:[eax],edx
0048A667 . E9 89000>jmp RamSmash.0048A6F5
0048A66C .^ E9 BB90F>jmp RamSmash.0040372C
0048A671 . E8 1E94F>call RamSmash.00403A94
0048A676 . EB 7D jmp short RamSmash.0048A6F5
0048A678 > 33C0 xor eax,eax
0048A67A . 55 push ebp
0048A67B . 68 EBA64>push RamSmash.0048A6EB
0048A680 . 64:FF30 push dword ptr fs:[eax]
0048A683 . 64:8920 mov dword ptr fs:[eax],esp
0048A686 . B2 01 mov dl,1
0048A688 . A1 A81A4>mov eax,dword ptr ds:[421AA8]
0048A68D . E8 1675F>call RamSmash.00421BA8
0048A692 . 8BD8 mov ebx,eax
0048A694 . BA 02000>mov edx,80000002
0048A699 . 8BC3 mov eax,ebx
0048A69B . E8 A875F>call RamSmash.00421C48
0048A6A0 . C743 18 >mov dword ptr ds:[ebx+18],20019 ; --->输入Serial Number时得到Name/Serial
0048A6A7 . 33C9 xor ecx,ecx
0048A6A9 . BA 78AA4>mov edx,RamSmash.0048AA78 ; ASCII "\Software\SWIFTDOG\RamSmash"
0048A6AE . 8BC3 mov eax,ebx
0048A6B0 . E8 F775F>call RamSmash.00421CAC
0048A6B5 . 8D4D FC lea ecx,dword ptr ss:[ebp-4]
0048A6B8 . BA 9CAA4>mov edx,RamSmash.0048AA9C ; ASCII "Name"
0048A6BD . 8BC3 mov eax,ebx
0048A6BF . E8 D077F>call RamSmash.00421E94 ; --->从注册表得到Name
0048A6C4 . 8D4D F8 lea ecx,dword ptr ss:[ebp-8]
0048A6C7 . BA ACAA4>mov edx,RamSmash.0048AAAC ; ASCII "Serial"
0048A6CC . 8BC3 mov eax,ebx
0048A6CE . E8 C177F>call RamSmash.00421E94 ; --->从注册表得到Serial
.............................................................
省略若干行
.............................................................
0048A724 . 68 841F4>push RamSmash.00491F84
0048A729 . 8D45 E4 lea eax,dword ptr ss:[ebp-1C]
0048A72C . 50 push eax
0048A72D . 8B55 F8 mov edx,dword ptr ss:[ebp-8]
0048A730 . B8 BCAA4>mov eax,RamSmash.0048AABC
0048A735 . E8 2A9FF>call RamSmash.00404664 ; --->以"+"为分格,取Serial第1段
0048A73A . 40 inc eax ; --->没"+"可啥也取不到啊,也别想注册成功
0048A73B . 50 push eax
0048A73C . 8B45 F8 mov eax,dword ptr ss:[ebp-8]
0048A73F . E8 DC9BF>call RamSmash.00404320
0048A744 . 8BC8 mov ecx,eax
0048A746 . 8B45 F8 mov eax,dword ptr ss:[ebp-8]
0048A749 . 5A pop edx
0048A74A . E8 319EF>call RamSmash.00404580
0048A74F . 8B55 E4 mov edx,dword ptr ss:[ebp-1C]
0048A752 . B8 BCAA4>mov eax,RamSmash.0048AABC
0048A757 . E8 089FF>call RamSmash.00404664 ; --->以"+"为分格,取Serial第2段
0048A75C . 48 dec eax
0048A75D . 50 push eax
0048A75E . 8D45 E0 lea eax,dword ptr ss:[ebp-20]
0048A761 . 50 push eax
0048A762 . 8B55 F8 mov edx,dword ptr ss:[ebp-8]
0048A765 . B8 BCAA4>mov eax,RamSmash.0048AABC
0048A76A . E8 F59EF>call RamSmash.00404664 ; --->以"+"为分格,取Serial第1段
0048A76F . 40 inc eax
0048A770 . 50 push eax
0048A771 . 8B45 F8 mov eax,dword ptr ss:[ebp-8]
0048A774 . E8 A79BF>call RamSmash.00404320
0048A779 . 8BC8 mov ecx,eax
0048A77B . 8B45 F8 mov eax,dword ptr ss:[ebp-8]
0048A77E . 5A pop edx
0048A77F . E8 FC9DF>call RamSmash.00404580
0048A784 . 8B45 E0 mov eax,dword ptr ss:[ebp-20]
0048A787 . BA 01000>mov edx,1
0048A78C . 59 pop ecx
0048A78D . E8 EE9DF>call RamSmash.00404580
0048A792 . BB 01000>mov ebx,1
0048A797 > 8D45 D8 lea eax,dword ptr ss:[ebp-28] ; --->开始计算Name
0048A79A . 50 push eax ; --->这是一个循环,对Name的每一位进行运算
0048A79B . B9 01000>mov ecx,1
0048A7A0 . 8BD3 mov edx,ebx
0048A7A2 . 8B45 FC mov eax,dword ptr ss:[ebp-4]
0048A7A5 . E8 D69DF>call RamSmash.00404580
0048A7AA . 8B45 D8 mov eax,dword ptr ss:[ebp-28]
0048A7AD . 0FB600 movzx eax,byte ptr ds:[eax] ; --->EAX=第i位Name字符(ASCII码)Name[i]
0048A7B0 . F7EB imul ebx ; --->于i相乘
0048A7B2 . 8945 D4 mov dword ptr ss:[ebp-2C],eax
0048A7B5 . DB45 D4 fild dword ptr ss:[ebp-2C]
0048A7B8 . E8 D782F>call RamSmash.00402A94
0048A7BD . 8945 CC mov dword ptr ss:[ebp-34],eax
0048A7C0 . 8955 D0 mov dword ptr ss:[ebp-30],edx
0048A7C3 . DF6D CC fild qword ptr ss:[ebp-34]
0048A7C6 . 83C4 F4 add esp,-0C
0048A7C9 . DB3C24 fstp tbyte ptr ss:[esp] ; |
0048A7CC . 9B wait ; |
0048A7CD . 8D55 DC lea edx,dword ptr ss:[ebp-24] ; |
0048A7D0 . B8 C8AA4>mov eax,RamSmash.0048AAC8 ; |
0048A7D5 . E8 8AF5F>call RamSmash.00409D64 ; --->将Name[i]的10进制ASCII码转换位字符串
0048A7DA . FF75 DC push dword ptr ss:[ebp-24]
0048A7DD . 8D55 C8 lea edx,dword ptr ss:[ebp-38]
0048A7E0 . 8BC3 mov eax,ebx
0048A7E2 . E8 05DDF>call RamSmash.004084EC
0048A7E7 . FF75 C8 push dword ptr ss:[ebp-38]
0048A7EA . FF35 841>push dword ptr ds:[491F84]
0048A7F0 . 8D45 EC lea eax,dword ptr ss:[ebp-14]
0048A7F3 . BA 03000>mov edx,3
0048A7F8 . E8 E39BF>call RamSmash.004043E0 ; --->将Name[i]按10进制转换的字符串与位数按10进制转换的字符串
、Serial第2段连接
0048A7FD . 8B45 EC mov eax,dword ptr ss:[ebp-14]
0048A800 . E8 23DEF>call RamSmash.00408628
0048A805 . 8BF0 mov esi,eax
0048A807 . 8B45 EC mov eax,dword ptr ss:[ebp-14]
0048A80A . E8 19DEF>call RamSmash.00408628 ; --->将该字符串按10进制转换为数字
0048A80F . 03F0 add esi,eax ; --->该数乘2
0048A811 . 8BC6 mov eax,esi
0048A813 . 8D55 C4 lea edx,dword ptr ss:[ebp-3C]
0048A816 . E8 D1DCF>call RamSmash.004084EC ; --->将该数转换为字符串
0048A81B . 8B55 C4 mov edx,dword ptr ss:[ebp-3C]
0048A81E . 8D45 EC lea eax,dword ptr ss:[ebp-14]
0048A821 . E8 D298F>call RamSmash.004040F8
0048A826 . 43 inc ebx
0048A827 . 8B45 FC mov eax,dword ptr ss:[ebp-4]
0048A82A . E8 F19AF>call RamSmash.00404320
0048A82F . 40 inc eax
0048A830 . 3BD8 cmp ebx,eax
0048A832 .^ 0F85 5FF>jnz RamSmash.0048A797
0048A838 . 6A 16 push 16 ; --->结束计算Name
0048A83A . 68 7C7DC>push 56C77D7C
0048A83F . 8B45 EC mov eax,dword ptr ss:[ebp-14]
0048A842 . E8 E1DDF>call RamSmash.00408628 ; --->将该字符串按10进制转换为数字
0048A847 . 99 cdq
0048A848 . E8 0FA7F>call RamSmash.00404F5C ; --->该数*0x1656C77D7C
0048A84D . 8945 CC mov dword ptr ss:[ebp-34],eax
0048A850 . 8955 D0 mov dword ptr ss:[ebp-30],edx ; --->结果按双精度数压入堆栈
0048A853 . DF6D CC fild qword ptr ss:[ebp-34]
0048A856 . 83C4 F4 add esp,-0C
0048A859 . DB3C24 fstp tbyte ptr ss:[esp] ; --->双精度转换为单精度
0048A85C . 9B wait ; |
0048A85D . 8D55 C0 lea edx,dword ptr ss:[ebp-40] ; |
0048A860 . B8 C8AA4>mov eax,RamSmash.0048AAC8 ; |
0048A865 . E8 FAF4F>call RamSmash.00409D64 ; --->将该单精度数按科学计数法转换为字符串M
0048A86A . 8B55 C0 mov edx,dword ptr ss:[ebp-40]
0048A86D . 8D45 EC lea eax,dword ptr ss:[ebp-14]
0048A870 . E8 8398F>call RamSmash.004040F8
0048A875 . 8D45 F0 lea eax,dword ptr ss:[ebp-10]
0048A878 . 50 push eax
0048A879 . 8B55 F8 mov edx,dword ptr ss:[ebp-8]
0048A87C . B8 BCAA4>mov eax,RamSmash.0048AABC
0048A881 . E8 DE9DF>call RamSmash.00404664
0048A886 . 8BC8 mov ecx,eax ; --->ECX=Serial第1段长度
0048A888 . 49 dec ecx
0048A889 . BA 01000>mov edx,1
0048A88E . 8B45 F8 mov eax,dword ptr ss:[ebp-8]
0048A891 . E8 EA9CF>call RamSmash.00404580 ; --->取得Serial第1段
0048A896 . 8B45 F0 mov eax,dword ptr ss:[ebp-10]
0048A899 . E8 1EF5F>call RamSmash.00409DBC ; --->按科学计数法将Serial第1段转换为单精度数
0048A89E . DB7D B4 fstp tbyte ptr ss:[ebp-4C]
0048A8A1 . 9B wait
0048A8A2 . 8B45 EC mov eax,dword ptr ss:[ebp-14]
0048A8A5 . E8 12F5F>call RamSmash.00409DBC ; --->按科学计数法将Name计算结果字符串M转换为单精度数
0048A8AA . DB6D B4 fld tbyte ptr ss:[ebp-4C]
0048A8AD . DEE1 fsubrp st(1),st
0048A8AF . D81D CCA>fcomp dword ptr ds:[48AACC]
0048A8B5 . DFE0 fstsw ax
0048A8B7 . 9E sahf
0048A8B8 . 0F87 2E0>ja RamSmash.0048A9EC
0048A8BE . 8B45 EC mov eax,dword ptr ss:[ebp-14]
0048A8C1 . E8 F6F4F>call RamSmash.00409DBC
0048A8C6 . DB7D A8 fstp tbyte ptr ss:[ebp-58]
0048A8C9 . 9B wait
0048A8CA . 8B45 F0 mov eax,dword ptr ss:[ebp-10]
0048A8CD . E8 EAF4F>call RamSmash.00409DBC
0048A8D2 . DB6D A8 fld tbyte ptr ss:[ebp-58]
0048A8D5 . DEE1 fsubrp st(1),st
0048A8D7 . D81D CCA>fcomp dword ptr ds:[48AACC] ; --->两数比较,相差小于0x4000则认证成功,不跳!!!
............................................................
省略代码若干行
中间好像还有和名单检测
............................................................
0048AA65 . 8A45 F7 mov al,byte ptr ss:[ebp-9]
0048AA68 . 5F pop edi
0048AA69 . 5E pop esi
0048AA6A . 5B pop ebx
0048AA6B . 8BE5 mov esp,ebp
0048AA6D . 5D pop ebp
0048AA6E . C3 retn ; --->函数结束
=============================
=============================
KeyGen
注册算法:
Name最后一位的ASCII码与Name长度的乘积,按10进制转换为字符串,
然后与Name长度按10进制转换的字符串、License个数按10进制转换的字符串依次连接,再按10进制转换为数字。
该数乘2后,与95945194876的乘积,如果乘积大于0xFFFF FFFF FFFF FFFF,则忽略高位,只取低8个字节。
结果按科学计数法精确到小数点后13位,转换为字符串,就是Serial的第1段。
例如:
Name="MrBeer[CCG]"
最后一位是']',ASCII=93,Name长度是11,乘积为93*11=1023,转换为字符串"1023"
Name长度转换为字符串"11",License个数假设为100,转换为字符串是"100",连接后为"102311100",转换为数字102311100。
102311100*2*95945194876=19632516854955847200= 0x1 1074 B60F E283 2E20(0xFFFF FFFF FFFF FFFF)。
忽略高位,得到0x1074 B60F E283 2E20=1185772781246295584=1.18577278124629e18 (忽略最低4位)
"1.18577278124629e18"即为注册码第1段,整个注册码为"1.18577278124629e18+100+"。
VC++源码:
KeyGen里的关键函数
CString Calculating(CString CName,CString CLicense)
{
int i,j;
double k;
char n;
CString CLength;
i=CName.GetLength();
n=CName.GetAt(i-1);
j=int(n)*i;
CLength.Format("%d",i);
CName.Format("%d",j);
CName=CName+CLength+CLicense;
k=atol((LPCTSTR)CName);
k=k*2*95945194876;
if(k<1e+18)
CName.Format("%.0f",k);
else
{
if(k>0xFFFFFFFFFFFFFFFF)
k=k-0xFFFFFFFFFFFFFFFF;
CName.Format("%.14e",k);
CName.Delete(17,2);
}
i=CName.GetLength();
CName.Insert(i,'+');
CName=CName+CLicense;
i=CName.GetLength();
CName.Insert(i,'+');
return(CName);
}
修改后的KeyGen可以计算1-999个License.
=============================
=============================
Greets
感谢PEiD/C32ASM/OllyDbg及其插件的开发、修改者,没有他们的杰出工作也就没有本文。
感谢SUNBIRD对PDiD Kanal插件使用的指导。
=============================
=============================
MrBeer[CCG]
11/Jul/2005
=============================