飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 5531|回复: 18

硬盘序列号的得到方式,相信对破解有所作用!

[复制链接]

该用户从未签到

发表于 2005-8-23 11:11:43 | 显示全部楼层 |阅读模式
From: "Wang Xianbing" <Wang.Xianbing@p0.f25.n662.z6.fidonet.org>
Subject: 关于硬盘序列号的探讨!
Newsgroups: programr.china

大家好:

近来在CFIDO和E-mail中一直有朋友询问关于如何能在Windows下用VB来
获取硬盘序列号的问题, 因为在VB的for Windows中版本中没有了端口存取
函数, 所以就写了一个HDIDE16.DLL的东东, 放在主页上免费发放, 并且
HDIDE32版本的即将出台, 孰知却遭到 XXX 肆意践踏, 理由如下:
---- CUT ----
WX> 好久一来, 一直未能发现有用VB来获取硬盘序列号的东东,
WX> 以致于VB的程序 不能得到很好的保护, 所以昨天做了个HDIDE16.DLL,
RT> 太烦了吧!
RT> 不是我想给你泼冷水,你的WINAPI知识有点欠乏!
RT> 取计算机硬盘序列号及卷名的函数是:
RT> 这个老早我就用了.
RT> 加密方法很多种,也不一定用硬盘序列号.
RT> 可惜不能说. :)))
--- CUT ---

对此, 我表示强烈的抗议, 并且为避免由此引起的误导, 特将基本的常识区
分如下, 附有完整的对比源程序:

关于盘序列号有两种:

硬盘序列号: 英文名 Hard Disk Serial Number, 该号是出厂时生产厂家为
区别产品而设置的, 是唯一的, 是只读的, 利用硬盘序列号的
加密往往是利用其唯一和只读的特性, 大多是针对有序列号的
IDE HDD而言, 对于没有序列号或SCSI HDD硬盘则无能为力,
这也是利用它进行加密的局限性.
卷的序列号: 英文名 Volume Serial Number, 该号既可指软磁盘要得, 如:
A:盘和B:盘的, 又可以指硬盘的逻辑盘, 如: C:, D:...的,
是高级格式化时随机产生的, 是可以修改的, 所以利用其进行
加密, 其唯一性还可, 而其可修改性对于安全而言就大打折扣
了.

那么如何获得它们呢? 这要视不同的平台而论, 核心实现方法如下:

DOS平台 Windows 3.X Windows 9.X

硬盘序列号: 端口I/O 端口I/O Ring0级I/O

卷的序列号: 中断调用 WINAPI WINAPI


为方便大家验证, 特贴如下两程序用TC或BC编译后运行在DOS下即可:

/* 程序1: 获得IDE硬盘C的序列号 */
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <string.h>

char *getascii (unsigned int in_data [], int off_start, int off_end);

void main (void)
{
unsigned int dd [256]; /* DiskData */
unsigned int dd_off; /* DiskData offset */

while (inp (0x1F7) != 0x50) /* Wait for controller not busy */
;

outp (0x1F6, 0xA0); /* Get first/second drive */

outp (0x1F7, 0xEC); /* Get drive info data */

while (inp (0x1F7) != 0x58) /* Wait for data ready */
;

for (dd_off = 0; dd_off != 256; dd_off++) /* Read "sector" */
dd [dd_off] = inpw (0x1F0);

printf ("The Serial Number Hard Disk [C] is %s", getascii (dd, 10, 19));

}

char *getascii (unsigned int in_data [], int off_start, int off_end)
{
static char ret_val [255];
int loop, loop1;

for (loop = off_start, loop1 = 0; loop <= off_end; loop++)
{
ret_val [loop1++] = (char) (in_data [loop] / 256); /* Get High byte */
ret_val [loop1++] = (char) (in_data [loop] % 256); /* Get Low byte */
}
ret_val [loop1] = ""; /* Make sure it ends in a NULL character */
return (ret_val);
}

/* 程序2: 获得逻辑盘C的序列号 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <dos.h>

void main(void)
{
char serial_no[10];
union REGS r;
struct SREGS s;
unsigned sno1, sno2;

r.x.ax = 0x6900;
r.h.bl = 3; /* A:=1, B:=2, C:=3 etc. */
segread(&s);
intdosx(&r, &r, &s);
if (r.x.cflag)
*serial_no = "";
else
{
sno2 = *((unsigned far *)MK_FP(s.ds, r.x.dx+2));
sno1 = *((unsigned far *)MK_FP(s.ds, r.x.dx+4));
sprintf(serial_no, "%04X-%04X
", sno1, sno2);
}
printf("The Serial Number of Login Disk [C] is %s", serial_no );

}

在Windows 3.X中:

