Skip to content

Commit

Permalink
Add per-process thread listing
Browse files Browse the repository at this point in the history
  • Loading branch information
diversenok committed Aug 27, 2020
1 parent 6e92fba commit ac7d013
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 15 deletions.
8 changes: 6 additions & 2 deletions AppContainerKnows.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ uses
NtUiLib.Icons in 'NtUtils\NtUiLib\NtUiLib.Icons.pas',
LdrSnapshot in 'LdrSnapshot.pas',
PsSnapshot in 'PsSnapshot.pas',
PsSnapshotThread in 'PsSnapshotThread.pas',
WorkerThreads in 'WorkerThreads.pas',
VclEx.ListView in 'VclEx\VclEx.ListView.pas',
MainForm in 'UI\MainForm.pas' {FormMain},
MainForm.Logic in 'UI\MainForm.Logic.pas';
MainForm.Logic in 'UI\MainForm.Logic.pas',
ProcessForm in 'UI\ProcessForm.pas' {FormProcessInfo},
TdSnapshot in 'TdSnapshot.pas',
NtUtils.WinUser in 'NtUtils\NtUtils.WinUser.pas',
DelphiUtils.Events in 'NtUtils\DelphiUtils.Events.pas';

{$R *.res}

Expand Down
9 changes: 8 additions & 1 deletion AppContainerKnows.dproj
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,19 @@
<DCCReference Include="NtUtils\NtUiLib\NtUiLib.Icons.pas"/>
<DCCReference Include="LdrSnapshot.pas"/>
<DCCReference Include="PsSnapshot.pas"/>
<DCCReference Include="PsSnapshotThread.pas"/>
<DCCReference Include="WorkerThreads.pas"/>
<DCCReference Include="VclEx\VclEx.ListView.pas"/>
<DCCReference Include="UI\MainForm.pas">
<Form>FormMain</Form>
</DCCReference>
<DCCReference Include="UI\MainForm.Logic.pas"/>
<DCCReference Include="UI\ProcessForm.pas">
<Form>FormProcessInfo</Form>
<FormType>dfm</FormType>
</DCCReference>
<DCCReference Include="TdSnapshot.pas"/>
<DCCReference Include="NtUtils\NtUtils.WinUser.pas"/>
<DCCReference Include="NtUtils\DelphiUtils.Events.pas"/>
<RcCompile Include="NtUtils\NtUiLib\NtUiLib.Exceptions.Messages.rc">
<Form>NtUiLib.Exceptions.Messages.res</Form>
</RcCompile>
Expand Down
4 changes: 0 additions & 4 deletions PsSnapshot.pas
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ TProcessData = record
Flags: TProcessFlags;
end;

TThreadData = record
PID: TProcessId;
end;

TPsSnapshot = class
private
PidToIndex: TDictionary<TProcessId, Integer>;
Expand Down
6 changes: 3 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Here is my collection of tricks that allow a program to retrieve peculiar detail

### Features
- [x] Listing all processes (PID, image name, file location)
- [ ] Listing all threads per-process (TID only)
- [x] Listing threads per-process (TID, GUI flag)
- [ ] Listing loaded modules per-process (filename, sometimes base address, might be incomplete)
- [ ] Listing services within each svchost process

