ZwQueryObject and the pipe bug, solution?

c++ / delphi package - dll injection and api hooking
Post Reply
softtouch
Posts: 105
Joined: Sat Jun 20, 2009 10:08 am
Contact:

ZwQueryObject and the pipe bug, solution?

Post by softtouch »

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.
mikec
Posts: 166
Joined: Sun Jul 16, 2006 9:01 pm
Location: UK

Post by mikec »

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
softtouch
Posts: 105
Joined: Sat Jun 20, 2009 10:08 am
Contact:

Post by softtouch »

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
This is mentioned many times, even here:

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.
maxi-hard
Posts: 1
Joined: Wed May 21, 2014 6:45 am

Re: ZwQueryObject and the pipe bug, solution?

Post by maxi-hard »

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

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.
Post Reply