硬盘序列号: 使用端口I/O即可, 将以上程序稍加修改并用VC或BC做成DLL
即可在VB中调用, 本人就是这样做的.
卷的序列号: 用那位朋友所说的WINAPI函数GetVolumeInformation即可.

在Windows 9.X中:

硬盘序列号: 必须使用VxD技术, 即便不写VxD文件, 要获得Ring0级的访问权
才能获得, 否则程序工作在Ring3级会死在以下语句处:
while (inp (0x1F7) != 0x50) /* Wait for controller not busy */
详细技术请访问http://dingkai.sdxf.com

卷的序列号: 用那位朋友所说的WINAPI函数GetVolumeInformation即可.


我欢迎每一位热情, 诚挚的朋友! 拒绝狂妄.

让我们携起手来, 从小做起, 共通推动民族软件发展!


~~~ 赏屯  光辉软件工作室 - 王献冰 屯屯屯屯屯屯屯屯屯屯[][?[X]? ? ===> 振兴民族软件 抗击北约暴行 <=== ?~~~ 韧屯 URL: http://glowsoft.126.com E-mail: wxbing@163.net 屯图

用过共享软件的人都知道,一般的共享软件(特别是国外的)在使用一段时间后都会
提出一些“苛刻”的要求,如让您输入注册号等等。如果您想在软件中实现该“功能”
的话,方法有很多。在这里我介绍一种我认为安全性比较高的一种,仅供参考。
  大家都知道,当您在命令行中键入“dir”指令后,系统都会读出一个称作Serial
Number的十六进制数字。这个数字理论上有上亿种可能,而且很难同时找到两个序列号
一样的硬盘。这就是我这种注册方法的理论依据,通过判断指定磁盘的序列号决定该机
器的注册号。
  要实现该功能,如何获得指定磁盘的序列号是最关键的。在Windows中,有一个Get
VolumeInformation的API函数,我们利用这个函数就可以实现。
  下面是实现该功能所需要的代码:
  Private Declare Function GetVolumeInformation& Lib "kernel32" _
  Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, _
  ByVal pVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, _
  lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, _
  lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, _
  ByVal nFileSystemNameSize As Long)
  Private Const MAX_FILENAME_LEN = 256
  Public Function DriveSerial(ByVal sDrv As String) As Long
  'Usage:
  'Dim ds As Long
  'ds = DriveSerial("C")
  Dim RetVal As Long
  Dim str As String * MAX_FILENAME_LEN
  Dim str2 As String * MAX_FILENAME_LEN
  Dim a As Long
  Dim b As Long
  GetVolumeInformation sDrv & ":\", str, MAX_FILENAME_LEN, RetVal, _
  a, b, str2, MAX_FILENAME_LEN
  DriveSerial = RetVal
  End Function
  如果我们需要某个磁盘的序列号的话,只要DriverSerial(该磁盘的盘符)即可。如
DriverASerialNumber=DriverSerial("A")。
  下面,我们就可以利用返回的磁盘序列号进行加密,需要用到一些数学知识。在这
里我用了俄罗斯密码表的加密算法对进行了数学变换的序列号进行加密。下面是注册码
验证部分的代码:
  Public Function IsValidate(ByVal SRC As Long, ByVal Value As String) As
Boolean
  Dim SourceString As String
  Dim NewSRC As Long
  For i = 0 To 30
  If (SRC And 2 ^ i) = 2 ^ i Then
  SourceString = SourceString + "1"
  Else
  SourceString = SourceString + "0"
  End If
  Next i
  If SRC < 0 Then
  SourceString = SourceString + "1"
  Else
  SourceString = SourceString + "0"
  End If
  Dim Table As String
  Dim TableIndex As Integer
  '=======================================================================
  '这是密码表,根据你的要求换成别的,不过长度要一致
  '=======================================================================
  '注意:这里的密码表变动后,对应的注册号生成器的密码表也要完全一致才能生成
正确的注册号
  Table = "JSDJFKLUWRUOISDH;KSADJKLWQ;ABCDEFHIHL;KLADSHKJAGFWIHERQOWRLQH"
  '=======================================================================
  Dim Result As String
  Dim MidWord As String
  Dim MidWordValue As Byte
  Dim ResultValue As Byte
  For t = 1 To 1
  For i = 1 To Len(SourceString)
  MidWord = Mid(SourceString, i, 1)
  MidWordValue = Asc(MidWord)
  TableIndex = TableIndex + 1
  If TableIndex > Len(Table) Then TableIndex = 1
  ResultValue = Asc(Mid(Table, TableIndex, 1)) Mod MidWordValue
  Result = Result + Hex(ResultValue)
  Next i
  SourceString = Result
  Next t
  Dim BitTORool As Integer
  For t = 1 To Len(CStr(SRC))
  BitTORool = SRC And 2 ^ t
  For i = 1 To BitTORool
  SourceString = Right(SourceString, 1) _
  + Left(SourceString, Len(SourceString) - 1)
  Next i
  Next t
  If SourceString = Value Then IsValidate = True
  End Function
  由于代码较长,还有一些部分的代码在此省略,您可以去我的网站(http://vbtech
nology.yeah.net)下载源程序研究一下。
  最后,我们就可以利用这些子程序进行加密了。

