NtQueryInformationProcess的ReturnLength参数记录如下:
ReturnLength
指向变量的指针,函数在该变量中返回所请求信息的大小。如果该函数成功,则为ProcessInformation参数所指向的写入缓冲区的信息大小,但是如果缓冲区太小,则为成功接收信息所需的最小缓冲区大小。
特别感兴趣的部分是:如果缓冲区太小,这是成功接收信息所需的缓冲区的最小大小。
我希望函数返回特定信息类所需的缓冲区大小。
我尝试了以下操作:
// for static linking
function NtQueryInformationProcess(ProcessHandle : THANDLE;
ProcessInformationClass : DWORD;
ProcessInformation : PPEB;
ProcessInformationLength : DWORD;
ReturnLength : PDWORD)
: NTSTATUS; stdcall; external ntdll;
// for dynamic linking
type
TNtQueryInformationProcess
= function (ProcessHandle : THANDLE;
ProcessInformationClass : DWORD;
ProcessInformation : PPEB;
ProcessInformationLength : DWORD;
ReturnLength : PDWORD) : NTSTATUS; stdcall;
var
ProcessHandle : THANDLE;
Peb : TPEB;
BufferSize : DWORD;
ReturnLength : DWORD;
NtResult : NTSTATUS;
NtdllHandle : HMODULE;
NtQueryInformationProcessPtr : TNtQueryInformationProcess;
begin
ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
GetCurrentProcessId());
ZeroMemory(@Peb, sizeof(Peb));
BufferSize := sizeof(PROCESS_BASIC_INFORMATION);
ReturnLength := 0;
NtResult := NtQueryInformationProcess(ProcessHandle,
ProcessBasicInformation,
@Peb,
BufferSize,
@ReturnLength);
writeln('NTSTATUS : ', IntToHex(NtResult, 0));
writeln('ReturnLength : ', ReturnLength);
// try calling the function by address
NtdllHandle := LoadLibrary('ntdll.dll');
pointer(NtQueryInformationProcessPtr) := GetProcAddress(NtdllHandle,
'NtQueryInformationProcess');
// reinitialize just to be safe
ZeroMemory(@Peb, sizeof(Peb));
BufferSize := sizeof(PROCESS_BASIC_INFORMATION);
ReturnLength := 0;
NtResult := NtQueryInformationProcessPtr(ProcessHandle,
ProcessBasicInformation,
@Peb,
BufferSize,
@ReturnLength);
writeln('NTSTATUS : ', IntToHex(NtResult, 0));
writeln('ReturnLength : ', ReturnLength);
writeln('program end.');
readln;
end.
当我将缓冲区的大小设置为ProcessBasicInformation的正确大小时,一切都按预期工作,但是,如果我将BufferSize设置为零(例如),希望该函数在ReturnLength变量中返回必要的缓冲区大小,则会得到返回0xC0000004(大小不匹配),这与预期的BUT相同,但ReturnLength变量未设置为成功获取ProcessBasicInformation所需的大小。
鉴于ReturnLength参数的描述,我希望API将ReturnLength设置为请求的信息类所需的任何大小。
我的问题是:代码中是否有错误或API是否无法按文档所述工作?还是我误解了ReturnLength参数的描述?
谢谢您的帮助。
答案 0 :(得分:3)
许多Win32 API旨在通过使用nil指针调用缓冲区大小并为ReturnLength指定0来告诉您正确的缓冲区大小。 Nt api的工作原理通常有所不同:
您只需将任意大小的缓冲区传递给它,它将通过返回STATUS_INFO_LENGTH_MISMATCH
来告诉您是否需要更多缓冲区。
我还建议您使用Jedi Api Library而不是自己翻译(Nt Api)函数/标题。
编辑:似乎您正在尝试读取ProcessBasicInformation类,该类具有固定的大小,并且不返回指向PEB的指针,而是指向PROCESS_BASIC_INFORMATION
结构/记录的指针。
以下是您要执行的操作的示例:
// uses JwaNative
function TDebugThread.ReadPEB: Boolean;
var
nts: NTSTATUS;
pbi: PROCESS_BASIC_INFORMATION;
dwBytes: DWORD;
begin
Result := False;
nts := NtQueryInformationProcess(pi.hProcess,
ProcessBasicInformation, @pbi, SizeOf(pbi), @dwBytes);
if nts <> STATUS_SUCCESS then
Exit;
New(PEB);
Result := ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, PEB, SizeOf(PEB^),
@dwBytes);
end;
在下面的示例中使用NtQuerySystemInformation
(完整的示例可以在我的GitHub repository上找到)。
{ TProcessList }
constructor TProcessList.Create(const AOwnsObjects: Boolean = True);
var
Current: PSystemProcesses;
SystemProcesses : PSystemProcesses;
dwSize: DWORD;
nts: NTSTATUS;
begin
inherited Create(AOwnsObjects);
dwSize := 200000;
SystemProcesses := AllocMem(dwSize);
nts := NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
SystemProcesses, dwSize, @dwSize);
while nts = STATUS_INFO_LENGTH_MISMATCH do
begin
ReAllocMem(SystemProcesses, dwSize);
nts := NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
SystemProcesses, dwSize, @dwSize);
end;
if nts = STATUS_SUCCESS then
begin
Current := SystemProcesses;
while True do
begin
Self.Add(TProcess.Create(Current^));
if Current^.NextEntryDelta = 0 then
Break;
Current := PSYSTEM_PROCESSES(DWORD_PTR(Current) + Current^.NextEntryDelta);
end;
end;
FreeMem(SystemProcesses);
end;
编辑:要回答@ScienceAmateur注释,以下测试代码的ReturnLength始终返回0:
uses
Windows,
System.SysUtils,
Rtti,
JwaNative,
JwaWinType,
JwaNtSecApi,
JwaNtStatus;
function NtStatusErrorMessage(const nts: NTSTATUS): String;
begin
Result := SysErrorMessage(LsaNtStatusToWinError(nts));
end;
var
nts: NTSTATUS;
pic: PROCESS_INFORMATION_CLASS;
Buffer: Pointer;
pil: DWORD;
ReturnLength: DWORD;
begin
for pic := Low(PROCESS_INFORMATION_CLASS) to High(PROCESS_INFORMATION_CLASS) do
begin
Buffer := nil;
pil := 0;
ReturnLength := 0;
nts := NtQueryInformationProcess(GetCurrentProcess,
ProcessBasicInformation, Buffer, pil, @ReturnLength);
WriteLn(Format('%s: returned 0x%.8x and ReturnLength: %d', [TRttiEnumerationType.GetName(pic), nts, ReturnLength]));
end;
WriteLn('Finished.');
if DebugHook <> 0 then
ReadLn;
输出:
ProcessBasicInformation: returned 0xC0000004 and ReturnLength: 0
ProcessQuotaLimits: returned 0xC0000004 and ReturnLength: 0
ProcessIoCounters: returned 0xC0000004 and ReturnLength: 0
ProcessVmCounters: returned 0xC0000004 and ReturnLength: 0
ProcessTimes: returned 0xC0000004 and ReturnLength: 0
ProcessBasePriority: returned 0xC0000004 and ReturnLength: 0
ProcessRaisePriority: returned 0xC0000004 and ReturnLength: 0
ProcessDebugPort: returned 0xC0000004 and ReturnLength: 0
ProcessExceptionPort: returned 0xC0000004 and ReturnLength: 0
ProcessAccessToken: returned 0xC0000004 and ReturnLength: 0
ProcessLdtInformation: returned 0xC0000004 and ReturnLength: 0
ProcessLdtSize: returned 0xC0000004 and ReturnLength: 0
ProcessDefaultHardErrorMode: returned 0xC0000004 and ReturnLength: 0
ProcessIoPortHandlers: returned 0xC0000004 and ReturnLength: 0
ProcessPooledUsageAndLimits: returned 0xC0000004 and ReturnLength: 0
ProcessWorkingSetWatch: returned 0xC0000004 and ReturnLength: 0
ProcessUserModeIOPL: returned 0xC0000004 and ReturnLength: 0
ProcessEnableAlignmentFaultFixup: returned 0xC0000004 and ReturnLength: 0
ProcessPriorityClass: returned 0xC0000004 and ReturnLength: 0
ProcessWx86Information: returned 0xC0000004 and ReturnLength: 0
ProcessHandleCount: returned 0xC0000004 and ReturnLength: 0
ProcessAffinityMask: returned 0xC0000004 and ReturnLength: 0
ProcessPriorityBoost: returned 0xC0000004 and ReturnLength: 0
ProcessDeviceMap: returned 0xC0000004 and ReturnLength: 0
ProcessSessionInformation: returned 0xC0000004 and ReturnLength: 0
ProcessForegroundInformation: returned 0xC0000004 and ReturnLength: 0
ProcessWow64Information: returned 0xC0000004 and ReturnLength: 0
ProcessImageFileName: returned 0xC0000004 and ReturnLength: 0
ProcessLUIDDeviceMapsEnabled: returned 0xC0000004 and ReturnLength: 0
ProcessBreakOnTermination: returned 0xC0000004 and ReturnLength: 0
ProcessDebugObjectHandle: returned 0xC0000004 and ReturnLength: 0
ProcessDebugFlags: returned 0xC0000004 and ReturnLength: 0
ProcessHandleTracing: returned 0xC0000004 and ReturnLength: 0
MaxProcessInfoClass: returned 0xC0000004 and ReturnLength: 0
答案 1 :(得分:0)
我对此功能有类似的问题。问题在于大小取决于位数,因为您需要提供指针的大小。
这些是函数的参数:
NTSTATUS(WINAPI*)(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength);
可以使用多个不同的PROCESSINFOCLASS调用NtQueryInformationProcess函数。然后,这确定ProcessInformation将是哪种类型。
它可以指向DWORD,例如,如果您使用ProcessDebugPort
调用它,或者指向PROCESS_BASIC_INFORMATION
,如果您使用ProcessBasicInformation
调用它。
如果在调用函数时提供sizeof(DWORD)
,则在32位环境下您的应用将可以运行。但是在64位中则不会。
这是因为在unsigned long
的32位DWORD中,其大小与unsigned long ptr
相同,但是在64位中,指针是64位。
因此,在这种情况下,您真正需要的大小是指针的大小,而不是类型,也不是您在调用中引用的变量。
基本上,此方法适用于所有地方:
DWORD debuggerPortNumber = 0;
NTSTATUS status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &debuggerPortNumber, sizeof(PDWORD), nullptr);
对于64位应用程序,这会中断:
DWORD debuggerPortNumber = 0;
NTSTATUS status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &debuggerPortNumber, sizeof(DWORD), nullptr);