最近群里有人讨论起了把硬盘以及CPU序列号用于加密方面的话题,虽然以前也曾听到过类似的讨论,但我还是听的一头雾水,于是就从网上寻找了大量的相关资料,并对其进行了整理,希望能对大家有所帮助!
一、硬盘序列号
和硬盘序列号相关的概念有三个,分别是硬盘商标号、硬盘序列号、驱动器序列号,当然要想将序列号用于加密,那我们首先就得知道如何获得序列号,最好的方法就是对类似功能的软件进行分析,本文我们就以磁盘序列号修改器为例来进行简单的分析以区分以上的概念。
1、区分硬盘商标号
我们在购买硬盘时,硬盘上都会有一个商标号,不同厂商的硬盘商标号是不一样的,对于加密来讲这个号码没有任何的意义,只是为了区分下面将要介绍的两个号码而已。以我的电脑为例,在“开始”——“运行”栏中输入compmgmt.msc打开计算机管理程序,单击“磁盘管理”,在右侧的磁盘0上点击右键并选择“属性”,在“常规”选项中我们看到的字符就是硬盘的商标号——WD1200BEVS-22UST0。
WD就表示为西部数据公司提供的硬盘,1200表示我的硬盘容量为120G,而后面的B表示转速为7200 RPM/分,这里我简单的介绍了一下商标号里面的意思,不同厂商的硬盘都有不同的描述方式,这些我们可以从网上来获得更多的相关信息!
2、硬盘序列号(Disk Serial Number)
硬盘序列号又称为Disk Serial Number,它具体唯一性和只读性,所以被广泛的应用于加密领域,对于硬盘序列号的获取,我们可以先通过CreateFile()打开物理设备,然后使用DeviceControl函数向打开的设备发送指定命令进行通信,根据返回的SENDCMDOUTPARAMS结构来得到物理序列号和模型号。
我们以磁盘序列号修改器为例来进行说明,程序本身加了aspack壳,可以使用ESP定律把壳脱掉,未注册版本无法修改驱动器的序列号,只要将字符串参考上面的两个跳转修改为相反的指令就可以破解了,对于这个程序的破解没有什么好说的,我们主要来看它是如何调用函数的。
将脱壳后的程序载人到OD中,然后使用“bp CreateFileA”下int 3断点,按F9运行程序,断下来后观察堆栈区的函数调用过程,也就是说我们需要找到类似下面的代码:
PysicafDrive后面的0表示的是硬盘号,它的范围为0-255,不过一般用于计算注册码的时候只要有一个硬盘就完全可以了,更何况现在没有硬盘而只有内存的计算机也实在是太少了。接下来我们按F2取消断点,按ALT+F9返回程序领空,再按F8单步走,不一会儿就发现了如下的代码:
这是第二个函数DeviceControl的用法,我们观察堆栈窗口,有些参数可以直接照抄过去,这是我学习的时候所使用的方法,可能比较笨,但是对于不太会用的API函数,与其查看全英文的MSDN,倒不如在OD中查看调用过程来的更实在,这可能也算是逆向的意义吧。
因为对硬盘的I/O操作是一种底层操作,所以需要Windows驱动开发环境中的一些数据结构,这里就不再赘述了,缩写好的程序及源码已经打包收录了,程序在WindowsXP SP3+VC6.0中编译通过,运行后大家仔细看一下,会发现硬盘商标号和序列号是完全不一样的。
3、驱动器序列号(Volume Serial Number)
驱动器序列号又称为Volume Serial Number(卷标号),这个号码是当我们进行硬盘格式化时随机生成的一个号码,它具有可修改性,但是绝大多数情况下当我们安装完Windows后就很少再对硬盘进行格式化操作了,所以它还是有一定的可取之处,同时获得驱动器号的方法也比较简单,只要调用GetVolumelnformation()就可以了,所以很多时候我们碰到的一般软件都可以使用这种方法来计算注册码,因此像磁盘序列号修改器类似的程序便有了生存空间。
将脱壳后的磁盘序列号修改器载入到OD中,在GetVolumelnformation下int 3断点,查看堆栈区,函数调用情况如下:
我们编写如下的代码就能获取驱动器的序列号了:
经过上面的介绍,相信大家对于那些基本硬盘序列号来计算注册码的软件要如何获得号码都已经有所了解了。
二、CPU序列号
CPU序列号是一个建立在处理器内部的、唯一的、不能修改的编号,它由96位全数字组成,正是因为其具有唯一性,所以很多时候也被用于计算注册码。
1、CPU序列号与CPUID
CPU序列号的唯一性体现在96位中的低64位,每一个CPU的都不相同,而高32位则称为CPU ID,用来识别CPU类型,它们的区别是必须要搞清楚的,前者每一个CPU都唯一,而后者相同系列的CPU则是一样,即CPU ID并不具备唯一性,CPU序列号包含了CPUID,同上的一些介绍资料不是描述的不详细就是写的不严谨,直接导致读者陷入了理解的误区。
2、CPUID指令与MSR寄存器
为了得到CPU序列号,Intel专门加入了一个CPUID指令,CPUID指令是一个类似函数调用的指令,通过eax传递相应的参数,关于CPUID待会儿我会作更详细的说明,至于MSR寄存器,确切的更应该称为MSR寄存器组,它有很多个,CPU通过MSR寄存器ID来识别,这个ID是通过ECX作为参数进行传递的,MSR寄存器中保存有CPU的一些特性数据,同时MSR寄存器也并不是目前所有的CPU都支持的,也就是说并不是IA32架构下的CPU必备的一个寄存器。为了更好的操作MSR寄存器,Intel加入了rdmsr(read msr)读MSR指令和wrmsr(write msr)写MSR指令,要想使用rdmsr和wrmsr操作MSR寄存器中的MSR位,首先需要任务运行在ring0级下,然后将ECX设置为读寄存器的ID,如果是读操作,EDX和ECX中保存有读到的内容,如果是写操作,需要在EDX和ECX中保存相应的数据。
3、CPUID指令与CPU信息获得
CPUID是一个比较复杂的命令,在早期intel的CPU中并不存在,要想知道一个系统的CPU是否支持该命令,可以通过查看EFLAGS中的第21位,如果为l就表示支持,如果为0就表示并不支持。
向eax中传递0参数,可以得到CPU的商标号——就是哪个生产厂家,返回的信息将依次保存在EBX、EDX、ECX中。同时因为Intel的CPU是little endian的排序,所以我们看到的EBX、EDX、ECX的信息应该倒着读——\x47\x65\x6e\x75\x69\x6e\x65\x49\x6e\x74\x65\x6c,也就是GenuineIntel的意思。
向eax中传递1参数,可以得到CPU的型号,返回的信息中eax的值就是当前CPU的型号。当然这个信息并不能完全相信,因为有一些不同型号的CPU也会有相同的签名,这里只是介绍CPUID的一种用法。
执行这条指令后相信很多朋友得的值都为0,因为从Pentium III处理器后,在引入MSR寄存器后都加入了MSR位,只有MSR=1时才可以读取到CPU序列号,所以我们在读取CPU序列号前需要先对MSR位进行调整。关于它们的更多的知识,大家请参考一些网上的资料,我把来自于Intel的一篇关于CPUID的说明文档也一起打包收录了,有兴趣的读者要吧继续研究一下!
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法破解行为。