一、 引言

在网络相当发达的今天,一大批优秀的软件涌现在互联网上,我们可以根据需要随意的下载、使用。这些软件有一部分是免费的,用户可以在免费的前提下无限期、不受限制的使用。但也有为数众多的共享软件是需要用户注册的,用户可以免费使用一段时间,超过期限之后如果还未注册,那么将不能继续使用软件或是一些先进的、常用的功能将无法正常使用。只有用户在完成注册或交纳一定的费用得到开发者反馈回的注册码之后方可正常使用。但现有的许多注册方式是存在漏洞的,现在不少破解网站通过正常渠道购买回一个注册码之后将其公布在其网站之上,更有甚者还提供有用于产成注册码的注册机。以上的种种行为侵犯了软件开发者的知识产权,干扰了共享软件的正常发布。虽然国家也有保护知识产权的相关法律条文,但显然在执行起来存在一定的难度,比如针对本文前面所述情况单是取证就相当困难。因此作为程序员的软件开发者应当具有自我保护意识,利用自己的专业特长,通过程序控制来确保自己的每一个注册码只针对于一个软件拷贝。本文将根据笔者的实际编程经验,对一种结合物理硬件信息的安全注册方法的实现过程进行较全面的介绍。

二、 注册源的采集

为了确保注册码的唯一性,在注册源的采集上应当尽量选取一些唯一的、不易复制的软、硬件信息作为原始信息。而硬件由于其不可复制性和物理唯一性成为了我们的首选目标,而且多数计算机配件在出厂时都有一个唯一的标识号,我们可以将其作为识别的依据。符合上述条件的标识号大致有硬盘的序列号、网卡的序列号、BiOS中的主版序列号或主机出厂日期和标志等几种,考虑到硬件通用性、实现起来的难易程度以及系统安全性等多种因素以硬盘序列号为佳,因为网卡随说唯一性最好但不能保证每台计算机都装有网卡,而ROM BIOS中F000H-FFFFH区域虽存有与硬件配置有关的信息、F000H:FFF5H-F000H:FFFFH存有主机出厂日期和主机标志值等参数,但在Windows 9x的保护模式下编程实现是比较困难的。虽然在Windows 9x保护模式下对硬盘序列号也不能按通常在DOS模式下的通过硬盘端口1F6H和1F7H直接读取,但Windows API函数中提供的下面这个函数可以非常简单的获取到指定磁盘驱动器的序列号:

GetVolumeInformation("C:\\",NULL,NULL,&dwIDESerial,NULL,NULL,NULL,NULL);

第一个参数设为"C:\\"表示我们要读取C盘驱动器的序列号。之所以选C盘,是因为我们不能保证用户有多个分区,而C盘却是每一个用户都具有的。该函数成功调用完毕后,在DWORD型的变量dwIDESerial中就存储了获取到的32位长的磁盘序列号。注册信息采集到后,关键的问题就是如何让用户将其返回给开发者。一种较简单的方法是把采集到的硬盘序列号与用户输入的注册名经过位操作的简单加密后存放到一个文本中通过邮件传送给开发者。

三、 注册机的设计

开发人员得到从用户返回的由硬盘序列号构成的注册原始信息后就要利用注册机生成注册码并反馈给用户。一般来说,注册机要利用既定的位操作和不可逆算法,生成用户比较容易操作的字符串注册码。在用户方面的共享软件里也应有一个具有相同算法的注册机用以动态计算以当前注册名进行注册的正确的注册码,然后对用户输入的注册码进行检验,如果不匹配则不予注册。如果是从开发者反馈来的正确注册码,则通过标记配置文件或是往注册表中写入特定内容的键值以表明当前软件是经过注册的。而且共享软件在每次运行时都要对用户注册码进行实时检测与判断,这样才能实现注册限制功能。如果应用程序被拷贝到其他计算机上则会由于硬盘序列号的改变而导致注册信息的不匹配,因此仍然需要重新注册,从而达到共享软件的发布目的。下面就是一段非常简单的注册机实现代码:

