=============================
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
=============================