See the [releases](https://github.com/diversenok/Things-AppContainer-Knows/releases) page to experiment with it yourself.

### Screenshots
Here you can see a complete list of processes on the system from a low-privileged AppContainer sandbox.
Here you can see a complete list of processes on the system from a low-privileged AppContainer sandbox. For every process you can also list all of its threads.

![](https://user-images.githubusercontent.com/30962924/91461377-31aff480-e889-11ea-87b9-7907651da219.png)
![](https://user-images.githubusercontent.com/30962924/91478606-75fabf00-e8a0-11ea-8b2c-8e50fcf8543e.png)
77 changes: 77 additions & 0 deletions TdSnapshot.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
unit TdSnapshot;

interface

uses
Winapi.WinNt;

type
TThreadFlag = (tfGUI);
TThreadFlags = set of TThreadFlag;

TThreadData = record
TID: TThreadID;
Flags: TThreadFlags;
end;

// Find all threads that belong to this process
function SnapshotThreads(PID: TProcessId): TArray<TThreadData>;

implementation

uses
Ntapi.ntdef, Ntapi.ntpsapi, Ntapi.ntobapi, Ntapi.ntstatus,
NtUtils.Objects, NtUtils.WinUser;

function SnapshotThreads(PID: TProcessId): TArray<TThreadData>;
var
ProcessInfo, ThreadInfo: TObjectTypeInfo;
i: Integer;
CID: TClientId;
hThread: THandle;
ObjAttr: TObjectAttributes;
Status: NTSTATUS;
begin
Result := nil;

// Determine the total amount of processes and threads
if not NtxQueryTypeObject(NtCurrentProcess, ProcessInfo).IsSuccess then
ProcessInfo.Other.HighWaterNumberOfObjects := 300;

// Fallback to reasonable limits on failure
if not NtxQueryTypeObject(NtCurrentThread, ThreadInfo).IsSuccess then
ThreadInfo.Other.HighWaterNumberOfObjects := 2000;

InitializeObjectAttributes(ObjAttr);
CID.UniqueProcess := PID;

for i := 3 to ProcessInfo.Other.HighWaterNumberOfObjects +
ThreadInfo.Other.HighWaterNumberOfObjects do
begin
// Thread IDs are always divisable by 4
CID.UniqueThread := i shl 2;

// Thy this pair of PID + TID
Status := NtOpenThread(hThread, 0, ObjAttr, CID);

// NtOpenThread performs a lookup first, and since we supplied both
// process and thread IDs, the status indicates whether the thread
// belongs to the process or not.
if Status <> STATUS_INVALID_CID then
begin
// Save it
SetLength(Result, Succ(Length(Result)));
Result[High(Result)].TID := CID.UniqueThread;
end;

if NT_SUCCESS(Status) then
NtClose(hThread);
end;

// Mark all GUI threads
for i := 0 to High(Result) do
if UsrxIsGuiThread(Result[i].TID) then
Include(Result[i].Flags, tfGUI);
end;

end.
11 changes: 11 additions & 0 deletions UI/MainForm.dfm
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ object FormMain: TFormMain
GridLines = True
ReadOnly = True
RowSelect = True
PopupMenu = PopupMenu
TabOrder = 0
ViewStyle = vsReport
OnDblClick = lvProcessesDblClick
ColoringItems = True
end
end
Expand Down Expand Up @@ -104,4 +106,13 @@ object FormMain: TFormMain
end
end
end
object PopupMenu: TPopupMenu
Left = 375
Top = 115
object cmInspect: TMenuItem
Caption = 'Inspect'
Default = True
ShortCut = 13
end
end
end
17 changes: 15 additions & 2 deletions UI/MainForm.pas
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls,
VclEx.ListView, Vcl.StdCtrls, DelphiUiLib.HysteresisList, PsSnapshot,
Vcl.ExtCtrls, PsSnapshotThread, Vcl.AppEvnts, Vcl.Menus;
Vcl.ExtCtrls, WorkerThreads, Vcl.AppEvnts, Vcl.Menus, DelphiUtils.Events;

type
TFormMain = class(TForm)
Expand All @@ -19,24 +19,29 @@ TFormMain = class(TForm)
cmAC: TMenuItem;
cmLPAC: TMenuItem;
StatusBar: TStatusBar;
PopupMenu: TPopupMenu;
cmInspect: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure AppEventsMinimize(Sender: TObject);
procedure AppEventsRestore(Sender: TObject);
procedure AppEventsException(Sender: TObject; E: Exception);
procedure cmACClick(Sender: TObject);
procedure cmLPACClick(Sender: TObject);
procedure lvProcessesDblClick(Sender: TObject);
private
Processes: THysteresisList<TProcessData>;
FirstUpdate: Boolean;
SnapshottingThread: TPsSnapshotThread;
FClosingEvent: TNotifyEventHandler;
procedure ColorItem(const Item: TProcessData; Index: Integer);
procedure AtAddStart(const Item: TProcessData; Index: Integer);
procedure AtAddFinish(const Item: TProcessData; Index: Integer);
procedure AtRemoveStart(const Item: TProcessData; Index: Integer);
procedure AtRemoveFinish(const Item: TProcessData; Index: Integer);
public
procedure ConsumeSnapshot(Snapshot: TPsSnapshot);
property OnMainFormClosing: TNotifyEventHandler read FClosingEvent;
end;

var
Expand All @@ -46,7 +51,7 @@ implementation

uses
NtUiLib.Icons, NtUtils.Files, NtUtils.Threads,NtUtils, DelphiUiLib.Strings,
NtUiLib.Exceptions, NtUiLib.Exceptions.Dialog, MainForm.Logic;
NtUiLib.Exceptions, NtUiLib.Exceptions.Dialog, MainForm.Logic, ProcessForm;

{$R *.dfm}

Expand Down Expand Up @@ -196,6 +201,7 @@ procedure TFormMain.ConsumeSnapshot(Snapshot: TPsSnapshot);

procedure TFormMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
OnMainFormClosing.Invoke(Self);
NtxQueueApcThread(SnapshottingThread.Handle, RequestShutdown);
NtxResumeThread(SnapshottingThread.Handle);
SnapshottingThread.WaitFor;
Expand Down Expand Up @@ -229,4 +235,11 @@ procedure TFormMain.FormCreate(Sender: TObject);
cmLPAC.Enabled := False;
end;

procedure TFormMain.lvProcessesDblClick(Sender: TObject);
begin
if Assigned(lvProcesses.Selected) then
with Processes[lvProcesses.Selected.Index] do
TFormProcessInfo.CreateForProcess(Self, Data).Show;
end;

end.
60 changes: 60 additions & 0 deletions UI/ProcessForm.dfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
object FormProcessInfo: TFormProcessInfo
Left = 0
Top = 0
BorderIcons = [biSystemMenu, biMaximize]
Caption = 'Process Information'
ClientHeight = 305
ClientWidth = 288
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
KeyPreview = True
OldCreateOrder = False
OnClose = FormClose
OnCreate = FormCreate
OnKeyPress = FormKeyPress
PixelsPerInch = 96
TextHeight = 13
object PageControl: TPageControl
AlignWithMargins = True
Left = 3
Top = 3
Width = 282
Height = 299
ActivePage = ThreadsTab
Align = alClient
TabOrder = 0
object ThreadsTab: TTabSheet
Caption = 'Threads'
object lvThreads: TListViewEx
AlignWithMargins = True
Left = 3
Top = 3
Width = 268
Height = 265
Align = alClient
Columns = <
item
Caption = 'Thread ID'
Width = 120
end
item
Caption = 'Flags'
Width = 100
end>
DoubleBuffered = True
GridLines = True
MultiSelect = True
ReadOnly = True
RowSelect = True
ParentDoubleBuffered = False
TabOrder = 0
ViewStyle = vsReport
ColoringItems = True
end
end
end
end
Loading

0 comments on commit ac7d013

Please sign in to comment.