开发者方:
//strSerial是由用户返回的同注册名进行过位运算的注册源信息
::strcpy(buf1,strSerial);
::strcpy(buf2,strName);
//再次进行位运算,取得硬盘序列号
for(int I=0;I<8;I++)
input[I]=buf1[I]^buf2[I];
//错序与位操作,产生与注册名有关的注册码
int Index[8]={7,5,3,1,6,4,2,0};
char Mask[8]={'Z','H','U','C','E','J','I','0'};
for(I=0;I<8;I++)
{
result[I]=input[Index[I]]^buf2[I];
result[I]=result[I]^Mask[I];
}
……

用户方:
//根据输入的字串计算出硬盘序列号
int Index[8]={7,5,3,1,6,4,2,0};
char Mask[8]={'Z','H','U','C','E','J','I','0'};
for(int I=0;I<8;I++)
{
input[I]=input[I]^buf2[I];
ouput[Index[I]]=input[I]^Mask[I];
}
dwSerialID=::atoll(output);//将序列号恢复成数值
//动态提取硬盘序列号
GetVolumeInformation("C:\\",NULL,NULL,&dwIDESerial,NULL,NULL,NULL,NULL);
If(dwSerialID==dwIDESerial)
{
……//注册成功
}
else
{
……//注册失败
}

以上两段代码仅用于举例说明实现的方法,并未进行复杂的逻辑运算,在实际应用中应视共享软件的自身价值、软件实现的复杂程度等多种因素来决定采取多大的加密深度。即使对于一般的注册机也应加以2层以上的函数逻辑运算。对于共享软件,不管其实现何种功能,最好采取在线注册的方式,而且在同一个软件系统中应有若干个注册入口,只要一处注册成功整个软件就算注册成功了。为了清晰的了解整个注册过程,流程图可以大致如下,并可根据具体做适当的调整:

小结:本文主要对共享软件的安全注册进行了讨论。为了更方便、安全地实现对共享软件的注册可以大致遵循这样几个原则:选准注册源、经常变动注册算法、加密注册码、管理好注册机防止外流、采用在线式的注册方法。以上所述基本是笔者自己的实际经验总结,由于能力有限,难免有所疏漏,应在具体编程中不断对其加以改进完善。本文所引程序在Windows 98下由Microsoft Visual C++ 6.0编译通过。

[ Last edited by brightsm on 2005-8-23 at 11:17 AM ]
PYG19周年生日快乐!
  • TA的每日心情
    开心
    2024-1-6 12:21
  • 签到天数: 90 天

    [LV.6]常住居民II

    发表于 2005-8-28 09:21:42 | 显示全部楼层
    不错!也给我上了一课!
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2005-9-2 17:29:33 | 显示全部楼层
    也给我上了一课!
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2005-9-10 10:36:59 | 显示全部楼层
    不错,支持!
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2018-2-26 08:32
  • 签到天数: 19 天

    [LV.4]偶尔看看III

    发表于 2006-1-13 21:38:35 | 显示全部楼层

    BD.DLL(硬盘序列号) V3.0 这个好用吗?

    BD.DLL(硬盘序列号) V3.0

    软件简介: 享软件的作者,您是不是一直在找一个可以从Windows95/98/Me/2000/XP/2003系统上都可以获得硬盘序列号的方法或者控件呢?bd.dll是你要找的东西了,它可以从以上系统中轻易地取得硬盘的出厂是的物理的序列号,这个序列号是不会因为硬盘格式化而改变的。保证您制作的软件能不被随意复制。他有以下特点:
    本DLL是纯VB制作的DLL,因此非常适合VB程序员调用,
    他非常小,仅仅只有32K,
    他适用范围广,windows系统均可以使用。
    他使用方便,你只需要5行代码就完成了读硬盘序列号的全过程。
    硬盘ID号是许多软件作者加密软件的方法,也是非常好的方法,它不会因为硬盘格式化而变化,具有长期可靠性。而VB中如何得到硬盘序列号成了一大难,本程序让您得到简单的方法,
    而且我们将不断升级本控件,保证它的安全性和更好的可靠性。
    PYG19周年生日快乐!
  • TA的每日心情
    擦汗
    2017-9-28 11:05
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2006-1-14 11:44:31 | 显示全部楼层
    学习了~
    楼主或版主修改一下文章

    禁用笑脸符号~~
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-2-9 19:13:58 | 显示全部楼层
    真的,很不错!!!
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-2-15 21:09:32 | 显示全部楼层
    不错的文章,学习
    PYG19周年生日快乐!
  • TA的每日心情
    奋斗
    2022-11-30 20:44
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2006-3-5 21:03:45 | 显示全部楼层
    这个真是好!
    PYG19周年生日快乐!
  • TA的每日心情
    奋斗
    2022-11-30 20:44
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2006-3-5 21:04:33 | 显示全部楼层
    顶一下。。。。。
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表