获得并修改硬件序列号--CPU、主板、内存、硬盘等(有源码)
大家都知道很多Anti Cheat会封硬件序列号,所以本文探索一下如何get and modify序列号。这个服务是比较贵的于是有了研究一下的想法。思路:1. 通过厂商自带的程序刷新固件。2. 自己写驱动修改。
思路1不讨论,要拿到厂商去修改,很不方便。这里重点讨论思路2。思路2是通过修改SMBIOS表,有两种方法,一种需要开机自启动,一种不需要,后面会介绍。
SMBIOS:SMBIOS(System Management BIOS)是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范。而DMI(Desktop Management Interface, DMI) 就是帮助收集电脑系统信息的管理系统。DMI信息的收集必须在严格遵照SMBIOS规范的前提下进行。两种方法都需要解析SMBIOS。
一、先介绍不需要重启的方法:1.从物理内存 0x000F0000-0x000FFFFF 之间寻找关键字 “ _SM_” 。2.找到后再向后16个字节,看后面5个BYTE是否是关键字“_DMI_”,如果是,EPS表即找到。 EPS表结构中16H以及18H处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表。 SMBIOS表结构由头和体组成,其中头的结构定义如下:体的结构由头的TYPE字段决定,如下:其中TYPE 0结构是BIOS information,TYPE 1结构是SYSTEM Information,其它类型大家查阅我附件里的文档。总之是依据文档标准去解析。这里给出关键代码:从而找到EPS和SMBIOS表。然后修改SMBIOS表中的字段。这样可以修改CPU、主板、内存、硬盘序列号。由于篇幅关系,先介绍到这里,具体代码见附件:GetSMBiosRing0.zip。效果如下:图右侧显示BIOS模式为"传统",代表BIOS,非UEFI。
二、再介绍需要重启的方法:1.先是想注册回调来修改,通过NtSetSystemInformation函数的SystemRegisterFirmwareTableInformationHandler,见下面的WRK代码:NTSTATUS
NTAPI
NtSetSystemInformation (
__in SYSTEM_INFORMATION_CLASS SystemInformationClass,
__in_bcount_opt(SystemInformationLength) PVOID SystemInformation,
__in ULONG SystemInformationLength
)
/*++
Routine Description:
This function set information about the system.
Arguments:
SystemInformationClass - The system information class which is to
be modified.
SystemInformation - A pointer to a buffer which contains the specified
information. The format and content of the buffer depend on the
specified system information class.
SystemInformationLength - Specifies the length in bytes of the system
information buffer.
Return Value:
Returns one of the following status codes:
STATUS_SUCCESS - Normal, successful completion.
STATUS_ACCESS_VIOLATION - The specified system information buffer
is not accessible.
STATUS_INVALID_INFO_CLASS - The SystemInformationClass parameter
did not specify a valid value.
STATUS_INFO_LENGTH_MISMATCH - The value of the SystemInformationLength
parameter did not match the length required for the information
class requested by the SystemInformationClass parameter.
STATUS_PRIVILEGE_NOT_HELD is returned if the caller does not have the
privilege to set the system time.
--*/
{
BOOLEAN Enable;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
ULONG TimeAdjustment;
PSYSTEM_SET_TIME_ADJUST_INFORMATION TimeAdjustmentInformation;
HANDLE EventHandle;
PVOID Event;
ULONG LoadFlags = MM_LOAD_IMAGE_IN_SESSION;
PAGED_CODE();
//
// Establish an exception handle in case the system information buffer
// is not accessible.
//
Status = STATUS_SUCCESS;
try {
//
// Get the previous processor mode and probe the input buffer for
// read access if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
ProbeForRead((PVOID)SystemInformation,
SystemInformationLength,
sizeof(ULONG));
}
//
// Dispatch on the system information class.
//
switch (SystemInformationClass) {
case SystemFlagsInformation:
if (SystemInformationLength != sizeof( SYSTEM_FLAGS_INFORMATION )) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (!SeSinglePrivilegeCheck( SeDebugPrivilege, PreviousMode )) {
return STATUS_ACCESS_DENIED;
}
else {
ULONG Flags;
Flags = ((PSYSTEM_FLAGS_INFORMATION)SystemInformation)->Flags &
~(FLG_KERNELMODE_VALID_BITS | FLG_BOOTONLY_VALID_BITS);
Flags |= NtGlobalFlag & (FLG_KERNELMODE_VALID_BITS | FLG_BOOTONLY_VALID_BITS);
NtGlobalFlag = Flags;
((PSYSTEM_FLAGS_INFORMATION)SystemInformation)->Flags = NtGlobalFlag;
}
break;
...... 省略
case SystemRegisterFirmwareTableInformationHandler:
Status = ExpRegisterFirmwareTableInformationHandler(SystemInformation,
SystemInformationLength,
PreviousMode);
break;编写代码:TableHandler.ProviderSignature = 'RSMB'; // (Raw SMBIOS)
TableHandler.Register = TRUE;
TableHandler.FirmwareTableHandler = &WmipRawSMBiosTableHandler1;
TableHandler.DriverObject = g_PnpDriverObject;
ntStatus = funZwSetSystemInfomation(SystemRegisterFirmwareTableInformationHandler,
(PVOID)&TableHandler, sizeof(SYSTEM_FIRMWARE_TABLE_HANDLER));发现ntStatus没有成功,返回STATUS_OBJECT_NAME_EXISTS,对象已经存在。于是尝试先删除已经存在的,TableHandler.Register设置为FALSE:VOID WmipRegisterFirmwareProviders()
{
// Register the SMBIOS raw provider.
TableHandler.ProviderSignature = 'RSMB'; // (Raw SMBIOS)
TableHandler.Register = FALSE;
TableHandler.FirmwareTableHandler = &WmipRawSMBiosTableHandler1;
TableHandler.DriverObject = g_PnpDriverObject/*IoPnpDriverObject*/;
ntStatus = funZwSetSystemInfomation(SystemRegisterFirmwareTableInformationHandler,
(PVOID)&TableHandler, sizeof(SYSTEM_FIRMWARE_TABLE_HANDLER));
TableHandler.ProviderSignature = 'RSMB'; // (Raw SMBIOS)
TableHandler.Register = TRUE;
TableHandler.FirmwareTableHandler = &WmipRawSMBiosTableHandler1;
TableHandler.DriverObject = g_PnpDriverObject;
ntStatus = funZwSetSystemInfomation(SystemRegisterFirmwareTableInformationHandler,
(PVOID)&TableHandler, sizeof(SYSTEM_FIRMWARE_TABLE_HANDLER));
}发现还是删除不了Raw SMBIOS,第一次funZwSetSystemInfomation返回STATUS_INVALID_PARAMETER,第二次返回STATUS_OBJECT_NAME_EXISTS。然后查看上面ZwSetSystemInfomation对SystemRegisterFirmwareTableInformationHandler的处理代码如下:NTSTATUS
ExpRegisterFirmwareTableInformationHandler(
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
IN KPROCESSOR_MODE PreviousMode
)
/*++
Description:
This routine registers and unregisters firmware table providers.
Parameters:
SystemInformation - points to the SYSTEM_FIRMWARE_TABLE_HANDLER
structure.
SystemInformationLength - returns the number of bytes written on success. if
the provided buffer was too small, it returns the
required size.
PreviousMode - Previous mode (Kernel / User).
Return Value:
STATUS_SUCCESS - On success.
STATUS_PRIVILEGE_NOT_HELD - If caller is from User mode.
STATUS_INFO_LENGTH_MISMATCH - Buffer too small.
STATUS_INSUFFICIENT_RESOURCES - On failure to allocate resources.
STATUS_OBJECT_NAME_EXISTS - Table already registered.
STATUS_INVALID_PARAMETER - Table not found / invalid request.
--*/
{
BOOLEAN HandlerFound = FALSE;
PSYSTEM_FIRMWARE_TABLE_HANDLER_NODE HandlerListCurrent = NULL;
PSYSTEM_FIRMWARE_TABLE_HANDLER_NODE HandlerListNew = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PSYSTEM_FIRMWARE_TABLE_HANDLER SystemTableHandler = NULL;
PAGED_CODE();
if (PreviousMode != KernelMode) {
Status = STATUS_PRIVILEGE_NOT_HELD;
} else {
if ((!SystemInformation) ||
(SystemInformationLength < sizeof(SYSTEM_FIRMWARE_TABLE_HANDLER))) {
Status = STATUS_INFO_LENGTH_MISMATCH;
} else {
//
// Grab the resource to prevent state change via reentry.
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&ExpFirmwareTableResource,
TRUE);
SystemTableHandler = (PSYSTEM_FIRMWARE_TABLE_HANDLER)SystemInformation;
EX_FOR_EACH_IN_LIST(SYSTEM_FIRMWARE_TABLE_HANDLER_NODE,
FirmwareTableProviderList,
&ExpFirmwareTableProviderListHead,
HandlerListCurrent) {
if (HandlerListCurrent->SystemFWHandler.ProviderSignature == SystemTableHandler->ProviderSignature) {
HandlerFound = TRUE;
break;
}
}
//
// Handler was not found and this is a register request, so
// allocate a new node and insert it into the list.
//
if ((!HandlerFound) && (SystemTableHandler->Register)) {
//
// This is a new Firmware table handler, allocate
// the space and add it to the list.
//
HandlerListNew = ExAllocatePoolWithTag(PagedPool,
sizeof(SYSTEM_FIRMWARE_TABLE_HANDLER_NODE),
'TFRA');
if (HandlerListNew) {
//
// Populate the new node.
//
HandlerListNew->SystemFWHandler.ProviderSignature = SystemTableHandler->ProviderSignature;
HandlerListNew->SystemFWHandler.FirmwareTableHandler = SystemTableHandler->FirmwareTableHandler;
HandlerListNew->SystemFWHandler.DriverObject = SystemTableHandler->DriverObject;
InitializeListHead(&HandlerListNew->FirmwareTableProviderList);
//
// Grab a reference to the providers driverobject so that the
// driver does not get unloaded without our knowledge. The
// handler must first be unregistered before unloading.
//
ObReferenceObject((PVOID)HandlerListNew->SystemFWHandler.DriverObject);
//
// Update the LinkList.
//
InsertTailList(&ExpFirmwareTableProviderListHead,
&HandlerListNew->FirmwareTableProviderList);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else if ((HandlerFound) && (!(SystemTableHandler->Register))) {
//
// Check to make sure that a matching driver object was sent in.
//
if (HandlerListCurrent->SystemFWHandler.DriverObject == SystemTableHandler->DriverObject) {
//
// Remove the entry from the list.
//
RemoveEntryList(&HandlerListCurrent->FirmwareTableProviderList);
//
// Deref the device object.
//
ObDereferenceObject((PVOID)HandlerListCurrent->SystemFWHandler.DriverObject);
//
// Free the unregistered list element.
//
ExFreePoolWithTag(HandlerListCurrent, 'TFRA');
} else {
Status = STATUS_INVALID_PARAMETER;
}
} else if ((HandlerFound) && (SystemTableHandler->Register)) {
//
// A handler for this table has already been registered. return
// error.
//
Status = STATUS_OBJECT_NAME_EXISTS;
} else {
Status = STATUS_INVALID_PARAMETER;
}
ExReleaseResourceLite(&ExpFirmwareTableResource);
KeLeaveCriticalRegion();
}
}
return Status;
}从上面返回的STATUS_OBJECT_NAME_EXISTS错误码和EX_FOR_EACH_IN_LIST代码:EX_FOR_EACH_IN_LIST(SYSTEM_FIRMWARE_TABLE_HANDLER_NODE,
FirmwareTableProviderList,
&ExpFirmwareTableProviderListHead,
HandlerListCurrent) {
if (HandlerListCurrent->SystemFWHandler.ProviderSignature == SystemTableHandler->ProviderSignature) {
HandlerFound = TRUE;
break;
}推断出WmipRegisterFirmwareProviders函数中的g_PnpDriverObject不对。WRK中IoPnpDriverObject未导出。
2.Hook ExpFirmwareTableProviderListHead指针来修改硬件Id:核心代码来自UC论坛,我对代码进行了简单的修改:BOOL HookWmipRawSMBiosTable()
{
PLIST_ENTRY pExpFirmwareTableProviderListHead = NULL;
PSYSMODULELIST pSysModuleList = NULL;
static UCHAR signature[] = "\x48\x8B\x05\x00\x00\x00\x00\x48\x83\xC0\xE8";
static UCHAR signature2[] = "\x48\x8B\x0D\x00\x00\x00\x00\x48\x83\xC1\xE8";
pSysModuleList = GetModuleList();
if (!pSysModuleList)
return FALSE;
ULONG_PTR ulNtoskrnlBase =0, ulNtoskrnlSize = 0;
FindModuleByName(pSysModuleList, "ntoskrnl.exe", &ulNtoskrnlBase, &ulNtoskrnlSize);
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER*)(ulNtoskrnlBase);
IMAGE_NT_HEADERS *kernelImage = (IMAGE_NT_HEADERS*)(ulNtoskrnlBase + dos->e_lfanew);
//PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)(g_ntoskrnl + 1);
//
// 获取DOS头部
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)ulNtoskrnlBase;
// 检查DOS头部的标志是否符合PE格式
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}
// 计算PE头部的地址
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)((DWORD_PTR)ulNtoskrnlBase + dosHeader->e_lfanew);
// 检查PE头部的标志是否符合PE格式
if (ntHeader->Signature != IMAGE_NT_SIGNATURE) {
return FALSE;
}
// 获取节表的地址
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD_PTR)ntHeader + sizeof(IMAGE_NT_HEADERS));
for (PIMAGE_SECTION_HEADER pSection = sectionHeader;
pSection < sectionHeader + kernelImage->FileHeader.NumberOfSections; pSection++)
{
if (*(PULONG)pSection->Name != 'EGAP')
{
continue;
}
DWORD64 found = find_pattern(ulNtoskrnlBase + pSection->VirtualAddress,
pSection->Misc.VirtualSize, (const char*)signature, "xxx????xxxx");
if (found == 0)
{
found = find_pattern(ulNtoskrnlBase + pSection->VirtualAddress,
pSection->Misc.VirtualSize, (const char*)signature2, "xxx????xxxx");
}
if (found != 0)
{
pExpFirmwareTableProviderListHead = (PLIST_ENTRY)found;
break;
}
}
if (pExpFirmwareTableProviderListHead != NULL)
{
// PAGE:00000001404A95A5 48 8B 05 >64 8C E2 FF mov rax, cs:ExpFirmwareTableProviderListHead
int relativeAddr = *(int*)((char*)pExpFirmwareTableProviderListHead + 3);
pExpFirmwareTableProviderListHead = (PLIST_ENTRY)((uintptr_t)(pExpFirmwareTableProviderListHead)+7 + relativeAddr);
if (!IsListEmpty(pExpFirmwareTableProviderListHead))
{
PLIST_ENTRY nextEntry = NULL;
for (PLIST_ENTRY pListEntry = pExpFirmwareTableProviderListHead->Flink; pListEntry != pExpFirmwareTableProviderListHea d; pListEntry = nextEntry)
{
nextEntry = pListEntry->Flink;
unsigned int val = *(unsigned int*)((uintptr_t)pListEntry - 0x18);
PVOID* funcPtr = (PVOID*)((uintptr_t)pListEntry - 0x18 + 0x8);
DbgPrint("ExpFirmwareTableProviderListHead entry: %u 0x%p", val, *funcPtr);
if (val == 'RSMB')
{
g_pFuncPtr = funcPtr;
pOriginalWmipRawSMBiosTableHandler = *(PFNFTH*)funcPtr;
InterlockedExchangePointer((volatile PVOID*)funcPtr, (PVOID)WmipRawSMBiosTableHandlerHook);
break;
}
}
}
}
return TRUE;
}函数指针替换成功,WmipRawSMBiosTableHandlerHook函数可以正确调用。但是,不能修改SMBios数据,只能读取。
下面说下我想到的能够修改主板序列号的方法:通过 WmipFindSMBiosStructure -> WmipSMBiosTablePhysicalAddress获得SMBios表的地址,然后找到WmipFindSMBiosStructure -> WmipSMBiosTableLength获得SMBios表的长度。代码如下:// WmipFindSMBiosStructure -> WmipSMBiosTablePhysicalAddress
PPHYSICAL_ADDRESS physical_address = (PPHYSICAL_ADDRESS)n_util::find_pattern_image(address,
"\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x74\x00\x8B\x15",
"xxx????xxxx?xx");
if (physical_address == 0) return false;
physical_address = reinterpret_cast<PPHYSICAL_ADDRESS>(reinterpret_cast<char*>(physical_address) + 7 + *reinterpret_cast<int*>(reinterpret_cast<char*>(physical_address) + 3));
if (physical_address == 0) return false;
n_log::printf("physical address : %llx \n", physical_address);
// WmipFindSMBiosStructure -> WmipSMBiosTableLength
DWORD64 physical_length_address = n_util::find_pattern_image(address,
"\x8B\x1D\x00\x00\x00\x00\x48\x8B\xD0\x44\x8B\xC3\x48\x8B\xCD\xE8\x00\x00\x00\x00\x8B\xD3\x48\x8B",
"xx????xxxxxxxxxx????xxxx");
if (physical_length_address == 0) return false;
unsigned long physical_length = *reinterpret_cast<unsigned long*>(static_cast<char*>((void*)physical_length_address) + 6 + *reinterpret_cast<int*>(static_cast<char*>((void*)physical_length_address) + 2));
if (physical_length == 0) return false;
n_log::printf("physical length : %d \n", physical_length);然后遍历、修改表。这里给出SMBios用到的几个结构体:typedef struct
{
UINT8 Type;
UINT8 Length;
UINT8 Handle;
} SMBIOS_HEADER,*PSMBIOS_HEADER;
typedef UINT8 SMBIOS_STRING;
typedef struct
{
SMBIOS_HEADER Hdr;
SMBIOS_STRING Vendor;
SMBIOS_STRING BiosVersion;
UINT8 BiosSegment;
SMBIOS_STRING BiosReleaseDate;
UINT8 BiosSize;
UINT8 BiosCharacteristics;
} SMBIOS_TYPE0;
typedef struct
{
SMBIOS_HEADER Hdr;
SMBIOS_STRING Manufacturer;
SMBIOS_STRING ProductName;
SMBIOS_STRING Version;
SMBIOS_STRING SerialNumber;
//
// always byte copy this data to prevent alignment faults!
//
GUID Uuid; // EFI_GUID == GUID?
UINT8 WakeUpType;
} SMBIOS_TYPE1;
typedef struct
{
SMBIOS_HEADER Hdr;
SMBIOS_STRING Manufacturer;
SMBIOS_STRING ProductName;
SMBIOS_STRING Version;
SMBIOS_STRING SerialNumber;
} SMBIOS_TYPE2;
typedef struct
{
SMBIOS_HEADER Hdr;
SMBIOS_STRING Manufacturer;
UINT8 Type;
SMBIOS_STRING Version;
SMBIOS_STRING SerialNumber;
SMBIOS_STRING AssetTag;
UINT8 BootupState;
UINT8 PowerSupplyState;
UINT8 ThermalState;
UINT8 SecurityStatus;
UINT8 OemDefined;
} SMBIOS_TYPE3;然后遍历、修改表代码如下:void process_smbios_table(SMBIOS_HEADER* header)
{
auto TableLength = [](PSMBIOS_HEADER pHeader) -> size_t
{
char* current = reinterpret_cast<char*>(pHeader) + pHeader->Length;
size_t i = 1;
for (i; current != '\0' || current != '\0'; i++)
{
// Scan until we find a double zero byte
}
return pHeader->Length + i + 1;
};
auto GetString = [](PSMBIOS_HEADER pHeader, unsigned char id, int nLen) -> char*
{
UNREFERENCED_PARAMETER(id);
UNREFERENCED_PARAMETER(nLen);
char* string = reinterpret_cast<char*>(pHeader) + pHeader->Length;
char hexOutput = { 0 };
stringToHex(string, hexOutput, nLen);
DbgPrint("%s\r\n", hexOutput);
for (DWORD i = 1; i < id; i++)
{
string += strlen(string) + 1;
}
return string;
};
{
char* serialNumber = NULL;
if (header->Type == 1) //主板bios
{
SMBIOS_TYPE1* pSystemInfoHeader = reinterpret_cast<SMBIOS_TYPE1*>(header);
serialNumber = GetString((SMBIOS_HEADER*)pSystemInfoHeader, pSystemInfoHeader->SerialNumber,
header->Length);
DbgPrint("read SystemInfo: %s\r\n", serialNumber);
}
else if (header->Type == 2) //主板物理序列号
{
SMBIOS_TYPE3* pBaseBoardHeader = reinterpret_cast<SMBIOS_TYPE3*>(header);
serialNumber = GetString((SMBIOS_HEADER*)pBaseBoardHeader, pBaseBoardHeader->SerialNumber,
header->Length);
DbgPrint("read BaseBoard: %s\r\n", serialNumber);
}
else if (header->Type == 4) // CPU
{
SMBIOS_TYPE4* pCpuHeader = reinterpret_cast<SMBIOS_TYPE4*>(header);
serialNumber = GetString((SMBIOS_HEADER*)pCpuHeader, pCpuHeader->ProcessorId,
header->Length);
DbgPrint("read CPU: %s\r\n", serialNumber);
}
if (serialNumber)
{
if (header->Type == 1) // SystemInfo 主板bios
{
SMBIOS_TYPE1* pSystemInfoHeader = reinterpret_cast<SMBIOS_TYPE1*>(header);
RandomizeSerialNumber(serialNumber, 12);
DbgPrint("write: %s\r\n", serialNumber);
serialNumber = GetString((SMBIOS_HEADER*)pSystemInfoHeader, pSystemInfoHeader->SerialNumber,
header->Length);
DbgPrint("re-read BaseBoard bios: %s.Len:%d\r\n", serialNumber, header->Length);
}
else if (header->Type == 2) // BaseBoard Physical SN
{
SMBIOS_TYPE3* pBaseBoardHeader = reinterpret_cast<SMBIOS_TYPE3*>(header);
RandomizeSerialNumber(serialNumber, 12);
DbgPrint("write: %s\r\n", serialNumber);
serialNumber = GetString((SMBIOS_HEADER*)pBaseBoardHeader, pBaseBoardHeader->SerialNumber,
header->Length);
DbgPrint("re-read BaseBoard: %s.Len:%d\r\n", serialNumber, header->Length);
}
else if (header->Type == 4) // CPU
{
SMBIOS_TYPE4* pCpuHeader = reinterpret_cast<SMBIOS_TYPE4*>(header);
RandomizeSerialNumber(serialNumber, 12);
DbgPrint("write: %s\r\n", serialNumber);
serialNumber = GetString((SMBIOS_HEADER*)pCpuHeader, pCpuHeader->ProcessorId,
header->Length);
DbgPrint("re-read CPU: %s.Len:%d\r\n", serialNumber, header->Length);
}
}
header = (PSMBIOS_HEADER)((char*)header + TableLength(header));
}
DbgPrint(" Serial numbers spoofed.");
}具体代码见附件HardwareSNModify(fix版).7z,是对附件HardwareSNModify.rar的升级。效果如下:上面只截了UEFI的图,BIOS也同样有效果。
这里分别对不同类型的SMBIOS进行了修改,CPU、主板、内存、硬盘序列号。经过详细测试,稳定运行。其实CPU虚拟化也可以修改CPU序列号,但考虑有些PC不开启VT,所以还是选择了上面的方法。其余的序列号修改可以参考附件里的手册。
之前想着直接清空硬件序列号,但这种方法可能会被Anti-Cheat forbid。所以实现为Random序列号。
注意:另外,若是需要重启的方法,驱动加载后需要修改注册表, 把Start键值改为1。如下图:总结:上文两种方法,不需要重启的方法,要求是BIOS引导。需要重启的方法,UEFI和BIOS的都兼容。第一种方法关于UEFI的下回再分解,第二种方法设置为开机启动,其实也很方便。
附件里还有SetupRwX64.exe,是上文所用到的RW工具。
附件下载:我用夸克网盘分享了「硬件序列号修改」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。链接:https://pan.quark.cn/s/20075b3caa29
好东西,谢谢楼主!!
页:
[1]