首页
社区
课程
招聘
[原创]对某个CAD绘图控件的分析
发表于: 2012-5-3 16:49 7285

[原创]对某个CAD绘图控件的分析

2012-5-3 16:49
7285

【文章标题】: 某个CAD绘图控件的分析
【文章作者】: LiXMX
【保护方式】: 序列号
【编写语言】: Borland Delphi 6.0 - 7.0
【使用工具】: PEiD,Regmon_fix,DEDE,UltraEdit
【操作平台】: Win XP
【软件介绍】: 一款Delphi和C++Builder环境下的矢量图绘制工具插件,功能简单但是较实用。
【作者声明】: 本人菜鸟一枚,发帖原因是从注册后到现在没发过像样的文章,注册好多年了,惭愧哇,主要是技术不到家啊。如果和版规冲突,请管理员删除,谢谢。
【分析原因】: 本菜分析它的原因仅仅是因为这个软件的授权方式让我很不爽(PS:我已经花钱买下来了>_<)。目前使用序列号方式授权,每次重装系统都要重新申请,虽

然提供加密狗,但是加密狗居然只支持Delphi,不能再C++Builder中使用,让只会C/C++的本小菜很受伤,深感受到了歧视,所以破解之

-------------------------------------------------------------------------------

【流程】:

初看对方提供的程序,分为三个部分:控件安装程序、机器码生成程序、加密狗一枚。

加密狗:
这个就不说了,等着过段时间在研究一下吧。

机器码生成程序:
该程序运行后就会生成一个8位机器码,机器码一般多为CpuID,分区卷序号之类的。
然后通过“只要不重装系统,每次的码都是一样的”排除了CpuID,初步判断是分区卷序号。
之后使用PEiD查看Import,发现确实有GetVolumeInformation函数被调用,几乎就确定是分区卷序号了。
最搞笑的是当我在CMD中输入dir命令时,我发现C盘的序列号赫然就是生成的机器码,晕,明文啊明文……
再看看他把我的注册号放到哪里了?
使用Regmon_fix监视了一下,发现是写道注册表中了。

那么大概就可以确定他的工作流程了:
1.获取C盘的序列号作为机器码发送给作者;
2.作者根据用户提供的机器码算出注册码后发送给用户;
3.用户使用“机器码生成程序”导入注册码到本地注册表中;
4.控件在启动时会检查C盘的序列号和注册表中的注册码是否匹配。

所以到了这里,第一种破解方式已经有了:用户只要把自己的C盘的序列号修改成和正版用户一样的就可以正常使用该控件了。

控件安装程序:
知道了大概的授权流程了之后,从授权流程的4步就可以看出,控件本身在启动时也需要获取C盘的序列号,一边使用C盘的序列号和注册码进行某种校验。
于是我们从控件程序本身下手,首先查看控件安装目录中的全部DCU文件,看看有没有什么可以的DCU文件。
于是发现了两个的文件:Encryption.hpp 和 Encryption.dcu(十分明目张胆的文件名啊)。
既然如此那就先看看他吧,看看是个诱饵还是个果子。
在Encryption.hpp头文件中看到有三个函数定义:
extern PACKAGE AnsiString __fastcall GetDiskSerialID(char cDriveName);
extern PACKAGE AnsiString __fastcall Encrypt(const AnsiString str);
extern PACKAGE AnsiString __fastcall Decrypt(const AnsiString str);

