使用PECMD编写软件许可证功能,绑定本地硬件(本地硬盘)
2023-9-27 911

用户需要有一个license文件,内容是加密之后的硬盘序列号信息;

程序需要通过自己特定算法,将本地硬盘序列号混淆后使用SHA1加密为固定字符串;

程序在关键代码处检测许可证状态,对比用户持有的license文件是否跟程序加密后的字符串相同,如果相同则进行授权,否则阻止下一步操作。

image.webp

image.webp

演示程序通过“Generate”按钮创建允许授权的license文件,“Check”按钮进行授权验证。

//https://learn.microsoft.com/zh-cn/windows/win32/fileio/disk-management-control-codes
//https://learn.microsoft.com/zh-cn/windows/win32/api/winioctl/ni-winioctl-ioctl_storage_query_property
//https://learn.microsoft.com/zh-cn/windows/win32/api/winioctl/ns-winioctl-storage_property_query
//https://blog.csdn.net/lengye7/article/details/122953234

set^ EnviMode=1
set^ ForceLocal=1
set$ nl=0d 0a
envi DriveSerialNumber=
envi usercode1=123
envi usercode2=456
envi hsn=

//set disk=\\?\STORAGE#Volume#{bd7e99c2-72f8-11ed-b079-806e6f6e6963}#0000000000100000#{7f108a28-9833-4b3b-b780-2c6b5fa5c062}  //检索设备接口路径
//set disk=\\?\scsi#disk&ven_st2000dm&prod_001-1ch164#4&15828421&0&030000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}  //检索设备接口路径
//set disk=\\?\scsi#disk&ven_samsung&prod_ssd_850_evo_500g#4&15828421&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}  //检索设备接口路径
//set disk=\\.\Volume{e1d1e74e-4d47-11ed-aa55-806e6f6e6963}     //检索卷
//set disk=\\.\HarddiskVolume2       //检索 MS-DOS 设备
set disk=\\.\PhysicalDrive0        //检索磁盘
//set disk=\\.\Harddisk0Partition1   //检索分区
//set disk=\\.\GLOBALROOT\device\Harddisk0\Partition1   //检索分区
//set disk=\\.\C:   //检索 C 盘

