Is there any way without a driver (I am using Delphi and cannot write driver) to prevent that the process hang when ZwQueryObject retrieves the filename based on a handle which is thehandle of a named pipe?
I am enumerating hndles to get the filenames of open files, which works fine, but soon I reach the handle of a named pipe, the ZwQueryObject does not return. This is a well known bug, and I was not able to find any solution.
ZwQueryObject and the pipe bug, solution?
This is mentioned many times, even here:mikec wrote:Again, can you show code or a demo???
Ive used NtQueryObject quite a bit and never had a problem - i've certainly never hurd of this bug - can you post a link to any informaiton about this?
Mike C
http://forum.sysinternals.com/forum_posts.asp?TID=10925
I encounter the same issue. It just freeze when it reaches a named pipe.
Anyway, I found a way around it with getfiletype before calling zwqueryobject and that works.
Re: ZwQueryObject and the pipe bug, solution?
Hello
I wrote a Delphi Program for killing processes with search by name in process objects. Program uses threads. It uses GetFileType() before ZwQueryObject(), so it's a solution for your problem. Hope it helps someone.
I am a noob at programming so any critics is welcome...
If you need full source mail me to ostrovskiy (at) tut (dot) by
I wrote a Delphi Program for killing processes with search by name in process objects. Program uses threads. It uses GetFileType() before ZwQueryObject(), so it's a solution for your problem. Hope it helps someone.
I am a noob at programming so any critics is welcome...
If you need full source mail me to ostrovskiy (at) tut (dot) by
Code: Select all
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, PsApi, ExtCtrls, Grids, IniFiles;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
Button2: TButton;
Label3: TLabel;
Label4: TLabel;
CheckBox1: TCheckBox;
GroupBox1: TGroupBox;
Edit1: TEdit;
StringGrid1: TStringGrid;
Edit2: TEdit;
CheckBox2: TCheckBox;
GroupBox2: TGroupBox;
SaveDialog1: TSaveDialog;
Button3: TButton;
Label5: TLabel;
Label6: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure StringGrid1DblClick(Sender: TObject);
procedure Edit1Change(Sender: TObject);
procedure Edit2Change(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
procedure KillProcess(PID: Cardinal);
{ Private declarations }
public
{ Public declarations }
end;
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure DoZwQueryObject;
end;
TThreadMain = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure DoMainProcedure;
end;
var
Form1: TForm1;
MyThread: TMyThread;
ThreadMain: TThreadMain;
process, h: cardinal;
p: pointer;
sdata: string;
_out : TStringList;
i: integer;
tm: string;
ArrPid: array of Cardinal;
ArrStr: array of String;
LengthArrays: integer;
IniFile: TIniFile;
const
SYSTEMHANDLEINFORMATION = 16;
OB_TYPE_FILE = 28;
type
PUnicodeString = ^TUnicodeString;
TUnicodeString = packed record
Length: Word;
MaximumLength: Word;
Buffer: PWideChar;
end;
TSYSTEM_HANDLE_INFORMATION = packed record
ProcessId: DWORD;
ObjectTypeNumber: Byte;
Flags: Byte;
Handle: Word;
pObject: Pointer;
GrantedAccess: DWORD;
end;
TSYSTEM_HANDLE_INFORMATION_EX = packed record
NumberOfHandles: DWORD;
Information: array [0 .. 0] of TSYSTEM_HANDLE_INFORMATION;
end;
PSYSTEM_HANDLE_INFORMATION_EX = ^TSYSTEM_HANDLE_INFORMATION_EX;
PObjectAttributes = ^TObjectAttributes;
TObjectAttributes = packed record
Length: Cardinal;
RootDirectory: THandle;
ObjectName: PUnicodeString;
Attributes: Cardinal;
SecurityDescriptor: Pointer;
SecurityQualityOfService: Pointer;
end;
FILE_BASIC_INFORMATION = Record
CreationTime:FileTime;
LastAccessTime:FileTime;
LastWriteTime:FileTime;
ChangeTime:FileTime;
FileAttributes:ULong;
end;
PFILE_BASIC_INFORMATION = ^FILE_BASIC_INFORMATION;
NTSTATUS = ULONG;
procedure RtlInitUnicodeString(DestinationString: PUnicodeString;
SourceString: LPWSTR);
stdcall; external 'ntdll.dll';
function NtQueryAttributesFile(ObjectAttributes: POBJECTATTRIBUTES;
FileInformation: PFILE_BASIC_INFORMATION): NTSTATUS;
stdcall; external 'ntdll.dll';
function ZwQuerySystemInformation(ASystemInformationClass: Cardinal;
ASystemInformation: Pointer;
ASystemInformationLength: Cardinal;
AReturnLength: PCardinal): Cardinal;
stdcall; external 'ntdll.dll';
function ZwQueryObject(ObjectHandle: THandle; ObjectInformationClass: Integer;
ObjectInformation: Pointer;
Length: DWORD; ReturnLength: PDWORD): cardinal;
stdcall; external 'ntdll.dll';
implementation
{$R *.dfm}
procedure TMyThread.Execute;
begin
DoZwQueryObject;
end;
procedure TMyThread.DoZwQueryObject;
var _h: cardinal;
_p: pointer;
_i: integer;
_tm: string;
obj_pipe: boolean;
begin
_h:= h; _p:= p; _i:= i; _tm:= tm;
if GetFileType(_h) = FILE_TYPE_PIPE then obj_pipe:= True else obj_pipe:= False;
ZwQueryObject(_h, 1, _p, max_path, nil);
if obj_pipe then begin sdata:= '[pipe] '+WideCharToString(TUnicodeString(_p^).Buffer);{showmessage('pipe '+_tm+' '+sdata);} end else
sdata:= WideCharToString(TUnicodeString(_p^).Buffer);
if Form1.CheckBox2.Checked then
_out.Add(FormatDateTime('hh:nn:ss',now)+' | object№ '+inttostr(_i)+' | '+_tm+' || '+sdata);
end;
procedure procThread;
begin
MyThread:=TMyThread.Create(False);
MyThread.FreeOnTerminate:=true;
if WaitForSingleObject(MyThread.Handle, 50)=STATUS_TIMEOUT then
begin
MyThread.Terminate;
TerminateThread(MyThread.Handle, 0);
FreeAndNil(MyThread);
end;
end;
function ProcessFileName(PID: DWORD): string;
var
ph, mh: THandle;
ModName: array[0..max_path] of char;
cm: Cardinal;
begin
if PID = 0 then
begin
Result := 'System Idle Process';
end
else if PID = 8 then
begin
Result := 'System';
end
//Gather info about all processes
else
begin
//get module name
ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
if ph > 0 then
begin
EnumProcessModules(ph, @mh, 4, cm);
GetModuleFileNameEx(ph, mh, ModName, sizeof(ModName));
Result := string(ModName)
end
else
Result := 'UNKNOWN';
CloseHandle(ph);
end;
end;
function GetInfoTable(ATableType: dword): Pointer;
var
mSize: dword;
mPtr: pointer;
St: cardinal;
begin
Result := nil;
mSize := $4000;
repeat
mPtr := VirtualAlloc(nil, mSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
if mPtr = nil then
Exit;
St := ZwQuerySystemInformation(ATableType, mPtr, mSize, nil);
if St = cardinal($C0000004) then
begin
VirtualFree(mPtr, 0, MEM_RELEASE);
mSize := mSize * 2;
end;
until St <> cardinal($C0000004);
if St = 0 then
Result := mPtr
else
VirtualFree(mPtr, 0, MEM_RELEASE);
end;
procedure InitializeObjectAttributes(var InitializedAttributes: TObjectAttributes;
ObjectName: PUnicodeString;
Attributes: ULONG;
RootDirectory: THandle;
SecurityDescriptor: Pointer;
SecurityQualityOfService : Pointer);
begin
InitializedAttributes.Length := SizeOf(TObjectAttributes);
InitializedAttributes.RootDirectory := RootDirectory;
InitializedAttributes.Attributes := Attributes;
InitializedAttributes.ObjectName := ObjectName;
InitializedAttributes.SecurityDescriptor := SecurityDescriptor;
InitializedAttributes.SecurityQualityOfService := SecurityQualityOfService;
end;
procedure GetHandles;
var
inf: PSYSTEM_HANDLE_INFORMATION_EX;
nameprocess: string;
EmptyName: TUNICODESTRING; ObjectAttr: TObjectAttributes;
BaseInfo: FILE_BASIC_INFORMATION;
begin
LengthArrays:= 0;
Form1.StringGrid1.RowCount:= 2;
Form1.StringGrid1.Rows[1].Clear;
_out := TStringList.Create;
if Form1.CheckBox2.Checked then
_out.Add('Program started at '+FormatDateTime('dd.mm.yyyy hh:nn:ss',now));
p := GetMemory(max_path);
ZeroMemory(p, max_path);
inf := GetInfoTable(SystemHandleInformation);
for i := 0 to inf.NumberOfHandles - 1 do
begin
if inf.Information[i].ObjectTypeNumber = OB_TYPE_FILE then
begin
tm := '';
process := OpenProcess(PROCESS_DUP_HANDLE, false, inf.Information[i].ProcessId);
tm := 'pid '+IntToStr(inf.Information[i].ProcessId) + ' | ';
DuplicateHandle(process, inf.Information[i].Handle, GetCurrentProcess,
@h, 0, false, DUPLICATE_SAME_ACCESS);
nameprocess:= ProcessFileName(inf.Information[i].ProcessId);
tm := tm + nameprocess;
sdata:= '';
RtlInitUnicodeString(@EmptyName, nil);
InitializeObjectAttributes(ObjectAttr, @EmptyName, $40, inf.Information[i].Handle, nil, nil);
NtQueryAttributesFile(@ObjectAttr, @BaseInfo);
procThread;
if (not Form1.CheckBox1.Checked) or
(AnsiPos(LowerCase(Form1.Edit1.Text), LowerCase(nameprocess))<>0) or
(AnsiPos(LowerCase(Form1.Edit1.Text), LowerCase(sdata))<>0) then
begin
Inc(LengthArrays);
SetLength(ArrPid, LengthArrays); SetLength(ArrStr, LengthArrays);
ArrPid[LengthArrays-1]:= inf.Information[i].ProcessId;
ArrStr[LengthArrays-1]:= {tm+' || '+} sdata;
Form1.StringGrid1.Cells[0,LengthArrays]:= inttostr(ArrPid[LengthArrays-1]);
Form1.StringGrid1.Cells[1,LengthArrays]:= nameprocess;
Form1.StringGrid1.Cells[2,LengthArrays]:= ArrStr[LengthArrays-1];
Form1.StringGrid1.RowCount:= Form1.StringGrid1.RowCount + 1;
end;
tm := '';
Form1.Label1.Caption:= nameprocess;
Form1.Label2.Caption:= sdata;
ZeroMemory(p, max_path);
CloseHandle(h);
CloseHandle(process);
end;
end;
if Form1.StringGrid1.RowCount > 2 then
Form1.StringGrid1.RowCount:= Form1.StringGrid1.RowCount - 1;
if Form1.CheckBox2.Checked then
begin
_out.Add('Program ended at '+FormatDateTime('dd.mm.yyyy hh:nn:ss',now));
DeleteFile(Form1.Edit2.Text);
_out.SaveToFile(Form1.Edit2.Text);
end;
_out.Free;
Form1.Button1.Enabled:= True;
Form1.CheckBox2.Enabled:= True;
Form1.Button2.Click;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Enabled:= False;
CheckBox2.Enabled:= False;
ThreadMain:=TThreadMain.Create(False);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Application.Terminate;
end;
procedure TThreadMain.Execute;
begin
DoMainProcedure;
end;
procedure TThreadMain.DoMainProcedure;
begin
GetHandles;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
FileName: string;
begin
FileName:= ExtractFilePath(Application.ExeName)+'settings.ini';
IniFile:= TIniFile.Create(FileName);
Form1.Edit1.Text:= IniFile.ReadString('ProcObj', 'Mask', 'explorer');
IniFile:= TIniFile.Create(FileName);
Form1.Edit2.Text:= IniFile.ReadString('Settings', 'Path', 'C:\log_file.txt');
Form1.StringGrid1.Cells[0,0]:= 'PID';
Form1.StringGrid1.Cells[1,0]:= 'process name';
Form1.StringGrid1.Cells[2,0]:= 'object/file name';
Form1.Label1.Caption:= '';
Form1.Label2.Caption:= '';
end;
procedure TForm1.KillProcess(PID: Cardinal);
var
exitcode:UINT;
x:THandle;
begin
x:=Openprocess(PROCESS_TERMINATE,false,PID);
if x <> 0 then begin
try
GetExitCodeProcess(x,ExitCode);
TerminateProcess(x,Exitcode);
finally
CloseHandle(x);
end;
end;
end;
procedure TForm1.StringGrid1DblClick(Sender: TObject);
begin
if Form1.Button1.Enabled then
case
MessageBox(Handle,PChar('Убить процесс с Pid: '+Form1.StringGrid1.Cells[0, Form1.StringGrid1.Row]+'?'),
PChar('Подтвердите действие'),MB_ICONINFORMATION+MB_OKCANCEL+MB_DEFBUTTON2) of
IDOK: KillProcess(StrToInt(Form1.StringGrid1.Cells[0, Form1.StringGrid1.Row]));
end;
end;
procedure TForm1.Edit1Change(Sender: TObject);
var
FileName: string;
begin
FileName:= ExtractFilePath(Application.ExeName)+'settings.ini';
IniFile:= TIniFile.Create(FileName);
IniFile.WriteString('ProcObj', 'Mask', Form1.Edit1.Text);
IniFile.Free;
end;
procedure TForm1.Edit2Change(Sender: TObject);
var
FileName: string;
begin
FileName:= ExtractFilePath(Application.ExeName)+'settings.ini';
IniFile:= TIniFile.Create(FileName);
IniFile.WriteString('Settings', 'Path', Form1.Edit2.Text);
IniFile.Free;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
if Form1.SaveDialog1.Execute then
Form1.Edit2.Text:= Form1.SaveDialog1.Files[0];
end;
end.