ZwQuerySystemInformation 安全使用心得 Delphi 版
ZwQuerySystemInformation 安全使用心得 Delphi 版。
作为 DELPHI 版本,需要引用 jwaNative, JwaWinType ,他们是 JEDI API 的一部分。JEDI 官网有下载。
先给出 2 个辅助函数 和 一些结构体。
type PRecord = ^TRecord; TRecord = record end; PSystemInformationList = ^TSystemInformationList; TSystemInformationList = record Count: ULONG; List: array [0 .. 0] of TRecord; end; PSYSTEM_HANDLE_Informations = ^TSYSTEM_HANDLE_Informations; _SYSTEM_HANDLE_Informations = record Count: ULONG; SH: array [0 .. 0] of _SYSTEM_HANDLE_INFORMATION; end; TSYSTEM_HANDLE_Informations = _SYSTEM_HANDLE_Informations; SYSTEM_HANDLE_Informations = _SYSTEM_HANDLE_Informations; PNM_INFO = ^TNM_INFO; _NM_INFO = record hFile: THandle; Info: _FILE_NAME_Information; Name: array [0 .. MAX_PATH - 1] of WideChar; end; TNM_INFO = _NM_INFO; NM_INFO = _NM_INFO; Function GetSystemInformationClassSize(Const ATableType: SYSTEM_INFORMATION_CLASS; Const Count: ULONG): ULONG; begin Result := 0; case ATableType of SystemBasicInformation: Result := $002C; SystemProcessorInformation: Result := $0000C; SystemPerformanceInformation: Result := $0138; // SystemTimeInformation: Result := $0020; // SystemPathInformation: Result := $0; // SystemProcessInformation: Result := $00C8 + Count; // SystemCallInformation: Result := $0018 + (Count * $0004); SystemConfigurationInformation: Result := $0018; // SystemProcessorCounters: Result := $0030 + Count;//per cpu SystemGlobalFlag: Result := $0004; // (fails if size != 4) // SystemCallTimeInformation: Result := $0; SystemModuleInformation: Result := $0004 + (Count * Sizeof(SYSTEM_MODULE_INFORMATION)); //(n * 0x011C) SystemLockInformation: Result := $0004 + (Count * Sizeof(SYSTEM_LOCK_INFORMATION)); //(n * 0x0024) // SystemStackTraceInformation: Result := $0; // SystemPagedPoolInformation: Result := $0; // SystemNonPagedPoolInformation: Result := $0; SystemHandleInformation: Result := $0004 + (Count * Sizeof(SYSTEM_HANDLE_INFORMATION)); //(n * 0x0010) // SystemObjectTypeInformation: Result := $0038+ + (Count * $0030);// +) SystemPageFileInformation: Result := $0018 + (Count * Sizeof(SYSTEM_PAGEFILE_INFORMATION)); // SystemVdmInstemulInformation: Result := $0088; // SystemVdmBopInformation: Result := $0; SystemCacheInformation: Result := $0024; SystemPoolTagInformation: Result := $0004 + (Count * Sizeof(SYSTEM_POOL_TAG_INFORMATION)); // (n * 0x001C) // SystemInterruptInformation: Result := $0000 + Count;//, or 0x0018 per cpu SystemDpcInformation: Result := $0014; // SystemFullMemoryInformation: Result := $0; // SystemLoadDriver: Result := $0018;//, set mode only // SystemUnloadDriver: Result := $0004;//, set mode only // SystemTimeAdjustmentInformation: Result := $000C;//, 0x0008 writeable // SystemSummaryMemoryInformation: Result := $0; // SystemNextEventIdInformation: Result := $0; // SystemEventIdsInformation: Result := $0; SystemCrashDumpInformation: Result := $0004; SystemExceptionInformation: Result := $0010; SystemCrashDumpStateInformation: Result := $0004; // SystemDebuggerInformation: Result := $0002; SystemContextSwitchInformation: Result := $0030; SystemRegistryQuotaInformation: Result := $000C; // SystemAddDriver: Result := $0008;//, set mode only // SystemPrioritySeparationInformatio: Result := $0004;//, set mode only // SystemPlugPlayBusInformation: Result := $0; // SystemDockInformation: Result := $0; // SystemPowerInfo: Result := $0060;// (XP only!) // SystemProcessorSpeedInformation: Result := $000C;// (XP only!) SystemTimeZoneInformation: Result := $00AC; SystemLookasideInformation: Result := Count * $0020; SystemSetTimeSlipEvent: Result := $0; SystemCreateSession: Result := $0; SystemDeleteSession: Result := $0; SystemInvalidInfoClass1: Result := $0; SystemRangeStartInformation: Result := $0004; // (fails if size != 4) SystemVerifierInformation: Result := $0; SystemAddVerifier: Result := $0; SystemSessionProcessesInformation: Result := $0; end; end; Function GetSystemInformationClassHasCount(Const ATableType: SYSTEM_INFORMATION_CLASS): BOOL; begin Result := False; case ATableType of // SystemProcessInformation, // SystemCallInformation, // SystemProcessorCounters, SystemModuleInformation, SystemLockInformation, SystemHandleInformation, // SystemObjectTypeInformation, //SystemPageFileInformation, //好像这个还不确定。 // SystemInterruptInformation, SystemPoolTagInformation: Result := True; end; //可以 和 GetSystemInformationClassSize 配合使用。 end;
上面的 NM_INFO 和本文无关。
大家 可以 方便的使用 GetSystemInformationTable 来 获取所需的系统信息数据。
Function GetSystemInformationTable(hHeap: THandle; Const ATableType: SYSTEM_INFORMATION_CLASS; var buffSize: ULONG): PVOID; var OldPrivilegeAttributes: ULONG; hFile, hQuery: THandle; Status: NTSTATUS; cbBuffer: Cardinal; AVOID: PVOID; MinBufSize, ReturnLength: ULONG; PReturnLength: PULONG; begin buffSize := 0; Result := nil; if not EnableDebugPrivilege(GetCurrentProcess, True, OldPrivilegeAttributes) then Exit; try ReturnLength := 0; cbBuffer := 0; AVOID := nil; PReturnLength := Addr(ReturnLength); Status := ZwQuerySystemInformation(ATableType, AVOID, 0, PReturnLength); if (ReturnLength > 0) then // ReturnLength 一个结构的大小。 begin cbBuffer := ReturnLength; MinBufSize := ReturnLength; AVOID := HeapAlloc(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, cbBuffer); if not Assigned(AVOID) then Exit; try ZeroMemory(AVOID, cbBuffer); Status := ZwQuerySystemInformation(ATableType, AVOID, cbBuffer, PReturnLength); if NTSTATUS_SUCCESS(Status) then begin Result := AVOID; buffSize := cbBuffer; end else if (Status = STATUS_INFO_LENGTH_MISMATCH) and GetSystemInformationClassHasCount(ATableType) then begin //调试中,下一秒,也许就不够用了。不调试,大概也需要多申请些空间 //cbBuffer := GetSystemInformationClassSize(ATableType, PSystemInformationList(AVOID).Count + 100); cbBuffer := Sizeof(PSystemInformationList(AVOID).Count) + (MinBufSize - Sizeof(PSystemInformationList(AVOID).Count)) * (PSystemInformationList(AVOID).Count + 100); HeapFree(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, AVOID); AVOID := HeapAlloc(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, cbBuffer); if not Assigned(AVOID) then Exit; try ZeroMemory(AVOID, cbBuffer); Status := ZwQuerySystemInformation(ATableType, AVOID, cbBuffer, PReturnLength); if NTSTATUS_SUCCESS(Status) then begin Result := AVOID; buffSize := cbBuffer; end else begin HeapFree(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, AVOID); end; finally end; end else begin HeapFree(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, AVOID); cbBuffer := $10000; repeat AVOID := HeapAlloc(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, cbBuffer); if not Assigned(AVOID) then Exit; ZeroMemory(AVOID, cbBuffer); Status := ZwQuerySystemInformation(ATableType, AVOID, cbBuffer, PReturnLength); if (Status = STATUS_INFO_LENGTH_MISMATCH) then begin HeapFree(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, AVOID); cbBuffer := cbBuffer * 2; end; if cbBuffer > $1000000 then begin Exit; end; until (Status <> STATUS_INFO_LENGTH_MISMATCH); if NTSTATUS_SUCCESS(Status) then begin Result := AVOID; buffSize := cbBuffer; end else begin HeapFree(hHeap, 0 or HEAP_GENERATE_EXCEPTIONS, AVOID); end; end; finally end; end else begin Result := nil; buffSize := 0; end; finally SetDebugPrivilege(GetCurrentProcess, OldPrivilegeAttributes, OldPrivilegeAttributes); end; end;
第一次 ZwQuerySystemInformation,主要是为了返回 ReturnLength,这个是 最小数据大小,一般对于单个的数据,就直接用这个值。但是对于多个的数据,这个值就是 每一个数据项的大小加上 4 。
第二次调用 ZwQuerySystemInformation,返回了一个数据区,如果多个的数据,肯定会 Status = STATUS_INFO_LENGTH_MISMATCH,所以需要第三次调用。大小有 2 种办法获取(具体看代码)。
如果不确定是不是 多个数据,但是又出现空间不够用的情况,就需要用网络上大家常见的循环增大空间的办法了。
另外 hHeap := GetProcessHeap; 可以得到 hHeap ,而且空间的申请,不一定非要用这个,也可以用 GetMem 等。
最后补上 权限提升函数。
Function SetDebugPrivilege(Const hProcess: THandle; Const PrivilegeAttributes: ULONG; var OldPrivilegeAttributes: ULONG): Boolean; var hToken: THandle; tp: TOKEN_PRIVILEGES; NewPrivilegeAttributes: ULONG; ReturnLength: ULONG; hProcessToken: THandle; begin hToken := NULL_Handle; Result := False; OldPrivilegeAttributes := SE_PRIVILEGE_ENABLED_BY_DEFAULT; NewPrivilegeAttributes := PrivilegeAttributes; hProcessToken := hProcess; if hProcessToken = NULL_Handle then begin hProcessToken := GetCurrentProcess; end; if (OpenProcessToken(hProcessToken, TOKEN_ADJUST_PRIVILEGES, hToken)) // 获得进程访问令牌的句柄 then begin try tp.PrivilegeCount := 1; LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid); // 查询进程的权限,获取一个权限对应的LUID值 OldPrivilegeAttributes := tp.Privileges[0].Attributes; tp.Privileges[0].Attributes := NewPrivilegeAttributes; Result := AdjustTokenPrivileges(hToken, False, tp, sizeof(tp), nil, ReturnLength); // 判断令牌权限,对这个访问令牌进行修改 finally CloseHandle(hToken); end; end; end; // 提升当前进程具有权限 Function EnableDebugPrivilege(Const hProcess: THandle; Const Enable: Boolean; var OldPrivilegeAttributes: ULONG): Boolean; var hToken: THandle; tp: TOKEN_PRIVILEGES; ReturnLength: ULONG; hProcessToken: THandle; begin hToken := NULL_Handle; Result := False; OldPrivilegeAttributes := SE_PRIVILEGE_ENABLED_BY_DEFAULT; hProcessToken := hProcess; if hProcessToken = NULL_Handle then begin hProcessToken := GetCurrentProcess; end; if (OpenProcessToken(hProcessToken, TOKEN_ADJUST_PRIVILEGES, hToken)) // 获得进程访问令牌的句柄 then begin try tp.PrivilegeCount := 1; LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid); // 查询进程的权限,获取一个权限对应的LUID值 OldPrivilegeAttributes := tp.Privileges[0].Attributes; if Enable then begin tp.Privileges[0].Attributes := tp.Privileges[0].Attributes or SE_PRIVILEGE_ENABLED; // tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; end else begin tp.Privileges[0].Attributes := tp.Privileges[0].Attributes and (not SE_PRIVILEGE_ENABLED); // tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED_BY_DEFAULT; // tp.Privileges[0].Attributes := 0; end; Result := AdjustTokenPrivileges(hToken, False, tp, sizeof(tp), nil, ReturnLength); // 判断令牌权限,对这个访问令牌进行修改 finally CloseHandle(hToken); end; end; end;
特别声明
禁止 win2003 (楚凡) QQ635887 使用本人修改的控件。
禁止 qiuyan81 (苦恋树) QQ46494153 使用本人修改的内容。
禁止 gfuchao QQ82715485 使用本人修改的内容。
禁止 supersk QQ未知,使用本人修改的版本。
禁止 yesin119 QQ未知,使用本人修改的版本。
禁止 263378440 使用本人修改的版本。
禁止 yanse 使用本人修改的版本。
禁止 www123 使用本人修改的版本
禁止 eliyh 使用本人修改的版本
禁止 ltshdp、ltsh(禁卫军) 使用本人修改的版本
禁止 zwjchinazwj (蒲石) 使用本人修改的版本
禁止 zhipu QQ:2001972 使用本人修改的版本
禁止 jackalan (nVicen) QQ:875271757 使用本人修改的版本
禁止 kencc2016 (小宇) QQ:2601759381 使用本人修改的版本
以上用户名均为 2CCC 的
禁止 QQ:191909837 使用本人修改的版本
禁止 QQ 81604691 使用本人修改的版本
当然,如果你们脸皮比较厚,就偷偷的用吧。
凡是想要骂我的,都可以偷偷的用,反正我是控制不了。
只要你们不鄙视自己就行。