set generic_read=0x80000000
set file_share_read=0x00000001
set file_share_write=0x00000002
calc dwShareMode=%file_share_read% | %file_share_write%
set open_existing=3
set file_attribute_normal=0x00000080
call $--qd --ret:h Kernel32.dll,CreateFileW,$%disk%,#%generic_read%,#%dwShareMode%,#0,#%open_existing%,#%file_attribute_normal%,#0  //如果函数成功,则返回值是指定文件、设备、命名管道或邮件槽的打开句柄。如果函数失败,则返回值为 INVALID_HANDLE_VALUE (-1)。 要获得更多的错误信息,请调用 GetLastError。
ifex $%h%<>-1,
{
	set ioctl_storage_base=0x0000002d
	set Function=0x0500
	set method_buffered=0
	set file_any_access=0
	calc ioctl_storage_query_property=shl(%ioctl_storage_base%,16) | shl(%file_any_access%,14) | shl(%Function%,2) | %method_buffered%    //0x2D1400

	set nInBufferSize=0xC  //结构体 storage_property_query 的长度
	set$# lpInBuffer=*%nInBufferSize% 0
	set-long PropertyId=0  //由 storage_property_id 枚举的 StorageDeviceProperty 值,指示调用方是请求设备描述符、适配器描述符、写入缓存属性、设备唯一 ID (DUID) ,还是设备 SCSI 重要产品数据 (VPD) 页中提供的设备标识符。
	set-long QueryType=0   //由 storage_query_type 枚举的 PropertyStandardQuery 值,要查询类型的标志
	set-long AdditionalParameters=0  //包含可用于检索特定查询的其他参数的字节数组
	set-copy lpInBuffer=PropertyId;0;4;0
	set-copy lpInBuffer=QueryType;0;4;4
	set-copy lpInBuffer=AdditionalParameters;0;4;(4 + 4)

	ifex $%::bX64%<3, set lpdword=4! set lpdword=8   //%&::bX64%=0, PECMD32+Win32  //%&::bX64%=1, PECMD32+WIN64  //%&::bX64%=3, PECMD64+WIN64
	calc nOutBufferSize=0x28 + 1K  //结构体 storage_device_descriptor 的长度
	set$# lpOutBuffer=*%nOutBufferSize% 0
	set$# lpBytesReturned=*%lpdword% 0  //LPDWORD 实际就是DWORD类型,int
	call $--qd --ret:RetDeviceIoControl Kernel32.dll,DeviceIoControl,#%h%,#%ioctl_storage_query_property%,*lpInBuffer,#%nInBufferSize%,*lpOutBuffer,#%nOutBufferSize%,*lpBytesReturned,#0  //如果操作成功完成,则返回值为非零 (TRUE) 。如果操作失败或挂起,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
	call $--qd --bool --ret:RetCloseHandle Kernel32.dll,CloseHandle,#%h%  //如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。若要获取扩展的错误信息,请调用 GetLastError。如果应用程序在调试器下运行,则函数在收到无效的句柄值或伪句柄值时将引发异常。如果关闭句柄两次,或者对 FindFirstFile 函数返回的句柄调用 CloseHandle,而不是调用 FindClose 函数,则可能会发生这种情况。
	ifex $%RetDeviceIoControl%<>0,
	{
		//结构体 storage_device_descriptor
		
		set?int lpOutBuffer=Size:4
		set?int lpOutBuffer=SerialNumberOffset:(4 + 4 + 1 + 1 + 1 + 1 + 4 + 4 + 4)
		call GetStr SerialNumber %SerialNumberOffset%

		set DriveSerialNumber=%SerialNumber%
	}
}
HASH $%usercode1%$%DriveSerialNumber%$%usercode2%,hsn,SHA1
READ %CurDir%\license.txt,0,userlic 
call keygen

_sub GetStr
	envi szId=
	ifex $%~2>0,
	{
		set-make lpHex=&lpOutBuffer@%~2;*(%Size% - %~2)
		code **ansi,lpHex,**uni,szId
		exit -
		//以下未使用
		getf -bin lpOutBuffer,%~2#(%Size% - %~2),szHex
		lpos* * szPos= 0x00,1,szHex
		ifex $%szPos%>0,
		{
			mstr * lpHex=1,%szPos%,szHex
			code ***ansi,szHex,**uni,szId
		}
	}
	set-ret %~1=%szId%
_end

_SUB keygen,W410H160,KeyGen
LABE labe1,L20T30W88H48,User license:
    LABE labe2,L140T30W288H28,%userlic%
    LABE labe3,L20T60W128H48,Local license:
    LABE labe4,L140T60W248H28,%hsn%
item item1,L20T100W80H20,Generate,call createlic
item item2,L120T100W80H20,Check,call checklic
labe reg1,L220T100W100H20,%register%
_END

_SUB createlic
WRIT *c %CurDir%\license.txt,$+0,%hsn%
READ %CurDir%\license.txt,0,userlic2
envi @labe2=%userlic2%
find $%userlic2%=%hsn%,mess License file created successfully!mess Failed to create license file
_END

_SUB checklic
find $%userlic%=%hsn%,envi @reg1=registered!envi @reg1=unregistered
_END

感谢527104427的代码:[原创]调用 api 实例(检索指定磁盘设备描述符,即磁盘型号、序列号、总线类型等)(storage_device_descriptor)-PECMD脚本源码分享-PECMD技术社区

收藏
点赞 1
分享
官方QQ群:872611894(群密码:pecmd.net)
最新回复 (4)
  • avatar image
    wxinchun92 2023-9-27

    学习一下

  • avatar image
    keketoco00 2023-9-28

    你非常擅长你所做的事情,非常感谢你

    Keketoco00
  • avatar image
    conthuongbome 2023-10-28

    thanks you very much

    Nasiboot
  • avatar image
    BFWL 2024-7-13

    谢谢老师分享!

    板凳要坐十年冷,文章不写一句空。
返回