首页
论坛
专栏
课程

[原创]浮点比较在注册算法中的应用-RamSmash 破解手记

2005-7-13 09:35 6821

[原创]浮点比较在注册算法中的应用-RamSmash 破解手记

2005-7-13 09:35
6821
=============================
SWIFTDOG RamSmash 1.7.4.2005 KeyGen tutorial
=============================

=============================
程序说明

RamSmash is a program which returns control of memory managment back to the user.  
Windows oftenly uses up all memory available and leaves the users computer at a slow pace.  
Also numerous applications that are improperly created also use up vast quantites of RAM/Memory
and never reallocate this memory back to the computer.  
With RamSmash you can at any time reallocate memory back to your computer
and gain more computer power in the process.  
While RAM/Memory is very inexpensive now to purchase it still is difficult
for the average person to try to install
and improper installation can cause major damage to a computer.  
Typically the average computer user has a service technician install the RAM/Memory modules
which adds significant cost to the hardware upgrade.  
RamSmash on the other hand is cheap and affordable and can in no way harm or destroy a computer,
while providing equivalent power that RAM/Memory modules would.  
RamSmash is quite easy to use and understand.  
With a few button pushes and setting changes the application is ready to work.  
RamSmash starts up automatically when Windows does and manages memory
according to the user's specifications.
Try RamSmash for 28 days and if you find it useful please do register.
=============================

=============================
破解说明

保护方式:
时间限制+注册码+网络认证

破解目的:
本软件是一款内存工具,界面友好。
该软件注册算法中使用了浮点数比较方式对注册码进行认证。

破解结果:
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
=============================

[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (7)
fcrjzmd 10 2005-7-13 10:22
2
0
支持一下,学习!。。
kyc 19 2005-7-13 14:50
3
0
学习...............
liyangsj 25 2005-7-13 15:51
4
0
辛苦了!!
newlaos 2005-7-13 17:42
5
0
这种文章才算精品,有意路有解释。
  比起那些帖大段代码没几句话的文章好上百倍
dfui 2005-8-1 14:41
6
0
我的脱壳成功但不能运行…………噢恼。修复也不行。WINXPSP2
middlebeer 5 2005-8-1 17:34
7
0
最初由 dfui 发布
我的脱壳成功但不能运行…………噢恼。修复也不行。WINXPSP2


参考peaceclub一年前写的《PeCompact 2.x 通用简单脱壳法》,应该可以的
yuqli 2 2005-8-2 15:52
8
0
http://bbs.pediy.com/showthread.php?s=&threadid=11194&highlight=PeCompact+2.x+%CD%A8%D3%C3%BC%F2%B5%A5%CD%D1%BF%C7%B7%A8
我来帖
游客
登录 | 注册 方可回帖
返回