Windows 10内核驱动开发中如何获取NetBIOS主机名
Windows 10内核驱动获取NetBIOS主机名实现方案
用户态下GetComputerNameExA(ComputerNameNetBIOS)可获取NetBIOS格式主机名,内核态下无需跨环调用用户态API,可通过以下两种稳定方案实现相同功能,适配Windows 10全版本。
方案1:调用内核原生API RtlGetComputerNameEx(推荐)
RtlGetComputerNameEx是ntoskrnl.exe导出的内核例程,和用户态GetComputerNameEx逻辑完全对等,WDK头文件未包含该函数声明,需手动定义原型后通过MmGetSystemRoutineAddress动态获取调用。
首先定义依赖的枚举和函数指针:
typedef enum _COMPUTER_NAME_FORMAT { ComputerNameNetBIOS, ComputerNameDnsHostname, ComputerNameDnsDomain, ComputerNameDnsFullyQualified, ComputerNamePhysicalNetBIOS, ComputerNamePhysicalDnsHostname, ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified, ComputerNameMax } COMPUTER_NAME_FORMAT; typedef NTSTATUS(NTAPI* PFN_RtlGetComputerNameEx)( _In_ COMPUTER_NAME_FORMAT NameType, _Inout_ PUNICODE_STRING lpBuffer );
调用示例代码:
// 函数返回后,outName中存储宽字符格式的NetBIOS主机名,使用完成后需释放outName->Buffer指向的内存 NTSTATUS GetKernelNetBIOSName(PUNICODE_STRING outName) { NTSTATUS ntStatus; UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"RtlGetComputerNameEx"); PFN_RtlGetComputerNameEx RtlGetComputerNameEx = (PFN_RtlGetComputerNameEx)MmGetSystemRoutineAddress(&routineName); if (RtlGetComputerNameEx == NULL) { return STATUS_PROCEDURE_NOT_FOUND; } // NetBIOS名最大长度15字符,预留足够缓冲区 const ULONG bufSize = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR); outName->Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, bufSize, 'mNbH'); if (outName->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } outName->Length = 0; outName->MaximumLength = bufSize; ntStatus = RtlGetComputerNameEx(ComputerNameNetBIOS, outName); if (!NT_SUCCESS(ntStatus)) { ExFreePool(outName->Buffer); outName->Buffer = NULL; } return ntStatus; }
如果需要和GetComputerNameExA输出完全一致的ANSI格式字符串,拿到UNICODE_STRING结果后调用RtlUnicodeStringToAnsiString转换即可。
方案2:读取注册表获取(备用兼容方案)
系统NetBIOS名持久化存储在注册表中,可通过内核注册表API直接读取,兼容所有Windows版本,适合作为上述API调用失败时的降级方案。
目标注册表路径:\Registry\Machine\System\CurrentControlSet\Control\ComputerName\ComputerName,键值名ComputerName,类型为REG_SZ,存储内容即为NetBIOS主机名。
调用示例代码:
NTSTATUS GetNetBIOSNameFromRegistry(PUNICODE_STRING outName) { NTSTATUS ntStatus; HANDLE hRegKey = NULL; OBJECT_ATTRIBUTES objAttrs; UNICODE_STRING keyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"); UNICODE_STRING valueName = RTL_CONSTANT_STRING(L"ComputerName"); ULONG retSize = 0; PKEY_VALUE_PARTIAL_INFORMATION pValueInfo = NULL; InitializeObjectAttributes(&objAttrs, &keyPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ntStatus = ZwOpenKey(&hRegKey, KEY_READ, &objAttrs); if (!NT_SUCCESS(ntStatus)) { return ntStatus; } // 先查询键值所需缓冲区长度 ntStatus = ZwQueryValueKey(hRegKey, &valueName, KeyValuePartialInformation, NULL, 0, &retSize); if (ntStatus != STATUS_BUFFER_TOO_SMALL) { ZwClose(hRegKey); return ntStatus; } pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, retSize, 'rNbH'); if (pValueInfo == NULL) { ZwClose(hRegKey); return STATUS_INSUFFICIENT_RESOURCES; } ntStatus = ZwQueryValueKey(hRegKey, &valueName, KeyValuePartialInformation, pValueInfo, retSize, &retSize); if (NT_SUCCESS(ntStatus)) { outName->Length = pValueInfo->DataLength; outName->MaximumLength = pValueInfo->DataLength; outName->Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, pValueInfo->DataLength, 'oNbH'); if (outName->Buffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { RtlCopyMemory(outName->Buffer, pValueInfo->Data, pValueInfo->DataLength); } } ExFreePool(pValueInfo); ZwClose(hRegKey); return ntStatus; }
开发注意事项
- 上述两种方法都必须在
IRQL = PASSIVE_LEVEL级别调用,禁止在Dispatch或更高IRQL下执行,否则会触发系统蓝屏 - 分配的内核内存使用完成后必须调用
ExFreePool释放,避免内核内存泄漏 - 禁止直接从内核态跳转调用用户态
GetComputerNameExA,跨环调用无异常处理保护,极易触发权限错误或系统崩溃 - NetBIOS主机名最大长度为15个字符,缓冲区预留
MAX_COMPUTERNAME_LENGTH + 1长度即可满足需求 - 若需兼容Windows 10 2004以前的版本,可将代码中
ExAllocatePool2替换为ExAllocatePoolWithTag即可
内容的提问来源于stack exchange,提问作者osok
相关产品推荐
相关产品推荐