我勒个去的,这不就是:获取磁盘序号,编码,解码三个步骤,全齐了(看来作者压根就没想过防破解啥的,嘿嘿,不差钱哦

之后,使用DEDE的DCU Dump功能获得该DCU的反汇编代码。

在DCU中的USES中发现有如下声明:
Windows
{
T:DWORD, A:SetErrorMode,
A:SEM_FAILCRITICALERRORS,
A:MAX_PATH,
A:GetVolumeInformation
}
出现了GetVolumeInformation,看来是找对地方了。

既然有了GetVolumeInformation,那么之后就应该是使用Encrypt和Decrypt对C盘序号和注册码进行加密解密了。

最初我的想法是修改DCU中判断注册码正误的相关跳转,来一个爆破算了,但是继续往下看,发现作者在这里又给了我一个惊喜……

extern PACKAGE AnsiString __fastcall Encrypt(const AnsiString str)函数的反汇编如下:

PS:不得不感慨,DEDE确实是DELPHI程序员的噩梦哇!!!

function Encrypt (str: System.AnsiString): System.AnsiString;
var
  result Result: System.AnsiString;
  i: System.Integer;
  sn: System.AnsiString;
begin
   00000000 : 55                            PUSH EBP
   00000001 : 8B EC                         MOV EBP,ESP
   00000003 : 83 C4 EC                      ADD ESP,-20
   00000006 : 53                            PUSH EBX
   00000007 : 33 C9                         XOR ECX,ECX // ECX清零
   00000009 : 89 4D EC                      MOV DWORD PTR [EBP-20],ECX  // 初始化为0,此处应该是编译器生成的temp临时变量,存放密码异或的结果
   0000000C : 89 4D F0                      MOV DWORD PTR [EBP-16{sn}],ECX // sn初始化为0(用于存放处理后的VolumeSerial)
   0000000F : 89 55 F8                      MOV DWORD PTR [EBP-8{Result}],EDX // result用于返回结果
   00000012 : 89 45 FC                      MOV DWORD PTR [EBP-4{str}],EAX // str就是输入的VolumeSerial
   00000015 : 33 C0                         XOR EAX,EAX // EAX清零
   00000017 : 55                            PUSH EBP
   00000018 : 68(79 00 00 00                PUSH Encrypt{0x3A}+121
   0000001D : 64 FF 30                      PUSH DWORD PTR FS:[EAX]
   00000020 : 64 89 20                      MOV DWORD PTR FS:[EAX],ESP
   00000023 : C7 45 F4 01 00 00 00          MOV DWORD PTR [EBP-12{i}],$00000001 // i计数器初值为1,应该是个for循环起始位置
   0000002A : 8D 45 EC                      LEA EAX,DWORD PTR [EBP-20]
   0000002D : 8A 55 F4                      MOV DL,BYTE PTR [EBP-12{i}] // i放入到DL中
   00000030 : 8B 4D F4                      MOV ECX,DWORD PTR [EBP-12{i}] // 计数器i放入到ECX中
   00000033 : 8B 5D FC                      MOV EBX,DWORD PTR [EBP-4{str}] // str的首地址放入到EBX中
   00000036 : 32 54 0B FF                   XOR DL,BYTE PTR [EBX+ECX-1] // str[i]和DL异或
   0000003A : E8(00 00 00 00                CALL @LStrFromChar{0x21} // 异或后的结果转换成char型
   0000003F : 8B 55 EC                      MOV EDX,DWORD PTR [EBP-20] // @LStrCat的参数,fastcall调用使用了EDX寄存器
   00000042 : 8D 45 F0                      LEA EAX,DWORD PTR [EBP-16{sn}] // @LStrCat的参数,fastcall调用使用了EAX寄存器
   00000045 : E8(00 00 00 00                CALL @LStrCat{0x22} // 追加EDX中的内容到字符串sn尾部
   0000004A : FF 45 F4                      INC DWORD PTR [EBP-12{i}] // i计数器自增
   0000004D : 83 7D F4 09                   CMP DWORD PTR [EBP-12{i}],9 // i计数器和9比较
   00000051 : 75 D7                         JNE -41; (0x2A) // 是否是小于9,小于9继续循环,大于等于则终止(因为VolumeSerial是8个字符长度)
   00000053 : 8B 45 F8                      MOV EAX,DWORD PTR [EBP-8{Result}] // @LStrAsg的参数,fastcall调用使用了EAX寄存器
   00000056 : 8B 55 F0                      MOV EDX,DWORD PTR [EBP-16{sn}] // @LStrAsg的参数,fastcall调用使用了EDX寄存器  
   00000059 : E8(00 00 00 00                CALL @LStrAsg{0x26} // @LStrAsg用于将字符串sn赋值给Result
   0000005E : 33 C0                         XOR EAX,EAX
   00000060 : 5A                            POP EDX
   00000061 : 59                            POP ECX
   00000062 : 59                            POP ECX
   00000063 : 64 89 10                      MOV DWORD PTR FS:[EAX],EDX
   00000066 : 68(80 00 00 00                PUSH Encrypt{0x3A}+128
   0000006B : 8D 45 EC                      LEA EAX,DWORD PTR [EBP-20]
   0000006E : BA 02 00 00 00                MOV EDX,$00000002
   00000073 : E8(00 00 00 00                CALL @LStrArrayClr{0x29}
   00000078 : C3                            RET NEAR
   00000079 : E9(00 00 00 00                JMP @HandleFinally{0x23}
   0000007E : EB EB                         JMP -21; (0x6B)
   00000080 : 5B                            POP EBX
   00000081 : 8B E5                         MOV ESP,EBP
   00000083 : 5D                            POP EBP
   00000084 : C3                            RET NEAR
end;

通过读代码发现关键的编码部分是一个for循环,而且使用的是xor运算……
这下子Decrypt函数连看都不用看了,关键代码肯定是一样的,因为A xor B xor B = A

上边的汇编的核心代码换成C++大概就是:

AnsiString __fastcall Encrypt(AnsiString str)
{   
    String sn = "";
    for(int i=1; i<9; i++)
    {
        sn.cat_sprintf("%c", str[i]^((char)i) );
    }
    return sn;
}

如果函数入口的str存放的是C盘序列号,那么返回的就是注册码,
反之如果函数入口的str存放的事注册码,那么返回的就是C盘序列号。

所以最后发现注册码的算法仅仅是将C盘序列号的每一个字符和自己在字符串中的偏移量进行了异或运算,要制作注册机的话,使用上面那个函数就足够了。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 504
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
DEDE對付不了新版本的delphi的
2012-5-3 20:55
0
雪    币: 2155
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
这个还真不知道哦,我碰到的DELPHI写的程序大部分都是6或者7的。
2012-5-4 09:02
0
雪    币: 20
活跃值: (99)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
对付新版本的delphi未加壳的程序,直接在OD里CTRL+B 搜索Button1Click什么的就能定位到Button1的按钮事件附近,向下就是按钮事件的代码,这个方法对付老版本也一样,严重怀疑DEDE是不是使用这种方法来定位按钮事件的。。。
2012-5-7 22:14
0
游客
登录 | 注册 方可回帖
返回
//