From 504ee67b17a01c3e62579f645e075e8ef60e76c3 Mon Sep 17 00:00:00 2001 From: wj32 Date: Mon, 12 Oct 2009 02:21:30 +0000 Subject: [PATCH] new ph-plugins branch git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2148 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- branches/ph-plugins/Assistant/Assistant.cs | 341 ++ .../ph-plugins/Assistant/Assistant.csproj | 87 + .../Assistant/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../Assistant/Properties/Resources.resx | 117 + .../Assistant/Properties/Settings.Designer.cs | 30 + .../Assistant/Properties/Settings.settings | 7 + branches/ph-plugins/Assistant/app.manifest | 22 + branches/ph-plugins/CHANGELOG.txt | 725 ++++ branches/ph-plugins/ExtraTools/ExtraTools.sln | 50 + .../ExtraTools/NtObjects/NtObjects.csproj | 92 + .../NtObjects/ObjectsWindow.Designer.cs | 163 + .../ExtraTools/NtObjects/ObjectsWindow.cs | 174 + .../ExtraTools/NtObjects/ObjectsWindow.resx | 1299 ++++++ .../ExtraTools/NtObjects/Program.cs | 20 + .../NtObjects/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../NtObjects/Properties/Resources.resx | 117 + .../NtObjects/Properties/Settings.Designer.cs | 30 + .../NtObjects/Properties/Settings.settings | 7 + .../ExtraTools/NtObjects/app.config | 3 + .../ExtraTools/NtProfiler/NtProfiler.csproj | 130 + .../NtProfiler/ProfilerWindow.Designer.cs | 304 ++ .../ExtraTools/NtProfiler/ProfilerWindow.cs | 267 ++ .../ExtraTools/NtProfiler/ProfilerWindow.resx | 944 +++++ .../ExtraTools/NtProfiler/Program.cs | 20 + .../NtProfiler/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../NtProfiler/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 30 + .../NtProfiler/Properties/Settings.settings | 7 + .../ExtraTools/NtProfiler/app.config | 3 + .../ExtraTools/NtProfiler/app.manifest | 22 + .../ProcessAnalyzer/MainWindow.Designer.cs | 353 ++ .../ExtraTools/ProcessAnalyzer/MainWindow.cs | 314 ++ .../ProcessAnalyzer/MainWindow.resx | 912 ++++ .../ProcessAnalyzer/ProcessAnalyzer.csproj | 98 + .../ExtraTools/ProcessAnalyzer/Program.cs | 20 + .../Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../ProcessAnalyzer/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 30 + .../Properties/Settings.settings | 7 + .../ExtraTools/ProcessAnalyzer/fake_base.ico | Bin 0 -> 47017 bytes .../SysCallHacker/EventProperties.Designer.cs | 312 ++ .../SysCallHacker/EventProperties.cs | 154 + .../SysCallHacker/EventProperties.resx | 120 + .../ExtraTools/SysCallHacker/LogEvent.cs | 29 + .../SysCallHacker/MainWindow.Designer.cs | 241 ++ .../ExtraTools/SysCallHacker/MainWindow.cs | 233 + .../ExtraTools/SysCallHacker/MainWindow.resx | 953 +++++ .../ExtraTools/SysCallHacker/Program.cs | 20 + .../SysCallHacker/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../SysCallHacker/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 30 + .../Properties/Settings.settings | 7 + .../SysCallHacker/SysCallHacker.csproj | 102 + branches/ph-plugins/HACKING.txt | 19 + .../ph-plugins/KProcessHacker/HACKING.txt | 67 + .../KProcessHacker/amd64/kprocesshacker.sys | Bin 0 -> 60416 bytes branches/ph-plugins/KProcessHacker/auto.cmd | 7 + .../ph-plugins/KProcessHacker/autoreload.cmd | 2 + branches/ph-plugins/KProcessHacker/handle.c | 355 ++ branches/ph-plugins/KProcessHacker/hook.c | 408 ++ .../KProcessHacker/i386/kprocesshacker.sys | Bin 0 -> 57856 bytes .../ph-plugins/KProcessHacker/include/debug.h | 35 + .../ph-plugins/KProcessHacker/include/ex.h | 262 ++ .../KProcessHacker/include/handle.h | 78 + .../KProcessHacker/include/handlep.h | 144 + .../ph-plugins/KProcessHacker/include/hook.h | 108 + .../ph-plugins/KProcessHacker/include/io.h | 34 + .../ph-plugins/KProcessHacker/include/ke.h | 95 + .../ph-plugins/KProcessHacker/include/kph.h | 503 +++ .../KProcessHacker/include/kprocesshacker.h | 165 + .../ph-plugins/KProcessHacker/include/mm.h | 37 + .../ph-plugins/KProcessHacker/include/ob.h | 168 + .../KProcessHacker/include/protect.h | 95 + .../ph-plugins/KProcessHacker/include/ps.h | 151 + .../ph-plugins/KProcessHacker/include/ref.h | 113 + .../ph-plugins/KProcessHacker/include/refp.h | 137 + .../ph-plugins/KProcessHacker/include/se.h | 55 + .../ph-plugins/KProcessHacker/include/sync.h | 320 ++ .../KProcessHacker/include/sysservice.h | 278 ++ .../KProcessHacker/include/sysservicedata.h | 166 + .../KProcessHacker/include/sysservicep.h | 414 ++ .../ph-plugins/KProcessHacker/include/test.h | 30 + .../ph-plugins/KProcessHacker/include/trace.h | 188 + .../ph-plugins/KProcessHacker/include/types.h | 7 + .../ph-plugins/KProcessHacker/include/util.h | 133 + .../KProcessHacker/include/version.h | 241 ++ .../ph-plugins/KProcessHacker/include/zw.h | 45 + branches/ph-plugins/KProcessHacker/io.c | 284 ++ branches/ph-plugins/KProcessHacker/kph.c | 373 ++ .../KProcessHacker/kprocesshacker.c | 2367 +++++++++++ branches/ph-plugins/KProcessHacker/makefile | 1 + branches/ph-plugins/KProcessHacker/mm.c | 703 +++ branches/ph-plugins/KProcessHacker/ob.c | 847 ++++ branches/ph-plugins/KProcessHacker/protect.c | 457 ++ branches/ph-plugins/KProcessHacker/ps.c | 1217 ++++++ branches/ph-plugins/KProcessHacker/ref.c | 574 +++ .../ph-plugins/KProcessHacker/resource.rc | 53 + branches/ph-plugins/KProcessHacker/se.c | 102 + branches/ph-plugins/KProcessHacker/sources | 29 + branches/ph-plugins/KProcessHacker/sync.c | 312 ++ .../ph-plugins/KProcessHacker/sysservice.c | 2050 +++++++++ .../KProcessHacker/sysservicedata.c | 513 +++ branches/ph-plugins/KProcessHacker/test.c | 71 + branches/ph-plugins/KProcessHacker/trace.c | 344 ++ branches/ph-plugins/KProcessHacker/util.c | 115 + branches/ph-plugins/KProcessHacker/version.c | 536 +++ branches/ph-plugins/LICENSE.txt | 674 +++ .../NProcessHacker/NProcessHacker.sln | 52 + .../NProcessHacker/NProcessHacker.vcproj | 410 ++ .../NProcessHackerHook.vcproj | 334 ++ .../NProcessHackerHook/nphhook.c | 23 + .../NProcessHacker/Release/NProcessHacker.dll | Bin 0 -> 52736 bytes .../NProcessHacker/Test/Test.vcproj | 343 ++ .../ph-plugins/NProcessHacker/Test/test.c | 29 + branches/ph-plugins/NProcessHacker/hook.c | 96 + branches/ph-plugins/NProcessHacker/hook.h | 93 + branches/ph-plugins/NProcessHacker/kph.c | 880 ++++ branches/ph-plugins/NProcessHacker/kph.h | 289 ++ branches/ph-plugins/NProcessHacker/kphhook.c | 180 + branches/ph-plugins/NProcessHacker/kphhook.h | 32 + .../ph-plugins/NProcessHacker/nativedefs.h | 175 + branches/ph-plugins/NProcessHacker/nph.c | 85 + branches/ph-plugins/NProcessHacker/nph.h | 70 + branches/ph-plugins/NProcessHacker/obj.c | 157 + branches/ph-plugins/NProcessHacker/obj.h | 46 + branches/ph-plugins/NProcessHacker/process.c | 117 + branches/ph-plugins/NProcessHacker/process.h | 53 + .../ph-plugins/NProcessHacker/resource.rc | 53 + branches/ph-plugins/NProcessHacker/secedit.c | 24 + branches/ph-plugins/NProcessHacker/secedit.h | 48 + branches/ph-plugins/NProcessHacker/verify.c | 200 + branches/ph-plugins/NProcessHacker/verify.h | 88 + .../x64/Release/NProcessHacker.dll | Bin 0 -> 53248 bytes .../ProcessHacker.Common/BaseConverter.cs | 176 + .../ProcessHacker.Common/ByteStreamReader.cs | 118 + .../ProcessHacker.Common/CircularBuffer.cs | 369 ++ .../ProcessHacker.Common/Delegates.cs | 19 + .../ProcessHacker.Common/DeltaManager.cs | 168 + .../ProcessHacker.Common/EnumComparer.cs | 124 + .../ExtensionAttribute.cs | 6 + .../ProcessHacker.Common/FreeList.cs | 126 + .../ProcessHacker.Common/HistoryManager.cs | 104 + .../ProcessHacker.Common/IResettable.cs | 11 + .../ProcessHacker.Common/IdGenerator.cs | 116 + .../Linq/GroupedEnumerable.cs | 43 + .../ProcessHacker.Common/Linq/Grouping.cs | 19 + .../ProcessHacker.Common/Linq/IGrouping.cs | 11 + .../ProcessHacker.Common/Linq/ILookup.cs | 14 + .../Linq/IOrderedEnumerable.cs | 16 + .../ProcessHacker.Common/Linq/License.txt | 20 + .../ProcessHacker.Common/Linq/Lookup.cs | 70 + .../Linq/OrderByEnumerable.cs | 58 + .../Aggregation Operators - Average.cs | 147 + .../Aggregation Operators - Sum.cs | 162 + .../Query Operators/Aggregation Operators.cs | 149 + .../Query Operators/Conversion Operators.cs | 111 + .../Linq/Query Operators/Element Operators.cs | 157 + .../Query Operators/Enumerable Private.cs | 19 + .../Query Operators/Filtering Operators.cs | 118 + .../Query Operators/Generation Operators.cs | 26 + .../Query Operators/Grouping Operators.cs | 41 + .../Linq/Query Operators/Joining Operators.cs | 53 + .../Linq/Query Operators/Misc Operators.cs | 46 + .../Query Operators/Ordering Operators.cs | 73 + .../Query Operators/Projection Operators.cs | 75 + .../Linq/Query Operators/Quantifiers.cs | 66 + .../Linq/Query Operators/Set Operators.cs | 63 + .../Linq/ThenByEnumerable.cs | 41 + .../ProcessHacker.Common/Logging.cs | 78 + .../ProcessHacker.Common/Messaging/Message.cs | 32 + .../Messaging/MessageQueue.cs | 64 + .../Messaging/MessageQueueListener.cs | 40 + .../Objects/BaseObject.cs | 504 +++ .../Objects/DelayedReleasePool.cs | 209 + .../Objects/HandleTable.cs | 301 ++ .../Objects/IRefCounted.cs | 55 + .../Objects/SecuredHandleTable.cs | 202 + .../ProcessHacker.Common.csproj | 129 + .../Properties/AssemblyInfo.cs | 36 + .../Settings/SettingDefaultAttribute.cs | 21 + .../Settings/SettingsBase.cs | 33 + .../Settings/SettingsManager.cs | 36 + .../Settings/SettingsStore.cs | 12 + .../Threading/FastMutex.cs | 92 + .../Threading/FastQueue.cs | 78 + .../Threading/FastStack.cs | 109 + .../Threading/RundownProtection.cs | 101 + .../Threading/SemaphorePair.cs | 60 + .../Threading/SpinLock.cs | 115 + .../Threading/ThreadTask.cs | 89 + .../Threading/WaitableQueue.cs | 112 + .../ProcessHacker.Common/Tokenizer.cs | 237 ++ .../Ui/ColumnHeaderExtensions.cs | 75 + .../Ui/SortedListViewComparer.cs | 317 ++ .../ph-plugins/ProcessHacker.Common/Utils.cs | 1439 +++++++ .../ProcessHacker.Common/WeakReference.cs | 39 + .../ProcessHacker.Common/WorkQueue.cs | 598 +++ .../ProcessHacker.Common/app.config | 3 + .../ProcessHacker.Native/Api/Enums.cs | 1072 +++++ .../ProcessHacker.Native/Api/Extensions.cs | 504 +++ .../ProcessHacker.Native/Api/Functions.cs | 2816 ++++++++++++ .../ProcessHacker.Native/Api/HResult.cs | 109 + .../Api/ISecurityInformation.cs | 80 + .../ProcessHacker.Native/Api/LsaEnums.cs | 120 + .../ProcessHacker.Native/Api/LsaFunctions.cs | 501 +++ .../ProcessHacker.Native/Api/LsaStructs.cs | 114 + .../Api/NativeDefinitions.cs | 105 + .../ProcessHacker.Native/Api/NativeEnums.cs | 2146 ++++++++++ .../Api/NativeFunctions.cs | 3771 +++++++++++++++++ .../ProcessHacker.Native/Api/NativeStructs.cs | 3206 ++++++++++++++ .../ProcessHacker.Native/Api/NtStatus.cs | 379 ++ .../ProcessHacker.Native/Api/Structs.cs | 834 ++++ .../ProcessHacker.Native/Api/Win32.cs | 350 ++ .../ProcessHacker.Native/Api/Win32Error.cs | 132 + .../ProcessHacker.Native/Cryptography.cs | 241 ++ .../Debugging/DebugBuffer.cs | 268 ++ .../Debugging/HeapInformation.cs | 63 + .../Debugging/LockInformation.cs | 57 + .../Debugging/ModuleInformation.cs | 54 + .../ProcessHacker.Native/FileUtils.cs | 161 + .../ProcessHacker.Native/ILoadedModule.cs | 16 + .../Image/ImageDirectoryEntry.cs | 26 + .../Image/ImageExports.cs | 171 + .../Image/ImageImports.cs | 200 + .../ProcessHacker.Native/Image/MappedImage.cs | 314 ++ .../ImpersonationContext.cs | 26 + .../ProcessHacker.Native/IntPtrExtensions.cs | 246 ++ .../ProcessHacker.Native/Io/BeepDevice.cs | 47 + .../ProcessHacker.Native/Io/MountManager.cs | 191 + .../Ipc/IpcCircularBuffer.cs | 223 + .../ProcessHacker.Native/KProcessHacker.cs | 1258 ++++++ .../ph-plugins/ProcessHacker.Native/Loader.cs | 40 + .../ProcessHacker.Native/Lpc/Port.cs | 23 + .../ProcessHacker.Native/Lpc/PortMessage.cs | 128 + .../Memory/AlignedMemoryAlloc.cs | 37 + .../ProcessHacker.Native/Memory/Heap.cs | 134 + .../Memory/LocalMemoryAlloc.cs | 51 + .../Memory/LsaMemoryAlloc.cs | 79 + .../Memory/MemoryAlloc.cs | 133 + .../Memory/MemoryRegion.cs | 367 ++ .../Memory/MemoryRegionStream.cs | 114 + .../Memory/PebMemoryAlloc.cs | 60 + .../Memory/PhysicalPages.cs | 132 + .../Memory/PhysicalPagesMapping.cs | 23 + .../Memory/PinnedObject.cs | 35 + .../ProcessHacker.Native/Memory/Section.cs | 167 + .../Memory/SectionView.cs | 78 + .../Memory/VirtualMemoryAlloc.cs | 25 + .../Memory/WtsMemoryAlloc.cs | 67 + .../ProcessHacker.Native/NProcessHacker.cs | 89 + .../ProcessHacker.Native/NativeBitmap.cs | 236 ++ .../ProcessHacker.Native/NativeObject.cs | 182 + .../ProcessHacker.Native/NativeTypeFactory.cs | 607 +++ .../ProcessHacker.Native/NativeUtils.cs | 229 + .../ProcessHacker.Native/OSVersion.cs | 233 + .../Objects/DebugObjectHandle.cs | 134 + .../Objects/DesktopHandle.cs | 77 + .../Objects/DirectoryHandle.cs | 181 + .../Objects/DriverHandle.cs | 111 + .../Objects/EnlistmentHandle.cs | 217 + .../Objects/EnvironmentBlock.cs | 200 + .../Objects/EventHandle.cs | 159 + .../Objects/EventPairHandle.cs | 191 + .../Objects/FileHandle.cs | 1813 ++++++++ .../Objects/ISynchronizable.cs | 42 + .../Objects/IWithToken.cs | 49 + .../Objects/IoCompletionHandle.cs | 122 + .../Objects/JobObjectHandle.cs | 226 + .../ProcessHacker.Native/Objects/KeyHandle.cs | 145 + .../Objects/KeyedEventHandle.cs | 167 + .../Objects/LsaAccountHandle.cs | 185 + .../ProcessHacker.Native/Objects/LsaHandle.cs | 82 + .../Objects/LsaPolicyHandle.cs | 504 +++ .../Objects/MailslotHandle.cs | 113 + .../Objects/MutantHandle.cs | 132 + .../Objects/NamedPipeHandle.cs | 400 ++ .../Objects/NativeHandle.cs | 663 +++ .../Objects/PortComHandle.cs | 168 + .../Objects/PortHandle.cs | 177 + .../Objects/PrivateNamespaceHandle.cs | 117 + .../Objects/ProcessHandle.cs | 2917 +++++++++++++ .../Objects/ProfileHandle.cs | 141 + .../Objects/RemoteHandle.cs | 66 + .../Objects/RemoteTokenHandle.cs | 63 + .../Objects/ResourceManagerHandle.cs | 184 + .../Objects/SectionHandle.cs | 257 ++ .../Objects/SemaphoreHandle.cs | 123 + .../Objects/ServiceBaseHandle.cs | 46 + .../Objects/ServiceHandle.cs | 301 ++ .../Objects/ServiceManagerHandle.cs | 77 + .../Objects/SymbolicLinkHandle.cs | 118 + .../Objects/TerminalServerHandle.cs | 535 +++ .../Objects/ThreadHandle.cs | 1355 ++++++ .../Objects/TimerHandle.cs | 230 + .../ProcessHacker.Native/Objects/TmHandle.cs | 220 + .../Objects/TokenHandle.cs | 595 +++ .../Objects/TokenWithLinkedToken.cs | 55 + .../Objects/TransactionHandle.cs | 213 + .../Objects/UserHandle.cs | 50 + .../Objects/WindowHandle.cs | 200 + .../Objects/WindowStationHandle.cs | 66 + .../ProcessHacker.Native.csproj | 262 ++ .../Properties/AssemblyInfo.cs | 36 + .../Security/AccessControl/Ace.cs | 130 + .../Security/AccessControl/Acl.cs | 322 ++ .../Security/AccessControl/KnownAce.cs | 93 + .../AccessControl/SecurityDescriptor.cs | 599 +++ .../Security/AccessControl/SecurityEditor.cs | 329 ++ .../Security/DebugObjectAccess.cs | 15 + .../Security/DesktopAccess.cs | 27 + .../Security/DirectoryAccess.cs | 15 + .../Security/EnlistmentAccess.cs | 22 + .../Security/EventAccess.cs | 13 + .../Security/EventPairAccess.cs | 10 + .../Security/FileAccess.cs | 39 + .../Security/FltPortAccess.cs | 11 + .../Security/ISecurable.cs | 14 + .../Security/IoCompletionAccess.cs | 14 + .../Security/JobObjectAccess.cs | 15 + .../Security/KeyAccess.cs | 23 + .../Security/KeyedEventAccess.cs | 12 + .../Security/LsaAccountAccess.cs | 19 + .../Security/LsaPolicyAccess.cs | 31 + .../Security/LsaSecretAccess.cs | 15 + .../Security/LsaTrustedAccess.cs | 22 + .../Security/MutantAccess.cs | 12 + .../Security/ObjectTypeAccess.cs | 11 + .../Security/PortAccess.cs | 12 + .../Security/Privilege.cs | 274 ++ .../Security/PrivilegeSet.cs | 188 + .../Security/ProcessAccess.cs | 25 + .../Security/ProfileAccess.cs | 11 + .../Security/ResourceManagerAccess.cs | 24 + .../Security/ScManagerAccess.cs | 17 + .../Security/SectionAccess.cs | 16 + .../Security/SemaphoreAccess.cs | 13 + .../Security/ServiceAccess.cs | 20 + .../ProcessHacker.Native/Security/Sid.cs | 435 ++ .../Security/StandardRights.cs | 27 + .../Security/SymbolicLinkAccess.cs | 13 + .../Security/ThreadAccess.cs | 23 + .../Security/TimerAccess.cs | 13 + .../ProcessHacker.Native/Security/TmAccess.cs | 21 + .../Security/TokenAccess.cs | 24 + .../Security/TransactionAccess.cs | 26 + .../Security/WindowStationAccess.cs | 27 + .../SsLogging/FilterType.cs | 23 + .../SsLogging/SsClientId.cs | 21 + .../ProcessHacker.Native/SsLogging/SsData.cs | 15 + .../ProcessHacker.Native/SsLogging/SsEvent.cs | 62 + .../SsLogging/SsHandle.cs | 51 + .../SsLogging/SsLogger.cs | 341 ++ .../SsLogging/SsObjectAttributes.cs | 41 + .../SsLogging/SsSimple.cs | 21 + .../SsLogging/SsUnicodeString.cs | 38 + .../Symbols/SymbolInformation.cs | 79 + .../Symbols/SymbolProvider.cs | 512 +++ .../Symbols/SymbolResolveLevel.cs | 53 + .../Threading/CurrentThread.cs | 70 + .../ProcessHacker.Native/Threading/Event.cs | 139 + .../Threading/EventPair.cs | 109 + .../Threading/KeyedEvent.cs | 122 + .../ProcessHacker.Native/Threading/Mutant.cs | 102 + .../Threading/NativeThreadPool.cs | 39 + .../Threading/Semaphore.cs | 115 + .../ProcessHacker.Native/Threading/Timer.cs | 216 + .../ProcessHacker.Native/Threading/Waiter.cs | 367 ++ .../Ui/ChooseProcessDialog.Designer.cs | 156 + .../Ui/ChooseProcessDialog.cs | 122 + .../Ui/ChooseProcessDialog.resx | 155 + .../Ui/HandlePropertiesWindow.Designer.cs | 327 ++ .../Ui/HandlePropertiesWindow.cs | 151 + .../Ui/HandlePropertiesWindow.resx | 120 + .../ProcessHacker.Native/Windows.cs | 1030 +++++ .../ProcessHacker.Native/WindowsException.cs | 131 + .../ProcessHacker.Native/app.config | 3 + branches/ph-plugins/ProcessHacker.sln | 47 + .../ProcessHacker/Build/7za/7za.exe | Bin 0 -> 536064 bytes .../ProcessHacker/Build/7za/copying.txt | 504 +++ .../ProcessHacker/Build/7za/license.txt | 30 + .../Build/Installer/Custom_Messages.iss | 81 + .../Build/Installer/Icons/ProcessHacker.ico | Bin 0 -> 29926 bytes .../Installer/Icons/ProcessHackerLarge.bmp | Bin 0 -> 206038 bytes .../Installer/Icons/ProcessHackerSmall.bmp | Bin 0 -> 9270 bytes .../Build/Installer/Icons/uninstall.ico | Bin 0 -> 2238 bytes .../Build/Installer/Languages/Greek.isl | 366 ++ .../Installer/Process_Hacker_installer.iss | 385 ++ .../Build/Installer/Services.iss | 207 + .../ph-plugins/ProcessHacker/Build/ngen.cmd | 1 + .../ProcessHacker/Build/release.cmd | 98 + .../ProcessHacker/Build/testsign.cmd | 3 + .../ProcessHacker/Common/Extensions.cs | 87 + .../ProcessHacker/Common/PhUtils.cs | 528 +++ .../Be.Windows.Forms.HexBox/ByteCollection.cs | 128 + .../Be.Windows.Forms.HexBox/DataBlock.cs | 42 + .../Be.Windows.Forms.HexBox/DataMap.cs | 318 ++ .../Design/HexFontEditor.cs | 70 + .../DynamicByteProvider.cs | 175 + .../DynamicFileByteProvider.cs | 548 +++ .../FileByteProvider.cs | 274 ++ .../Be.Windows.Forms.HexBox/FileDataBlock.cs | 96 + .../Be.Windows.Forms.HexBox/HexBox.bmp | Bin 0 -> 246 bytes .../Be.Windows.Forms.HexBox/HexBox.cs | 3454 +++++++++++++++ .../Be.Windows.Forms.HexBox/HexBox.resx | 42 + .../Be.Windows.Forms.HexBox/HexBox.snk | Bin 0 -> 596 bytes .../Be.Windows.Forms.HexBox/IByteProvider.cs | 75 + .../MemoryDataBlock.cs | 87 + .../Be.Windows.Forms.HexBox/NativeMethods.cs | 29 + .../Components/ColorModifier.Designer.cs | 61 + .../ProcessHacker/Components/ColorModifier.cs | 83 + .../Components/ColorModifier.resx | 120 + .../EventPairProperties.Designer.cs | 78 + .../Components/EventPairProperties.cs | 44 + .../Components/EventPairProperties.resx | 120 + .../Components/EventProperties.Designer.cs | 155 + .../Components/EventProperties.cs | 76 + .../Components/EventProperties.resx | 120 + .../Components/ExtendedListView.cs | 454 ++ .../Components/ExtendedTreeView.cs | 49 + .../Components/FileNameBox.Designer.cs | 96 + .../ProcessHacker/Components/FileNameBox.cs | 92 + .../ProcessHacker/Components/FileNameBox.resx | 123 + .../Components/HandleList.Designer.cs | 164 + .../ProcessHacker/Components/HandleList.cs | 620 +++ .../ProcessHacker/Components/HandleList.resx | 126 + .../Components/Indicator.Designer.cs | 47 + .../ProcessHacker/Components/Indicator.cs | 203 + .../ProcessHacker/Components/Indicator.resx | 120 + .../Components/JobProperties.Designer.cs | 815 ++++ .../ProcessHacker/Components/JobProperties.cs | 234 + .../Components/JobProperties.resx | 123 + .../Components/MemoryList.Designer.cs | 203 + .../ProcessHacker/Components/MemoryList.cs | 605 +++ .../ProcessHacker/Components/MemoryList.resx | 126 + .../Components/ModuleList.Designer.cs | 240 ++ .../ProcessHacker/Components/ModuleList.cs | 632 +++ .../ProcessHacker/Components/ModuleList.resx | 126 + .../Components/MutantProperties.Designer.cs | 125 + .../Components/MutantProperties.cs | 52 + .../Components/MutantProperties.resx | 120 + .../Components/NetworkList.Designer.cs | 137 + .../ProcessHacker/Components/NetworkList.cs | 496 +++ .../ProcessHacker/Components/NetworkList.resx | 155 + .../ProcessHacker/Components/NodePlotter.cs | 98 + .../Components/Plotter.Designer.cs | 63 + .../ProcessHacker/Components/Plotter.cs | 551 +++ .../ProcessHacker/Components/Plotter.resx | 123 + .../Components/ProcessStatistics.Designer.cs | 852 ++++ .../Components/ProcessStatistics.cs | 170 + .../Components/ProcessStatistics.resx | 120 + .../Components/ProcessTree/ProcessNode.cs | 699 +++ .../ProcessTree/ProcessToolTipProvider.cs | 234 + .../ProcessTree/ProcessTree.Designer.cs | 1035 +++++ .../Components/ProcessTree/ProcessTree.cs | 485 +++ .../Components/ProcessTree/ProcessTree.resx | 120 + .../ProcessTree/ProcessTreeModel.cs | 380 ++ .../RestartRecoveryLib/RecoveryData.cs | 73 + .../RestartRecoveryLib/RecoverySettings.cs | 92 + .../RestartRecoveryInterop.cs | 149 + .../RestartRecoveryManager.cs | 229 + .../RestartRecoveryLib/RestartSettings.cs | 79 + .../Components/SectionProperties.Designer.cs | 99 + .../Components/SectionProperties.cs | 29 + .../Components/SectionProperties.resx | 120 + .../SemaphoreProperties.Designer.cs | 127 + .../Components/SemaphoreProperties.cs | 63 + .../Components/SemaphoreProperties.resx | 120 + .../Components/ServiceList.Designer.cs | 131 + .../ProcessHacker/Components/ServiceList.cs | 298 ++ .../ProcessHacker/Components/ServiceList.resx | 214 + .../Components/ServiceProperties.Designer.cs | 459 ++ .../Components/ServiceProperties.cs | 495 +++ .../Components/ServiceProperties.resx | 123 + .../ProcessHacker/Components/SplitButton.cs | 855 ++++ .../Components/StructViewer.Designer.cs | 164 + .../ProcessHacker/Components/StructViewer.cs | 215 + .../Components/StructViewer.resx | 129 + .../Components/TargetWindowButton.cs | 192 + .../Components/TaskDialog/ActiveTaskDialog.cs | 428 ++ .../Components/TaskDialog/TaskDialog.cs | 1255 ++++++ .../TaskDialog/TaskDialogCommonDialog.cs | 104 + .../TaskDialog/TaskDialogNotificationArgs.cs | 111 + .../TaskDialog/UnsafeNativeMethods.cs | 446 ++ .../Components/TaskbarLib/Interop/COMTypes.cs | 284 ++ .../Components/TaskbarLib/Interop/Interop.cs | 257 ++ .../TaskbarLib/JumpLists/JumpListImpl.cs | 456 ++ .../TaskbarLib/JumpLists/JumpListManager.cs | 567 +++ .../ThumbnailButtons/ThumbButton.cs | 162 + .../ThumbnailButtons/ThumbButtonManager.cs | 173 + .../Components/TaskbarLib/Windows7Taskbar.cs | 401 ++ .../Components/ThreadList.Designer.cs | 592 +++ .../ProcessHacker/Components/ThreadList.cs | 1364 ++++++ .../ProcessHacker/Components/ThreadList.resx | 129 + .../Components/TimerProperties.Designer.cs | 123 + .../Components/TimerProperties.cs | 57 + .../Components/TimerProperties.resx | 123 + .../Components/TmRmProperties.Designer.cs | 101 + .../Components/TmRmProperties.cs | 40 + .../Components/TmRmProperties.resx | 120 + .../Components/TmTmProperties.Designer.cs | 101 + .../Components/TmTmProperties.cs | 41 + .../Components/TmTmProperties.resx | 120 + .../Components/TokenGroupsList.Designer.cs | 80 + .../Components/TokenGroupsList.cs | 108 + .../Components/TokenGroupsList.resx | 120 + .../Components/TokenProperties.Designer.cs | 659 +++ .../Components/TokenProperties.cs | 412 ++ .../Components/TokenProperties.resx | 126 + .../Components/UtilitiesButton.Designer.cs | 240 ++ .../Components/UtilitiesButton.cs | 192 + .../Components/UtilitiesButton.resx | 126 + .../VerticleProgressBar.Designer.cs | 46 + .../Components/VerticleProgressBar.cs | 61 + .../Components/VerticleProgressBar.resx | 120 + .../Components/VistaMenu/OwnerDrawnMenu.cs | 235 + .../Components/VistaMenu/VistaMenu.cs | 411 ++ .../Components/VistaSearchBox.cs | 343 ++ .../Components/VistaSearchBox.designer.cs | 96 + .../Components/VistaSearchBox.resx | 132 + .../Forms/AboutWindow.Designer.cs | 540 +++ .../ProcessHacker/Forms/AboutWindow.cs | 145 + .../ProcessHacker/Forms/AboutWindow.resx | 120 + .../Forms/ChooseColumnsWindow.Designer.cs | 114 + .../Forms/ChooseColumnsWindow.cs | 101 + .../Forms/ChooseColumnsWindow.resx | 120 + .../Forms/ComboBoxPickerWindow.Designer.cs | 111 + .../Forms/ComboBoxPickerWindow.cs | 77 + .../Forms/ComboBoxPickerWindow.resx | 120 + .../Forms/CreateServiceWindow.Designer.cs | 247 ++ .../Forms/CreateServiceWindow.cs | 92 + .../Forms/CreateServiceWindow.resx | 120 + .../Forms/EditDEPWindow.Designer.cs | 131 + .../ProcessHacker/Forms/EditDEPWindow.cs | 179 + .../ProcessHacker/Forms/EditDEPWindow.resx | 120 + .../Forms/ErrorDialog.Designer.cs | 163 + .../ProcessHacker/Forms/ErrorDialog.cs | 213 + .../ProcessHacker/Forms/ErrorDialog.resx | 120 + .../Forms/GetProcAddressWindow.Designer.cs | 135 + .../Forms/GetProcAddressWindow.cs | 100 + .../Forms/GetProcAddressWindow.resx | 120 + .../Forms/HackerWindow.Designer.cs | 1499 +++++++ .../ProcessHacker/Forms/HackerWindow.cs | 3459 +++++++++++++++ .../ProcessHacker/Forms/HackerWindow.resx | 1774 ++++++++ .../Forms/HandleFilterWindow.Designer.cs | 219 + .../ProcessHacker/Forms/HandleFilterWindow.cs | 333 ++ .../Forms/HandleFilterWindow.resx | 151 + .../Forms/HandleStatisticsWindow.Designer.cs | 104 + .../Forms/HandleStatisticsWindow.cs | 83 + .../Forms/HandleStatisticsWindow.resx | 120 + .../Forms/HeapsWindow.Designer.cs | 173 + .../ProcessHacker/Forms/HeapsWindow.cs | 209 + .../ProcessHacker/Forms/HeapsWindow.resx | 126 + .../Forms/HelpWindow.Designer.cs | 84 + .../ProcessHacker/Forms/HelpWindow.cs | 107 + .../ProcessHacker/Forms/HelpWindow.resx | 145 + .../Forms/HiddenProcessesWindow.Designer.cs | 195 + .../Forms/HiddenProcessesWindow.cs | 510 +++ .../Forms/HiddenProcessesWindow.resx | 1750 ++++++++ .../Forms/IPInfoWindow.Designer.cs | 113 + .../ProcessHacker/Forms/IPInfoWindow.cs | 329 ++ .../ProcessHacker/Forms/IPInfoWindow.resx | 1750 ++++++++ .../Forms/InformationBox.Designer.cs | 120 + .../ProcessHacker/Forms/InformationBox.cs | 114 + .../ProcessHacker/Forms/InformationBox.resx | 120 + .../ProcessHacker/Forms/JobWindow.Designer.cs | 84 + .../ProcessHacker/Forms/JobWindow.cs | 39 + .../ProcessHacker/Forms/JobWindow.resx | 120 + .../Forms/ListPickerWindow.Designer.cs | 100 + .../ProcessHacker/Forms/ListPickerWindow.cs | 67 + .../ProcessHacker/Forms/ListPickerWindow.resx | 120 + .../Forms/ListWindow.Designer.cs | 103 + .../ProcessHacker/Forms/ListWindow.cs | 62 + .../ProcessHacker/Forms/ListWindow.resx | 120 + .../ProcessHacker/Forms/LogWindow.Designer.cs | 175 + .../ProcessHacker/Forms/LogWindow.cs | 165 + .../ProcessHacker/Forms/LogWindow.resx | 123 + .../Forms/MemoryEditor.Designer.cs | 315 ++ .../ProcessHacker/Forms/MemoryEditor.cs | 422 ++ .../ProcessHacker/Forms/MemoryEditor.resx | 154 + .../Forms/MessageBoxWindow.Designer.cs | 191 + .../ProcessHacker/Forms/MessageBoxWindow.cs | 84 + .../ProcessHacker/Forms/MessageBoxWindow.resx | 120 + .../Forms/MiniSysInfo.Designer.cs | 117 + .../ProcessHacker/Forms/MiniSysInfo.cs | 107 + .../ProcessHacker/Forms/MiniSysInfo.resx | 120 + .../Forms/NetInfoWindow.Designer.cs | 445 ++ .../ProcessHacker/Forms/NetInfoWindow.cs | 377 ++ .../ProcessHacker/Forms/NetInfoWindow.resx | 123 + .../Forms/OptionsWindow.Designer.cs | 1212 ++++++ .../ProcessHacker/Forms/OptionsWindow.cs | 681 +++ .../ProcessHacker/Forms/OptionsWindow.resx | 120 + .../ProcessHacker/Forms/PEWindow.Designer.cs | 411 ++ .../ProcessHacker/Forms/PEWindow.cs | 334 ++ .../ProcessHacker/Forms/PEWindow.resx | 148 + .../Forms/ProcessAffinity.Designer.cs | 99 + .../ProcessHacker/Forms/ProcessAffinity.cs | 106 + .../ProcessHacker/Forms/ProcessAffinity.resx | 120 + .../Forms/ProcessPickerWindow.Designer.cs | 117 + .../Forms/ProcessPickerWindow.cs | 89 + .../Forms/ProcessPickerWindow.resx | 120 + .../Forms/ProcessWindow.Designer.cs | 1238 ++++++ .../ProcessHacker/Forms/ProcessWindow.cs | 1389 ++++++ .../ProcessHacker/Forms/ProcessWindow.resx | 135 + .../ProcessHacker/Forms/PromptBox.Designer.cs | 107 + .../ProcessHacker/Forms/PromptBox.cs | 95 + .../ProcessHacker/Forms/PromptBox.resx | 120 + .../Forms/ProtectProcessWindow.Designer.cs | 167 + .../Forms/ProtectProcessWindow.cs | 131 + .../Forms/ProtectProcessWindow.resx | 120 + .../Forms/ResultsWindow.Designer.cs | 235 + .../ProcessHacker/Forms/ResultsWindow.cs | 575 +++ .../ProcessHacker/Forms/ResultsWindow.resx | 154 + .../ProcessHacker/Forms/RunWindow.Designer.cs | 257 ++ .../ProcessHacker/Forms/RunWindow.cs | 287 ++ .../ProcessHacker/Forms/RunWindow.resx | 120 + .../Forms/ScratchpadWindow.Designer.cs | 97 + .../ProcessHacker/Forms/ScratchpadWindow.cs | 74 + .../ProcessHacker/Forms/ScratchpadWindow.resx | 909 ++++ .../Forms/SearchWindow.Designer.cs | 424 ++ .../ProcessHacker/Forms/SearchWindow.cs | 184 + .../ProcessHacker/Forms/SearchWindow.resx | 120 + .../Forms/ServiceWindow.Designer.cs | 60 + .../ProcessHacker/Forms/ServiceWindow.cs | 65 + .../ProcessHacker/Forms/ServiceWindow.resx | 145 + .../SessionInformationWindow.Designer.cs | 251 ++ .../Forms/SessionInformationWindow.cs | 36 + .../Forms/SessionInformationWindow.resx | 120 + .../Forms/StructWindow.Designer.cs | 78 + .../ProcessHacker/Forms/StructWindow.cs | 63 + .../ProcessHacker/Forms/StructWindow.resx | 145 + .../Forms/SysInfoWindow.Designer.cs | 1715 ++++++++ .../ProcessHacker/Forms/SysInfoWindow.cs | 403 ++ .../ProcessHacker/Forms/SysInfoWindow.resx | 624 +++ .../Forms/TerminatorWindow.Designer.cs | 128 + .../ProcessHacker/Forms/TerminatorWindow.cs | 440 ++ .../ProcessHacker/Forms/TerminatorWindow.resx | 161 + .../Forms/ThreadWindow.Designer.cs | 148 + .../ProcessHacker/Forms/ThreadWindow.cs | 331 ++ .../ProcessHacker/Forms/ThreadWindow.resx | 176 + .../Forms/TokenWindow.Designer.cs | 85 + .../ProcessHacker/Forms/TokenWindow.cs | 67 + .../ProcessHacker/Forms/TokenWindow.resx | 120 + .../Forms/UpdaterDownloadWindow.Designer.cs | 169 + .../Forms/UpdaterDownloadWindow.cs | 329 ++ .../Forms/UpdaterDownloadWindow.resx | 1783 ++++++++ .../Forms/VirtualProtectWindow.Designer.cs | 123 + .../Forms/VirtualProtectWindow.cs | 101 + .../Forms/VirtualProtectWindow.resx | 134 + .../VirusTotalUploaderWindow.Designer.cs | 145 + .../Forms/VirusTotalUploaderWindow.cs | 355 ++ .../Forms/VirusTotalUploaderWindow.resx | 120 + .../Forms/WaitChainWindow.Designer.cs | 154 + .../ProcessHacker/Forms/WaitChainWindow.cs | 452 ++ .../ProcessHacker/Forms/WaitChainWindow.resx | 120 + branches/ph-plugins/ProcessHacker/Help.htm | 757 ++++ .../ProcessHacker/Icons/ApplicationXP.ico | Bin 0 -> 1150 bytes .../ph-plugins/ProcessHacker/Icons/Bricks.ico | Bin 0 -> 1150 bytes .../ph-plugins/ProcessHacker/Icons/CogGo.ico | Bin 0 -> 1150 bytes .../ph-plugins/ProcessHacker/Icons/Help.ico | Bin 0 -> 1150 bytes .../ProcessHacker/Icons/Keyboard.ico | Bin 0 -> 1150 bytes .../ProcessHacker/Icons/PageEdit.ico | Bin 0 -> 1150 bytes .../ProcessHacker/Icons/Process.ico | Bin 0 -> 48386 bytes .../ProcessHacker/Icons/ProcessHacker.ico | Bin 0 -> 29926 bytes .../ProcessHacker/Icons/ProcessHacker.png | Bin 0 -> 67507 bytes .../Icons/ProcessHacker_small.ico | Bin 0 -> 2862 bytes .../ProcessHacker/Icons/Process_small.ico | Bin 0 -> 2862 bytes .../ph-plugins/ProcessHacker/Icons/Table.ico | Bin 0 -> 1150 bytes .../ProcessHacker/Icons/Terminal.ico | Bin 0 -> 2862 bytes .../ProcessHacker/ProcessHacker.csproj | 1010 +++++ .../ProcessHacker/ProcessHacker.ico | Bin 0 -> 97449 bytes .../ProcessHacker/Program/ExtendedCmd.cs | 329 ++ .../ProcessHacker/Program/Program.cs | 1405 ++++++ .../ph-plugins/ProcessHacker/Program/Save.cs | 423 ++ .../ProcessHacker/Program/Settings.cs | 36 + .../ProcessHacker/Program/ThemingScope.cs | 27 + .../ProcessHacker/Program/Updater.cs | 267 ++ .../ProcessHacker/Properties/AssemblyInfo.cs | 37 + .../Properties/Resources.Designer.cs | 476 +++ .../ProcessHacker/Properties/Resources.resx | 298 ++ .../Properties/Settings.Designer.cs | 1744 ++++++++ .../Properties/Settings.settings | 435 ++ .../ProcessHacker/Providers/HandleProvider.cs | 147 + .../Providers/Internal/IProvider.cs | 20 + .../Providers/Internal/Provider.cs | 400 ++ .../Internal/SharedThreadProvider.cs | 127 + .../ProcessHacker/Providers/MemoryProvider.cs | 177 + .../ProcessHacker/Providers/ModuleProvider.cs | 243 ++ .../Providers/NetworkProvider.cs | 270 ++ .../Providers/ProcessSystemProvider.cs | 1211 ++++++ .../Providers/ServiceProvider.cs | 150 + .../ProcessHacker/Providers/ThreadProvider.cs | 599 +++ .../Resources/VirusTotal-logo.png | Bin 0 -> 5599 bytes .../ProcessHacker/Resources/active_search.png | Bin 0 -> 340 bytes .../ProcessHacker/Resources/application.png | Bin 0 -> 464 bytes .../Resources/application_delete.png | Bin 0 -> 610 bytes .../Resources/application_form_magnify.png | Bin 0 -> 612 bytes .../Resources/application_go.png | Bin 0 -> 634 bytes .../Resources/application_view_detail.png | Bin 0 -> 576 bytes .../ProcessHacker/Resources/arrow_refresh.png | Bin 0 -> 685 bytes .../ProcessHacker/Resources/arrow_right.png | Bin 0 -> 349 bytes .../ProcessHacker/Resources/arrow_up.png | Bin 0 -> 372 bytes .../Resources/asterisk_orange.png | Bin 0 -> 760 bytes .../ProcessHacker/Resources/bricks.png | Bin 0 -> 825 bytes .../ProcessHacker/Resources/chart_curve.png | Bin 0 -> 710 bytes .../ProcessHacker/Resources/chart_line.png | Bin 0 -> 526 bytes .../ProcessHacker/Resources/cog.png | Bin 0 -> 512 bytes .../ProcessHacker/Resources/cog_edit.png | Bin 0 -> 865 bytes .../Resources/control_equalizer.png | Bin 0 -> 432 bytes .../Resources/control_equalizer_blue.png | Bin 0 -> 764 bytes .../ProcessHacker/Resources/control_pause.png | Bin 0 -> 598 bytes .../Resources/control_pause_blue.png | Bin 0 -> 721 bytes .../ProcessHacker/Resources/control_play.png | Bin 0 -> 592 bytes .../Resources/control_play_blue.png | Bin 0 -> 717 bytes .../ProcessHacker/Resources/control_stop.png | Bin 0 -> 403 bytes .../Resources/control_stop_blue.png | Bin 0 -> 695 bytes .../ProcessHacker/Resources/cross.png | Bin 0 -> 655 bytes .../ProcessHacker/Resources/delete.png | Bin 0 -> 715 bytes .../ProcessHacker/Resources/disk.png | Bin 0 -> 620 bytes .../ProcessHacker/Resources/door_out.png | Bin 0 -> 688 bytes .../ProcessHacker/Resources/find.png | Bin 0 -> 659 bytes .../Resources/folder_explore.png | Bin 0 -> 679 bytes .../ProcessHacker/Resources/folder_go.png | Bin 0 -> 694 bytes .../ProcessHacker/Resources/group.png | Bin 0 -> 753 bytes .../ProcessHacker/Resources/help.png | Bin 0 -> 786 bytes .../Resources/inactive_search.png | Bin 0 -> 477 bytes .../ProcessHacker/Resources/information.png | Bin 0 -> 778 bytes .../ProcessHacker/Resources/lightbulb_off.png | Bin 0 -> 700 bytes .../ProcessHacker/Resources/lock.png | Bin 0 -> 749 bytes .../ProcessHacker/Resources/lock_edit.png | Bin 0 -> 861 bytes .../ProcessHacker/Resources/money.png | Bin 0 -> 738 bytes .../ProcessHacker/Resources/mouse.png | Bin 0 -> 634 bytes .../ProcessHacker/Resources/page.png | Bin 0 -> 635 bytes .../ProcessHacker/Resources/page_copy.png | Bin 0 -> 663 bytes .../ProcessHacker/Resources/page_edit.png | Bin 0 -> 807 bytes .../ProcessHacker/Resources/page_gear.png | Bin 0 -> 833 bytes .../ProcessHacker/Resources/page_save.png | Bin 0 -> 774 bytes .../Resources/page_white_text.png | Bin 0 -> 342 bytes .../ProcessHacker/Resources/pencil.png | Bin 0 -> 450 bytes .../ProcessHacker/Resources/pencil_go.png | Bin 0 -> 666 bytes .../ProcessHacker/Resources/report.png | Bin 0 -> 649 bytes .../ProcessHacker/Resources/report_user.png | Bin 0 -> 785 bytes .../ProcessHacker/Resources/sflogo.png | Bin 0 -> 1718 bytes .../ProcessHacker/Resources/table.png | Bin 0 -> 566 bytes .../Resources/table_relationship.png | Bin 0 -> 663 bytes .../ProcessHacker/Resources/table_sort.png | Bin 0 -> 678 bytes .../ProcessHacker/Resources/tick.png | Bin 0 -> 537 bytes .../ProcessHacker/Resources/user.png | Bin 0 -> 741 bytes .../ProcessHacker/Searchers/HeapSearcher.cs | 81 + .../Searchers/LiteralSearcher.cs | 141 + .../ProcessHacker/Searchers/RegexSearcher.cs | 148 + .../ProcessHacker/Searchers/SearchOptions.cs | 139 + .../ProcessHacker/Searchers/Searcher.cs | 122 + .../ProcessHacker/Searchers/StringSearcher.cs | 173 + .../ProcessHacker/Searchers/StructSearcher.cs | 115 + .../ProcessHacker/SharpDevelop/LICENSE.txt | 458 ++ .../ProcessHacker/SharpDevelop/MTA2STA.cs | 252 ++ .../ProcessHacker/SharpDevelop/NDebugger.cs | 89 + .../CorPub/Autogenerated/CorPublishClass.cs | 60 + .../CorPub/Autogenerated/CorpubPublish.cs | 23 + .../CorPub/Autogenerated/ICorPublish.cs | 31 + .../Autogenerated/ICorPublishAppDomain.cs | 27 + .../Autogenerated/ICorPublishAppDomainEnum.cs | 35 + .../CorPub/Autogenerated/ICorPublishEnum.cs | 30 + .../Autogenerated/ICorPublishProcess.cs | 31 + .../Autogenerated/ICorPublishProcessEnum.cs | 32 + .../Wrappers/CorPub/ICorPublish.cs | 34 + .../Wrappers/CorPub/ICorPublishProcess.cs | 68 + .../SharpDevelop/Wrappers/NativeMethods.cs | 29 + .../SharpDevelop/Wrappers/ResourceManager.cs | 114 + .../SharpDevelop/Wrappers/Util.cs | 54 + .../ProcessHacker/Structs/FieldType.cs | 52 + .../ProcessHacker/Structs/FieldValue.cs | 37 + .../Structs/IStructIOProvider.cs | 34 + .../ProcessHacker/Structs/ProcessMemoryIO.cs | 54 + .../ProcessHacker/Structs/StructDef.cs | 346 ++ .../ProcessHacker/Structs/StructField.cs | 159 + .../ProcessHacker/Structs/StructParser.cs | 615 +++ .../Symbols/SymbolProviderExtensions.cs | 171 + .../UI/Actions/ElevationLevel.cs | 13 + .../UI/Actions/ProcessActions.cs | 540 +++ .../UI/Actions/ServiceActions.cs | 253 ++ .../UI/Actions/SessionActions.cs | 133 + .../ProcessHacker/UI/Async/AsyncUtils.cs | 235 + .../ProcessHacker/UI/Async/HandleFilter.cs | 259 ++ .../ProcessHacker/UI/ColumnSettings.cs | 168 + .../ProcessHacker/UI/GenericViewMenu.cs | 268 ++ .../UI/HighlightedListViewItem.cs | 245 ++ .../UI/Icons/CommitHistoryIcon.cs | 53 + .../ProcessHacker/UI/Icons/CpuHistoryIcon.cs | 53 + .../ProcessHacker/UI/Icons/CpuUsageIcon.cs | 123 + .../ProcessHacker/UI/Icons/IoHistoryIcon.cs | 68 + .../UI/Icons/PhysMemHistoryIcon.cs | 54 + .../ProcessHacker/UI/Icons/PlotterIcon.cs | 137 + .../ProcessHacker/UI/Icons/ProviderIcon.cs | 71 + .../ProcessHacker/UI/Icons/UsageIcon.cs | 152 + .../ProcessHacker/UI/WindowFromHandle.cs | 22 + branches/ph-plugins/ProcessHacker/app.config | 441 ++ .../ph-plugins/ProcessHacker/app.manifest | 22 + branches/ph-plugins/ProcessHacker/base.txt | 39 + branches/ph-plugins/ProcessHacker/structs.txt | 237 ++ branches/ph-plugins/README.txt | 43 + .../TreeViewAdv/Aga.Controls.csproj | 258 ++ .../ph-plugins/TreeViewAdv/BitmapHelper.cs | 46 + branches/ph-plugins/TreeViewAdv/GifDecoder.cs | 864 ++++ .../ph-plugins/TreeViewAdv/NumericTextBox.cs | 189 + .../TreeViewAdv/Properties/AssemblyInfo.cs | 15 + .../Properties/Resources.Designer.cs | 133 + .../TreeViewAdv/Properties/Resources.resx | 151 + .../ph-plugins/TreeViewAdv/ResourceHelper.cs | 48 + .../TreeViewAdv/Resources/DVSplit.cur | Bin 0 -> 326 bytes .../TreeViewAdv/Resources/Folder.bmp | Bin 0 -> 1334 bytes .../TreeViewAdv/Resources/FolderClosed.bmp | Bin 0 -> 822 bytes .../ph-plugins/TreeViewAdv/Resources/Leaf.bmp | Bin 0 -> 1334 bytes .../TreeViewAdv/Resources/Thumbs.db | Bin 0 -> 10752 bytes .../TreeViewAdv/Resources/check.bmp | Bin 0 -> 574 bytes .../TreeViewAdv/Resources/loading_icon | Bin 0 -> 6979 bytes .../TreeViewAdv/Resources/minus.bmp | Bin 0 -> 306 bytes .../ph-plugins/TreeViewAdv/Resources/plus.bmp | Bin 0 -> 306 bytes .../TreeViewAdv/Resources/uncheck.bmp | Bin 0 -> 574 bytes .../TreeViewAdv/Resources/unknown.bmp | Bin 0 -> 574 bytes .../TreeViewAdv/StringCollectionEditor.cs | 24 + branches/ph-plugins/TreeViewAdv/TextHelper.cs | 45 + .../Threading/AbortableThreadPool.cs | 118 + .../TreeViewAdv/Threading/WorkItem.cs | 45 + .../TreeViewAdv/Threading/WorkItemStatus.cs | 14 + .../ph-plugins/TreeViewAdv/TimeCounter.cs | 59 + .../TreeViewAdv/Tree/AutoRowHeightLayout.cs | 151 + .../TreeViewAdv/Tree/ClassDiagram.cd | 84 + .../TreeViewAdv/Tree/ColumnCollection.cs | 21 + .../TreeViewAdv/Tree/DrawContext.cs | 60 + .../TreeViewAdv/Tree/DropPosition.cs | 23 + .../TreeViewAdv/Tree/EditorContext.cs | 47 + branches/ph-plugins/TreeViewAdv/Tree/Enums.cs | 40 + .../TreeViewAdv/Tree/FixedRowHeightLayout.cs | 61 + .../ph-plugins/TreeViewAdv/Tree/IRowLayout.cs | 34 + .../TreeViewAdv/Tree/IToolTipProvider.cs | 12 + .../ph-plugins/TreeViewAdv/Tree/ITreeModel.cs | 18 + .../TreeViewAdv/Tree/IncrementalSearch.cs | 143 + .../Tree/Input/ClickColumnState.cs | 45 + .../TreeViewAdv/Tree/Input/ColumnState.cs | 21 + .../TreeViewAdv/Tree/Input/InputState.cs | 33 + .../Tree/Input/InputWithControl.cs | 30 + .../TreeViewAdv/Tree/Input/InputWithShift.cs | 69 + .../Tree/Input/NormalInputState.cs | 209 + .../Tree/Input/ReorderColumnState.cs | 101 + .../Tree/Input/ResizeColumnState.cs | 53 + branches/ph-plugins/TreeViewAdv/Tree/Node.cs | 257 ++ .../TreeViewAdv/Tree/NodeControlInfo.cs | 38 + .../Tree/NodeControls/BaseTextControl.cs | 294 ++ .../Tree/NodeControls/BindableControl.cs | 201 + .../Tree/NodeControls/ClassDiagram.cd | 105 + .../Tree/NodeControls/DrawEventArgs.cs | 58 + .../Tree/NodeControls/EditableControl.cs | 189 + .../Tree/NodeControls/ExpandingIcon.cs | 80 + .../Tree/NodeControls/InteractiveControl.cs | 38 + .../Tree/NodeControls/NodeCheckBox.cs | 184 + .../Tree/NodeControls/NodeComboBox.cs | 80 + .../Tree/NodeControls/NodeControl.cs | 193 + .../NodeControls/NodeControlValueEventArgs.cs | 21 + .../NodeControls/NodeControlsCollection.cs | 94 + .../Tree/NodeControls/NodeDecimalTextBox.cs | 46 + .../Tree/NodeControls/NodeEventArgs.cs | 20 + .../TreeViewAdv/Tree/NodeControls/NodeIcon.cs | 41 + .../Tree/NodeControls/NodeIntegerTextBox.cs | 37 + .../Tree/NodeControls/NodeNumericUpDown.cs | 111 + .../Tree/NodeControls/NodePlusMinus.cs | 127 + .../Tree/NodeControls/NodeStateIcon.cs | 41 + .../Tree/NodeControls/NodeTextBox.cs | 121 + .../TreeViewAdv/Tree/SortedTreeModel.cs | 81 + .../ph-plugins/TreeViewAdv/Tree/TreeColumn.cs | 367 ++ .../TreeViewAdv/Tree/TreeColumnCollection.cs | 100 + .../TreeViewAdv/Tree/TreeColumnEventArgs.cs | 20 + .../TreeViewAdv/Tree/TreeListAdapter.cs | 65 + .../ph-plugins/TreeViewAdv/Tree/TreeModel.cs | 127 + .../TreeViewAdv/Tree/TreeModelBase.cs | 41 + .../TreeViewAdv/Tree/TreeModelEventArgs.cs | 50 + .../TreeViewAdv/Tree/TreeNodeAdv.cs | 493 +++ .../Tree/TreeNodeAdvMouseEventArgs.cs | 59 + .../ph-plugins/TreeViewAdv/Tree/TreePath.cs | 60 + .../TreeViewAdv/Tree/TreePathEventArgs.cs | 28 + .../TreeViewAdv/Tree/TreeViewAdv.Designer.cs | 58 + .../TreeViewAdv/Tree/TreeViewAdv.Draw.cs | 261 ++ .../TreeViewAdv/Tree/TreeViewAdv.Input.cs | 555 +++ .../Tree/TreeViewAdv.Properties.cs | 691 +++ .../TreeViewAdv/Tree/TreeViewAdv.cs | 1270 ++++++ .../TreeViewAdv/Tree/TreeViewAdv.resx | 129 + .../Tree/TreeViewAdvCancelEventArgs.cs | 23 + .../TreeViewAdv/Tree/TreeViewAdvEventArgs.cs | 21 + branches/ph-plugins/TreeViewAdv/app.config | 3 + branches/ph-plugins/TreeViewAdv/key.snk | Bin 0 -> 596 bytes branches/ph-plugins/native.html | 1325 ++++++ 896 files changed, 188819 insertions(+) create mode 100644 branches/ph-plugins/Assistant/Assistant.cs create mode 100644 branches/ph-plugins/Assistant/Assistant.csproj create mode 100644 branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/Assistant/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/Assistant/Properties/Resources.resx create mode 100644 branches/ph-plugins/Assistant/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/Assistant/Properties/Settings.settings create mode 100644 branches/ph-plugins/Assistant/app.manifest create mode 100644 branches/ph-plugins/CHANGELOG.txt create mode 100644 branches/ph-plugins/ExtraTools/ExtraTools.sln create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Program.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings create mode 100644 branches/ph-plugins/ExtraTools/NtObjects/app.config create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Program.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/app.config create mode 100644 branches/ph-plugins/ExtraTools/NtProfiler/app.manifest create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings create mode 100644 branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings create mode 100644 branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj create mode 100644 branches/ph-plugins/HACKING.txt create mode 100644 branches/ph-plugins/KProcessHacker/HACKING.txt create mode 100644 branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys create mode 100644 branches/ph-plugins/KProcessHacker/auto.cmd create mode 100644 branches/ph-plugins/KProcessHacker/autoreload.cmd create mode 100644 branches/ph-plugins/KProcessHacker/handle.c create mode 100644 branches/ph-plugins/KProcessHacker/hook.c create mode 100644 branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys create mode 100644 branches/ph-plugins/KProcessHacker/include/debug.h create mode 100644 branches/ph-plugins/KProcessHacker/include/ex.h create mode 100644 branches/ph-plugins/KProcessHacker/include/handle.h create mode 100644 branches/ph-plugins/KProcessHacker/include/handlep.h create mode 100644 branches/ph-plugins/KProcessHacker/include/hook.h create mode 100644 branches/ph-plugins/KProcessHacker/include/io.h create mode 100644 branches/ph-plugins/KProcessHacker/include/ke.h create mode 100644 branches/ph-plugins/KProcessHacker/include/kph.h create mode 100644 branches/ph-plugins/KProcessHacker/include/kprocesshacker.h create mode 100644 branches/ph-plugins/KProcessHacker/include/mm.h create mode 100644 branches/ph-plugins/KProcessHacker/include/ob.h create mode 100644 branches/ph-plugins/KProcessHacker/include/protect.h create mode 100644 branches/ph-plugins/KProcessHacker/include/ps.h create mode 100644 branches/ph-plugins/KProcessHacker/include/ref.h create mode 100644 branches/ph-plugins/KProcessHacker/include/refp.h create mode 100644 branches/ph-plugins/KProcessHacker/include/se.h create mode 100644 branches/ph-plugins/KProcessHacker/include/sync.h create mode 100644 branches/ph-plugins/KProcessHacker/include/sysservice.h create mode 100644 branches/ph-plugins/KProcessHacker/include/sysservicedata.h create mode 100644 branches/ph-plugins/KProcessHacker/include/sysservicep.h create mode 100644 branches/ph-plugins/KProcessHacker/include/test.h create mode 100644 branches/ph-plugins/KProcessHacker/include/trace.h create mode 100644 branches/ph-plugins/KProcessHacker/include/types.h create mode 100644 branches/ph-plugins/KProcessHacker/include/util.h create mode 100644 branches/ph-plugins/KProcessHacker/include/version.h create mode 100644 branches/ph-plugins/KProcessHacker/include/zw.h create mode 100644 branches/ph-plugins/KProcessHacker/io.c create mode 100644 branches/ph-plugins/KProcessHacker/kph.c create mode 100644 branches/ph-plugins/KProcessHacker/kprocesshacker.c create mode 100644 branches/ph-plugins/KProcessHacker/makefile create mode 100644 branches/ph-plugins/KProcessHacker/mm.c create mode 100644 branches/ph-plugins/KProcessHacker/ob.c create mode 100644 branches/ph-plugins/KProcessHacker/protect.c create mode 100644 branches/ph-plugins/KProcessHacker/ps.c create mode 100644 branches/ph-plugins/KProcessHacker/ref.c create mode 100644 branches/ph-plugins/KProcessHacker/resource.rc create mode 100644 branches/ph-plugins/KProcessHacker/se.c create mode 100644 branches/ph-plugins/KProcessHacker/sources create mode 100644 branches/ph-plugins/KProcessHacker/sync.c create mode 100644 branches/ph-plugins/KProcessHacker/sysservice.c create mode 100644 branches/ph-plugins/KProcessHacker/sysservicedata.c create mode 100644 branches/ph-plugins/KProcessHacker/test.c create mode 100644 branches/ph-plugins/KProcessHacker/trace.c create mode 100644 branches/ph-plugins/KProcessHacker/util.c create mode 100644 branches/ph-plugins/KProcessHacker/version.c create mode 100644 branches/ph-plugins/LICENSE.txt create mode 100644 branches/ph-plugins/NProcessHacker/NProcessHacker.sln create mode 100644 branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj create mode 100644 branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj create mode 100644 branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c create mode 100644 branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll create mode 100644 branches/ph-plugins/NProcessHacker/Test/Test.vcproj create mode 100644 branches/ph-plugins/NProcessHacker/Test/test.c create mode 100644 branches/ph-plugins/NProcessHacker/hook.c create mode 100644 branches/ph-plugins/NProcessHacker/hook.h create mode 100644 branches/ph-plugins/NProcessHacker/kph.c create mode 100644 branches/ph-plugins/NProcessHacker/kph.h create mode 100644 branches/ph-plugins/NProcessHacker/kphhook.c create mode 100644 branches/ph-plugins/NProcessHacker/kphhook.h create mode 100644 branches/ph-plugins/NProcessHacker/nativedefs.h create mode 100644 branches/ph-plugins/NProcessHacker/nph.c create mode 100644 branches/ph-plugins/NProcessHacker/nph.h create mode 100644 branches/ph-plugins/NProcessHacker/obj.c create mode 100644 branches/ph-plugins/NProcessHacker/obj.h create mode 100644 branches/ph-plugins/NProcessHacker/process.c create mode 100644 branches/ph-plugins/NProcessHacker/process.h create mode 100644 branches/ph-plugins/NProcessHacker/resource.rc create mode 100644 branches/ph-plugins/NProcessHacker/secedit.c create mode 100644 branches/ph-plugins/NProcessHacker/secedit.h create mode 100644 branches/ph-plugins/NProcessHacker/verify.c create mode 100644 branches/ph-plugins/NProcessHacker/verify.h create mode 100644 branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll create mode 100644 branches/ph-plugins/ProcessHacker.Common/BaseConverter.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Delegates.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/FreeList.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/IResettable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/License.txt create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Conversion Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Element Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Enumerable Private.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Filtering Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Generation Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Grouping Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Joining Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Misc Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Ordering Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Projection Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Quantifiers.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Set Operators.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Linq/ThenByEnumerable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Logging.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Messaging/Message.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueue.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueueListener.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Objects/BaseObject.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Objects/DelayedReleasePool.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Objects/HandleTable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Objects/IRefCounted.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Objects/SecuredHandleTable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/ProcessHacker.Common.csproj create mode 100644 branches/ph-plugins/ProcessHacker.Common/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Settings/SettingDefaultAttribute.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Settings/SettingsBase.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Settings/SettingsManager.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Settings/SettingsStore.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/FastMutex.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/FastQueue.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/FastStack.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/RundownProtection.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/SemaphorePair.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/SpinLock.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/ThreadTask.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Threading/WaitableQueue.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Tokenizer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Ui/ColumnHeaderExtensions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Ui/SortedListViewComparer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/Utils.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/WeakReference.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/WorkQueue.cs create mode 100644 branches/ph-plugins/ProcessHacker.Common/app.config create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Enums.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Extensions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Functions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/HResult.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/ISecurityInformation.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/LsaEnums.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/LsaFunctions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/LsaStructs.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/NativeDefinitions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/NativeEnums.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/NativeFunctions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/NativeStructs.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/NtStatus.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Structs.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Win32.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Api/Win32Error.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Cryptography.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Debugging/DebugBuffer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Debugging/HeapInformation.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Debugging/LockInformation.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Debugging/ModuleInformation.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/FileUtils.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/ILoadedModule.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Image/ImageDirectoryEntry.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Image/ImageExports.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Image/ImageImports.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Image/MappedImage.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/ImpersonationContext.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/IntPtrExtensions.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Io/BeepDevice.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Io/MountManager.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ipc/IpcCircularBuffer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/KProcessHacker.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Loader.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Lpc/Port.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Lpc/PortMessage.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/AlignedMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/Heap.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/LocalMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/LsaMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/MemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegion.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegionStream.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/PebMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPages.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPagesMapping.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/PinnedObject.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/Section.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/SectionView.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/VirtualMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Memory/WtsMemoryAlloc.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/NProcessHacker.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/NativeBitmap.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/NativeObject.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/NativeTypeFactory.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/NativeUtils.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/OSVersion.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/DebugObjectHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/DesktopHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/DirectoryHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/DriverHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/EnlistmentHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/EnvironmentBlock.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/EventHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/EventPairHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/FileHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ISynchronizable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/IWithToken.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/IoCompletionHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/JobObjectHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/KeyHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/KeyedEventHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/LsaAccountHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/LsaHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/LsaPolicyHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/MailslotHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/MutantHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/NamedPipeHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/NativeHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/PortComHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/PortHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/PrivateNamespaceHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ProcessHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ProfileHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/RemoteHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/RemoteTokenHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ResourceManagerHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/SectionHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/SemaphoreHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ServiceBaseHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ServiceHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ServiceManagerHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/SymbolicLinkHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TerminalServerHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/ThreadHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TimerHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TmHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TokenHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TokenWithLinkedToken.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/TransactionHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/UserHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/WindowHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Objects/WindowStationHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/ProcessHacker.Native.csproj create mode 100644 branches/ph-plugins/ProcessHacker.Native/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Ace.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Acl.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/KnownAce.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityDescriptor.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityEditor.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/DebugObjectAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/DesktopAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/DirectoryAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/EnlistmentAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/EventAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/EventPairAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/FileAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/FltPortAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ISecurable.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/IoCompletionAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/JobObjectAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/KeyAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/KeyedEventAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/LsaAccountAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/LsaPolicyAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/LsaSecretAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/LsaTrustedAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/MutantAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ObjectTypeAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/PortAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/Privilege.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/PrivilegeSet.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ProcessAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ProfileAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ResourceManagerAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ScManagerAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/SectionAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/SemaphoreAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ServiceAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/Sid.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/StandardRights.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/SymbolicLinkAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/ThreadAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/TimerAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/TmAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/TokenAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/TransactionAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Security/WindowStationAccess.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/FilterType.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsClientId.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsData.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsEvent.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsLogger.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsObjectAttributes.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsSimple.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/SsLogging/SsUnicodeString.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolInformation.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolResolveLevel.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/CurrentThread.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/Event.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/EventPair.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/KeyedEvent.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/Mutant.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/NativeThreadPool.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/Semaphore.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/Timer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Threading/Waiter.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.resx create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker.Native/Windows.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/WindowsException.cs create mode 100644 branches/ph-plugins/ProcessHacker.Native/app.config create mode 100644 branches/ph-plugins/ProcessHacker.sln create mode 100644 branches/ph-plugins/ProcessHacker/Build/7za/7za.exe create mode 100644 branches/ph-plugins/ProcessHacker/Build/7za/copying.txt create mode 100644 branches/ph-plugins/ProcessHacker/Build/7za/license.txt create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Custom_Messages.iss create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Icons/ProcessHacker.ico create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Icons/ProcessHackerLarge.bmp create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Icons/ProcessHackerSmall.bmp create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Icons/uninstall.ico create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Languages/Greek.isl create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Process_Hacker_installer.iss create mode 100644 branches/ph-plugins/ProcessHacker/Build/Installer/Services.iss create mode 100644 branches/ph-plugins/ProcessHacker/Build/ngen.cmd create mode 100644 branches/ph-plugins/ProcessHacker/Build/release.cmd create mode 100644 branches/ph-plugins/ProcessHacker/Build/testsign.cmd create mode 100644 branches/ph-plugins/ProcessHacker/Common/Extensions.cs create mode 100644 branches/ph-plugins/ProcessHacker/Common/PhUtils.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/ByteCollection.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/DataBlock.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/DataMap.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/Design/HexFontEditor.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/DynamicByteProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/DynamicFileByteProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/FileByteProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/FileDataBlock.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/HexBox.bmp create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/HexBox.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/HexBox.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/HexBox.snk create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/IByteProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/MemoryDataBlock.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Be.Windows.Forms.HexBox/NativeMethods.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ColorModifier.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ColorModifier.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ColorModifier.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventPairProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventPairProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventPairProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/EventProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ExtendedListView.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ExtendedTreeView.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/FileNameBox.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/FileNameBox.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/FileNameBox.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/HandleList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/HandleList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/HandleList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/Indicator.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Indicator.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Indicator.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/JobProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/JobProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/JobProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/MemoryList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/MemoryList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/MemoryList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ModuleList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ModuleList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ModuleList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/MutantProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/MutantProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/MutantProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/NetworkList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/NetworkList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/NetworkList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/NodePlotter.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Plotter.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Plotter.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/Plotter.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessStatistics.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessStatistics.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessStatistics.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessNode.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessToolTipProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessTree.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessTree.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessTree.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ProcessTree/ProcessTreeModel.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/RestartRecoveryLib/RecoveryData.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/RestartRecoveryLib/RecoverySettings.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/RestartRecoveryLib/RestartRecoveryInterop.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/RestartRecoveryLib/RestartRecoveryManager.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/RestartRecoveryLib/RestartSettings.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/SectionProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/SectionProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/SectionProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/SemaphoreProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/SemaphoreProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/SemaphoreProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ServiceProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/SplitButton.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/StructViewer.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/StructViewer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/StructViewer.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TargetWindowButton.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskDialog/ActiveTaskDialog.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskDialog/TaskDialog.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskDialog/TaskDialogCommonDialog.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskDialog/TaskDialogNotificationArgs.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskDialog/UnsafeNativeMethods.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/Interop/COMTypes.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/Interop/Interop.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/JumpLists/JumpListImpl.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/JumpLists/JumpListManager.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/ThumbnailButtons/ThumbButton.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/ThumbnailButtons/ThumbButtonManager.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TaskbarLib/Windows7Taskbar.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ThreadList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ThreadList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/ThreadList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TimerProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TimerProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TimerProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmRmProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmRmProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmRmProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmTmProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmTmProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TmTmProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenGroupsList.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenGroupsList.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenGroupsList.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenProperties.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenProperties.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/TokenProperties.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/UtilitiesButton.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/UtilitiesButton.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/UtilitiesButton.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/VerticleProgressBar.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VerticleProgressBar.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VerticleProgressBar.resx create mode 100644 branches/ph-plugins/ProcessHacker/Components/VistaMenu/OwnerDrawnMenu.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VistaMenu/VistaMenu.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VistaSearchBox.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VistaSearchBox.designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Components/VistaSearchBox.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/AboutWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/AboutWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/AboutWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ChooseColumnsWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ChooseColumnsWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ChooseColumnsWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ComboBoxPickerWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ComboBoxPickerWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ComboBoxPickerWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/CreateServiceWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/CreateServiceWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/CreateServiceWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/EditDEPWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/EditDEPWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/EditDEPWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ErrorDialog.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ErrorDialog.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ErrorDialog.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/GetProcAddressWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/GetProcAddressWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/GetProcAddressWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HackerWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HackerWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HackerWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleFilterWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleFilterWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleFilterWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleStatisticsWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleStatisticsWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HandleStatisticsWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HeapsWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HeapsWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HeapsWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HelpWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HelpWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HelpWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HiddenProcessesWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HiddenProcessesWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/HiddenProcessesWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/IPInfoWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/IPInfoWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/IPInfoWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/InformationBox.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/InformationBox.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/InformationBox.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/JobWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/JobWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/JobWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListPickerWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListPickerWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListPickerWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ListWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/LogWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/LogWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/LogWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MemoryEditor.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MemoryEditor.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MemoryEditor.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MessageBoxWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MessageBoxWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MessageBoxWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MiniSysInfo.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MiniSysInfo.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/MiniSysInfo.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/NetInfoWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/NetInfoWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/NetInfoWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/OptionsWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/OptionsWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/OptionsWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PEWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PEWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PEWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessAffinity.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessAffinity.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessAffinity.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessPickerWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessPickerWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessPickerWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProcessWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PromptBox.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PromptBox.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/PromptBox.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProtectProcessWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProtectProcessWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ProtectProcessWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ResultsWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ResultsWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ResultsWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/RunWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/RunWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/RunWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ScratchpadWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ScratchpadWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ScratchpadWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SearchWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SearchWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SearchWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ServiceWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ServiceWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ServiceWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SessionInformationWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SessionInformationWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SessionInformationWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/StructWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/StructWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/StructWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SysInfoWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SysInfoWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/SysInfoWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TerminatorWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TerminatorWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TerminatorWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ThreadWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ThreadWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/ThreadWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TokenWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TokenWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/TokenWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/UpdaterDownloadWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/UpdaterDownloadWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/UpdaterDownloadWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirtualProtectWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirtualProtectWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirtualProtectWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirusTotalUploaderWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirusTotalUploaderWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/VirusTotalUploaderWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Forms/WaitChainWindow.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/WaitChainWindow.cs create mode 100644 branches/ph-plugins/ProcessHacker/Forms/WaitChainWindow.resx create mode 100644 branches/ph-plugins/ProcessHacker/Help.htm create mode 100644 branches/ph-plugins/ProcessHacker/Icons/ApplicationXP.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Bricks.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/CogGo.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Help.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Keyboard.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/PageEdit.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Process.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/ProcessHacker.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/ProcessHacker.png create mode 100644 branches/ph-plugins/ProcessHacker/Icons/ProcessHacker_small.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Process_small.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Table.ico create mode 100644 branches/ph-plugins/ProcessHacker/Icons/Terminal.ico create mode 100644 branches/ph-plugins/ProcessHacker/ProcessHacker.csproj create mode 100644 branches/ph-plugins/ProcessHacker/ProcessHacker.ico create mode 100644 branches/ph-plugins/ProcessHacker/Program/ExtendedCmd.cs create mode 100644 branches/ph-plugins/ProcessHacker/Program/Program.cs create mode 100644 branches/ph-plugins/ProcessHacker/Program/Save.cs create mode 100644 branches/ph-plugins/ProcessHacker/Program/Settings.cs create mode 100644 branches/ph-plugins/ProcessHacker/Program/ThemingScope.cs create mode 100644 branches/ph-plugins/ProcessHacker/Program/Updater.cs create mode 100644 branches/ph-plugins/ProcessHacker/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/ProcessHacker/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Properties/Resources.resx create mode 100644 branches/ph-plugins/ProcessHacker/Properties/Settings.Designer.cs create mode 100644 branches/ph-plugins/ProcessHacker/Properties/Settings.settings create mode 100644 branches/ph-plugins/ProcessHacker/Providers/HandleProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/Internal/IProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/Internal/Provider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/Internal/SharedThreadProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/MemoryProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/ModuleProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/NetworkProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/ProcessSystemProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/ServiceProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Providers/ThreadProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Resources/VirusTotal-logo.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/active_search.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/application.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/application_delete.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/application_form_magnify.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/application_go.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/application_view_detail.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/arrow_refresh.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/arrow_right.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/arrow_up.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/asterisk_orange.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/bricks.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/chart_curve.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/chart_line.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/cog.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/cog_edit.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_equalizer.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_equalizer_blue.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_pause.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_pause_blue.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_play.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_play_blue.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_stop.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/control_stop_blue.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/cross.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/delete.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/disk.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/door_out.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/find.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/folder_explore.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/folder_go.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/group.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/help.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/inactive_search.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/information.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/lightbulb_off.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/lock.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/lock_edit.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/money.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/mouse.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page_copy.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page_edit.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page_gear.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page_save.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/page_white_text.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/pencil.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/pencil_go.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/report.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/report_user.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/sflogo.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/table.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/table_relationship.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/table_sort.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/tick.png create mode 100644 branches/ph-plugins/ProcessHacker/Resources/user.png create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/HeapSearcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/LiteralSearcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/RegexSearcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/SearchOptions.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/Searcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/StringSearcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/Searchers/StructSearcher.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/LICENSE.txt create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/MTA2STA.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/NDebugger.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/CorPublishClass.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/CorpubPublish.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublish.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublishAppDomain.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublishAppDomainEnum.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublishEnum.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublishProcess.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/Autogenerated/ICorPublishProcessEnum.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/ICorPublish.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/CorPub/ICorPublishProcess.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/NativeMethods.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/ResourceManager.cs create mode 100644 branches/ph-plugins/ProcessHacker/SharpDevelop/Wrappers/Util.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/FieldType.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/FieldValue.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/IStructIOProvider.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/ProcessMemoryIO.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/StructDef.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/StructField.cs create mode 100644 branches/ph-plugins/ProcessHacker/Structs/StructParser.cs create mode 100644 branches/ph-plugins/ProcessHacker/Symbols/SymbolProviderExtensions.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Actions/ElevationLevel.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Actions/ProcessActions.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Actions/ServiceActions.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Actions/SessionActions.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Async/AsyncUtils.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Async/HandleFilter.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/ColumnSettings.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/GenericViewMenu.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/HighlightedListViewItem.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/CommitHistoryIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/CpuHistoryIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/CpuUsageIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/IoHistoryIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/PhysMemHistoryIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/PlotterIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/ProviderIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/Icons/UsageIcon.cs create mode 100644 branches/ph-plugins/ProcessHacker/UI/WindowFromHandle.cs create mode 100644 branches/ph-plugins/ProcessHacker/app.config create mode 100644 branches/ph-plugins/ProcessHacker/app.manifest create mode 100644 branches/ph-plugins/ProcessHacker/base.txt create mode 100644 branches/ph-plugins/ProcessHacker/structs.txt create mode 100644 branches/ph-plugins/README.txt create mode 100644 branches/ph-plugins/TreeViewAdv/Aga.Controls.csproj create mode 100644 branches/ph-plugins/TreeViewAdv/BitmapHelper.cs create mode 100644 branches/ph-plugins/TreeViewAdv/GifDecoder.cs create mode 100644 branches/ph-plugins/TreeViewAdv/NumericTextBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Properties/AssemblyInfo.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Properties/Resources.Designer.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Properties/Resources.resx create mode 100644 branches/ph-plugins/TreeViewAdv/ResourceHelper.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/DVSplit.cur create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/Folder.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/FolderClosed.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/Leaf.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/Thumbs.db create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/check.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/loading_icon create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/minus.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/plus.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/uncheck.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/Resources/unknown.bmp create mode 100644 branches/ph-plugins/TreeViewAdv/StringCollectionEditor.cs create mode 100644 branches/ph-plugins/TreeViewAdv/TextHelper.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Threading/AbortableThreadPool.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Threading/WorkItem.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Threading/WorkItemStatus.cs create mode 100644 branches/ph-plugins/TreeViewAdv/TimeCounter.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/AutoRowHeightLayout.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/ClassDiagram.cd create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/ColumnCollection.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/DrawContext.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/DropPosition.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/EditorContext.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Enums.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/FixedRowHeightLayout.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/IRowLayout.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/IToolTipProvider.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/ITreeModel.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/IncrementalSearch.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/ClickColumnState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/ColumnState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/InputState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/InputWithControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/InputWithShift.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/NormalInputState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/ReorderColumnState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Input/ResizeColumnState.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/Node.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControlInfo.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/BaseTextControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/BindableControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/ClassDiagram.cd create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/DrawEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/EditableControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/ExpandingIcon.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/InteractiveControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeCheckBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeComboBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeControl.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeControlValueEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeControlsCollection.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeDecimalTextBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeIcon.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeIntegerTextBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeNumericUpDown.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodePlusMinus.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeStateIcon.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/NodeControls/NodeTextBox.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/SortedTreeModel.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeColumn.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeColumnCollection.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeColumnEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeListAdapter.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeModel.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeModelBase.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeModelEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeNodeAdv.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeNodeAdvMouseEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreePath.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreePathEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.Designer.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.Draw.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.Input.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.Properties.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdv.resx create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdvCancelEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/Tree/TreeViewAdvEventArgs.cs create mode 100644 branches/ph-plugins/TreeViewAdv/app.config create mode 100644 branches/ph-plugins/TreeViewAdv/key.snk create mode 100644 branches/ph-plugins/native.html diff --git a/branches/ph-plugins/Assistant/Assistant.cs b/branches/ph-plugins/Assistant/Assistant.cs new file mode 100644 index 000000000..880edd639 --- /dev/null +++ b/branches/ph-plugins/Assistant/Assistant.cs @@ -0,0 +1,341 @@ +/* + * Process Hacker Assistant + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace Assistant +{ + static class Program + { + static void SetDesktopWinStaAccess() + { + using (var wsHandle = new WindowStationHandle("WinSta0", (WindowStationAccess)StandardRights.WriteDac)) + wsHandle.SetSecurity(SecurityInformation.Dacl, new SecurityDescriptor()); + + using (var dhandle = new DesktopHandle("Default", false, + (DesktopAccess)StandardRights.WriteDac | DesktopAccess.ReadObjects | DesktopAccess.WriteObjects)) + dhandle.SetSecurity(SecurityInformation.Dacl, new SecurityDescriptor()); + } + + static Dictionary ParseArgs(string[] args) + { + Dictionary dict = new Dictionary(); + string argPending = null; + + foreach (string s in args) + { + if (s.StartsWith("-")) + { + if (dict.ContainsKey(s)) + throw new Exception("Option already specified."); + + dict.Add(s, ""); + argPending = s; + } + else + { + if (argPending != null) + { + dict[argPending] = s; + argPending = null; + } + else + { + if (dict.ContainsKey("")) + throw new Exception("Input file already specified."); + + dict.Add("", s); + } + } + } + + return dict; + } + + static void PrintUsage() + { + Console.Write("Process Hacker Assistant\nCopyright (c) 2008 wj32. Licensed under the GNU GPL v3.\n\nUsage:\n" + + "\tassistant [-w] [-k] [-P pid] [-u username] [-p password] [-t logontype] [-s sessionid] [-d dir] " + + "[-c cmdline] [-f filename] [-E name]\n\n" + + "-w\t\tSpecifies that the permissions of WinSta0 and WinSta0\\Default should be " + + "modified with all access. You should use this option as a normal user (\"assistant -w\") before attempting to " + + "use this program as a Windows service.\n" + + "-k\t\tDebugging purposes: specifies that this program should sleep after completion.\n" + + "-P pid\t\t\"Steals\" the token of the specified process to start the specified program. You must not use " + + "the -u and -p options with this option.\n" + + "-u username\tSpecifies the user under which the program should be run. The username can be specified " + + "as username, domain\\username, or username@domain. On Windows XP, specifying NT AUTHORITY\\SYSTEM does " + + "not work by itself. You must specify \"-t newcredentials\" as well.\n" + + "-p password\tSpecifies the password for the user.\n" + + "-t logontype\tSpecifies the logon type. For logons to normal users, specify \"interactive\". For logons " + + "to NT AUTHORITY\\SYSTEM, LOCAL SERVICE or NETWORK SERVICE, specify \"service\" (see above for using SYSTEM on " + + "Windows XP).\n" + + "-s sessionid\tSpecifies the session ID under which the program should be run.\n" + + "-d dir\t\tSpecifies the current directory for the program.\n" + + "-c cmdline\tSpecifies the command line for the program. You must not use the -f option if you use this.\n" + + "-f filename\tSpecifies the full path to the program.\n" + + "-E name\tSpecifies the partial name of the mailslot to write a 4-byte error code to.\n" + + "\n" + + "This application is not useful by itself; even Administrators do not normally have " + + "SeAssignPrimaryTokenPrivilege and SeTcbPrivilege, both of which are required for the useful " + + "functioning of this program. You must create a Windows service for this program:\n" + + "\tsc.exe create PHAssistant binPath= \"\\\"[path to this program]\\\" -u \\\"SYSTEM@NT AUTHORITY\\\" " + + "-t service -s [your session Id, normally 0 on XP and 1 on Vista] -c calc.exe\"\n" + + "then start it:\n\tsc.exe start PHAssistant\n" + + "and finally delete it:\n\tsc.exe delete PHAssistant\n"); + } + + static void Exit(int exitCode) + { + if (args.ContainsKey("-k")) + System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); + + if (args.ContainsKey("-E")) + { + string mailslotName = args["-E"]; + + using (var fhandle = new FileHandle( + @"\Device\Mailslot\" + mailslotName, + FileShareMode.ReadWrite, + FileAccess.GenericWrite + )) + fhandle.Write(exitCode.GetBytes()); + } + + Environment.Exit(exitCode); + } + + static void Exit() + { + Exit(0); + } + + static Dictionary args; + + static bool EnablePrivilege(string name) + { + try + { + Privilege.Enable(name); + return true; + } + catch + { + return false; + } + } + + static void Main() + { + EnablePrivilege("SeAssignPrimaryTokenPrivilege"); + EnablePrivilege("SeBackupPrivilege"); + EnablePrivilege("SeRestorePrivilege"); + + try + { + args = ParseArgs(Environment.GetCommandLineArgs()); + + bool bad = false; + + if (!args.ContainsKey("-w")) + { + if (!args.ContainsKey("-c") && !args.ContainsKey("-f")) + bad = true; + + if (args.ContainsKey("-c") && args.ContainsKey("-f")) + bad = true; + + if (!args.ContainsKey("-u") && !args.ContainsKey("-P")) + bad = true; + + if (args.ContainsKey("-u") && args.ContainsKey("-P")) + bad = true; + } + + if (args.ContainsKey("-v") || args.ContainsKey("-h")) + bad = true; + + if (bad) + { + PrintUsage(); + Exit(); + } + } + catch + { + PrintUsage(); + Exit(); + } + + if (args.ContainsKey("-w")) + { + try + { + SetDesktopWinStaAccess(); + } + catch (Exception ex) + { + Console.WriteLine("Warning: Could not set desktop and window station access: " + ex.Message); + } + } + + IntPtr token = IntPtr.Zero; + string domain = null; + string username = ""; + + if (args.ContainsKey("-u")) + { + string user = args["-u"]; + + if (user.Contains("\\")) + { + domain = user.Split('\\')[0]; + username = user.Split('\\')[1]; + } + else if (user.Contains("@")) + { + username = user.Split('@')[0]; + domain = user.Split('@')[1]; + } + else + { + username = user; + } + + LogonType type = LogonType.Interactive; + + if (args.ContainsKey("-t")) + { + try + { + type = (LogonType)Enum.Parse(typeof(LogonType), args["-t"], true); + } + catch + { + Console.WriteLine("Error: Invalid logon type."); + Exit(-1); + } + } + + if (!Win32.LogonUser(username, domain, args.ContainsKey("-p") ? args["-p"] : "", type, + LogonProvider.Default, out token)) + { + Console.WriteLine("Error: Could not logon as user: " + Win32.GetLastErrorMessage()); + Exit(Marshal.GetLastWin32Error()); + } + } + else + { + int pid = System.Diagnostics.Process.GetCurrentProcess().Id; + + try + { + if (args.ContainsKey("-P")) + pid = int.Parse(args["-P"]); + } + catch + { + Console.WriteLine("Error: Invalid PID."); + } + + IntPtr handle = IntPtr.Zero; + + try + { + handle = System.Diagnostics.Process.GetProcessById(pid).Handle; + } + catch + { + Console.WriteLine("Error: Could not open process."); + } + + + if (!Win32.OpenProcessToken(handle, TokenAccess.All, out token)) + { + Console.WriteLine("Error: Could not open process token: " + Win32.GetLastErrorMessage()); + Exit(Marshal.GetLastWin32Error()); + } + + if (Environment.OSVersion.Version.Major != 5) + { + IntPtr dupToken; + + if (!Win32.DuplicateTokenEx(token, TokenAccess.All, IntPtr.Zero, SecurityImpersonationLevel.SecurityImpersonation, + TokenType.Primary, out dupToken)) + { + Console.WriteLine("Error: Could not duplicate own token: " + Win32.GetLastErrorMessage()); + Exit(Marshal.GetLastWin32Error()); + } + + Win32.CloseHandle(token); + token = dupToken; + } + } + + if (args.ContainsKey("-s")) + { + int sessionId = int.Parse(args["-s"]); + + if (!Win32.SetTokenInformation(token, TokenInformationClass.TokenSessionId, ref sessionId, 4)) + { + Console.WriteLine("Error: Could not set token session Id: " + Win32.GetLastErrorMessage()); + } + } + + if (args.ContainsKey("-c") || args.ContainsKey("-f")) + { + if (!args.ContainsKey("-e")) + { + StartupInfo info = new StartupInfo(); + ProcessInformation pinfo = new ProcessInformation(); + IntPtr environment; + + Win32.CreateEnvironmentBlock(out environment, token, false); + + info.Size = Marshal.SizeOf(info); + info.Desktop = "WinSta0\\Default"; + + if (!Win32.CreateProcessAsUser(token, + args.ContainsKey("-f") ? args["-f"] : null, + args.ContainsKey("-c") ? args["-c"] : null, + IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CreateUnicodeEnvironment, environment, + args.ContainsKey("-d") ? args["-d"] : null, + ref info, out pinfo)) + { + Console.WriteLine("Error: Could not create process: " + Win32.GetLastErrorMessage()); + Exit(Marshal.GetLastWin32Error()); + } + + Win32.CloseHandle(token); + } + } + + Exit(); + } + } +} diff --git a/branches/ph-plugins/Assistant/Assistant.csproj b/branches/ph-plugins/Assistant/Assistant.csproj new file mode 100644 index 000000000..46724f8ff --- /dev/null +++ b/branches/ph-plugins/Assistant/Assistant.csproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {0710ADEF-F89E-4CBC-8150-B340460BC9D6} + Exe + Properties + Assistant + Assistant + v2.0 + 512 + Assistant.Program + app.manifest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + {8A448157-E1A7-4DDF-954E-287F1117832B} + ProcessHacker.Native + + + + + + mkdir "$(SolutionDir)\ProcessHacker\$(OutDir)" +copy "$(TargetPath)" "$(SolutionDir)\ProcessHacker\$(OutDir)" +copy "$(TargetDir)\Assistant.pdb" "$(SolutionDir)\ProcessHacker\$(OutDir)" + + \ No newline at end of file diff --git a/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs b/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..e3968b1c7 --- /dev/null +++ b/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Process Hacker Assistant")] +[assembly: AssemblyDescription("Process Hacker Assistant")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("Process Hacker")] +[assembly: AssemblyCopyright("Licensed under the GNU GPL.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("10a62c48-2a7a-4e76-9103-ac46d4d55ea8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs b/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs new file mode 100644 index 000000000..1eae069fc --- /dev/null +++ b/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.1434 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Assistant.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Assistant.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/branches/ph-plugins/Assistant/Properties/Resources.resx b/branches/ph-plugins/Assistant/Properties/Resources.resx new file mode 100644 index 000000000..ffecec851 --- /dev/null +++ b/branches/ph-plugins/Assistant/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs b/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs new file mode 100644 index 000000000..dc7bdcb9a --- /dev/null +++ b/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.1434 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Assistant.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/branches/ph-plugins/Assistant/Properties/Settings.settings b/branches/ph-plugins/Assistant/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/branches/ph-plugins/Assistant/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/branches/ph-plugins/Assistant/app.manifest b/branches/ph-plugins/Assistant/app.manifest new file mode 100644 index 000000000..eb3c3ae01 --- /dev/null +++ b/branches/ph-plugins/Assistant/app.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/branches/ph-plugins/CHANGELOG.txt b/branches/ph-plugins/CHANGELOG.txt new file mode 100644 index 000000000..9b169170b --- /dev/null +++ b/branches/ph-plugins/CHANGELOG.txt @@ -0,0 +1,725 @@ +Process Hacker + +1.6 + * NEW/IMPROVED: + * #2817429 - "Add Port and IP Address columns to Network tab" + * #2845829 - "System uptime" + * #2853452 - "Find handles window: Minor usability improvements" + * Update system + * Network tools: ping, traceroute and whois + * Object security editor + * Displays IPv6 network connections + * VirusTotal uploader + * Two new terminator tests: W1 (send WM_DESTROY messages) and + W2 (send WM_QUIT messages) + * Wait chain analysis + * Mutant owner information + * Elevation prompt when attempting to view process properties + * All list views now have sort arrows + * Network list now displays process tooltips + * Enabled DLL injection into processes from other sessions + * Decreased memory and CPU usage + * Added a glossary to Help + * Configurable elevation prompts + * Better Windows 7 user interface support + * Exception reporting from within Process Hacker + * Slimmer thread call stack window + * FIXED: + * #2820170 - "System.ArgumentOutOfRangeException in network list" + * #2845427 - "Indicator integer overflow" + * #2847691 - "Apply button bug" + * #2849052 - "not defined by current visual style" + * #2863305 - "Unhandled exception in comparer" + * Critical KProcessHacker denial-of-service security issues + * Crashes within the process tree model + * Incorrect menu items for System threads + * Crash when viewing error details in the event + of a corrupt configuration file + * Notification icons were fixed at 16x16 size + * Sort order for the relative start time column + * Incorrect process priority class display + * Commit and Physical Memory History icon colors + * Packed file detection, PE reader + * Performance history buffer problems + * REMOVED: + * Messages in the main window's status bar + +1.5 + * NEW/IMPROVED: + * #2831605 - "Add handle count by type to process properties handle tab" + * #2836706 - "Signature Column in Processes" + * Improved kernel modules list + * Detects custom kernels + * Performance improvements + * KTM resource manager information + * FIXED: + * Windows XP BSODs + * Incorrect drive letter resolving for file handles + * Linked token display on x64 + +1.4 + * NEW/IMPROVED: + * Full support for Windows 7 SP0 + * Basic support for Windows 64-bit + * Ability to unload drivers + * Handle names for Kernel Transaction Manager (KTM) objects + * Ability to save details for processes + * Improved handle granted access display + * Improved process window exit status display + * Improved user prompts + * Ability to open key handles in regedit + * Thread list is more responsive + * Process exit notification (in the process window) is now instant + * Improved control tab indicies + * Small performance improvements + * FIXED: + * #2821437 - "Windows 7 PsTerminateProcess crash" + * #2834578 - "Unable to replace Task Manager with Process Hacker error" + * Properties menu item for handles was disabled most of the time + * Handle names could not be viewed properly without KPH and + on systems without the VC++ 9 runtime + * Minor KPH pool leak + * Annoying popup when Process Hacker replaces Task Manager on + Windows 7 + * No symbols for protected processes + +1.3.9.0 + * NEW/IMPROVED: + * #2812814 - "Auto-scroll option should be remembered" + * Kernel-mode stack traces + * POSIX process support, including command lines and + highlighting + * Hidden processes scanner can now detect FUTo + * Highlighting for .NET and relocated DLLs + * Ability to terminate system threads + * Ability to force terminate threads + * Ability to create services + * Ability to set DEP status of processes in other sessions + * Ability to unload modules of processes in other sessions + * Ability to dump memory to a file + * Process window manipulation + * Process heap information + * Paged and non-paged pool limit display + * Terminator test: TP1a (TP1, alternative method) + * Terminator test: TT1a (TT1, alternative method) + * Terminator test: TT4 (dangerous thread termination) + * Better file object names without KProcessHacker + * Better IP address resolving + * CPU, I/O and memory indicators + * Child windows float by default + * dbghelp is now set up automatically when Process Hacker is + run the first time + * Thread wait analysis now detects NtQueryObject hangs and + named pipe connections + * Automatic tree text coloring, allowing for dark highlighting + colors + * Small performance improvements + * FIXED: + * #2811733 - "System.NullReferenceException" + * Broken system thread start addresses due to sign-extending + instead of zero-extending pointers + * Broken Ctrl+A for memory, results, and PE lists + * Removed several annoying thread-related warnings + * Network connections now display process IDs + * Ctrl+C in the log window + * Window location problem when hiding and restoring the window + * ObjectDisposedExceptions when closing search options windows + * "Inject DLL" menu item enabling problem + * InvalidCastException when attempting to close handles in + the handle filter window + +1.3.8.5 + * NEW/IMPROVED: + * Full support for Windows Vista SP2 + * Users/sessions list + * Window process finder + * Thread wait analysis - right-click a thread and choose + Analyze > Wait to see what a thread is hanging on + * Added ability to create dump files for processes + * Added ability to detach processes from debuggers + * Added "scroll down process tree on startup" option + * Notification icon process list is now sorted + * Lists are dramatically faster (especially the handle list) + * Detailed handle properties + * Event objects can now be modified - set, clear, pulse, reset + * Event pair objects can now be modified - set high, set low + * Semaphore objects can now be modified - acquire, release + * Statistics for token objects + * Token object names now include their session LUIDs + * Added Shift+Del for Terminate Process Tree + * FIXED: + * #2795871 - "Hidden Processes window resizing problem" + * #2800710 - "System.ObjectDisposedException" + * Windows 7 RC BSOD (Windows 7 Beta is no longer supported) at + startup; support is STILL EXPERIMENTAL + * Memory search addresses being in decimal + * Disabling "Warn about dangerous actions" now disables all + process-related prompts + * Terminator window would be hidden if the main window was top-most + * Using the keyboard (Up/Down/Left/Right) in the process list was fixed + * Potential BSOD with KphReadVirtualMemory and KphWriteVirtualMemory + due to incorrect address probing + * Get Function Address window would return incorrect hex addresses + +1.3.8.0 + * NEW/IMPROVED: + * KProcessHacker can now perform process memory reading/writing + by itself and does not require MmCopyVirtualMemory + * KProcessHacker can now bypass all handle-opening protections + * Experimental process protection feature + * Ability to set handle flags such as protect-from-close and inherit + * Better highlighting + * Terminator test: TD1 (debugs a process and closes the debug object) + * Terminator test: TT3 (TT1 is now completely user-mode) + * Shows function file and line numbers where available + * Icon updating is now done on the shared thread to avoid the GUI + blocking when explorer.exe is suspended or is hanging + * FIXED: + * #2785648 - "cursor down crashes PH" + * #2790404 - "System.InvalidOperationException" + * Incomplete or inaccurate thread call stacks + * Windows 7 BSOD + * Crash upon executing terminator test M1 + * Unexpected actions being performed when a key was pressed in + the memory and handle lists + * Changed I/O tray icon tooltip from ROW to RWO + * Corrupted usernames + * .NET processes getting recognized as packed + * Start times like "20 centuries ago" + * Unable to change service configurations + * "Access denied" when changing DEP status or unloading a module + on Windows XP + +1.3.7.5 + * NEW/IMPROVED: + * #2780260 - "add key to open Proc Properties" + * #2780277 - "add to shortcut list for default action" + * #2781625 - "System Idle Process should not have network connections" + * #2784954 - "Ctrl+F find DLLs and not just Handles" + * Customizable tray icons - CPU History, CPU Usage, I/O History, + Commit History and Physical Memory History + * Base Priority, Start Time, and CPU Time columns + * "Terminate Process Tree" + * Can close TCP connections + * Process tree loads instantly + * Process properties (appears to) loads faster + * Decreased CPU usage + * Significantly less memory usage, especially when opening + process properties + * Thread termination now prompts + * Implemented Esc to close windows + * Cycles, Page Priority and I/O Priority in process statistics + * Integrity, I/O priority and page priority columns + * Windows are protected from being offscreen when they load + * Process property window locations are now saved + * The main window dimensions are saved when exiting minimized + * Enabled Reduce Working Set for multiple processes at a time + * Hides Process Hacker network connections by default + * FIXED: + * #2782808 - "Exception generated when CPU History is set to first column." + * #2784922 - "Hidden processes window: window location not remembered" + * #2784924 - "Network connections: process's icon not shown" + * Unhandled exception when process properties is closed within 100ms of + being opened + * Handle filter took a while to start up + * Forgot to add sorting for Private WS, Shared WS and Shareable WS + * dbghelp warnings were not being shown the first time process + properties opened + * Random file-object-related BSODs + * Increased PID limit in Hidden Processes to 65536 + * Inaccurate I/O Total rates when using a refresh interval other than + 1000ms + * Incorrect thread start addresses + +1.3.7.1 + * NEW: + * "-nokph" command line switch to disable KProcessHacker + * FIXED: + * #2779558 - "TreeViewAdv font cannot be initialized" + * KProcessHacker BSOD on some Vista systems + * Minor issue where new handle providers in the process window would + not be added to the shared thread provider + +1.3.7.0 + * NEW/IMPROVED: + * Terminating processes and threads now bypasses all but the most + advanced anti-termination methods + * Better hidden processes scanner (similar to Blacklight's and IceSword's) + which can now detect both Hacker Defender and FU. + * Basic support for Windows 7 in Process Hacker and KProcessHacker + * Proper symbol support with dbghelp.dll + * Private, Shared and Shareable Working Set columns + * Improved handle viewing with KProcessHacker - more object types are visible, + including ALPC Ports and protected process handles + * Stack viewing uses KProcessHacker on Windows Vista + * Handle highlighting + * Lists now have column sorting priority + * Memory list is much faster + * Better thread start addresses, especially on Windows XP + * Job termination + * Elevation button in Options now spawns a child options window instead of + restarting Process Hacker elevated + * Can open process properties from the handle list + * Better "could not initialize configuration" message for Windows Vista + * New Terminator method: assigns a job object to the process and terminates it + * Process Properties menu item in the handle filter window + * Can now close multiple handles at once from the handle filter window + * FIXED: + * Service properties Key handle leak + * Handle deletion detection + * Unhandled exceptions when viewing performance/statistics for a non-existent process + * Network connections for processes without icons would not be displayed + * Virtualization menu item visible on Windows XP + * When processes are terminated they are deselected (to provide feedback to the user) + * When Native API calls failed they would pass through the exception handling + code unchecked, causing random crashes (rarely) + * REMOVED: + * Useless Window menu items (PITA + causes memory leaks due to Microsoft's poor + implementation of MenuItem) + * Registers from the thread window + +1.3.6.5 + * NEW/IMPROVED: + * #2702907 - "CSR Processes: Enable termination of multiple processes" + * #2702909 - "CSR Processes: Show process name when confirming termination" + * #2702911 - "Show process name when terminating process in properties" + * #2702929 - "Add a Cancel button in Options" + * #2713088 - "Network: Ctrl+A and Ctrl+C should copy the processes' names" + * #2714130 - "Option to disable/enable all highlighting colors" + * Job information + * "Inject DLL" function + * Statistics times in System Information and process properties + * Highlighting system + * Configurable max. samples and plotter step size + * Network list with icons + * Can close multiple handles at once + * Less memory usage + * Confirmation dialogs are now consistent and use new Vista interfaces where possible + * KProcessHacker now retrieves thread start addresses + * KProcessHacker now performs memory manipulation (allowing the command lines of more + processes to be displayed) + * KProcessHacker now performs process suspending/resuming on Windows Vista + * Custom module information querying; can now display the modules for protected processes + * Displays service DLL paths + * Thread list displays cycles instead of context switches on Windows Vista + * GUI threads are highlighted (with KProcessHacker) + * Suspended and GUI thread highlighting can be configured + * Special tooltip information for dllhost.exe (shows COM target) + * FIXED: + * #2642442 - "System Information label text gets clipped" + * #2694437 - "Crash when sorting the process list" + * #2713087 - "Processes: Copy should copy only the columns currently used" + * #2716815 - "PH crashes during EnumProcesses" + * Network connections would be readded if their state changed + * Integer overflows in the process provider and system statistics + * Memory leaks with various windows + * Crash when saving with the I/O or CPU History column visible + * Inconsistent Copy menu after columns are modified + * Crash when F5 (Refresh) is held down + * Resizable statusbar + * Module unloading now works properly + * REMOVED: + * Disassembler - not used very often + +1.3.6.1 + * NEW: + * CPU and I/O history columns + * System Cache value in System Information + * FIXED: + * #2625167 - "Commit charge limit should not be 16EB" + * #2642385 - "Maximum File Cache size should not be 16EB" + * Minimize size of the System Information window + * Settings were lost between versions + * Handle and memory leaks + * Integer overflows in System Information + +1.3.6.0 + * NEW: + * #2596473 - "Add "Save Processes"" + * #2596481 - "Add option for one instance of Process Hacker" + * #2601397 - "Ability to sort by name, type etc to all of the windows" + * #2605155 - "CSR Processes: Add Save Processes" + * #2625192 - "Log: Add clear log" + * #2625193 - "Add warning before shutting down, or restarting pc etc." + * #2647235 - "Update Log even when it’s open" + * #2647387 - "Add descriptions to Memory Editor's buttons" + * #2647418 - "Add ctrl+A and ctrl+C to threads, token, modules etc" + * #2647422 - "Suggest filename in Memory Editor->Data->Save" + * #2647435 - "Add descriptions to Memory search results window" + * #2657138 - "Add a toolbar" + * #2657143 - "Add Changelog in Help menu" + * #2675859 - "Log: auto-scroll option" + * #2675864 - "System Information improvements" + * #2675871 - "Suggest file extension everywhere" + * Full CPU, I/O and memory usage history for processes and the OS + * Tooltips for graphs + * Modules tab shows mapped files + * "Reduce Working Set" function + * Can change virtualization for processes + * Customizable columns for the process list/tree + * All lists now have Ctrl+A and Ctrl+C support + * Vista-style lists + * Changing service settings under a limited account now prompts for + elevation instead of giving an Access Denied error + * Run As now works under a limited account and prompts for elevation + * Shows environment variables + * Unicode string memory scanning + * Better Terminate Process confirmation dialog + * FIXED: + * #2602541 - "Bug with 'Hide When Minimized' enabled -svn635" + * Replace Task Manager works correctly when Start hidden is enabled + * csc.exe launching + * Black border bug when restoring from minimized state + * Nulls at the ends of handle names + * Errors when changing service settings + * Slow closing of various windows + * "Hide when minimized" is better + * Opened process properties when a plus/minus was double-clicked + * Redrawing of the process tree when the window is activated/deactivated + * Double-escaping in the Run As tool + * Priority getting/setting under limited accounts + * Run As tool sets new environment variables for the child process + * .NET processes are sometimes labelled as packed + * KProcessHacker BSOD + +1.3.5.0 + * NEW: + * #2596502 - "Add access keys" + * #2596509 - "Double click on mutant-->Open properties window" + * #2596512 - "General tab window goes in the background" + * #2600995 - "Improvements to Help file" + * #2602538 - "Move some options to new window" + * #2602553 - "Add access key for Help" + * #2602606 - "Display a warning when running on 64bit WIN" + * #2605000 - "Add descriptions to some buttons" + * #2605133 - "Add access key to Log" + * #2605140 - "Save Log-->Suggest Log.txt" + * #2605146 - "When you open Log, the entries shouldn't be selected" + * #2605148 - "CSR Processes: Add select all and/or ctrl+A" + * #2605158 - "Log: Add Copy to clipboard and ctrl+A" + * #2605167 - "Add "Hide When Closed" option" + * #2608710 - "Notifications menu improvements" + * #2608801 - "Add Shutdown options" + * #2609039 - "Use thousands separator" + * #2613838 - "Add process' icon before process' name in process' window" + * #2615707 - "Improvements to Log" + * #2617637 - "Help file improvements No2" + * #2617691 - "Add option to replace task manager with Process Hacker" + * #2628961 - "Add ctrl+A in Services and Network window" + * #2628967 - "Add ctrl+C in Processes, Services and Network window" + * #2642505 - "Make some columns' size bigger" + * KProcessHacker is now enabled by default + * KProcessHacker now supports multiple clients at one time + * Process termination now uses KProcessHacker + * Process Hacker can now use KProcessHacker under a limited user account if it is + loaded, giving it full admin-like permissions to processes + * Process Hacker no longer requires UAC elevation - instead, there are two extra + menu items, "Run As Administrator..." and "Show Details for All Processes". It + also automatically prompts for elevation for specific tasks. + * "Run" function + * "Restart" function + * "Set Token" function for Windows XP + * "Verify File Signature" function + * "Re-analyze" function + * Around 50% reduction in CPU usage + * Specific highlighting can be enabled/disabled + * CSR Processes tool - displays hidden processes (this does not detect Hacker + Defender but does detect simple kernel-mode rootkits) + * Processes list in the notification icon menu, for quick actions on processes + * Options window has a detailed description for each setting + * Process window has descriptions for each field + * File processing (signature verification, checks for packed images) is now + asynchronous. This means that there are no more delays when starting + certain programs. + * Notification icon looks better and shows both user and kernel time + * GDI and USER handle counts + * Terminator's TT2 test is more effective now + * FIXED: + * Bug #2553406 - "COMException: Class not registered" + * Bug #2601383 - "Threads' column's width is not remembered." + * Bug #2612242 - "CPU usage > 100%" + * Bug #2615591 - "Bug with Show one graph per cpu?" + * Limited user account weirdness + * No longer deletes the KProcessHacker service if it's already started + * Memory searching now uses KProcessHacker if possible + * Incorrect labeling of process files which can't be read (due to permissions) + as packed + * Insane memory usage on Windows XP + * Now uses the proper method of detecting .NET processes + * System Information now runs on a separate thread + * Disables "Show one graph per CPU" in System Information if there is only one CPU + * Process tree weirdness for users with limited privileges + * CPU usage is now correctly displayed in graphs - K+U are not overlayed anymore + * "Require signatures for" names are now forced to be lower-case + * "Hide handles with no name" is now unique to each process window - + changing it in one window does not affect others + * Dialog/input boxes are now more consistent + * Very old bug where the refresh interval isn't applied at startup + * Displays-2-more-processes-than-actual-number bug + * System Information screwing up when the user tries to open it when it's + already open + * "a hour" -> "an hour", "yesterday ago" -> "a day ago" + * Handle leaks in the process window, thread window, handle list and process updater + * GDI+ handle leaks in the icon menu + * PH window is permanently offscreen if it is hidden when minimized + * Process colors not being refreshed when items are removed + +1.3.2.0 + * NEW: + * Network tab - shows current network connections + * KProcessHacker - an experimental kernel-mode driver for Process Hacker which + finally enables Process Hacker to display all file handles without freezing + * Can protect and unprotect processes (Vista's DRM protection) + * System Information menu item in the tray icon menu + * Hide when minimized option + * Now resolves device names into drive letters - e.g. "\Device\Harddisk1\FileName" into + "C:\FileName". + * Properly verifies system components instead of just checking file permissions + * Configurable dangerous-process-names highlighting + * Added protection against PEB file name spoofing + * Customizable fonts + * Command line switch: -m to hide Process Hacker + * Nice relative times - "2 seconds ago", "14 minutes and 33 seconds ago", etc. + * Displays service descriptions, dependencies, and dependents + * Offers to reset settings if they are corrupt + * FIXED: + * Bug #2527154 - "Process Hacker crashes on Win XP" + * Small UI enabling/disabling fixes + * Now the process tree and service list update instantly after + starting Process Hacker instead of waiting + * Random "Generic GDI+ Error" exceptions + * Small performance improvements + * Now shows proper command lines of programs where they contain null characters + * Handle filter actually works now + * Relative RunDLL targets + * Handle leaks with threads + +1.3.1.0 + * NEW: + * "Free" and "Decommit" actions for memory regions + * "Description" column for processes + * Current Directory for processes automatically updates + * Can now display the file names of DRM-protected + processes (like audiodg.exe) + * Now displays thread information under the thread list + * Module file name info for thread start addresses + and stack traces + * Highlighting for .NET processes and packed executables + * Shows CPU usage and physical memory usage in the status bar + * "Reload Struct Definitions" menu item + * Struct Searcher - displays addresses which match the specified + struct definition + * Ability to unload remote modules (by remote thread + injection) + * New float and double types for structs + * Special tooltip info for rundll32.exe + * Verifies file signatures (and detects Windows components by + checking the files' owners and ACLs) + * Highlights processes which have invalid signatures or are + pretending to be system processes + * Better method for suspending/resuming processes + * FIXED: + * System information window resizing + * "Overflow error" exceptions + * Problems with the search button + * Process properties for DPCs and Interrupts + * Disabled expanding of processes when double-clicking + them + * Now shows non-existent parent PIDs + * build-and-clean script is now XP compatible + * Redrawing problems with the lists in the process window + +1.3.0.0 + * NEW: + * Process tree using TreeViewAdv + * Process properties window with statistics and graphs + * System Information Window with statistics and graphs + * Detailed token information, including source, owner and primary group + * Information about remote handles to events, mutants, sections and tokens + * Terminator tool - tries many techniques to terminate processes + * Highlighting for UAC elevated processes and processes in job objects + * Struct reader for examining PEBs and TEBs - note that this can be + extended by writing your own definitions + * Better tray icon - displays a graph, and the tooltip contains the current + CPU usage and the process using most of the CPU + * FIXED: + * Shows SIDs without names (like Logon IDs) in SDDL format + * Uses PROCESS_QUERY_LIMITED_INFORMATION on Windows Vista - e.g. on audiodg.exe + * Symbols are now bound to each process - no more weird stack traces/symbols + * Handle filtering is now much faster - uses a cache for session ID checking + +1.2.6.5 + * A new member of the project - Dean + * Can view thread usernames, groups and privileges + * The handle filter window is now faster + * Added thread start addresses in thread list + * The process list now has less CPU usage + * Fixed some processes not having icons + +1.2.6.0 + * Fixed the fix for the huge regression - the cause was a double "free" of the same handle + * Added Assistant - can start processes as any user, including SYSTEM, + LOCAL SERVICE and NETWORK SERVICE. Injector's create process item is + now deprecated. + * Fixed service handle leaks which caused service deletions to be + undetected. + * Fixed service list in process tooltips when new own-process services are started and when + shared-process services are stopped. + +1.2.5.1 + * Fixed huge regression caused by revision #250 in Win32.ProcessHandle.~ProcessHandle() + +1.2.5.0 + * Fixed wrong usernames when Process Hacker is running as a non-admin user + * Gets command line of processes without using Injector + * Added highlighting for debugged processes + * Added viewing/setting of process affinity + * Fixed all handle leaks + * Added I/O counters to misc. info + * Added CPU usage column + * Fixed services in tooltips + * Added highlighting for processes with services + * System and System Idle Processes now have usernames (hardcoded in) + * Handle finder now only searches in processes with same session ID (faster, + avoids hangs) + * Fixed window activation when showing Process Hacker from the tray icon + * Fixed highlighting + * Fixed various memory corruption issues + +1.2.3.5 + * Added handle viewing/closing support + * Fixed random crashes + * Doesn't update threads/handles unless viewing them + * Fixed most handle leaks + * Fixed randomly changing process usernames + * Added WaitReason column in thread list + * Highlights suspended threads in thread list + * Only require highestAvailable elevation for UAC + +1.2.3.0 + * Fixed incorrect messages about "new services" (actually fixed this time) + * Fixed Injector - now uses MinGW instead of Visual C++ + * "Create Process..." feature of Injector works on XP now + * Added StartType column in services list + +1.2.2.5 + * Now shows function parameters in call stack + * Added Injector + * Added "Get Symbol Name From Address..." and "Find SYSTEM processes with same Session ID" + * Services list now displays a blank space when there is no PID + * Fixed services with own process and interactive type + * Fixed random crashes with the process list and services list + * Added copyright information (to comply with VistaMenu and SplitButton licensing) + * Now displays user names of most processes using the Terminal Server APIs + * Fixed incorrect messages about "new services" + * Added tray icon + +1.2.1.5 + * Fixed disassemblies from PE window - they disassemble the right function now + * Fixed UI inconsistencies + * Allow Toolhelp module listings + * Updated help + * Fixed service start/stop buttons in service window + +1.2.1.0 + * Can now display services + * Displays services in process tooltips + +1.2.0.5 + * Updated credits + * Fixed crash when opening two thread inspectors with the same process and thread + * Disassembler window can now display backward short jumps (>0x7fffffff) + * Now unloads symbols for all other EXEs before opening the thread inspector + * Improvements in symbol loading + +1.2.0.0 + * Added PE Inspector (can read exe/dll/sys files and their properties) + * Added "Search Online" menu item + * Added highlighting of processes and threads + * Added "Always on Top" menu item + * Added status bar messages and log + * Added "Go to Parent" menu item + * Removed "Close Active Window" menu item - practically same as "Terminate Process" + * Added disassembler + * Added Thread Inspector - shows call stack of threads + +1.1.5.0 + * Fixed memory leak with process icons + * Removed useless starting "\" in account names + * Fixed group menu item enabling and disabling + * Added filtering of search results + +1.1.4.5 + * Fixed crash when attempting to open privileges for System on XP + * Added Groups Window (to view process groups) + * Fixed Search window startup position + +1.1.4.0 + * Enabled Select All for all lists on the main window + * Added Privileges Window (to enable, disable and remove privileges) + * Fixed error messages - they now have the correct Win32 error descriptions + +1.1.3.5 + * Now saves column settings + * Now saves results and memory window settings + * Now saves selected tab + +1.1.3.0 + * Completely rewrote thread list code + * Can now display kernel threads on Vista + * Disables thread priority menu item when there is an error + * Now displays process 0 as System Idle Process + * Fixed thread list for System Idle Process + * Now displays description for System + * Now has a good system of copying list items (currently in every ListView) + +1.1.2.0 + * Completely rewrote process list code + * Added lots of information to process tooltips + +1.1.1.0 + * Memory editor is now properly activated from the Hacker Window + * Fixed obscure bug where the process list disables itself + * Added my email address + * Added CONTEXT code + * Now bolds the kernel name when the System process is selected + * Fixed opening Memory Editor from Results Window + * Changed Search button to a SplitButton + * Added username column + * Now shows process description and filename in tooltip + * Added more "options" + * Added keyboard shortcuts for terminating process(es) (Del) and refreshing (F5) + * Added some menu items - Select All and Refresh + +1.1.0.5 + * Upgraded messageboxes when performing operations on processes and threads to OKCancel + * Fixed checking for dangerous PIDs on XP + * Fixed getting icons for certain processes on XP + * Fixed crashes when attempting to read from invalid memory locations + * Fixed crash when opening help window after closing it + * Fixed default menu item text drawing + +1.1.0.0 + * Fixed certain programs' modules not having a description + * Major refactoring in code + * Moved memory editor panel into its own form + * Moved search into its own form + * Merged string scanning and heap scanning into the Search form + * Removed debug programs option + * Now has proper main menu + * Search results window is now on its own thread + * Search results list now uses VirtualMode - huge speed increase + * Added "prevent overlapping results" option + * Made memory editor windows spawn in separate threads + * Removed all Application.DoEvents() calls + * Fixed cursor problems + * Now displays busy cursor on startup + * Intersect menu is now sorted + * Fixed form focusing + * Added Window menu items in all forms + +1.0.1.0 + * Fixed occasional "Object not set to an instance of object" error + * Added link to SourceForge project page + * Fixed FlatStyle inconsistencies + * Fixed "invalid characters in path name" problem on Windows X \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/ExtraTools.sln b/branches/ph-plugins/ExtraTools/ExtraTools.sln new file mode 100644 index 000000000..bc820629f --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ExtraTools.sln @@ -0,0 +1,50 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Native", "..\ProcessHacker.Native\ProcessHacker.Native.csproj", "{8A448157-E1A7-4DDF-954E-287F1117832B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Common", "..\ProcessHacker.Common\ProcessHacker.Common.csproj", "{8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NtObjects", "NtObjects\NtObjects.csproj", "{06AC6477-D3DA-4997-9A4A-4F809F7C9396}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NtProfiler", "NtProfiler\NtProfiler.csproj", "{E3CEB6D7-7080-4089-B54E-41025E30CE46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessAnalyzer", "ProcessAnalyzer\ProcessAnalyzer.csproj", "{A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SysCallHacker", "SysCallHacker\SysCallHacker.csproj", "{39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.Build.0 = Release|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.Build.0 = Release|Any CPU + {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Release|Any CPU.Build.0 = Release|Any CPU + {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Release|Any CPU.Build.0 = Release|Any CPU + {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Release|Any CPU.Build.0 = Release|Any CPU + {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj b/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj new file mode 100644 index 000000000..80fb1ef7a --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj @@ -0,0 +1,92 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {06AC6477-D3DA-4997-9A4A-4F809F7C9396} + WinExe + Properties + NtObjects + NtObjects + v2.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + + + + + + + + Form + + + ObjectsWindow.cs + + + + + ObjectsWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + {8A448157-E1A7-4DDF-954E-287F1117832B} + ProcessHacker.Native + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs new file mode 100644 index 000000000..4399d9a38 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs @@ -0,0 +1,163 @@ +namespace NtObjects +{ + partial class ObjectsWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ObjectsWindow)); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.treeDirectories = new System.Windows.Forms.TreeView(); + this.imageList = new System.Windows.Forms.ImageList(this.components); + this.listObjects = new System.Windows.Forms.ListView(); + this.columnName = new System.Windows.Forms.ColumnHeader(); + this.columnType = new System.Windows.Forms.ColumnHeader(); + this.columnData = new System.Windows.Forms.ColumnHeader(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.treeDirectories); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.listObjects); + this.splitContainer.Size = new System.Drawing.Size(794, 441); + this.splitContainer.SplitterDistance = 264; + this.splitContainer.TabIndex = 0; + // + // treeDirectories + // + this.treeDirectories.Dock = System.Windows.Forms.DockStyle.Fill; + this.treeDirectories.HideSelection = false; + this.treeDirectories.ImageKey = "directory"; + this.treeDirectories.ImageList = this.imageList; + this.treeDirectories.Location = new System.Drawing.Point(0, 0); + this.treeDirectories.Name = "treeDirectories"; + this.treeDirectories.SelectedImageKey = "directory"; + this.treeDirectories.Size = new System.Drawing.Size(264, 441); + this.treeDirectories.TabIndex = 0; + this.treeDirectories.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeDirectories_MouseDown); + this.treeDirectories.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.treeDirectories_NodeMouseClick); + // + // imageList + // + this.imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream"))); + this.imageList.TransparentColor = System.Drawing.Color.Transparent; + this.imageList.Images.SetKeyName(0, "object"); + this.imageList.Images.SetKeyName(1, "directory"); + this.imageList.Images.SetKeyName(2, "symboliclink"); + this.imageList.Images.SetKeyName(3, "event"); + this.imageList.Images.SetKeyName(4, "mutant"); + this.imageList.Images.SetKeyName(5, "device"); + this.imageList.Images.SetKeyName(6, "key"); + this.imageList.Images.SetKeyName(7, "alpc port"); + this.imageList.Images.SetKeyName(8, "port"); + this.imageList.Images.SetKeyName(9, "section"); + this.imageList.Images.SetKeyName(10, "job"); + this.imageList.Images.SetKeyName(11, "callback"); + this.imageList.Images.SetKeyName(12, "type"); + this.imageList.Images.SetKeyName(13, "windowstation"); + this.imageList.Images.SetKeyName(14, "desktop"); + this.imageList.Images.SetKeyName(15, "filterconnectionport"); + this.imageList.Images.SetKeyName(16, "semaphore"); + this.imageList.Images.SetKeyName(17, "session"); + this.imageList.Images.SetKeyName(18, "keyedevent"); + this.imageList.Images.SetKeyName(19, "driver"); + // + // listObjects + // + this.listObjects.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnName, + this.columnType, + this.columnData}); + this.listObjects.Dock = System.Windows.Forms.DockStyle.Fill; + this.listObjects.FullRowSelect = true; + this.listObjects.HideSelection = false; + this.listObjects.Location = new System.Drawing.Point(0, 0); + this.listObjects.Name = "listObjects"; + this.listObjects.Size = new System.Drawing.Size(526, 441); + this.listObjects.SmallImageList = this.imageList; + this.listObjects.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listObjects.TabIndex = 0; + this.listObjects.UseCompatibleStateImageBehavior = false; + this.listObjects.View = System.Windows.Forms.View.Details; + this.listObjects.DoubleClick += new System.EventHandler(this.listObjects_DoubleClick); + // + // columnName + // + this.columnName.Text = "Name"; + this.columnName.Width = 200; + // + // columnType + // + this.columnType.Text = "Type"; + this.columnType.Width = 100; + // + // columnData + // + this.columnData.Text = "Data"; + this.columnData.Width = 200; + // + // ObjectsWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(794, 441); + this.Controls.Add(this.splitContainer); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ObjectsWindow"; + this.Text = "NtObjects"; + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + this.splitContainer.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.TreeView treeDirectories; + private System.Windows.Forms.ImageList imageList; + private System.Windows.Forms.ListView listObjects; + private System.Windows.Forms.ColumnHeader columnName; + private System.Windows.Forms.ColumnHeader columnType; + private System.Windows.Forms.ColumnHeader columnData; + + } +} + diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs new file mode 100644 index 000000000..23ae7eeb7 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace NtObjects +{ + public partial class ObjectsWindow : Form + { + private class TreeViewSorter : System.Collections.IComparer + { + public int Compare(object x, object y) + { + return ((TreeNode)x).Text.CompareTo(((TreeNode)y).Text); + } + } + + public ObjectsWindow() + { + InitializeComponent(); + + try + { + using (var thandle = ProcessHandle.GetCurrent().GetToken(TokenAccess.AdjustPrivileges)) + { + try { thandle.SetPrivilege("SeCreateGlobalPrivilege", SePrivilegeAttributes.Enabled); } + catch { } + } + } + catch + { } + + treeDirectories.TreeViewNodeSorter = new TreeViewSorter(); + Win32.SetWindowTheme(treeDirectories.Handle, "explorer", null); + treeDirectories.Nodes.Add("\\", "\\"); + treeDirectories.SelectedNode = treeDirectories.Nodes["\\"]; + this.PopulateDirectories(); + this.ChangeDirectory(); + treeDirectories.SelectedNode.Expand(); + } + + private void treeDirectories_MouseDown(object sender, MouseEventArgs e) + { + treeDirectories.SelectedNode = treeDirectories.GetNodeAt(e.Location); + } + + private void PopulateDirectories() + { + this.PopulateDirectory("\\"); + } + + private void PopulateDirectory(string directory) + { + try + { + using (DirectoryHandle dhandle = + new DirectoryHandle(directory, DirectoryAccess.Query)) + { + var objects = dhandle.GetObjects(); + + foreach (var obj in objects) + { + if (obj.TypeName != "Directory") + continue; + + this.GetTreeNode(directory).Nodes.Add(obj.Name, obj.Name); + + this.PopulateDirectory(this.NormalizePath(directory + "\\" + obj.Name)); + } + } + } + catch (WindowsException) + { } + } + + private string NormalizePath(string path) + { + string[] s = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + + return "\\" + string.Join("\\", s); + } + + private TreeNode GetTreeNode(string path) + { + return this.GetTreeNode(treeDirectories.Nodes["\\"], path, 0); + } + + private TreeNode GetTreeNode(TreeNode root, string path, int index) + { + string[] s = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + + if (index >= s.Length) + return root; + + if (root.Nodes[s[index]] == null) + return null; + + return this.GetTreeNode(root.Nodes[s[index]], path, index + 1); + } + + private void ChangeDirectory() + { + listObjects.Items.Clear(); + + if (treeDirectories.SelectedNode != null) + { + listObjects.BeginUpdate(); + + try + { + using (DirectoryHandle dhandle = + new DirectoryHandle(this.NormalizePath(treeDirectories.SelectedNode.FullPath), DirectoryAccess.Query)) + { + var objects = dhandle.GetObjects(); + + foreach (var obj in objects) + { + var item = listObjects.Items.Add(new ListViewItem(new string[] { obj.Name, obj.TypeName, "" })); + + if (imageList.Images.ContainsKey(obj.TypeName.ToLower())) + item.ImageKey = obj.TypeName.ToLower(); + else + item.ImageKey = "object"; + + if (obj.TypeName == "SymbolicLink") + { + try + { + using (SymbolicLinkHandle shandle = + new SymbolicLinkHandle( + this.NormalizePath( + treeDirectories.SelectedNode.FullPath + + "\\" + obj.Name), + SymbolicLinkAccess.Query)) + item.SubItems[2].Text = shandle.GetTarget(); + } + catch + { } + } + } + } + } + catch (WindowsException) + { } + + listObjects.EndUpdate(); + } + } + + private void treeDirectories_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + { + this.ChangeDirectory(); + } + + private void listObjects_DoubleClick(object sender, EventArgs e) + { + if (listObjects.SelectedItems.Count != 1) + return; + + if (listObjects.SelectedItems[0].SubItems[1].Text == "Directory") + { + treeDirectories.SelectedNode = + treeDirectories.SelectedNode.Nodes[listObjects.SelectedItems[0].SubItems[0].Text]; + this.ChangeDirectory(); + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx new file mode 100644 index 000000000..f35146b97 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx @@ -0,0 +1,1299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo + WAAAAk1TRnQBSQFMAgEBFAEAARwBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABYAMAAQEBAAEgBgABYP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8APgADAgEDATsBTQFZ + AXwBKAFhAY8B6QEmAV4BjAHsATgBWAF3AbsBFwEYARkBIGQAAUMBWwFrAYUBSQGVAcIB6QFDAYQBswHg + ATMBOwFCAVYcAAE9AXoBzwHVASgBgQL/ASABfAL/AToBZgGlAa9EAAE8AVIBXwGCAR0BcAGnAf8BKgGL + Ab0B/wEzAaMB2QH/ATIBnwHZAf8BJgF4AbMB/wE7AUwBWwGCFAADCgENAw8BEwFyAU8BPQGYAZMBRgEt + AeMBpAFGAScB9QGpAUcBJAH6AagBRQEkAfoBogFDASUB9gGVAUIBKQHqAYgBUwE8AboDEQEWAwoBDRgA + AwoBDQMmATEBUAGUAbYB0wGGAekB+QH/AUwB2QH1Af8BPQGOAcIB8wE1AT0BRQFdFAABSQF+AcQBzAFg + AaQC/wGXAcIC/wGUAcAC/wFQAZgC/wE7AWYBpQGvOAABLAEzATcBRwEeAXgBrQH/AS8BmAHGAf8BSgHI + AeMB/wFGAcUB5wH/ASgBnQHIAf8BJwG2AeQB/wEtAZcB0AH/ATUBYAGDAckQAAFNAVkBZAF5ASYBdwHJ + AfsBhQJgAfsBvwFfATQB/wH+AbkBYAH/Af4BuQFhAf8B/gG5AWEB/wH+AbkBYQH/Af4BuQFgAf8B/gG5 + AWAB/wGxAUgBIwH/AXcBYwFsAfUBMQF9AcgB+AFRAV8BbAGDCAADGgEhA28BnQOYAfADoQH/A6sB/wFZ + AaoB1gH/AaEB5gH4Af8BNwHSAfIB/wFGAdYB9gH/AT0BkQHFAfYBNwE/AUgBYgMKAQ0DCgENCAABSAGV + Av8BoAHHAv8BgwG3Av8BfgG0Av8BlwHCAv8BUgGZAv8BPAFnAaUBrzAAATwBaQGAAa4BLgGOAb0B/wE3 + AYwBugH/ASoBdwGvAf8BQQGXAcYB/wELAVoBlQH/AQgBSQGIAf8BGwGSAc4B/wEzAacB3QH/ASwBZgGS + AeEQAAEpAX0B0AH+AYIBugHuAf8BnwFlAVcB/wH1AbsBhAL/AawBWgH/Af4BqAFZAf8B/gGiAVYB/wH+ + AZwBUgL/AaMBVAL/AZ8BTwH/AfgBrgF3Af8BpAFdAUkB/wGDAbwB7wH/ASoBdwHJAf4EAAM5AUoDoAH0 + A84B/wPtAf8D9AH/A/UB/wGtAdMB6wH/AVwBrgHgAf8BmQHiAfYB/wFSAdwB9QH/AUUB2QH2Af8BOgGN + AcQB9gE8AX0BvAHoAT0BfgG/AegBMQE4AT8BUwQAAVIBnQL/AaQBywL/AYsBvAL/AXYBsAL/AYABtQL/ + AZgBwwL/AVUBnAL/AT4BZwGiAawoAAEyAXsBoQHZATABoAHIAf8BVgGeAcgB/wFyAb0B3gH/AYIB3gH2 + Af8BWAG0AdoB/wEMAVwBkQH/ARUBgAG9Af8BJAGyAfMB/wE8Aa4B4gH/ATIBbAGRAdUQAAEpAXsBywH8 + AXcBswHqAf8BswGeAZQC/wG3AV8C/wG2AWIB/wH+AbIBYAH/Af4BrAFcAf8B/gGlAVgB/wH9AZ4BUgH/ + Af4BlwFNAv8BjQFCAf8BvAGPAYIB/wF9AbgB7QH/ASoBcwHDAfoEAAOeAeoD3gH/A/MB/wPbAf8D0gH/ + A9sB/wPWAf8BogG3AcUB/wFgAbAB3AH/AWIBvgHlAf8BXQHZAfIB/wFNAdsB9gH/AVoB3QH3Af8BVAHY + AfUB/wEyAX8BxwH3ATMBOgFDAVkBUQFoAYcBkgF4AbMC/wGlAcwC/wGNAb0C/wF4AbIC/wGDAbYC/wGZ + AcMC/wFXAZwC/wE/AWgBogGsIAABOAF6AZkBxgExAacBzwH/AXkB3QHyAf8BLQF2Aa8B/wGMAeAB9gH/ + AXcB0AHtAf8BKwGKAbgB/wEdAY0ByQH/ASUBtQHvAf8BHwGdAd0B/wFDAbUB5QH/ASkBegGsAe4QAAE9 + ATUBMQFQAYoBUwFDAf8B/AHIAasC/wHRAZgB/wH+AccBbAH/Af4BvwFnAf8B/gG5AWMB/wH+AbEBXQH/ + Af4BqAFYAf8B/QGgAVMC/wG3AXkB/wH+AakBgAH/AYgBTwFBAf8BRQE8ATcBYAQAA6AB6gPwAf8D3gH/ + A9QB/wPSAf8D2wH/A9YB/wO/Af8DsAH/AW8BpQHHAf8BiQHdAfQB/wFpAeAB9gH/AXIB4gH3Af8BXgHf + AfYB/wFUAdoB9gH/ATsBfQHDAfMEAAFSAWkBhwGSAXoBtAL/AaYBzAL/AY4BvgL/AXsBswL/AYUBuAL/ + AZsBxQL/AVgBngL/AUABaAGgAaoYAAE/AWgBewGaATsBtAHaAf8BewHZAe4B/wFOAa4B1AH/ATcBgQG0 + Af8BPwGSAb0B/wEgAYsBvAH/AS0BrAHfAf8BLwG5Ae4B/wEgAZIB0gH/AR4BnQHdAf8BQwG3AegB/wEq + AZYBzwH/ASwBMwE3AUcPAAEBAUkBOwEzAWABxAFLAR4B/wH2AeQB1gL/AeQBpAL/AdQBcQL/AckBaAL/ + AcABYgL/AbYBXgL/AcEBgAH/AfYB1wHGAf8BxQFIAR4B/wFNAT0BNQFpAwMBBAQAA6IB6gPyAf8D4gH/ + A9gB/wPVAf8D3AH/A9gB/wPAAf8DswH/AWEBqAHRAf8BqQHuAfkB/wF9AeYB+AH/AZoB6AH4Af8BfQHR + AfAB/wGAAeIB9gH/AUIBkgHLAfAIAAFTAWkBhwGSAXsBtgL/AagBzgL/AZABvwL/AYsBvQL/AaAByAL/ + AWABpAL/AUUBdwG+AckYAAEwAZUBvQHlAWgB3QHyAf8BggHdAfIB/wErAaQBygH/ASkBqgHQAf8BNQGp + AdUB/wE1AakB3AH/ATUBuAHpAf8BIwGRAcsB/wEiAZABzgH/ASMBpgHjAf8BRAG6AesB/wE7AawB4QH/ + ATwBYwF7AawQAAMFAQcBTwE/ATYBaQG8AUcBGwH/AfQB4gHUAf8BTQF6AakB/wFMAXoBqAH/AUwBegGo + Af8BTQF6AakB/wHzAdYBwwH/Ab4BRQEbAf8BUQFAATcBbwMIAQoIAAOkAeoD8wH/A+cB/wPdAf8D2QH/ + A+AB/wPbAf8DxAH/A7gB/wGDAawBxwH/AV0BwQHqAf8BowHwAfsB/wGAAdQB8AH/AX0BxwHsAf8BUQGe + AdMB9QE0AT0BRgFZDAABVAFqAYcBkgF9AbcC/wGpAc4C/wGoAc0C/wFvAawC/wFeAZoB7gH/A4cB9wMb + ASMUAAE8AYcBnQG8AVQB0gHrAf8BnQHoAfkB/wF3AeEB9gH/AWAB2AH2Af8BUQHTAfcB/wFAAcEB6gH/ + ATABqgHbAf8BKgGfAdYB/wElAZcB0QH/ASYBpwHgAf8BKgGvAekB/wFFAbYB5gH/AS0BdQGfAeIUAAME + AQUBWwFIAT8BogEzAWwBpwH/AZwBzAH4Af8BrwHUAfcB/wGvAdQB9wH/AaUBzwH2Af8BMwFzAa4B/wFe + AUoBQgGtAwcBCQwAA6UB6gP0Af8D6gH/A+EB/wPdAf8D4wH/A94B/wPJAf8DvQH/A78B/wGRAb4B3gH/ + AW4ByQHsAf8ByQHzAfsB/wFYAa4B1QHtAS0BNQE7AUgUAAFUAWsBhwGSAYEBtwL/AXsBtAL/AW4BpwHy + Af8D0gH/A7UB/wOIAf4DhgH7A4QB+wN5AdYDTQFwBAABGwEdAR4BJQExAagBxgHmASYBsgHZAf4BfQHT + AesB/wHLAfAB+wH/AYQB3QH1Af8BRgHGAesB/wE9Ab0B6QH/ATYBtQHlAf8BLAGmAdwB/wErAawB4wH/ + ASoBsAHpAf8BRwG5AegB/wEkAYQBuwH5GAABPgFbAXwBxAGmAcoB7gH/AasBzAHqAf8BpwHQAfYB/wGo + AdAB9gH/AasBzAHqAf8BpwHNAe4B/wE+AV8BhAHMEAADpwHqA/UB/wPuAf8D5gH/A+IB/wPmAf8D4QH/ + A80B/wPCAf8DwgH/AuIB4wH/AWEBmAG5AfUBTgGOAbABywE4AUgBUwFjHAABVgFrAYcBkgFkAY0BwgHP + A5EB+wPHAf8DzAH/A8cB/wPGAf8DwwH/A8AB/wOCAfUDTQFwCAABEwIUARkBPQGLAZ8BuwFXAcsB5gH/ + AakB4AHzAf8BogHjAfgB/wFJAcsB8QH/AUABxAHvAf8BOQG9AewB/wEyAbgB6wH/AS0BtAHqAf8BSAG8 + AeoB/wElAY0ByAH7GAABKAFeAZQB7QHZAegB9wH/AZcBxQHxAf8BjgG7AeUB/wF+AakB0QH/AYkBtQHf + Af8BzQHfAe4B/wEqAWcBoAHxAwQBBgwAA6gB6gP2Af8D6wH/A94B/wPWAf8D1QH/A9EB/wPDAf8DvAH/ + A8AB/wPlAf8DmQHqLAADNgFIA5IB/gPUAf8DyAH/A7wB/wO6Af8DwgH/A8QB/wN6AdYQAAEyAUEBRQFT + ATgBvwHgAf8BigHWAesB/wG2AeoB+gH/AVUBzgHxAf8BQQHDAe4B/wEqAa8B6QH/ATYBsAHoAf8BQgG4 + AegB/wExAXgBnQHYGAABCwE9AYcB/wF7AZcBuAH/AYoBtwHkAf8BcAGcAcgB/wEUAT8BbQH/ARgBQwFx + Af8BIQFEAWoB/wEUATwBZQH6AwUBBwwAA6oB6gP3Af8D5wH/A+8B/wP2Af8D+wH/A/oB/wPwAf8D3gH/ + A8MB/wPmAf8DmgHqMAADlAH8A90B/wPFAf8DeAHBA3kBwwOsAf8D1wH/A4YB+xQAARsCHgElASoBsQHU + AfcBbAHPAekB/wG8AeoB+AH/AbkB6QH5Af8BcAHMAfAB/wFAAbMB6gH/AS8BmAHUAf8BPAFUAWEBfxgA + AQ4BSgGXAf8BEQFXAZ8B/wEOAUkBigH/AQ4BSgGHAf8BEAFKAYcB/wEUAUsBhQH/AREBQAF0Af8BGAE4 + AV0B8RAAA6sB6gP4Af8D/hn/A/sB/wPqAf8DnAHqMAADlgH7A+QB/wPPAf8DegHCBAADjgH/A4wB/wOJ + AfscAAE/AXQBggGZATMBogHAAeIBLQGnAcsB8QEmAawB0wH9ATQBtgHiAf8BIQGOAcUB/gErAY0BxgH/ + ATsBaQGBAbMUAAE4AUABSwF3AREBTgGVAf4BEQFXAZsB/wERAVcBmQH/ARABUgGTAf8BDgFJAYcB/wEO + AT4BcQH+ATkBPwFIAYEQAAOPAb8D4QH/A/4Z/wP7Af8DzwH/A3QBoTAAA4kB1gPiAf8D5wH/A7kB/wOT + Af8sAAEQAhEBFQEeASEBIgEqAQwCDQEQAT4BYwFuAYMBMQG0AeAB/wFDAbUB6wH/ASIBggG0AfsYAAE5 + AUEBTgF9ARgBSgGKAfQBDwFKAZAB/wEOAUcBigH/ARYBQwF7AfUBOgFCAU0BhBQAAyIBKgOcAdED0AH/ + A+gB/wPzAf8D/QH/A/wB/wPtAf8D4AH/A8IB/wOLAcMDFQEbMAADUgFwA5cB9QPkAf8D7gH/A5YB/zwA + AT4BhAGYAbQBJgGqAdEB+wE8AXMBiwGvSAADBQEHA0MBVQOEAa4DnQHWA7QB+wOyAfkDlQHNA34BqQM3 + AUYDAgEDOAADUgFwA4oB1gOZAfsDlwH7KwABAQFJAUUBQgFkAZwBdgFXAfQBsgGAAVUB/wGvAX0BUQH/ + AZwBcwFTAfYBUgFMAUcBc1gAAXkBYQFSAakBwwGOAWcB/wHAAYsBZQH/Ab4BiAFjAf8BuwGFAWAB/wG5 + AYMBXgH/AbQBfQFbAf8BsQF6AVcB/wGuAXgBVgH/Aa0BdQFVAf8BqQFwAVAB/wF5AWEBUgGpWAABLwEt + ASwBPQGVAXcBXAHnAbIBgQFXAf8BywGrAYkB/wHRAbQBlQH/AbsBjgFiAf8BtQGHAVkB/wGrAXYBTAH/ + AZMBcgFYAesBNQEyATEBRlAAAcgBkgFrKf8BqQFxAVAB/1AAAQ8BEAEPARQDBgEIAawBfwFWAf4B1QG7 + AZ8B/wHWAbsBngH/AdMBuAGcAf8B0QGzAZQB/wG3AYkBXAH/AboBjgFhAf8BuAGNAWAB/wGyAYEBVQH/ + AacBdQFNAf4BeQFhAVIBqQHDAY4BZwH/AcABiwFlAf8BvgGIAWMB/wG7AYUBYAH/AbkBgwFeAf8BtAF9 + AVsB/wGyAXsBWQH/AbEBegFXAf8BrgF4AVYB/wGtAXUBVQH/AasBdAFTAf8BqQFyAVIB/wGpAXABUAH/ + AXkBYQFSAakUAAHYAaIBeB//Af4C/wL+Bf8BtwGBAV0B/0cAAQEBQgFHAUIBYwFhAZEBZQH0AWIBogFp + Af8BYAGhAWgB/wGxAX0BUQH/AeEBzQG4Af8B2AHAAaUB/wHYAcABpwH/AdQBugGdAf8BuAGMAV8B/wG3 + AYoBXwH/AbgBjQFgAf8BugGOAWEB/wGxAX0BUQH/AcgBkgFrNf8BqQFxAVAB/xQAAdkBowF4G/8B/gL/ + Av4B/wP+Bf8BugGFAV8B/0AAASwBLgEtAT0BZgGNAWoB5wFlAaMBawH/AZMBwAGZAf8BngHHAaQB/wFw + AawBdwH/Aa8BfQFQAf8B4wHQAbwB/wHaAcMBqwH/AdMBuAGeAf8BxwGjAXwB/wHBAZgBbgH/AbYBiQFb + Af8BtwGKAV8B/wG6AY4BYQH/AbEBgAFTAf8BygGUAW0L/wH+A/8B/QH/Av4B/QH/Av4B/AH/Av4B/AH/ + Av4B/AH/Av4B/AH/Av4B+gH/Av4B+gH/AvwB+QX/AaoBcgFSAf8MAAFsAVoBTwGUAa0BhQFmAeAB2wGk + AXkp/wG9AYcBYgH/QAABYwGeAWsB/gGpAc0BrwH/AaYBzAGsAf8BogHJAakB/wGZAcUBnwH/AWoBqQFz + Af8BrgF7AU4B/wHcAcgBsAH/Ab8BnwGBAf8BuAGNAWQB/wHRAbMBjwH/AdEBswGPAf8BuwGQAWUB/wG8 + AZEBZwH/AbcBigFfAf8BsQF9AVEB/wHMAZcBbgf/AfwD/wH9Af8C/gH8Af8C/gH8Af8C/gH7Af8C/QH6 + Af8C/QH6Af8C/QH6Af8C/QH6Af8C/AH3Af8C+wH2Bf8BrAF0AVMB/wwAAbEBhwFpAeAD2wHgAdwBpwF6 + Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHc + AacBegH/AdwBpwF6Af8B3AGnAXoB/wHAAYsBZQH/BAADAgEDAyIBMANPAd8DZAH0A2sB9wNqAfkDPAFe + Ay8BQwNqAfIDawH3A2QB9ANPAd8DIgEwAwIBAwQAAWEBoQFoAf8BwAHaAcUB/wGtAdABswH/AasBzgGx + Af8BngHIAaYB/wFsAaoBdQH/AZUBegF9Af8BWQFgAcgB/wFPAVcB4wH/AU4BVQHgAf8BVwFeAcgB/wGQ + AXcBhAH/AbsBkAFlAf8B0QGzAY8B/wHGAaIBegH/AagBeAFPAf4B0QGcAXIF/wL+AfwB/wL+AfwB/wL+ + AfwB/wL9AfsB/wL9AfsB/wL9AfoB/wL9AfgB/wL7AfkB/wH7AfoB9wH/AfsB+gH2Af8B+wH4AfQF/wGw + AXkBVwH/DAABvQGTAXQB4APbAeAB3QGtAYYB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/ + AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/Ab8BkAFuAf0EAAMd + ASkDUgHXA4gB7wOhAe0DrgH/A5cB/wNwAf8DdwH/A5cB/wOuAf8DoQHtA4gB7wNSAdcDHQEpBAABXwGg + AWcB/wHFAd4ByQH/AbQB1AG5Af8BpAHJAaoB/wGBAasBmgH/AWABbAHDAf8BTwFXAeAB/wFlAWcB6wH/ + ApMB9AH/AWABYgHqAf8BVwFaAeQB/wFIAVEB3AH/AV8BYgG+Af8BpgGJAX4B/wHBAZoBcAH/AYkBdQFm + AcgB1AGeAXQF/wL+AfwB/wL9AfsB/wL9AfwB/wL9AfsB/wL9AfkB/wL8AfgB/wH7AfkB9wH/AfsB+QH1 + Af8B+wH4AfQB/wH7AfcB8gH/AfsB9QHyBf8BsgF7AVkB/wwAAb4BlAF0AeAD2wHgAcgBswGnAe0B3QGz + AZAB/gHcAacBegH/AdwBpgF5Af8B2gGkAXkB/wHYAaIBeAH/AdUBoAF1Af8B0gGdAXIB/wHPAZoBcQH/ + Ac4BmQFvAf8BxAGaAXgB/wFRAUcBQQFrBAADOwFpA2YB7wNTAXQDVAG5A0IB+gNFAf8DUAH/A1AB/wNF + Af8DQgH6A1QBuQNTAXQDZgHvAzsBaQQAAVwBnwFkAf8BuQHWAb4B/wGHAboBjwH/AXABrAF3Af8BUgFY + AdwB/wFlAWkB6wH/AZgBlgH0Af8CkQHzAf8BiQGKAfAB/wFaAV4B5wH/AV4BYQHpAf8BXAFgAegB/wFQ + AVcB5AH/AUkBVAHXAf4BMwIxAUMDCAEKAdUBoAF1Bf8C/QH8Af8C/QH7Af8C/QH6Af8C/AH5Af8B/AH7 + AfcB/wH7AfkB9QH/AfsB+AH0Af8B+wH3AfMB/wH7AfUB8gH/AfoB8wHvAf8B+AHyAewF/wG1AX0BWwH/ + BAABYAFSAUkBggGZAXkBYwHEAdIBnwF3AfgB8QHrAegB+AHwAesB6AH4AfAB6gHnAfgB7wHqAecB+AHv + AeoB5wH4Ae8B6gHnAfgB7wHqAeYB+AHvAekB5gH4AecB5QHiAfAD2wHgAagBfwFiAeAMAANCAYoDgQH3 + A0oBZwMIAQoDQgHzA70B/wPOAf8DwgH/A60B/wNCAfMDCAEKA0oBZwOBAfcDQgGKBAABZAGfAWsB/gGG + AboBjwH/AZkBxgGiAf8BcwGtAXsB/wFOAVYB4gH/AbQBsQH5Af8BlwGWAfQB/wKTAfQB/wGMAY0B8AH/ + AVsBXwHoAf8BWwFgAecB/wFcAWAB6AH/AV4BYQHpAf8BTgFWAeIB/wIkASYBMAQAAdgBogF4Bf8C/QH6 + Af8C/AH6Af8B/AH7AfkB/wH7AfoB9gH/AfsB+AH1Af8B+wH3AfQB/wH7AfYB8QH/AfgB9AHuAf8B9wHy + AesB/wH3AfAB6gH/AfYB7AHoBf8BtwGBAV0B/wQAAZwBfAFlAcQDvAHEAdkBrAGGAfgB2QGsAYYB+AHZ + AawBhgH4AdkBrAGGAfgB2QGsAYYB+AHZAawBhgH4AdkBrAGGAfgB2QGsAYYB+AHZAawBhgH4AdEBnwF2 + AfgBwAGYAXYB4AGrAYIBZQHgDAADMQFOA3AB8APGAe4DVwG2A0oB/ANkAf8DkgH/A3gB/wNkAf8DSgH8 + A1cBtgOYAeEDcAHwAzEBTgQAAWwBhQFvAcgBewG0AYUB/wF1Aa8BfQH/AW4BqwF3Af8BTQFTAeEB/wG0 + AbEB+QH/AZUBlgH1Af8BZQFpAesB/wFuAXAB7AH/AW0BcQHsAf8BWQFbAeUB/wFbAWAB5wH/AV4BYQHp + Af8BUAFXAeIB/wIkASYBMAQAAdkBowF4Bf8B/AH7AfkB/wH8AfsB+AH/AfsB+QH3Af8B+wH3AfQB/wH6 + AfcB8gH/AfkB9QHwAf8B9wHzAe0B/wH2Ae8B6gH/AfUB6wHnAf8B8wHqAeQB/wHyAecB3gX/AboBhQFf + Af8EAAGlAYUBbQHEA7wBxAHZAa8BjgH3AeMBuwGbAfgB4wG7AZsB+AHjAbsBmwH4AeMBuwGbAfgB4wG7 + AZsB+AHjAbsBmwH4AeMBuwGbAfgB4wG7AZsB+AHcAbABiwH4AcoBpgGHAeABqQGEAWsB3gwAAwQBBgNK + AYoDkQH/A+gB/wPdAf8DwQH/A3kB3gNtAcID0wH4A90B/wPEAf8DkQH/A0oBigMEAQYEAAMIAQoBKwEt + ASsBOwE+AUIBPgFbAW0BqAF2Af8BSwFRAeAB/wKiAfQB/wFpAWsB7AH/AWABYgHqAf8BlwGTAfcB/wGX + AZMB9wH/AWMBZwHpAf8BZAFlAeoB/wFbAWAB5wH/AU4BVgHiAf8CJAEmATAEAAHbAaQBeTX/Ab0BhwFi + Af8EAAGmAYUBbQHEA7wBxAG5AagBngHaAdgBtAGXAfYB2QGsAYYB+AHZAasBhQH4AdgBqQGFAfgB1gGn + AYQB+AHTAaUBgQH4AdABowF/AfgBzgGgAX4B+AHGAZUBbQH4Aa4BjQF0AeABRwFAATwBXhAAAwgBCwNK + AXUDgQHuA54B/wN2AeEDKAE3AxIBGAN1Ad0DngH/A4EB7gNKAXUDCAELGAABVAFaAdoB/gJ7AfIB/wGX + AZMB9wH/AWMBZwHpAf8BUQFXAeMB/wFRAVcB4wH/AWMBZwHpAf8BlwGTAfcB/wJ7AfIB/wFNAVYB2AH+ + Ah0BHwEmBAAB3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGn + AXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/ + AcABiwFlAf8EAAGmAYYBbgHEA7wBxAO8AcQDvAHEA7wBxAO8AcQDvAHEA7wBxAO8AcQDvAHEA7wBxAGV + AXUBXwHEYAABawFuAaMByAFtAW4B7AH/AWUBZwHrAf8BXgFhAekB/wJ3AfAB/wJzAfAB/wFeAWEB6QH/ + AWgBagHrAf8BbgFwAewB/wFqAW0BoQHICAAB2wGrAYUB/QHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHo + AbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGS + Af8B6AG5AZIB/wHoAbkBkgH/Ab8BkAFuAf0EAAGnAYgBbwHEAacBiAFvAcQBpwGIAW8BxAGnAYgBbwHE + AacBiAFvAcQBpwGIAW8BxAGnAYgBbwHEAacBiAFvAcQBpwGIAW8BxAGnAYgBbwHEAacBiAFvAcQBlwF4 + AWEBxGAAAwgBCgErASwBLwE7AT4BPwFIAVsBYAFiAeMB/wFQAVYB4gH/AVABVgHiAf8BXgFhAeMB/wE/ + AUABSgFeASsBLAEvATsDCAEKCAABUQFHAUEBawHSAaoBigH0AdwBpwF6Af8B3AGmAXkB/wHaAaQBeQH/ + AdgBogF4Af8B1QGgAXUB/wHUAZ4BdAH/AdIBnQFyAf8BzwGaAXEB/wHOAZkBbwH/AcsBlgFuAf8ByQGU + AWsB/wG8AZUBeAH0AVEBRwFBAWsEAAGlAYkBcwHCAa4BkwF8AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8 + AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8AcQBlgF6AWYBwtAA + AT8BOQE2AVIBoAGIAXUBvAGnAYgBbwHEAacBiAFuAcQBpgGGAW4BxAGjAYQBawHEAaMBggFrAcQBoQGC + AWkBxAGgAYABaQHEAZ8BgAFoAcQBmQGAAW0BxAE/ATkBNgFSUAACEgERARcBqwFoATwB/wGiAV8BOwH2 + ATABLAEqAT90AAMGAQgBSQFRAVsBbgFjAXsBlgGzAWYBiwG1Ad0BXAGNAb8B9AFOAYYBvQH9AT4BegGz + Af8BLgFvAagB/wEiAWUBngH9ARoBXQGRAfQBIAFYAYQB3QExAVYBcgGzATUBRAFOAW4DBgEICAABsgF7 + AVkC1QGDAVAB/wHRAXwBSgH/AY8BZgFNAa8sAAG6AXoBSQH/AcABiQFeAf8BvwGJAWEB/wGvAWwBRgH/ + ATIBLQErAUFwAAFoAYMBpAHEAYcBtwHgAf8BlQHGAeYB/wGbAc0B6QH/AZwBzQHpAf8BmQHHAecB/wGV + AcEB5AH/AY4BuAHgAf8BiQGwAd0B/wGBAacB1wH/AXIBmwHPAf8BXAGMAcIB/wE9AXcBrgH/ASwBWAF6 + AcQEAAGsAX4BYgHMAd8BpAGBAf8B6gHCAasB/wHqAcABqAH/AdwBmQFwAf8BjwFnAU4BrygAAcMBiAFY + Af8BzwGiAXwB/wHNAaIBgAH/AcABjAFlAf8BsAFxAUgB/wFrAVMBRgGVATsBNQExAU4BLAEoAScBOQEX + AhYBHgMDAQRcAAFoAYMBpAHEAZgBuQHUAf8BxQHHAcQB/wHsAdgBxwH/AfYB6wHjAf8B+gH1AfEB/wHz + AecB3gH/Ad8BvwGmAf8B0AGkAYAB/wHLAZoBcwH/AcgBlwFwAf8BowGTAYwB/wFhAX4BnAH/ASwBWAF6 + AcQEAAHbAZcBbgH/Ae0ByAGzAf8B5wG4AZsB/wHmAbQBmAH/AeoBwwGrAf8B3gGcAXIB/wGPAWgBTwGv + JAABWwFQAUcBcQHMAZkBcAH/AdABowGBAf8BzwGkAYMB/wHKAZ4BegH/AbwBhQFcAf8BrwFwAUgB/wGn + AWMBPwH/AZ4BVgE3Af8BfwFQAUABxgMHAQlYAAIiASEBKgHmAcMBpQH/Ae8B1gHAAf8B+wHyAeoB/wH+ + AfwB+gL/Af4B/QH/Af4B/QH8Af8B/AH2AfAB/wH4AegB2QH/AfYB4wHRAf8B9gHiAc4B/wHmAcgBrQH/ + Ac4BoAF6Af8BIQIgASoEAAHdAZ8BeAH/Ae0BzAG3Af8B6AG9AaMB/wHkAbEBkgH/AeYBtgGaAf8B6gHD + AawB/wHeAZwBcwH/AY4BaAFRAawkAAFdAVEBSQFyAc4BnQF1Af8B1QGsAYwB/wHLAZsBdQH/AcwBoAF7 + Af8ByAGbAXUB/wHFAZUBbgH/AcABjwFoAf8BrAFsAUcB/wFUAUUBPwF2FAADfwG3A6AB/wObAf8DlwH/ + A5IB/wOOAf8DigH/A4UB/wOBAf8DfAH/A3gB/wN0Af8DcQH/A20B/wNqAf8DYQG3CAABpAGQAX4BuQHt + AdMBuwH/AfoB8AHlAf8B/QH5AfUB/wH+AfwB+gH/Af4B/QH8Af8B/QH5AfQB/wH2AeMBzwH/AfcB5QHT + Af8B9gHjAdEB/wHdAbkBmgH/AZQBegFmAbgIAAF8AWkBXQGSAeYBswGUAf8B7gHMAbgB/wHpAb4BpQH/ + AeUBswGUAf8B5gG3AZsB/wHqAcQBrQH/Ad4BngF3Af8BjgFpAVEBrCQAAZABdgFiAbEB0wGoAYYB/wHW + AawBjgH/AckBmAFwAf8BxAGQAWcB/wG/AYoBXgH/AcIBjwFmAf8BvwGLAWMB/wF+AWYBRwH7ASABHgEd + ASkQAAOpAf8D6QH/A9MB/wPSAf8D0QH/A9AB/wPOAf8DzQH/A80B/wPLAf8DywH/A8oB/wPJAf8DyAH/ + A+IB/wNqAf8MAAGbAYoBewGwAegBxwGrAf4B9wHqAd4B/wH+AfoB9wH/Af4B/AH5Af8B/QH3AfIB/wH6 + Ae0B4gH/AesB0AG3Af8B0AGhAX4B/gGOAXcBZgGuEAABfAFpAV4BkgHmAbQBmAH/Ae4BzQG6Af8B6QG/ + AaUB/wHlAbQBlgH/AecBuQGdAf8B6wHGAa4B/wHeAZ8BeAH/AY0BaAFSAaogAAFAATwBOAFOAdQBowF9 + Af8B3AG1AZgB/wHQAaEBfAH/AcwBmgFzAf8BzwGkAYMB/wHIAZoBdQH/AXwBjAFjAf8BaQG4AXkB/wGL + AVABNQH7AXABSwFAAa8DBwEJCAADrgH/A9YB/wOxAf8DqAH/A6YB/wOkAf8DogH/A6EB/wOeAf8DnAH/ + A5wB/wOaAf8DmQH/A6IB/wPIAf8DbgH/EAABMQEvAS0BOgHLAbQBnwHhAfMB4gHSAf8B/AH0Ae0B/wH7 + AfEB5wH/AekBzAG1Af8BuQGYAX4B3AEuASwBKgE4GAABfQFqAV8BkgHmAbcBmQH/Ae4BzgG7Af8B6QHA + AacB/wHoAb0BowH/AewByAGzAf8B3wGkAYEB/wGmAXgBXAHJIAABLwEtASsBOQHbAasBiQH/AeEBvQGi + Af8B1gGqAYcB/wHZAbMBlAH/Ac4BnwF5Af8BlAGIAV8B+wFsAbwBewH/AZMBbgFFAf8BsAF0AU4B/wGk + AWEBQAH/AYMBSgE5AdoDAwEEBAADsgH/A9kB/wNwAf8DZgH/A2UB/wNjAf8DYwH/A2IB/wNgAf8DYAH/ + A18B/wNdAf8DXAH/A3cB/wPKAf8DcgH/GAABsgGdAYoBxwH0AeMB0AH/AfMB3wHNAf8BqAGPAXwBwyQA + AX0BawFfAZIB5gG3AZwB/wHvAc8BvAH/Ae4BzgG6Af8B4gGuAYwB/wHSAZsBegH/A4cB9wMbASMcAAEZ + ARgBFwEeAeABsQGPAf8B5gHEAasB/wHiAb8BpAH/AdgBrQGOAf8BpgGSAXAB+gFaAVIBSAF3AaIBdwFH + AfwBuwGEAVwB/wHAAY8BaAH/AbwBigFgAf8BnAFRATQB/wEXAhYBHgQAA7cB/wPdAf8DtwH/A60B/wOs + Af8DqgH/A6gB/wOmAf8DpQH/A6MB/wOhAf8DngH/A50B/wOoAf8DzwH/A3YB/xgAAa8BmQGHAcMB9AHh + Ac4B/wHzAd8BzAH/AagBkAF8AcIoAAF9AWsBYAGSAecBuQGcAf8B5gG2AZgB/wHYAakBjAH/A9IB/wO1 + Af8DiAH+A4YB+wOEAfsDeQHWA00BcAwAAwMBBAHCAaABiQHaAeQBuwGfAf8B5AG7AZ8B/wGeAaQBgwH8 + AXUBwQF+Af8BrgGNAVkB/AHHAZYBbgH/AcsBngF6Af8BvAGFAVgB/wHDAZIBawH/AaYBYgE+Af8BLAEo + AScBOQQAA48BvQPaAf8DyAH/A8EB/wPAAf8DvgH/A7sB/wO5Af8DuAH/A7YB/wO0Af8DsQH/A7AB/wO0 + Af8DwwH/A20BvRAAAS8BLgEsATgBzAGzAZ0B3QHzAd0ByQH/AfoB7gHiAf8B+QHrAd4B/wHtAdABtgH/ + Ab8BogGIAdsBLwEtASsBOCQAAX4BbAFhAZIBsAGPAXoBzwORAfsDxwH/A8wB/wPHAf8DxgH/A8MB/wPA + Af8DggH1A00BcAwAAwcBCQGZAYMBdgGvAbYBrwGQAfkBdgHFAYIB/wG1AbABgQH/AdgBsAGSAf8B1wGu + AY8B/wHJAZcBbgH/AcMBjwFlAf8ByAGbAXUB/wGxAXABSQH/ATwBNQExAU4EAAOSAb0D4wH/A+cB/wPh + Af8D4AH/A+AB/wPfAf8D3wH/A94B/wPdAf8D3QH/A9wB/wPbAf8D4AH/A9AB/wNvAb0MAAGhAZIBhAGv + AfQB1QG4Af4B+QHsAeAB/wH9AfgB9AH/AfsB8AHmAf8B+AHnAdcB/wH5AesB3gH/AfIB2gHFAf8B4QG4 + AZYB/gGVAYIBcgGuKAADNgFIA5IB/gPUAf8DyAH/A7wB/wO6Af8DwgH/A8QB/wN6AdYUAAIfAR4BJgHD + AbIBhgH9AeIBvgGjAf8B3wG3AZoB/wHVAagBhgH/AdABoQF8Af8BywGaAXIB/wHOAaIBgAH/Ab8BiwFh + Af8BbgFXAUcBlQQAAzMBPwPOAf8D7AH/A94B/wPYAf8D0gH/A8wB/wPIAf8DxwH/A8gB/wPLAf8D0AH/ + A90B/wPoAf8DpQH/AzEBQggAAawBnAGNAbgB+QHjAc0B/wH7AfMB7AH/Af4B+gH3Af8B/gH7AfgB/wH8 + AfUB7QH/AfcB5gHVAf8B9gHhAcwB/wH5AewB3wH/AfkB6wHdAf8B6wHOAbQB/wGeAYkBeAG4KAADlAH8 + A90B/wPFAf8DeAHBA3kBwwOsAf8D1wH/A4YB+xgAAWUBWwFUAXYB5AG6AZ0B/wHmAcQBqwH/AeIBvgGk + Af8B3gG5AZwB/wHZAbIBkwH/AdEBowF+Af8B0QGmAYUB/wG7AX4BUAH/ATEBLQErAT8EAAOUAb0D4wH/ + A9sB/wPiAf8D4gH/A+EB/wPgAf8D4AH/A+AB/wPfAf8DwwH/A9EB/wPTAf8DdgG9CAABIwIiASoBwAHF + Ac0B/wGaAbcB3AH/AXwBpgHVAf8BYgGVAcoB/wFOAYcBvgH/AT4BegGzAf8BLgFvAagB/wEiAWYBnwH/ + AR0BYgGYAf8BKwFqAZoB/wFSAYMBpwH/AYgBlAGaAf8CIQEgASokAAOWAfsD5AH/A88B/wN6AcIEAAOO + Af8DjAH/A4kB+xgAAwcBCQGvAZMBgAHGAeMBtAGTAf8B3wGxAY4B/wHaAasBiQH/AdoBrQGMAf8B3AG1 + AZgB/wHXAa8BkAH/AcwBmwFzAf8BswF3AUUB+gQAAzMBPwPRAf8D8wH/A+0B/wPtAf8D7QH/A+0B/wPt + Af8D7AH/A+wB/wPsAf8D8QH/A7AB/wMyAUIIAAFxAY0BrgHRAYcBtwHgAf8BlQHGAeYB/wGbAc0B6QH/ + AZwBzQHpAf8BmQHHAecB/wGVAcEB5AH/AY4BuAHgAf8BiQGwAd0B/wGBAacB1wH/AXIBmwHPAf8BXAGM + AcIB/wE9AXcBrgH/AS8BXQGAAdEkAAOJAdYD4gH/A+cB/wO5Af8DkwH/KAADAwEEARkCGAEeATABLQEs + ATkBQQE8ATkBTgF8AWwBXwGUAdgBpwGCAf8B1wGsAYsB/wHTAacBhAH/AboBggFWAfYIAAOWAb0DxQH/ + A8EB/wO+Af8DugH/A7YB/wOyAf8DrgH/A6kB/wOlAf8DoQH/A34BvQwAAWgBgwGkAcQBhwG3AeAB/wGV + AcYB5gH/AZ0BzwHqAf8BoQHTAewB/wGgAdEB6wH/AZsBygHoAf8BlgHCAeUB/wGPAboB4gH/AYcBsQHd + Af8BeAGjAdQB/wFgAZABxAH/AT0BdwGuAf8BLAFYAXoBxCQAA1IBcAOXAfUD5AH/A+4B/wOWAf88AAEz + ATABLgE+AdABngF6AfsByAGWAW4B9gE6ATYBMgFIRAADBgEIAUkBUQFbAW4BYwF7AZYBswFsAZABuQHd + AW8BmgHNAfQBcAGfAdUB/QFoAZkB0QH/AVsBkAHIAf8BTgGGAb0B/QFBAXkBrAH0ATsBawGXAd0BPAFf + AXoBswE3AUUBTwFuAwYBCCgAA1IBcAOKAdYDmQH7A5cB+xAAATUBnQHZAf8BMAGZAdgB/wErAZQB1wH/ + AScBkAHWAf8BIgGMAdUB/wEdAYgB1AH/ARkBhAHTAf8BFAGAAdIB/wEQAXsB0QH/AQ0BeAHRAf8BCQF1 + AdAB/wEGAXIBzwH/AQMBbwHPAf8BAAFtAc4B/xwAA0IBYwNYAb8DVQG/Az4BYzAAAwoBDQMmATEDJAEv + AwUBBxgAAhIBEQEXAasBaAE8Af8BogFfATsB9gEwASwBKgE/NAABPAGjAdoB/wG8AesB+gH/AbwB6wH8 + Af8BvwHuAf4B/wHGAfQC/wHOAfgC/wHTAfoC/wHQAfgC/wHHAfIC/wG6AekB/AH/AbMB5AH5Af8BsAHi + AfgB/wGwAeIB+AH/AQQBcAHPAf8QAAMfASkDCwEOAwEBAgN8AeoDvQH/A7IB/wNcAeoDAQECAwsBDgMe + ASkYAAMaASEDbwGdA5gB8AOhAf8DqwH/A6cB/wOVAf8DhgHnA1wBigMRARYMAAG6AXoBSQH/AcABiQFe + Af8BvwGJAWEB/wGvAWwBRgH/ATIBLQErAUEwAAFCAagB2wH/Ab8B7AH7Af8BWAHPAfUB/wFAAbAB7AH/ + AU0BugHvAf8BWQHCAe8B/wFfAcYB7wH/AVsBxAHvAf8BSwG2Ae8B/wE2AaUB5gH/ASkBmgHhAf8BNwG4 + Ae4B/wGxAeMB+AH/AQgBdAHQAf8MAANiAZsDbgH9A2QB5wMTARkDfgHnA8sB/wPHAf8DYgHnAxMBGQNa + AecDTgH9A0wBmxAAAzkBSgOgAfQDzgH/A+0B/wP0Af8D9QH/A/QB/wPvAf8D4gH/A7oB/wODAecDKAE1 + CAABwwGIAVgB/wHPAaIBfAH/Ac0BogGAAf8BwAGMAWUB/wGwAXEBSAH/AWsBUwFGAZUBOwE1ATEBTgEs + ASgBJwE5ARcCFgEeAwMBBBwAAUgBrQHcAf8BwQHuAfsB/wFeAdMB9wH/AWsB2wH8Af8BfgHlAv8BjwHt + Av8BlwHyAv8BkwHtAv8BewHfAv8BWgHMAfgB/wFFAb4B7wH/ATsBugHuAf8BswHjAfkB/wENAXgB0QH/ + CAADWgF7A7wB/wPeAf8DpgH/A4AB9AOEAf4DxAH/A8IB/wNtAf4DbAH0A6YB/wPSAf8DgAH/A0gBewwA + A54B6gPeAf8D8wH/A9sB/wPSAf8D2wH/A9YB/wPAAf8DyQH/A+YB/wPEAf8DhQHqCAABWwFQAUcBcQHM + AZkBcAH/AdABowGBAf8BzwGkAYMB/wHKAZ4BegH/AbwBhQFcAf8BrwFwAUgB/wGnAWMBPwH/AZ4BVgE3 + Af8BfwFQAUABxgMHAQkYAAFNAbIB3QH/AcMB7wH7Af8BZAHWAfgB/wFLAbYB7AH/AVkBvQHvAf8BlQHr + Av8BLwGXAd0B/wFMAYIBqwH/AYQB4QL/AUABqQHpAf8BMQGfAeEB/wFBAb4B7wH/AbQB5QH5Af8BEgF9 + AdIB/wgAA14BfQOlAf4D1QH/A8UB/wPLAf8D0QH/A8kB/wPHAf8DzAH/A8UB/wO9Af8DywH/A24B/gNN + AX0MAAOgAeoD8AH/A94B/wPUAf8D0gH/A9sB/wPWAf8DvwH/A7AB/wOzAf8D3gH/A4kB6gwAAV0BUQFJ + AXIBzgGdAXUB/wHVAawBjAH/AcsBmwF1Af8BzAGgAXsB/wHIAZsBdQH/AcUBlQFuAf8BwAGPAWgB/wGs + AWwBRwH/AVQBRQE/AXYYAAFSAbcB3gH/AcYB8AH8Af8BaQHZAfgB/wF7AeIB/QH/AZAB6AL/AZkB6QL/ + ATEBnwHfAf8BUwGLAbIB/wGKAeIC/wFpAdAB+QH/AU8BxQHxAf8BRQHBAfAB/wG2AecB+QH/ARcBgwHT + Af8MAANjAYUDxQH/A8EB/wPFAf8DxwH/A6oB/wOnAf8DwQH/A74B/wO1Af8DqgH/A1EBhRAAA6IB6gPy + Af8D4gH/A9gB/wPVAf8D3AH/A9gB/wPAAf8DswH/A7cB/wPgAf8DjgHqEAABkAF2AWIBsQHTAagBhgH/ + AdYBrAGOAf8ByQGYAXAB/wHEAZABZwH/Ab8BigFeAf8BwgGPAWYB/wG/AYsBYwH/AX4BZgFHAfsBIAEe + AR0BKRQAAVcBuwHfAf8BxwHxAfwB/wFuAdwB+QH/AVUBuwHtAf8BYAG9Ae8B/wGbAecC/wE0AaYB4gH/ + AUoBpAHhAf8BkAHiAv8BSAGtAekB/wE3AaQB4wH/AUgBxAHwAf8BuAHoAfkB/wEdAYgB1AH/BAADigHN + A4UB4wOYAe4DzwH/A8YB/wPMAf8DhAHGAzQBRAMzAUQDegHGA8EB/wO8Af8DuQH/A2MB7gNZAeMDVgHN + CAADpAHqA/MB/wPnAf8D3QH/A9kB/wPgAf8D2wH/A8QB/wO4Af8DuwH/A+EB/wOSAeoQAAFAATwBOAFO + AdQBowF9Af8B3AG1AZgB/wHQAaEBfAH/AcwBmgFzAf8BzwGkAYMB/wHIAZoBdQH/AXwBjAFjAf8BaQG4 + AXkB/wGLAVABNQH7AXABSwFAAa8DBwEJDAABWwG/AeAB/wHIAfMB/AH/AXQB3wH5Af8BiQHmAf0B/wGV + AecC/wGaAeUC/wGqAe4C/wGoAe0C/wGZAeMC/wFzAdUB+QH/AVgBzAHzAf8BTgHIAfEB/wG7AekB+gH/ + ASMBjQHVAf8EAAO+Af0D4gH/A9IB/wPGAf8DzQH/A7EB/wMzAUQIAAM0AUQDqAH/A8IB/wO3Af8DwAH/ + A9IB/wNgAf0IAAOlAeoD9AH/A+oB/wPhAf8D3QH/A+MB/wPeAf8DyQH/A70B/wO/Af8D4gH/A5QB6hAA + AS8BLQErATkB2wGrAYkB/wHhAb0BogH/AdYBqgGHAf8B2QGzAZQB/wHOAZ8BeQH/AZQBiAFfAfsBbAG8 + AXsB/wGTAW4BRQH/AbABdAFOAf8BpAFhAUAB/wGDAUoBOQHaAwMBBAgAAV8BwgHhAf8ByQHzAfwB/wHL + AfMB/QH/AdQB9gH+Af8B1wH2Av8B2AH0Av8B4AH4Av8B3wH4Av8B2gH1Av8BzQHxAfwB/wHCAe0B+gH/ + Ab0B6wH6Af8BvQHrAfoB/wEqAZMB1gH/BAADwgH9A+kB/wPWAf8DyQH/A84B/wOlAf8DMgFECAADNAFE + A6wB/wPEAf8DugH/A8YB/wPdAf8DagH9CAADpwHqA/UB/wPuAf8D5gH/A+IB/wPmAf8D4QH/A80B/wPC + Af8DwgH/A+MB/wOWAeoQAAEZARgBFwEeAeABsQGPAf8B5gHEAasB/wHiAb8BpAH/AdgBrQGOAf8BpgGS + AXAB+gFaAVIBSAF3AaIBdwFHAfwBuwGEAVwB/wHAAY8BaAH/AbwBigFgAf8BnAFRATQB/wEXAhYBHggA + AWABwwHhAf8BiAGgAagB/wORAf8DjgH/AVkBuQHcAf8BVAG4Ad8B/wFQAbUB3gH/AUwBsQHdAf8BSAGt + AdwB/wFFAagB1wH/A3cB/wN1Af8BZAF9AY0B/wEwAZkB2AH/BAADogHNA68B4wO0Ae4D2AH/A80B/wO8 + Af8DcwHGAzEBRAMyAUQDegHGA8MB/wPCAf8DzQH/A4cB7gN/AeMDdQHNCAADqAHqA/YB/wPrAf8D3gH/ + A9YB/wPVAf8D0QH/A8MB/wO8Af8DwAH/A+UB/wOZAeoQAAMDAQQBwgGgAYkB2gHkAbsBnwH/AeQBuwGf + Af8BngGkAYMB/AF1AcEBfgH/Aa4BjQFZAfwBxwGWAW4B/wHLAZ4BegH/AbwBhQFYAf8BwwGSAWsB/wGm + AWIBPgH/ASwBKAEnATkMAAN/AcMDxgH/A5QB/wMGAQgQAAMGAQgDfAH/A6sB/wNrAcMQAANqAYUD1AH/ + A8wB/wPJAf8DugH/A5wB/wOhAf8DwgH/A8YB/wPBAf8DtwH/A1kBhRAAA6oB6gP3Af8D5wH/A+8B/wP2 + Af8D+wH/A/oB/wPwAf8D3gH/A8MB/wPmAf8DmgHqFAADBwEJAZkBgwF2Aa8BtgGvAZAB+QF2AcUBggH/ + AbUBsAGBAf8B2AGwAZIB/wHXAa4BjwH/AckBlwFuAf8BwwGPAWUB/wHIAZsBdQH/AbEBcAFJAf8BPAE1 + ATEBTgwAA3YBrgPEAf8DoQH/AyABKRAAAx8BKQOJAf8DqQH/A2YBrgwAA2UBfQPDAf4D3AH/A9QB/wPZ + Af8D2wH/A9YB/wPUAf8D2QH/A9IB/wPLAf8DyAH/A3kB/gNQAX0MAAOrAeoD+AH/A/4Z/wP7Af8D6gH/ + A5wB6hwAAh8BHgEmAcMBsgGGAf0B4gG+AaMB/wHfAbcBmgH/AdUBqAGGAf8B0AGhAXwB/wHLAZoBcgH/ + Ac4BogGAAf8BvwGLAWEB/wFuAVcBRwGVDAADVgF1A7oB/wO/Af8DigHdAxgBHwMEAQUDBAEFAxgBHwOA + AeUDqAH/A54B/wNOAXUMAANkAXsD3AH/A+0B/wPbAf8DugH0A70B/gPWAf8D1AH/A68B/gOmAfQDywH/ + A+cB/wO3Af8DVQF7DAADjwG/A+EB/wP+Gf8D+wH/A88B/wN0AaEgAAFlAVsBVAF2AeQBugGdAf8B5gHE + AasB/wHiAb4BpAH/Ad4BuQGcAf8B2QGyAZMB/wHRAaMBfgH/AdEBpgGFAf8BuwF+AVAB/wExAS0BKwE/ + CAADCQEMA5AB2wPEAf8DvgH/A6EB/wOWAf8DkwH/A5cB/wOuAf8DrgH/A3sB2wMHAQkQAAN/AZsDzAH9 + A7gB5wMUARkDsQHnA94B/wPdAf8DpAHnAxQBGQOfAecDpwH9A24BmxAAAyIBKgOcAdED0AH/A+gB/wPz + Af8D/QH/A/wB/wPtAf8D4AH/A8IB/wOLAcMDFQEbIAADBwEJAa8BkwGAAcYB4wG0AZMB/wHfAbEBjgH/ + AdoBqwGJAf8B2gGtAYwB/wHcAbUBmAH/AdcBrwGQAf8BzAGbAXMB/wGzAXcBRQH6DAADJgEwA5IB3gO8 + Af8DygH/A8wB/wPKAf8DwgH/A60B/wOCAd4DJQEwGAADIQEpAwsBDgMBAQIDtwHqA+UB/wPkAf8DoAHq + AwEBAgMLAQ4DIAEpGAADBQEHA0MBVQOEAa4DnQHWA7QB+wOyAfkDlQHNA34BqQM3AUYDAgEDKAADAwEE + ARkCGAEeATABLQEsATkBQQE8ATkBTgF8AWwBXwGUAdgBpwGCAf8B1wGsAYsB/wHTAacBhAH/AboBggFW + AfYQAAMJAQwDXgGBA38BugOGAcwDhAHMA3oBugNaAYEDCQEMKAADUAFjA5cBvwOVAb8DTgFjiAABMwEw + AS4BPgHQAZ4BegH7AcgBlgFuAfYBOgE2ATIBSIgAAT0BjAFEAf8BOQGHAUAB/wE2AYIBPAH/ATIBfQE4 + Af80AANxAZoDPAFOGAADNgFNAzQBSgMCAQMIAAE4AVgBcQG3ARoBVwGIAf8BGQFVAYYB/wEZAVQBhQH/ + ARgBUwGDAf8BGAFTAYIB/wEXAVIBgAH/ARcBUQF+Af8BFgFQAX0B/wEWAU8BfAH/ARUBTgF6Af8BFQFN + AXkB/wEVAU0BeAH/ARUBTAF4Af8BFAFMAXcB/wEzAVEBawHASAABQQGRAUgB/wGBAcUBhwH/AX0BwwGF + Af8BNgGDAT0B/zQAAzsBSwObAeQDWwF8EAADUwGAA2cBwAMxAUUMAAEjAVoBhwHwAX0BrQHgAf8BgQGv + AeQB/wF+AasB4gH/AX0BqAHgAf8BewGmAd8B/wF5AaIB3gH/AXgBoAHdAf8BdgGeAdwB/wF1AZsB2wH/ + AXQBmQHaAf8BcwGYAdoB/wFzAZgB2gH/AXMBmAHaAf8BcwGYAdoB/wEUAUwBdwH/BAABPAFNAV0BcAFC + AWoBjAGmATUBgQHFAeoBMQGHAdEB9wExAYcB0QH3ATEBhwHRAfcBMQGHAdEB9wExAYcB0QH3ATEBhwHR + AfcBMQGHAdEB9wExAYcB0QH3ATEBhgHRAfcBMwGFAcwB8QFBAV8BewGTDAABRQGXAU0B/wGFAccBiwH/ + AYIBxgGJAf8BOgGJAUEB/zgAA4oBvAOmAfcDbgGcCAADZwGjA30B8wNlAa4QAAEjAVsBhwHwAYABsgHj + Af8BUAGUAdwB/wFDAYoB2AH/AUEBhQHVAf8BPwGAAdQB/wE8AXsB0QH/AToBdgHPAf8BNwFyAc0B/wE1 + AW8BzAH/ATMBbAHKAf8BMQFoAckB/wEwAWYByAH/AS8BZAHIAf8BcwGYAdoB/wEVAUwBeAH/BAABPAGG + AcMB5gHOAeAB6AHwAacB2wHyAf0BngHbAfQB/wGWAdoB8wH/AY4B2AHzAf8BhgHXAfMB/wF+AdQB8gH/ + AXgB0wHyAf8BcQHSAfEB/wFrAdAB8QH/AWgBzwHxAf8BwQHpAfcB/gE4AYkBywHwDAABSQGcAVEB/wGJ + AcoBkAH/AYYByAGNAf8BPgGOAUYB/zgAAyYBMAOyAf0DqQH+A4UBxAOAAcQDjgH+A4QB9AMcASQQAAEk + AVwBiQHwAYMBtwHlAf8BVAGdAd8B/wFJAZIB2wH/AUYBjQHZAf8BQwGJAdcB/wFAAYMB1QH/AT4BfgHT + Af8BOwF6AdAB/wE5AXUBzgH/ATYBcQHNAf8BNAFtAcsB/wEyAWoBygH/ATEBZwHJAf8BdAGYAdoB/wEV + AU0BeQH/BAABNwGRAdMB9wHvAfoB/gH/AaEB6QH5Af8BkQHlAfgB/wGBAeEB9wH/AXEB3gH2Af8BYgHa + AfUB/wFTAdcB9AH/AUYB0wHzAf8BOAHQAfIB/wEtAc0B8QH/ASUBywHwAf8BygHyAfsB/wE3AZEB0wH3 + DAABTAGiAVUB/wGNAcsBlAH/AYoBywGRAf8BQgGUAUoB/zwAA3QBlgOxAfwBzwHrAv8BywHpAv8DlwH8 + A1wBhxQAASQBXQGKAfABhQG9AeYB/wFYAaQB4wH/AU4BmwHeAf8BSwGWAdwB/wFIAZEB2gH/AUYBjAHY + Af8BQgGHAdYB/wE/AYIB1AH/AT0BfAHRAf8BOwF5AdAB/wE3AXMBzgH/ATYBcAHMAf8BMwFtAcsB/wF1 + AZsB2wH/ARUBTgF6Af8EAAE5AZgB1AH4AfIB+gH9Af8BswHtAfoB/wGkAekB+QH/AZUB5gH4Af8BhQHi + AfcB/wF1Ad4B9gH/AWQB2wH1Af8BVgHXAfQB/wFIAdQB8wH/AToB0QHyAf8BLwHOAfEB/wHMAfIB+wH/ + ATcBlQHTAfcMAAFQAacBWgH/AZABzwGZAf8BjgHMAZYB/wFGAZkBTwH/KAABRAFQAWQBbwFAAUsBWwFm + AwwBDwgAAw4BEgO0AfMDsgH+A6kB/gOWAeUDCQEMDAABKQEvATYBPwEyATwBSwFVASUBXQGLAfABiAHB + AegB/wFdAawB5gH/AVMBowHjAf8BTwGeAeEB/wFMAZkB3gH/AUoBlAHcAf8BRwGPAdoB/wFDAYoB2AH/ + AUEBhQHVAf8BPwGAAdQB/wE8AXsB0QH/AToBdgHPAf8BNwFyAc0B/wF3AZ8B3QH/ARYBTwF7Af8EAAE4 + AZ4B1QH5AfYB/AH+Af8ByAHyAfwB/wG5Ae8B+wH/AawB7AH6Af8BnAHoAfkB/wGLAeMB9wH/AXsB4AH2 + Af8BawHcAfYB/wFcAdkB9QH/AU4B1gH0Af8BQwHTAfMB/wHQAfMB/AH/ATcBnAHTAfcMAAFUAawBXgH/ + AZQB0AGdAf8BkgHPAZoB/wFKAZ8BUwH/KAABZAGRAdUB2wF0Aa0C/wFYAZYB9AH2AwQBBgErAS8BNgE+ + BAADgAGjAbkB0AL/AboB3AL/A3UBogQAAR8BIQElASwDBAEFAS4BdwHfAeQBMwGIAv8BNAF0Ac8B1QEl + AV4BjAHwAYsBxgHqAf8BYQGzAekB/wFXAaoB5gH/AVQBpgHkAf8BUQGiAeIB/wFOAZwB4AH/AUwBmAHd + Af8BSQGSAdsB/wFGAY0B2QH/AUMBiQHXAf8BQAGDAdUB/wE+AX4B0wH/ATsBegHQAf8BegGjAd4B/wEW + AVABfQH/BAABOAGkAdUB+gH+A/8B+AH9Av8B9gH9Av8B9QH8Av8B8wH8Af4B/wHYAfYB/AH/AZQB5gH4 + Af8BhQHjAfcB/wF1Ad8B9gH/AWcB2wH1Af8BWwHYAfQB/wHXAfQB/AH/ATcBoQHTAfcMAAFXAbEBYgH/ + AZgB0wGhAf8BlgHRAZ4B/wFqAbYBcwH/AUwBZgFPAY8MAAFDAVUBRAF7AwIBAxAAAXABqAH4AfkBnQHE + Av8BZAGjAv8DBwEJAVYBmAL/AVIBjwHmAeoBLAEvATQBPAPBAfsDuAH6ATEBNgE/AUgBOwF8AdwB4QEs + AX4C/wMHAQkBIwF5Av8BaQGmAv8BJgF6AfEB8wEmAV8BjgHwAY0ByAHtAf8BZAG4AesB/wFaAbIB6QH/ + AVgBrgHnAf8BVgGpAeUB/wFTAaUB4wH/AVABoAHiAf8BTgGbAd4B/wFLAZYB3AH/AUgBkQHaAf8BRgGM + AdgB/wFCAYcB1gH/AT8BggHUAf8BfQGoAeAB/wEXAVEBfgH/BAABNgGoAdUB+gHoAfYB+wH/AZQB1AHv + Af8BiAHOAe4B/wFyAcEB6QH/AckB6QH2Af8B8gH8Af4B/wHzAfwB/gH/AfIB/AH+Af8B8AH8Af4B/wHv + AfsB/gH/Ae4B+wH+Af8B/gP/ATgBpwHTAfcMAAFaAYkBXwG3AYkBygGSAf8BkgHQAZsB/wGPAc0BlwH/ + AVMBlgFaAeIBTAFmAU8BjwgAAUoBgQFQAdIBSQFoAUwBnxAAAXkBrAH4AfkBowHGAv8BbwGqAv8DAgED + AWIBnQH0AfYBZwGjAv8BLAEwATYEPwFOAyIBKgErAS8BNgE/AUoBkwL/ATYBgAH0AfYDAgEDAScBegL/ + AWwBpQL/ASkBdwHtAfABJgFgAY8B8AGPAcwB7gH/AY8BzAH0Af8BhwHFAfIB/wGDAcMB8QH/AYABvwHv + Af8BfAG8Ae4B/wF5AbgB6wH/AXQBtAHqAf8BcAGwAegB/wFtAasB5gH/AWgBpwHlAf8BZQGjAeMB/wFh + AZ4B4QH/AYABrQHjAf8BFwFSAYEB/wQAAToBowHPAfIB8QH6Af0B/wGUAd4B9QH/AZMB3AH0Af8BgQHV + AfIB/wFpAcoB7QH/AWsBywHqAf8BhQHTAe8B/wGAAdIB7wH/AXkB0AHvAf8BdQHPAe4B/wFxAc8B7gH/ + AekB9wH7Af8BOAGoAc8B8wwAAUYBVwFIAXABeAHEAYQB/wGdAdUBpgH/AZsB1AGjAf8BkQHOAZkB/wFs + AbgBdAH/AUwBoQFVAf8BSQGcAVEB/wFjAa4BawH/AWABqgFnAf8BRwFiAUsBkwwAAXsBngHSAdgBpAHG + Av8BeQGvAv8DBwEJAV8BeQGhAasBgQGzAv8BWAF0AaABqgEsATABNgE/ASwBMAE2AT8BTwFuAaABqgFl + AaMC/wFKAW0BoQGrAwcBCQEyAYIC/wFoAaQC/wE4AXIBzwHVASYBYQGQAfABdgGxAdkB/wEoAW0BpAH/ + ARYBYAGbAf8BFgFdAZkB/wEWAVsBlgH/ARUBWgGSAf8BFAFYAY4B/wEUAVUBiwH/ARMBUwGHAf8BEwFQ + AYIB/wERAU0BfQH/AREBSwF5Af8BOAFnAYwB/wFmAY4BsAH/ARgBUwGCAf8EAAE6AaYBzAHwAfcB/AH+ + Af8BjgHkAfgB/wGRAd4B9QH/AZ8B4AH1Af8BrAHhAfYB/wHvAfsB/gH/AfQB/QH+Af8B8wH8Af4B/wHx + AfwB/gH/Ae8B+wH+Af8B7gH7Af4B/wHzAfYB+AH5AUMBlgG0AdQMAAEWARcBFgEdAVoBhgFfAbABiAHL + AZEB/wGeAdYBpwH/AZQB0QGeAf8BmQHUAaIB/wGXAdIBoAH/AZQB0AGdAf8BkwHPAZsB/wGPAc0BlwH/ + AWABqwFoAf8BRwFeAUkBiggAAWEBbwGFAZABngHEAv8BkAG+Av8BMAE0ATkBQgEVAhYBGwFtAZkB2AHe + AYMBtQL/AW8BqAL/AWkBpgL/AXABqAL/AVQBigHYAd4BFAEVARYBGwEsATEBOQFCAUwBkwL/AVsBnAL/ + AUIBXAGBAY0BMAFhAYkB2wFAAXsBqgH/AWMBlgG+Af8BKgFuAaYB/wEfAWcBoQH/AR8BZAGfAf8BHwFj + AZwB/wEeAWEBmQH/AR4BXwGVAf8BHQFeAZEB/wEdAVwBjwH/ARwBWAGKAf8BPgFvAZgB/wFaAYQBpgH/ + ATcBagGUAf8BOAFUAWwBrwQAATkBrwHUAfgB/QL+Af8B/gP/Av4C/wH9Af4C/wH+A/8B6gH3AfsB/wFq + AcIB3gH5AWoBwgHcAfgBagHCAdwB+AFqAcIB3AH4AXcBxwHeAfcBbwGzAcgB4QE1AUMBRwFWEAABKQEu + ASoBOQFaAYYBYAGwAXoBxgGFAf8BjAHNAZUB/wGXAdIBoAH/AZsB1AGkAf8BmQHTAaIB/wGWAdEBngH/ + AZIBzwGaAf8BZAGvAW0B/wFHAVwBSQGECAABIwEkASYBLQGQAb0B+wH8AaYByAL/AYUBtQH7AfwBGAEZ + ARoBHwIJAQoBDAFUAWMBeQGEAWgBiwG+AcYBZAGHAb4BxgFPAWABeQGEAgkBCgEMARcBGAEaAR8BTAGR + AfsB/AFxAaoC/wE/AYgB+wH8AR4BIAEjASoBDQIOARIBKAFhAZEB7gE+AXkBqAH/AV8BkwG8Af8BYAGU + AbwB/wFgAZMBvAH/AV8BkgG7Af8BXwGRAbkB/wFfAZABtwH/AV8BjwG1Af8BXgGOAbMB/wFeAYwBsAH/ + AVkBiAGrAf8BNwFsAZgB/wE7AUwBWAGDCAABQQGXAbAB0AFdAb4B3AH6AV8BvwHdAfoBXwG/Ad0B+gFf + Ab8B3QH6AV4BvwHdAfoBRQGhAb0B3QIRARIBFgELAgwBDwELAgwBDwELAgwBDwELAgwBDwELAgwBDwMD + AQQUAAEWARcBFgEdAUYBWAFIAXABWgGJAWEBtwFcAagBZAHqAVcBsQFhAf8BVAGsAV4B/wFvAboBdwH/ + AWwBtgF1Af8BSwFmAU4BkBAAAWQBcAGFAZABpAHJAv8BpAHIAv8BiQG3AfsB/AExATQBOQFCAwcBCQgA + AwcBCQEvATMBOQFCAV0BnAH7AfwBdgGsAv8BaAGjAv8BSgFgAYEBjQgAAwkBDAE9AVkBbgGkASYBYgGR + AfEBJQFgAZIB8wElAWABkQHzASUBXwGQAfMBJQFfAY4B8wElAV4BjQHzASQBXQGMAfMBJAFdAYsB8wEj + AVwBiQHzASgBXAGFAecBOgFKAVUBfHAAAVcBkwFeAdIBUQFxAVQBnBQAAwQBBgF6AY4BrgG3AaQByAL/ + AawBzgL/AZcBwAL/AYgBtgL/AYUBtwL/AXwBsAL/AXYBrgL/AX0BsQL/AY4BvgL/AXkBrwL/AVkBegGu + AbcDBAEGrAABSAFcAUsBeAMCAQMcAAFTAVoBZwFyAZMBtwH0AfYBqAHJAv8BsQHRAv8BswHRAv8BsAHQ + Av8BowHFAv8BlQHAAv8BcwGnAfQB9gFKAVYBZwFy3AACHwEhAScBYwFvAYEBjQGCAZsBxAHMAZMBvgH7 + AfwBjgG6AfsB/AF6AZgBxAHMAV0BawGBAY0BHgEfASEBJxAAAUIBTQE+BwABPgMAASgDAAFAAwABYAMA + AQEBAAEBBgABAxYAA/+BAAH+AQcC/wH8AT8BhwH/AfwBBwHAAQMB8AEfAQMB/wHwAQcBgAEBAYABAwEB + Af8B4AEHAYABAQEAAQEBAAH/AcABBwGAAQEDAAF/AYABBwGAAQECAAGAAT8BAAEDAYABAQIAAcABPwEA + AQMBwAEDAgAB4AEfAQABAwHgAQcBAAEBAfABAQEAAQMB8AEPAQABAwH4AQABwAEDAfABBwEAAQ8B/gEA + AfABAwHwAQcBAAEPAf8BAAH4AQMB8AEPAQABDwH/AQgB/gEBAfABDwEAAQ8B/wEHAf8BAQH4AR8BAAEP + Af8BBwH/AfEC/wGAAR8B/wGHAf4BAwL/AfABAAL/AfwBAAL/AfABAAL/AfACAAEBAfABAAL/AYACAAEB + AfABAAL/AwABAQHAAQAC/wMAAQEBwAEAAYABAQMAAQEBwAEAAYABAQMAAQEBwAEAAYABAQMAAQEBAAED + AYABAQEAAQEBAAEBAQABAwGAAQEBAAEBAQABAQEAAQMBgAEBAQABAQEAAQEBAAEDAcABAwHwAQEBAAEB + AQABDwL/AfABAwEAAQEBAAEPAv8B8AEDAQABAQEAAQ8G/wEAAQ8C/wEPA/8BgAEBAYcB/wEHA/8BgAEB + AQMB/wEAAT8C/wGAAgEB/wEAAR8C/wGAAQEBAAH/AYABHwIAAcABAwEAAX8BwAEPAgAB4AEHAYABPwHA + AQMCAAHwAQ8BwAE/AcABAQIAAfwBPwHgAR8BwAEBAgAB/AE/AfABAQHAAQECAAHwAQ8B+AEAAeABAQIA + AeABBwH+AQAB+AEBAgABwAEDAf8BAAH8AQABgAEBAYABAQH/AQgB/AEAAYABAQGAAQEB/wEHAf4BAAHA + AQMBgAEBAf8BBwH/AfAC/wGAAQEB/wGHAYABAQH8AT8B/AE/AQ8B/wGAAQEB4AEHAeACBwH/AYABAQHA + AQMBwAEDAQABPwGAAQEBgAEBAcABAwEAAR8BgAEBAYABAQHAAQMBgAEfAYABAQHAAQMBwAEDAcABDwGA + AQECAAHAAQMBwAEDAYACAQGAAcABAwHAAQEBgAIBAYABwAEDAcABAQGAAQECAAHAAQMBwAEBAsMBwAED + AcABAwHgAQECwwGAAQEBwAEDAfgBAQHAAQMBgAEBAcABAwH8AQABwAEDAcABAwHAAQMB/AEAAeABBwHg + AQcB4AEHAf4BAAHwAQ8B/AE/A/8B8AT/AcMB/wHnAeMCAAL/AcMB/wHjAccCAAGAAQEBwwH/AfEBjwIA + AYABAQHDAf8B8AEPAgABgAEBAcMB/wH4AR8CAAGAAQEBwwH/ARgBHAIAAYABAQHDAf8BBAEgAgABgAEB + AcEBzwQAAYABAQHAAc8EAAGAAQEBwAEHBAABgAEBAcABAwQAAYABAQHgAQMDAAEBAYABAQHwAQcCgQGA + AQMD/wHPAYABAQX/Ac8B4AEHBv8B8AEPCw== + + + + + + AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA + CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA + AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls + AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE + AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA + AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr + a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA + AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2 + tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA + AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb + 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA + AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu + p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq + 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen + p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq + eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6 + eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu + 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u + qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u + ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq + 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q + 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q + rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP// + AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/ + AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA + AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA + AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8 + 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 + Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA + ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa + GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm + JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv + Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt + LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy + MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy + MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0 + NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA + AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA + AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5 + OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA + AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9 + PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09 + PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA + AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA + QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk + JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA + A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u + gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA + 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 + HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL + CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA + AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ + AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj + IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk + JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm + JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo + KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u + Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/ + ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA + /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A + uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA + PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA + PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA + 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 + ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46 + AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/ + AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD + AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ + AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O + AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT + ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ + ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd + ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i + AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2 + qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo + AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3 + qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt + AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA + AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy + AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7 + swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53 + AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8 + ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ + ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA + vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD + xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4 + 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8 + 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH + zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8 + 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI + 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM + 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD + 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8 + 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI + zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI + 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO + 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT + 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba + 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe + 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl + +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp + +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw + /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u//// + ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O + 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA + AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO + 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO + 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf + OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr + SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw + UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8 + YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA + NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI + cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA + O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP + eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA + QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A + W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI + uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA + R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI + vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H + wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA + 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 + SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH + au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI + fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz + fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z + pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi + bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na + JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA + JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO + RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy + IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA + /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A + /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA + /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA + /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA + /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A + /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA + /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA + /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A + /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A + /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA + AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA + /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao + /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA + /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer + /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A + /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv + /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA + /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz + /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA + /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA + /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA + AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA + /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66 + /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+ + /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB + /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF + /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL + egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM + /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ + /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU + /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY + /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM + egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP + /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM + ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL + ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM + ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O + 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA + AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO + 7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAA + AAAADu4AAAAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO + 7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wA + AAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4AAAAAAAAO7gAAAAAAAA7uKAAAACAAAABAAAAAAQAg + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Ly2/+y8s//svLPf7Lyz3+y8s9/svLPf7Lyz3+y8s9/svL + Pf7Lyz3hrq5Ay5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iY + R8uYmEfLmJhC/svLPf7Lyz3+y8s9/svLPf7Lyz3+y8tq/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGUyMjxmMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2UyMloAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+fOwD/nzsA/587AP+fOwD/nzsA/587AP+fOwD/nzsA + /587AP+fOwD/nzsA/587AP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPf7Ly1T+y8sDAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzNVZjMz/6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA + /6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9/svL + VP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM1VmMzP/q0oA/6tKAP+rSgD/q0oA + /6tKAP+rSgD/q0oA/6tKAP+rSgD/q0oA/6tKAP+rSgD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAA + AP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+wUQD/sFEA + /7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP9mMzP/ZTIygAAAAAAAAAAAAAAA + AAAAAAAAAAAA/svLPeG6xm9da7BXADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVADOhVQAyoCZmMzNVZjMz + /7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/2YzM/9lMjKAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADKg + e2YzM1VmMzP/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/ZjMz + /2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh/wA2qf8ANqn/ADap/wA2qf8ANqn/ADap + /wAzof8AMqCAZjMzVWYzM//DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA + /8NpAP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPamYvaYAM6H/ADit/wA4rf8AOK3/ADit + /wA4rf8AOK3/ADOh/wAyoIBmMzNVZjMz/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA + /8hwAP/IcAD/yHAA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AO7T/ADu0 + /wA7tP8AO7T/ADu0/wA7tP8AM6H/ADKggGYzM1VmMzP/z3gA/894AP/PeAD/z3gA/894AP/PeAD/z3gA + /894AP/PeAD/z3gA/894AP/PeAD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh + /wA9uP8APbj/AD24/wA9uP8APbj/AD24/wAzof8AMqCAZjMzVWYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svL + PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh + /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m + /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV + /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza + /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp + /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH + b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs + /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv + /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv + /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv + /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz + /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2 + /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9 + wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38 + /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38 + /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM + AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL + VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB + pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA + Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA + AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl + o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz + /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh + /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer + /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2 + /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB + /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh + /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv + /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo + /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr + /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu + /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy + /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31 + /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4 + /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7 + /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs + QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ== + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Program.cs b/branches/ph-plugins/ExtraTools/NtObjects/Program.cs new file mode 100644 index 000000000..da2872772 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace NtObjects +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ObjectsWindow()); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c34b08eca --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NtObjects")] +[assembly: AssemblyDescription("NtObjects")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("NtObjects")] +[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5cea10c2-8c9e-45c7-be29-93a5ec8e3f30")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs new file mode 100644 index 000000000..152144e10 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3074 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NtObjects.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NtObjects.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx new file mode 100644 index 000000000..ffecec851 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs new file mode 100644 index 000000000..e906f1ebc --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3074 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NtObjects.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/branches/ph-plugins/ExtraTools/NtObjects/app.config b/branches/ph-plugins/ExtraTools/NtObjects/app.config new file mode 100644 index 000000000..b7db28170 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtObjects/app.config @@ -0,0 +1,3 @@ + + + diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj b/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj new file mode 100644 index 000000000..f156eeca4 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj @@ -0,0 +1,130 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E3CEB6D7-7080-4089-B54E-41025E30CE46} + WinExe + Properties + NtProfiler + NtProfiler + v2.0 + 512 + + + NtProfiler.Program + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + app.manifest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + + + + + + + + + + + Form + + + ProfilerWindow.cs + + + + + ProfilerWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + False + .NET Framework 2.0 %28AnyCPU%29 + true + + + False + .NET Framework 3.0 %28AnyCPU%29 + false + + + False + .NET Framework 3.5 + false + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + {8A448157-E1A7-4DDF-954E-287F1117832B} + ProcessHacker.Native + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs new file mode 100644 index 000000000..b8e5f9bf4 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs @@ -0,0 +1,304 @@ +namespace NtProfiler +{ + partial class ProfilerWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProfilerWindow)); + this.menuStripMain = new System.Windows.Forms.MenuStrip(); + this.profilerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.profileProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.profileKernelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tabControl = new System.Windows.Forms.TabControl(); + this.tabModules = new System.Windows.Forms.TabPage(); + this.listModules = new System.Windows.Forms.ListView(); + this.columnName = new System.Windows.Forms.ColumnHeader(); + this.columnHits = new System.Windows.Forms.ColumnHeader(); + this.columnFileName = new System.Windows.Forms.ColumnHeader(); + this.tabFunctions = new System.Windows.Forms.TabPage(); + this.toolStripContainer = new System.Windows.Forms.ToolStripContainer(); + this.toolStripProfileControl = new System.Windows.Forms.ToolStrip(); + this.toolStripButtonStart = new System.Windows.Forms.ToolStripButton(); + this.toolStripButtonStop = new System.Windows.Forms.ToolStripButton(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.listFunctions = new System.Windows.Forms.ListView(); + this.columnFunction = new System.Windows.Forms.ColumnHeader(); + this.columnFunctionHits = new System.Windows.Forms.ColumnHeader(); + this.menuStripMain.SuspendLayout(); + this.tabControl.SuspendLayout(); + this.tabModules.SuspendLayout(); + this.tabFunctions.SuspendLayout(); + this.toolStripContainer.ContentPanel.SuspendLayout(); + this.toolStripContainer.TopToolStripPanel.SuspendLayout(); + this.toolStripContainer.SuspendLayout(); + this.toolStripProfileControl.SuspendLayout(); + this.SuspendLayout(); + // + // menuStripMain + // + this.menuStripMain.Dock = System.Windows.Forms.DockStyle.None; + this.menuStripMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.profilerToolStripMenuItem}); + this.menuStripMain.Location = new System.Drawing.Point(0, 0); + this.menuStripMain.Name = "menuStripMain"; + this.menuStripMain.Size = new System.Drawing.Size(661, 24); + this.menuStripMain.TabIndex = 0; + this.menuStripMain.Text = "menuStrip1"; + // + // profilerToolStripMenuItem + // + this.profilerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.profileProcessToolStripMenuItem, + this.profileKernelToolStripMenuItem, + this.toolStripMenuItem1, + this.exitToolStripMenuItem}); + this.profilerToolStripMenuItem.Name = "profilerToolStripMenuItem"; + this.profilerToolStripMenuItem.ShowShortcutKeys = false; + this.profilerToolStripMenuItem.Size = new System.Drawing.Size(57, 20); + this.profilerToolStripMenuItem.Text = "&Profiler"; + // + // profileProcessToolStripMenuItem + // + this.profileProcessToolStripMenuItem.Name = "profileProcessToolStripMenuItem"; + this.profileProcessToolStripMenuItem.Size = new System.Drawing.Size(160, 22); + this.profileProcessToolStripMenuItem.Text = "Profile &Process..."; + this.profileProcessToolStripMenuItem.Click += new System.EventHandler(this.profileProcessToolStripMenuItem_Click); + // + // profileKernelToolStripMenuItem + // + this.profileKernelToolStripMenuItem.Name = "profileKernelToolStripMenuItem"; + this.profileKernelToolStripMenuItem.Size = new System.Drawing.Size(160, 22); + this.profileKernelToolStripMenuItem.Text = "Profile &Kernel"; + this.profileKernelToolStripMenuItem.Click += new System.EventHandler(this.profileKernelToolStripMenuItem_Click); + // + // tabControl + // + this.tabControl.Controls.Add(this.tabModules); + this.tabControl.Controls.Add(this.tabFunctions); + this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl.Location = new System.Drawing.Point(0, 0); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(661, 404); + this.tabControl.TabIndex = 1; + // + // tabModules + // + this.tabModules.Controls.Add(this.listModules); + this.tabModules.Location = new System.Drawing.Point(4, 22); + this.tabModules.Name = "tabModules"; + this.tabModules.Padding = new System.Windows.Forms.Padding(3); + this.tabModules.Size = new System.Drawing.Size(653, 378); + this.tabModules.TabIndex = 0; + this.tabModules.Text = "Modules"; + this.tabModules.UseVisualStyleBackColor = true; + // + // listModules + // + this.listModules.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnName, + this.columnHits, + this.columnFileName}); + this.listModules.Dock = System.Windows.Forms.DockStyle.Fill; + this.listModules.FullRowSelect = true; + this.listModules.HideSelection = false; + this.listModules.Location = new System.Drawing.Point(3, 3); + this.listModules.Name = "listModules"; + this.listModules.ShowItemToolTips = true; + this.listModules.Size = new System.Drawing.Size(647, 372); + this.listModules.TabIndex = 0; + this.listModules.UseCompatibleStateImageBehavior = false; + this.listModules.View = System.Windows.Forms.View.Details; + this.listModules.DoubleClick += new System.EventHandler(this.listModules_DoubleClick); + // + // columnName + // + this.columnName.Text = "Name"; + this.columnName.Width = 160; + // + // columnHits + // + this.columnHits.Text = "Hits"; + this.columnHits.Width = 100; + // + // columnFileName + // + this.columnFileName.Text = "File Name"; + this.columnFileName.Width = 300; + // + // tabFunctions + // + this.tabFunctions.Controls.Add(this.listFunctions); + this.tabFunctions.Location = new System.Drawing.Point(4, 22); + this.tabFunctions.Name = "tabFunctions"; + this.tabFunctions.Padding = new System.Windows.Forms.Padding(3); + this.tabFunctions.Size = new System.Drawing.Size(653, 378); + this.tabFunctions.TabIndex = 1; + this.tabFunctions.Text = "Functions"; + this.tabFunctions.UseVisualStyleBackColor = true; + // + // toolStripContainer + // + // + // toolStripContainer.ContentPanel + // + this.toolStripContainer.ContentPanel.Controls.Add(this.tabControl); + this.toolStripContainer.ContentPanel.Size = new System.Drawing.Size(661, 404); + this.toolStripContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.toolStripContainer.Location = new System.Drawing.Point(0, 0); + this.toolStripContainer.Name = "toolStripContainer"; + this.toolStripContainer.Size = new System.Drawing.Size(661, 453); + this.toolStripContainer.TabIndex = 1; + this.toolStripContainer.Text = "toolStripContainer1"; + // + // toolStripContainer.TopToolStripPanel + // + this.toolStripContainer.TopToolStripPanel.Controls.Add(this.menuStripMain); + this.toolStripContainer.TopToolStripPanel.Controls.Add(this.toolStripProfileControl); + // + // toolStripProfileControl + // + this.toolStripProfileControl.Dock = System.Windows.Forms.DockStyle.None; + this.toolStripProfileControl.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButtonStart, + this.toolStripButtonStop}); + this.toolStripProfileControl.Location = new System.Drawing.Point(3, 24); + this.toolStripProfileControl.Name = "toolStripProfileControl"; + this.toolStripProfileControl.Size = new System.Drawing.Size(58, 25); + this.toolStripProfileControl.TabIndex = 1; + // + // toolStripButtonStart + // + this.toolStripButtonStart.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButtonStart.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonStart.Image"))); + this.toolStripButtonStart.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButtonStart.Name = "toolStripButtonStart"; + this.toolStripButtonStart.Size = new System.Drawing.Size(23, 22); + this.toolStripButtonStart.Text = "Start"; + this.toolStripButtonStart.Click += new System.EventHandler(this.toolStripButtonStart_Click); + // + // toolStripButtonStop + // + this.toolStripButtonStop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButtonStop.Enabled = false; + this.toolStripButtonStop.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonStop.Image"))); + this.toolStripButtonStop.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButtonStop.Name = "toolStripButtonStop"; + this.toolStripButtonStop.Size = new System.Drawing.Size(23, 22); + this.toolStripButtonStop.Text = "Stop"; + this.toolStripButtonStop.Click += new System.EventHandler(this.toolStripButtonStop_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(157, 6); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(160, 22); + this.exitToolStripMenuItem.Text = "E&xit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // listFunctions + // + this.listFunctions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnFunction, + this.columnFunctionHits}); + this.listFunctions.Dock = System.Windows.Forms.DockStyle.Fill; + this.listFunctions.FullRowSelect = true; + this.listFunctions.HideSelection = false; + this.listFunctions.Location = new System.Drawing.Point(3, 3); + this.listFunctions.Name = "listFunctions"; + this.listFunctions.ShowItemToolTips = true; + this.listFunctions.Size = new System.Drawing.Size(647, 372); + this.listFunctions.TabIndex = 1; + this.listFunctions.UseCompatibleStateImageBehavior = false; + this.listFunctions.View = System.Windows.Forms.View.Details; + // + // columnFunction + // + this.columnFunction.Text = "Function"; + this.columnFunction.Width = 300; + // + // columnFunctionHits + // + this.columnFunctionHits.Text = "Hits"; + this.columnFunctionHits.Width = 100; + // + // ProfilerWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(661, 453); + this.Controls.Add(this.toolStripContainer); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MainMenuStrip = this.menuStripMain; + this.Name = "ProfilerWindow"; + this.Text = "NtProfiler"; + this.menuStripMain.ResumeLayout(false); + this.menuStripMain.PerformLayout(); + this.tabControl.ResumeLayout(false); + this.tabModules.ResumeLayout(false); + this.tabFunctions.ResumeLayout(false); + this.toolStripContainer.ContentPanel.ResumeLayout(false); + this.toolStripContainer.TopToolStripPanel.ResumeLayout(false); + this.toolStripContainer.TopToolStripPanel.PerformLayout(); + this.toolStripContainer.ResumeLayout(false); + this.toolStripContainer.PerformLayout(); + this.toolStripProfileControl.ResumeLayout(false); + this.toolStripProfileControl.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menuStripMain; + private System.Windows.Forms.ToolStripMenuItem profilerToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem profileProcessToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem profileKernelToolStripMenuItem; + private System.Windows.Forms.TabControl tabControl; + private System.Windows.Forms.TabPage tabModules; + private System.Windows.Forms.TabPage tabFunctions; + private System.Windows.Forms.ListView listModules; + private System.Windows.Forms.ColumnHeader columnName; + private System.Windows.Forms.ColumnHeader columnHits; + private System.Windows.Forms.ColumnHeader columnFileName; + private System.Windows.Forms.ToolStripContainer toolStripContainer; + private System.Windows.Forms.ToolStrip toolStripProfileControl; + private System.Windows.Forms.ToolStripButton toolStripButtonStart; + private System.Windows.Forms.ToolStripButton toolStripButtonStop; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.ListView listFunctions; + private System.Windows.Forms.ColumnHeader columnFunction; + private System.Windows.Forms.ColumnHeader columnFunctionHits; + } +} + diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs new file mode 100644 index 000000000..1a11d43a7 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs @@ -0,0 +1,267 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using ProcessHacker.Common.Ui; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Symbols; + +namespace NtProfiler +{ + public partial class ProfilerWindow : Form + { + private readonly IntPtr _userModeBase; + private readonly IntPtr _userModeLimit; + private readonly IntPtr _kernelModeBase; + private readonly IntPtr _kernelModeLimit; + + private ProfileHandle _profileHandle; + private Dictionary _kernelModules; + private SymbolProvider _kernelSymbols; + private IntPtr _profileBase; + private uint _profileSize; + private int _bucketSizeLog; + private uint _bucketSize; + + public ProfilerWindow() + { + InitializeComponent(); + + unchecked + { + _userModeBase = new IntPtr(0x00000000); + _userModeLimit = new IntPtr(0x7fffffff); + _kernelModeBase = new IntPtr((int)0x80000000); + _kernelModeLimit = new IntPtr((int)0xffffffff); + } + + try { KProcessHacker.Instance = new KProcessHacker(); } + catch { } + + try + { + using (var thandle = ProcessHandle.GetCurrent().GetToken(TokenAccess.Query | TokenAccess.AdjustPrivileges)) + thandle.SetPrivilege("SeSystemProfilePrivilege", SePrivilegeAttributes.Enabled); + } + catch + { } + + Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll"); + SymbolProvider.Options |= SymbolOptions.DeferredLoads; + + listModules.ListViewItemSorter = new SortedListViewComparer(listModules) + { + SortColumn = 1, + SortOrder = SortOrder.Descending + }; + listFunctions.ListViewItemSorter = new SortedListViewComparer(listFunctions) + { + SortColumn = 1, + SortOrder = SortOrder.Descending + }; + } + + private uint GetKernelModeCodeRange(out IntPtr baseAddress) + { + IntPtr minAddress = _kernelModeLimit; + IntPtr maxAddress = _kernelModeBase; + + foreach (var module in Windows.GetKernelModules()) + { + if (module.BaseAddress.CompareTo(_kernelModeBase) == -1) + continue; + + if (module.BaseAddress.CompareTo(minAddress) == -1) + minAddress = module.BaseAddress; + if (module.BaseAddress.CompareTo(maxAddress) == 1) + maxAddress = module.BaseAddress; + } + + baseAddress = minAddress; + + return maxAddress.Decrement(minAddress).ToUInt32(); + } + + private IntPtr GetAddress(int bufferIndex) + { + return _profileBase.Increment(_bucketSize * bufferIndex); + } + + private void LoadKernelSymbols() + { + _kernelSymbols = new SymbolProvider(new ProcessHandle(4, ProcessAccess.QueryInformation)); + _kernelSymbols.PreloadModules = true; + + foreach (var module in Windows.GetKernelModules()) + { + try + { + _kernelSymbols.LoadModule(module.FileName, module.BaseAddress); + } + catch + { } + } + } + + private void LoadProfileModules() + { + int[] counters = _profileHandle.Collect(); + Dictionary modules = new Dictionary(); + + for (int i = 0; i < counters.Length; i++) + { + if (counters[i] != 0) + { + IntPtr realAddress = this.GetAddress(i); + IntPtr baseAddress; + + _kernelSymbols.GetModuleFromAddress(realAddress, out baseAddress); + + if (!modules.ContainsKey(baseAddress)) + modules.Add(baseAddress, 0); + + modules[baseAddress]++; + } + } + + listModules.Items.Clear(); + + foreach (var moduleBase in modules.Keys) + { + listModules.Items.Add(new ListViewItem( + new string[] + { + _kernelModules[moduleBase].BaseName, + modules[moduleBase].ToString("N0"), + _kernelModules[moduleBase].FileName + }) + { + Tag = moduleBase + } + ); + } + } + + private void LoadProfileFunctions(IntPtr moduleBase) + { + int[] counters = _profileHandle.Collect(); + Dictionary functions = new Dictionary(); + + for (int i = 0; i < counters.Length; i++) + { + if (counters[i] != 0) + { + IntPtr realAddress = this.GetAddress(i); + IntPtr baseAddress; + + _kernelSymbols.GetModuleFromAddress(realAddress, out baseAddress); + + if (baseAddress != moduleBase) + continue; + + string fileName; + string symbolName; + ulong displacement; + + symbolName = _kernelSymbols.GetSymbolFromAddress(realAddress.ToUInt64(), out fileName, out displacement); + + if (symbolName != null) + { + if (!functions.ContainsKey(symbolName)) + functions.Add(symbolName, 0); + + functions[symbolName]++; + } + } + } + + listFunctions.Items.Clear(); + + foreach (var function in functions.Keys) + { + listFunctions.Items.Add(new ListViewItem( + new string[] + { + function, + functions[function].ToString("N0") + })); + } + } + + #region Menu Items + + #region Profiler + + private void profileProcessToolStripMenuItem_Click(object sender, EventArgs e) + { + + } + + private void profileKernelToolStripMenuItem_Click(object sender, EventArgs e) + { + IntPtr baseAddress; + uint size = this.GetKernelModeCodeRange(out baseAddress); + + _kernelModules = new Dictionary(); + + foreach (var module in Windows.GetKernelModules()) + _kernelModules.Add(module.BaseAddress, module); + + _profileBase = baseAddress; + _profileSize = size; + _bucketSizeLog = 6; // 64 byte bucket size + _bucketSize = (uint)(2 << (_bucketSizeLog - 1)); + _profileHandle = ProfileHandle.Create( + null, + baseAddress, + size, + _bucketSizeLog, + KProfileSource.ProfileTime, + IntPtr.Zero + ); + ProfileHandle.SetInterval(KProfileSource.ProfileTime, 1); // 100 nanoseconds + + this.LoadKernelSymbols(); + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + this.Close(); + } + + #endregion + + #endregion + + #region Toolbar + + #region Profile Control + + private void toolStripButtonStart_Click(object sender, EventArgs e) + { + _profileHandle.Start(); + toolStripButtonStart.Enabled = false; + toolStripButtonStop.Enabled = true; + } + + private void toolStripButtonStop_Click(object sender, EventArgs e) + { + _profileHandle.Stop(); + toolStripButtonStart.Enabled = true; + toolStripButtonStop.Enabled = false; + this.LoadProfileModules(); + } + + #endregion + + #endregion + + private void listModules_DoubleClick(object sender, EventArgs e) + { + this.LoadProfileFunctions((IntPtr)listModules.SelectedItems[0].Tag); + tabControl.SelectedTab = tabFunctions; + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx new file mode 100644 index 000000000..fad04cebc --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx @@ -0,0 +1,944 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 125, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAdhJREFUOE+tU8mq + IjEUfR/lP/kB1frKXtUPiKCCijtBEVy4cCUIBhVBHHCK4sKF81hqOaCoeF9OeFUiiIumA5eCSs5w70m+ + vv736na7NlGaKNbpdHTOud5qtVij0dCq1arto16v17MLIF+tVnQ6neh+v8s6Ho80n8+pUqnwcrlsf0sC + sFA0LpcLYeG73+/JMAw6n8/yH0hLpZJRLBZfSX5tcxO83W5ps9lQPp8nXddpvV4TXJkk2WyWi3q2g57N + AyYYQKfzm3K5nAQvFgtZWP1+n1KplGa10m63GezBKoCmqsPhJBRjTIIxB8wD7hKJBLMIms2mjmHtdjsJ + huJyuRRghyyX6y+JBGg6nUqi6/VK0WhUtwhERJIA9k0wDgKsqi5SlD9ybzKZWC4ikciToF6vM0wcbZi9 + wq6qqi/g8XgskxkMBhQOh58tCHsaNh+Ph1SYzWbSrqIoUhl7o9GIhsMh3W43EjFSKBR6DlFkaysUChwz + wIJVFMAmEKoQAIkA82Aw+HorM5mMPZ1OG8gcBw+Hg+UGlwnKIBFgw+v1vr+NyWTSHo/Hea1Wky1gJugZ + uSNKn8/HPR7Pe7AZSSwWs4kBaYFAgPn9fl2o6QLE3G63Jgg+P6Z/edk/yU9ET/seY4MAAAAASUVORK5C + YII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAcNJREFUOE+tU0ur + QVEYvT/KfzLCXzgYS6GQmSJlYGCklD2QkkdemwwMvJ8bxyNCvrvX7u6jW+4d3O6ulXLOenzr2+fj479P + r9ezSRgSrNvtCs65aLfbrNlsGrVazfarX7/ft0siX6/XdD6f6fF4KJxOJ1osFlStVnmlUrG/FQFZOprX + 65Vw8Hs4HMg0TbpcLuo/iJbLZbNUKn0X+YrNNXm329F2uyUhhMJmsyGk0iKFQoFLvMbBzPqFn8jL5ZIA + nMFgQNls1rBG6XQ6DPEQVbu6XC4CnE4neb1eRUYP6APp0uk0swRarZZAWfv9XgkgDYgaEJjP5zSbzZTQ + 7XajRCIhLAG5IiWA+CCvViuL7HA4yOPx0HQ6VdAp4vH4S6DRaDA0jjH0rHAFEXC73Yo8mUzUZobDIcVi + sdcI8oIYePh8PpWDjqtd8Ww8HtNoNKL7/U5yjRSNRl8lyt3aisUiRwc474hwhQFEJJlHIpHvtzKfz9tz + uZyJnePF4/FopcFlgjNEJNkMBALvb2Mmk7GnUiler9dV4+gEM2PvjDEKBoPc7/e/J+uVJJNJmyzICIfD + LBQKCekmJIn5fD5DCvz+Mf3ly/4E08xAcyCRlTQAAAAASUVORK5CYII= + + + + + AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA + CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA + AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls + AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE + AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA + AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr + a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA + AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2 + tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA + AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb + 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA + AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu + p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq + 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen + p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq + eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6 + eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu + 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwMDAwMDAwMDAwMDAwMDAwMDAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAQQFBQUFBQUFBQUFBQUFBQUFBQUFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AQYHBwcHBwcHBwcHBwcHBwcHBwcHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgJCQkJCQkJCQkJ + CQkJCQkJCQkJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoMlZWVlZWVlZWVlZWVlZWVlZWVAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQwNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAQ4PEA8PDw8PDw8PDw8PDw8PDw8PAQAAAAAAAAAAAAAAGxsbGxsbGxsbGxsbHAAA + AQ8QEBAQEBAQEBAQEBAQEBAQEBAQAQAAAAAAAAAAAAAAGxwdHR0dHR0dHR0dGwAAARASERERERERERER + ERERERERERERAQAAAAAAAAAAAAAAHB4eHh4eHh4eHh4eGwAAARITExMTExMTExMTExMTExMTExMTAQAA + AAAAAAAAAAAAGx4eHh8eHx4fHh8fGwAAARMVFRUVFRUVFRUVFRUVFRUVFRUVAQAAAAAAAAAAAAAAGx8f + Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA + ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa + GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm + JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv + Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt + LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy + MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy + MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0 + NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA + AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA + AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5 + OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA + 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 + 3LH/AOvR/wD///8AAAAAAAgALwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+Ef8AWDH/AHFR/wCMcf8A + ppH/AL+x/wDa0f8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB + AQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAECAgICAgICAgICAgIBKQAAAAAAAAAAAAAAAAAA + AAAAAQMDAwMDAwMDAwMDAwEpAAAAAAAAAAAAAAAAAAAAAAABBAQEBAQEBAQEBAQEASkAAAAAAAAAAAAA + AAAAAAAAAAEFBQUFBQUFBQUFBQUBKQAAAAAAAAAAAAAAAAAAAAAAAQYGBgYGBgYGBgYGBgEpAAAAAAAA + LAsLCwsLCwsLAAABBwcHBwcHBwcHBwcHASkAAAAAAAAsCwwMDAwMDAsYAAEICAgICAgICAgICAgBKQAA + AAAAACwLDQ0NDQ0NCxgAAQkJCQkJCQkJCQkJCQEpAAAAAAAALAsODg4ODg4LGAABCgoKCgoKCgoKCgoK + ASkAAAAAAAAsCw8PDw8PDwsYAAEBAQEBAQEBAQEBAQEBKQAAAAAAACwLERERERERCxcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAALAsSEhISEhILEBAQEBAQEBAQEBAQEBAQEBAQEBAQECosCxMTExMTEwsQGhoaGhoa + GhoaGhoaGhoaGhoaGhoQGSwLFBQUFBQUCxAbGxsbGxsbGxsbGxsbGxsbGxsbGxAZLAsVFRUVFRULEBwc + HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL + CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA + AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ + AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj + IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk + JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm + JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo + KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u + Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/ + ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA + /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A + uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA + PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA + PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA + g73iAJrA3wClw9sApsbcALfH3AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAAsDYAAM9AAADwSgAR/1sA + Mf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAA + IP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAA + Z/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAA + qc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46 + AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/ + AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD + AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ + AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O + AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT + ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ + ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd + ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i + AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2 + qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo + AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3 + qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt + AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA + AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy + AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7 + swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53 + AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8 + ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ + ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA + vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD + xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4 + 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8 + 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH + zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8 + 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI + 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM + 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD + 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8 + 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI + zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI + 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO + 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT + 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba + 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe + 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl + +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp + +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw + /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u//// + ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O + 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA + AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO + 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO + 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf + OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr + SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw + UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8 + YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA + NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI + cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA + O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP + eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA + QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A + W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI + uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA + R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI + vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H + wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA + W698oMzHvNQAM6EATNoATNoATNoATNoATNoATNoAM6EAW69HxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlH + xOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOkAW698oMzHvNQAM6EAM6EAM6EAM6EAM6EA + M6EAM6EAM6EAW69HyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtH + yOtHyOtHyOtHyOsAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69Hy+xHy+xHy+xHy+xH + y+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+wAW698oMwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAW69Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5G + z+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+4AW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69G + 0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u8A + W698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69G1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG + 1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fIAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAW69G2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG + 2fNG2fNG2fNG2fMAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F3PVF3PVF3PVF3PVF + 3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PUAW698oMwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF + 3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/YAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F + 4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/gA + W698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF + 5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vkAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAW69E6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE + 6vtE6vtE6vtE6vsAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69E7fxE7fxE7fxE7fxE + 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 + bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na + JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA + JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO + RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy + IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA + /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A + /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA + /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA + /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA + /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A + /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA + /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA + /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A + /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A + /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA + AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA + /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao + /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA + /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer + /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A + /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv + /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA + /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz + /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA + /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA + /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA + AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA + /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66 + /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+ + /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB + /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF + /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL + egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM + /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ + /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU + /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY + /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM + egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP + /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM + ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL + ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM + ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O + 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA + AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO + 7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAA + AAAADu4AAAAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO + 7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wA + AAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4AAAAAAAAO7gAAAAAAAA7uKAAAACAAAABAAAAAAQAg + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Ly2/+y8s//svLPf7Lyz3+y8s9/svLPf7Lyz3+y8s9/svL + Pf7Lyz3hrq5Ay5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iY + R8uYmEfLmJhC/svLPf7Lyz3+y8s9/svLPf7Lyz3+y8tq/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGUyMjxmMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2UyMloAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+fOwD/nzsA/587AP+fOwD/nzsA/587AP+fOwD/nzsA + /587AP+fOwD/nzsA/587AP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPf7Ly1T+y8sDAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzNVZjMz/6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA + /6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9/svL + VP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM1VmMzP/q0oA/6tKAP+rSgD/q0oA + /6tKAP+rSgD/q0oA/6tKAP+rSgD/q0oA/6tKAP+rSgD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAA + AP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+wUQD/sFEA + /7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP9mMzP/ZTIygAAAAAAAAAAAAAAA + AAAAAAAAAAAA/svLPeG6xm9da7BXADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVADOhVQAyoCZmMzNVZjMz + /7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/2YzM/9lMjKAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADKg + e2YzM1VmMzP/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/ZjMz + /2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh/wA2qf8ANqn/ADap/wA2qf8ANqn/ADap + /wAzof8AMqCAZjMzVWYzM//DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA + /8NpAP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPamYvaYAM6H/ADit/wA4rf8AOK3/ADit + /wA4rf8AOK3/ADOh/wAyoIBmMzNVZjMz/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA + /8hwAP/IcAD/yHAA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AO7T/ADu0 + /wA7tP8AO7T/ADu0/wA7tP8AM6H/ADKggGYzM1VmMzP/z3gA/894AP/PeAD/z3gA/894AP/PeAD/z3gA + /894AP/PeAD/z3gA/894AP/PeAD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh + /wA9uP8APbj/AD24/wA9uP8APbj/AD24/wAzof8AMqCAZjMzVWYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svL + PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh + /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m + /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV + /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza + /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp + /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH + b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs + /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv + /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv + /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv + /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz + /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2 + /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9 + wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38 + /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38 + /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM + AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL + VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB + pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA + Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA + AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl + o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz + /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh + /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer + /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2 + /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB + /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh + /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv + /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo + /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr + /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu + /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy + /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31 + /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4 + /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7 + /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs + QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ== + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs new file mode 100644 index 000000000..372899312 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace NtProfiler +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ProfilerWindow()); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..54c29984e --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NtProfiler")] +[assembly: AssemblyDescription("NtProfiler")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("NtProfiler")] +[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ad8532b1-3718-4e5d-904a-54085eb45e77")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs new file mode 100644 index 000000000..72cdade61 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NtProfiler.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NtProfiler.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx new file mode 100644 index 000000000..ffecec851 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs new file mode 100644 index 000000000..6fa45666e --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NtProfiler.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/app.config b/branches/ph-plugins/ExtraTools/NtProfiler/app.config new file mode 100644 index 000000000..b7db28170 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/app.config @@ -0,0 +1,3 @@ + + + diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest b/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest new file mode 100644 index 000000000..25c0371c1 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs new file mode 100644 index 000000000..0207b7b86 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs @@ -0,0 +1,353 @@ +namespace ProcessAnalyzer +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow)); + this.menuStrip = new System.Windows.Forms.MenuStrip(); + this.analyzerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tabControl = new System.Windows.Forms.TabControl(); + this.tabHandleTracing = new System.Windows.Forms.TabPage(); + this.listHandleStack = new System.Windows.Forms.ListView(); + this.columnAddress = new System.Windows.Forms.ColumnHeader(); + this.columnSymbol = new System.Windows.Forms.ColumnHeader(); + this.listHandleTraces = new System.Windows.Forms.ListView(); + this.columnIndex = new System.Windows.Forms.ColumnHeader(); + this.columnHandle = new System.Windows.Forms.ColumnHeader(); + this.columnType = new System.Windows.Forms.ColumnHeader(); + this.columnTid = new System.Windows.Forms.ColumnHeader(); + this.columnHandleName = new System.Windows.Forms.ColumnHeader(); + this.buttonSnapshot = new System.Windows.Forms.Button(); + this.buttonDisableHandleTracing = new System.Windows.Forms.Button(); + this.buttonEnableHandleTracing = new System.Windows.Forms.Button(); + this.tabHiddenObjects = new System.Windows.Forms.TabPage(); + this.labelObjectsScanProgress = new System.Windows.Forms.Label(); + this.buttonScanHiddenObjects = new System.Windows.Forms.Button(); + this.listHiddenObjects = new System.Windows.Forms.ListView(); + this.columnObjectType = new System.Windows.Forms.ColumnHeader(); + this.columnObjectId = new System.Windows.Forms.ColumnHeader(); + this.columnObjectInfo = new System.Windows.Forms.ColumnHeader(); + this.menuStrip.SuspendLayout(); + this.tabControl.SuspendLayout(); + this.tabHandleTracing.SuspendLayout(); + this.tabHiddenObjects.SuspendLayout(); + this.SuspendLayout(); + // + // menuStrip + // + this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.analyzerToolStripMenuItem}); + this.menuStrip.Location = new System.Drawing.Point(0, 0); + this.menuStrip.Name = "menuStrip"; + this.menuStrip.Size = new System.Drawing.Size(695, 24); + this.menuStrip.TabIndex = 0; + this.menuStrip.Text = "menuStrip1"; + // + // analyzerToolStripMenuItem + // + this.analyzerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openProcessToolStripMenuItem, + this.toolStripMenuItem1, + this.exitToolStripMenuItem}); + this.analyzerToolStripMenuItem.Name = "analyzerToolStripMenuItem"; + this.analyzerToolStripMenuItem.Size = new System.Drawing.Size(64, 20); + this.analyzerToolStripMenuItem.Text = "Analyzer"; + // + // openProcessToolStripMenuItem + // + this.openProcessToolStripMenuItem.Name = "openProcessToolStripMenuItem"; + this.openProcessToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); + this.openProcessToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.openProcessToolStripMenuItem.Text = "&Open Process..."; + this.openProcessToolStripMenuItem.Click += new System.EventHandler(this.openProcessToolStripMenuItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(195, 6); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.exitToolStripMenuItem.Text = "E&xit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // tabControl + // + this.tabControl.Controls.Add(this.tabHandleTracing); + this.tabControl.Controls.Add(this.tabHiddenObjects); + this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl.Location = new System.Drawing.Point(0, 24); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(695, 469); + this.tabControl.TabIndex = 1; + // + // tabHandleTracing + // + this.tabHandleTracing.Controls.Add(this.listHandleStack); + this.tabHandleTracing.Controls.Add(this.listHandleTraces); + this.tabHandleTracing.Controls.Add(this.buttonSnapshot); + this.tabHandleTracing.Controls.Add(this.buttonDisableHandleTracing); + this.tabHandleTracing.Controls.Add(this.buttonEnableHandleTracing); + this.tabHandleTracing.Location = new System.Drawing.Point(4, 22); + this.tabHandleTracing.Name = "tabHandleTracing"; + this.tabHandleTracing.Padding = new System.Windows.Forms.Padding(3); + this.tabHandleTracing.Size = new System.Drawing.Size(687, 443); + this.tabHandleTracing.TabIndex = 0; + this.tabHandleTracing.Text = "Handle Tracing"; + this.tabHandleTracing.UseVisualStyleBackColor = true; + // + // listHandleStack + // + this.listHandleStack.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listHandleStack.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnAddress, + this.columnSymbol}); + this.listHandleStack.FullRowSelect = true; + this.listHandleStack.HideSelection = false; + this.listHandleStack.Location = new System.Drawing.Point(6, 226); + this.listHandleStack.MultiSelect = false; + this.listHandleStack.Name = "listHandleStack"; + this.listHandleStack.Size = new System.Drawing.Size(675, 211); + this.listHandleStack.TabIndex = 1; + this.listHandleStack.UseCompatibleStateImageBehavior = false; + this.listHandleStack.View = System.Windows.Forms.View.Details; + // + // columnAddress + // + this.columnAddress.Text = "Address"; + this.columnAddress.Width = 100; + // + // columnSymbol + // + this.columnSymbol.Text = "Symbol"; + this.columnSymbol.Width = 300; + // + // listHandleTraces + // + this.listHandleTraces.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listHandleTraces.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnIndex, + this.columnHandle, + this.columnType, + this.columnTid, + this.columnHandleName}); + this.listHandleTraces.FullRowSelect = true; + this.listHandleTraces.HideSelection = false; + this.listHandleTraces.Location = new System.Drawing.Point(6, 35); + this.listHandleTraces.MultiSelect = false; + this.listHandleTraces.Name = "listHandleTraces"; + this.listHandleTraces.Size = new System.Drawing.Size(675, 185); + this.listHandleTraces.TabIndex = 1; + this.listHandleTraces.UseCompatibleStateImageBehavior = false; + this.listHandleTraces.View = System.Windows.Forms.View.Details; + this.listHandleTraces.SelectedIndexChanged += new System.EventHandler(this.listHandleTraces_SelectedIndexChanged); + // + // columnIndex + // + this.columnIndex.Text = "Index"; + // + // columnHandle + // + this.columnHandle.Text = "Handle"; + // + // columnType + // + this.columnType.Text = "Type"; + this.columnType.Width = 120; + // + // columnTid + // + this.columnTid.Text = "TID"; + // + // columnHandleName + // + this.columnHandleName.Text = "Handle Name"; + this.columnHandleName.Width = 300; + // + // buttonSnapshot + // + this.buttonSnapshot.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonSnapshot.Location = new System.Drawing.Point(168, 6); + this.buttonSnapshot.Name = "buttonSnapshot"; + this.buttonSnapshot.Size = new System.Drawing.Size(75, 23); + this.buttonSnapshot.TabIndex = 0; + this.buttonSnapshot.Text = "Snapshot"; + this.buttonSnapshot.UseVisualStyleBackColor = true; + this.buttonSnapshot.Click += new System.EventHandler(this.buttonSnapshot_Click); + // + // buttonDisableHandleTracing + // + this.buttonDisableHandleTracing.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonDisableHandleTracing.Location = new System.Drawing.Point(87, 6); + this.buttonDisableHandleTracing.Name = "buttonDisableHandleTracing"; + this.buttonDisableHandleTracing.Size = new System.Drawing.Size(75, 23); + this.buttonDisableHandleTracing.TabIndex = 0; + this.buttonDisableHandleTracing.Text = "Disable"; + this.buttonDisableHandleTracing.UseVisualStyleBackColor = true; + this.buttonDisableHandleTracing.Click += new System.EventHandler(this.buttonDisableHandleTracing_Click); + // + // buttonEnableHandleTracing + // + this.buttonEnableHandleTracing.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonEnableHandleTracing.Location = new System.Drawing.Point(6, 6); + this.buttonEnableHandleTracing.Name = "buttonEnableHandleTracing"; + this.buttonEnableHandleTracing.Size = new System.Drawing.Size(75, 23); + this.buttonEnableHandleTracing.TabIndex = 0; + this.buttonEnableHandleTracing.Text = "Enable"; + this.buttonEnableHandleTracing.UseVisualStyleBackColor = true; + this.buttonEnableHandleTracing.Click += new System.EventHandler(this.buttonEnableHandleTracing_Click); + // + // tabHiddenObjects + // + this.tabHiddenObjects.Controls.Add(this.labelObjectsScanProgress); + this.tabHiddenObjects.Controls.Add(this.buttonScanHiddenObjects); + this.tabHiddenObjects.Controls.Add(this.listHiddenObjects); + this.tabHiddenObjects.Location = new System.Drawing.Point(4, 22); + this.tabHiddenObjects.Name = "tabHiddenObjects"; + this.tabHiddenObjects.Padding = new System.Windows.Forms.Padding(3); + this.tabHiddenObjects.Size = new System.Drawing.Size(687, 443); + this.tabHiddenObjects.TabIndex = 1; + this.tabHiddenObjects.Text = "Hidden Objects"; + this.tabHiddenObjects.UseVisualStyleBackColor = true; + // + // labelObjectsScanProgress + // + this.labelObjectsScanProgress.AutoSize = true; + this.labelObjectsScanProgress.Location = new System.Drawing.Point(87, 11); + this.labelObjectsScanProgress.Name = "labelObjectsScanProgress"; + this.labelObjectsScanProgress.Size = new System.Drawing.Size(41, 13); + this.labelObjectsScanProgress.TabIndex = 4; + this.labelObjectsScanProgress.Text = "Ready."; + // + // buttonScanHiddenObjects + // + this.buttonScanHiddenObjects.Enabled = false; + this.buttonScanHiddenObjects.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonScanHiddenObjects.Location = new System.Drawing.Point(6, 6); + this.buttonScanHiddenObjects.Name = "buttonScanHiddenObjects"; + this.buttonScanHiddenObjects.Size = new System.Drawing.Size(75, 23); + this.buttonScanHiddenObjects.TabIndex = 3; + this.buttonScanHiddenObjects.Text = "Scan"; + this.buttonScanHiddenObjects.UseVisualStyleBackColor = true; + this.buttonScanHiddenObjects.Click += new System.EventHandler(this.buttonScanHiddenObjects_Click); + // + // listHiddenObjects + // + this.listHiddenObjects.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listHiddenObjects.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnObjectType, + this.columnObjectId, + this.columnObjectInfo}); + this.listHiddenObjects.FullRowSelect = true; + this.listHiddenObjects.HideSelection = false; + this.listHiddenObjects.Location = new System.Drawing.Point(6, 35); + this.listHiddenObjects.MultiSelect = false; + this.listHiddenObjects.Name = "listHiddenObjects"; + this.listHiddenObjects.Size = new System.Drawing.Size(675, 402); + this.listHiddenObjects.TabIndex = 2; + this.listHiddenObjects.UseCompatibleStateImageBehavior = false; + this.listHiddenObjects.View = System.Windows.Forms.View.Details; + // + // columnObjectType + // + this.columnObjectType.Text = "Type"; + this.columnObjectType.Width = 100; + // + // columnObjectId + // + this.columnObjectId.Text = "ID"; + // + // columnObjectInfo + // + this.columnObjectInfo.Text = "Information"; + this.columnObjectInfo.Width = 300; + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(695, 493); + this.Controls.Add(this.tabControl); + this.Controls.Add(this.menuStrip); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MainMenuStrip = this.menuStrip; + this.Name = "MainWindow"; + this.Text = "Process Analyzer"; + this.menuStrip.ResumeLayout(false); + this.menuStrip.PerformLayout(); + this.tabControl.ResumeLayout(false); + this.tabHandleTracing.ResumeLayout(false); + this.tabHiddenObjects.ResumeLayout(false); + this.tabHiddenObjects.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menuStrip; + private System.Windows.Forms.ToolStripMenuItem analyzerToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem openProcessToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.TabControl tabControl; + private System.Windows.Forms.TabPage tabHandleTracing; + private System.Windows.Forms.Button buttonDisableHandleTracing; + private System.Windows.Forms.Button buttonEnableHandleTracing; + private System.Windows.Forms.Button buttonSnapshot; + private System.Windows.Forms.ListView listHandleTraces; + private System.Windows.Forms.ColumnHeader columnHandle; + private System.Windows.Forms.ColumnHeader columnType; + private System.Windows.Forms.ColumnHeader columnTid; + private System.Windows.Forms.ListView listHandleStack; + private System.Windows.Forms.ColumnHeader columnAddress; + private System.Windows.Forms.ColumnHeader columnSymbol; + private System.Windows.Forms.ColumnHeader columnHandleName; + private System.Windows.Forms.ColumnHeader columnIndex; + private System.Windows.Forms.TabPage tabHiddenObjects; + private System.Windows.Forms.ListView listHiddenObjects; + private System.Windows.Forms.ColumnHeader columnObjectType; + private System.Windows.Forms.ColumnHeader columnObjectId; + private System.Windows.Forms.ColumnHeader columnObjectInfo; + private System.Windows.Forms.Button buttonScanHiddenObjects; + private System.Windows.Forms.Label labelObjectsScanProgress; + } +} + diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs new file mode 100644 index 000000000..589a54aff --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs @@ -0,0 +1,314 @@ +using System; +using System.Windows.Forms; +using ProcessHacker.Common; +using ProcessHacker.Common.Ui; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Symbols; +using ProcessHacker.Native.Ui; + +namespace ProcessAnalyzer +{ + public partial class MainWindow : Form + { + private int _pid; + + public MainWindow() + { + InitializeComponent(); + + try + { + KProcessHacker.Instance = new KProcessHacker(); + } + catch + { } + + Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll"); + + listHandleTraces.ListViewItemSorter = new SortedListViewComparer(listHandleTraces); + } + + private void openProcessToolStripMenuItem_Click(object sender, EventArgs e) + { + this.ChooseProcess(); + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void ShowException(string operation, Exception ex) + { + MessageBox.Show(operation + ": " + ex.Message, "Process Analyzer", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + #region Handle Tracing + + private SymbolProvider _symbols; + private ProcessHandleTraceCollection _currentHtCollection; + + private void ChooseProcess() + { + ChooseProcessDialog cpd = new ChooseProcessDialog(); + + if (cpd.ShowDialog() == DialogResult.OK) + _pid = cpd.SelectedPid; + } + + private void PopulateHandleTraceList() + { + listHandleTraces.BeginUpdate(); + listHandleTraces.Items.Clear(); + + for (int i = 0; i < _currentHtCollection.Count; i++) + { + var trace = _currentHtCollection[i]; + ListViewItem item = new ListViewItem( + new string[] + { + i.ToString(), + "0x" + trace.Handle.ToString("x"), + trace.Type.ToString(), + trace.ClientId.ThreadId.ToString() + }); + + item.Tag = i; + listHandleTraces.Items.Add(item); + } + + listHandleTraces.EndUpdate(); + } + + private void buttonEnableHandleTracing_Click(object sender, EventArgs e) + { + try + { + using (var phandle = new ProcessHandle(_pid, ProcessAccess.SetInformation)) + phandle.EnableHandleTracing(); + } + catch (Exception ex) + { + this.ShowException("Error enabling handle tracing", ex); + } + } + + private void buttonDisableHandleTracing_Click(object sender, EventArgs e) + { + try + { + using (var phandle = new ProcessHandle(_pid, ProcessAccess.SetInformation)) + phandle.DisableHandleTracing(); + } + catch (Exception ex) + { + this.ShowException("Error disabling handle tracing", ex); + } + } + + private void buttonSnapshot_Click(object sender, EventArgs e) + { + try + { + using (var phandle = new ProcessHandle(_pid, ProcessAccess.QueryInformation | ProcessAccess.VmRead)) + { + _currentHtCollection = phandle.GetHandleTraces(); + + if (_symbols != null) + _symbols.Dispose(); + + SymbolProvider.Options |= SymbolOptions.DeferredLoads; + _symbols = new SymbolProvider(phandle); + + WorkQueue.GlobalQueueWorkItem(new Action(() => + { + var symbols = _symbols; + + _symbols.PreloadModules = true; + + try + { + foreach (var module in phandle.GetModules()) + { + try + { + symbols.LoadModule(module.FileName, module.BaseAddress); + } + catch + { } + } + } + catch + { } + + try + { + foreach (var module in Windows.GetKernelModules()) + { + try + { + symbols.LoadModule(module.FileName, module.BaseAddress); + } + catch + { } + } + } + catch + { } + })); + } + + this.PopulateHandleTraceList(); + } + catch (Exception ex) + { + this.ShowException("Error getting the handle trace snapshot", ex); + } + } + + private void listHandleTraces_SelectedIndexChanged(object sender, EventArgs e) + { + if (_currentHtCollection == null || listHandleTraces.SelectedItems.Count != 1) + return; + + var trace = _currentHtCollection[(int)listHandleTraces.SelectedItems[0].Tag]; + + listHandleStack.BeginUpdate(); + listHandleStack.Items.Clear(); + + foreach (var address in trace.Stack) + { + ListViewItem item = new ListViewItem(); + + item.Text = "0x" + address.ToInt32().ToString("x8"); + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, _symbols.GetSymbolFromAddress(address.ToUInt64()))); + listHandleStack.Items.Add(item); + } + + listHandleStack.EndUpdate(); + } + + #endregion + + #region Hidden Objects + + private struct KVars + { + public IntPtr NonPagedPoolStartAddress; + public IntPtr NonPagedPoolSizeAddress; + public IntPtr NonPagedPoolStart; + public uint NonPagedPoolSize; + public IntPtr PsProcessTypeAddress; + public IntPtr PsProcessType; + public IntPtr PsThreadTypeAddress; + public IntPtr PsThreadType; + } + + private unsafe KVars GetKVars() + { + SymbolProvider symbols = new SymbolProvider(); + + symbols.LoadModule(Windows.KernelFileName, Windows.KernelBase); + + KVars vars = new KVars(); + + vars.NonPagedPoolStartAddress = symbols.GetSymbolFromName("MmNonPagedPoolStart").Address.ToIntPtr(); + vars.NonPagedPoolSizeAddress = symbols.GetSymbolFromName("MmMaximumNonPagedPoolInBytes").Address.ToIntPtr(); + vars.PsProcessTypeAddress = symbols.GetSymbolFromName("PsProcessType").Address.ToIntPtr(); + vars.PsThreadTypeAddress = symbols.GetSymbolFromName("PsThreadType").Address.ToIntPtr(); + + int bytesRead; + + KProcessHacker.Instance.KphReadVirtualMemoryUnsafe( + ProcessHandle.Current, + vars.NonPagedPoolStartAddress.ToInt32(), + &vars.NonPagedPoolStart, + IntPtr.Size, + out bytesRead + ); + KProcessHacker.Instance.KphReadVirtualMemoryUnsafe( + ProcessHandle.Current, + vars.NonPagedPoolSizeAddress.ToInt32(), + &vars.NonPagedPoolSize, + sizeof(uint), + out bytesRead + ); + KProcessHacker.Instance.KphReadVirtualMemoryUnsafe( + ProcessHandle.Current, + vars.PsProcessTypeAddress.ToInt32(), + &vars.PsProcessType, + IntPtr.Size, + out bytesRead + ); + KProcessHacker.Instance.KphReadVirtualMemoryUnsafe( + ProcessHandle.Current, + vars.PsThreadTypeAddress.ToInt32(), + &vars.PsThreadType, + IntPtr.Size, + out bytesRead + ); + + symbols.Dispose(); + + return vars; + } + + private unsafe void ScanHiddenObjects() + { + KVars vars = this.GetKVars(); + int bytesRead; + + throw new NotSupportedException(); + + listHiddenObjects.Items.Clear(); + + using (var currentPage = new MemoryAlloc(Windows.PageSize)) + { + for ( + IntPtr address = vars.NonPagedPoolStart; + address.CompareTo(vars.NonPagedPoolStart.Increment(vars.NonPagedPoolSize)) == -1; + address = address.Increment(Windows.PageSize) + ) + { + try + { + KProcessHacker.Instance.KphReadVirtualMemoryUnsafe( + ProcessHandle.Current, + address.ToInt32(), + (IntPtr)currentPage, + Windows.PageSize, + out bytesRead + ); + } + catch + { + continue; + } + + for ( + IntPtr inner = address; + inner.CompareTo(address.Increment(Windows.PageSize)) == -1; + inner = inner.Increment(8) + ) + { + } + + labelObjectsScanProgress.Text = string.Format("Scanned 0x{0:x8}", address.ToInt32()); + Application.DoEvents(); + } + } + + labelObjectsScanProgress.Text = "Finished."; + } + + private void buttonScanHiddenObjects_Click(object sender, EventArgs e) + { + this.ScanHiddenObjects(); + } + + #endregion + } +} diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx new file mode 100644 index 000000000..0a2236b3e --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx @@ -0,0 +1,912 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA + CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA + AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls + AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE + AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA + AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr + a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA + AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2 + tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA + AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb + 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA + AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu + p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq + 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen + p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq + eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6 + eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu + 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u + qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u + ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq + 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q + 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q + rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP// + AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/ + AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA + AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA + AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8 + 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 + Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA + ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa + GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm + JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv + Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt + LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy + MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy + MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0 + NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA + AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA + AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5 + OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA + AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9 + PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09 + PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA + AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA + QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk + JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA + A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u + gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA + 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 + HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL + CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA + AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ + AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj + IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk + JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm + JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo + KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u + Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/ + ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA + /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A + uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA + PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA + PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA + g73iAJrA3wClw9sApsbcALfH3AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAAsDYAAM9AAADwSgAR/1sA + Mf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAA + IP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAA + Z/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAA + qc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAA + sI4AAM+pAADwwwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAA + kD4AALBNAADPWwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAA + cAYAAJAJAACwCgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4A + UAAXAHAAIQCQACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAA + LwAgAFAANgBwAEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8A + AAAAACwALwBLAFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A + ////AAAAAAAbAC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A + 69H/AP///wAAAAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8A + v7H/ANrR/wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAACwEBAQEBAQYAAAAAAAAAAAwCAgICAgIHAAAA + Nx8fHwANAwMDAwMDCAAAAB0QEBATDgUFBQUFBQoAAAAeERERGQ8EBAQEBAQJAAAAIBQUFBg5Ojo6Ojo6 + Ojo6OyAVFRUSGigoKCgoKCgoKCYhFxcXFhsvLy8vLy8vLy8mOCMjIyIcMDAwMDAwMDAwJwAAAAAAKjIy + MjIyMjIyMiQAAAAAACszMzMzMzMzMzMlAAAAAAAsNDQ0NDQ0NDQ0JQAAAAAALTU1NTU1NTU1NSkAAAAA + AC42NjY2NjY2NjYxAAAAAAA8PT09PT09PT09Pv//AAD4BwAA+AcAAAgHAAAABwAAAAcAAAAAAAAAAAAA + AAAAAAAAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAACJUE5HDQoaCgAAAA1JSERSAAABAAAAAQAIBgAA + AFxyqGYAAA0ASURBVHja7dp1tJdFHsfxi4KigihggB3YPXSrlL1u79rd2B3Y3Qp2YGzvuiLdl7i03dgF + SkiJICB7OHvOXWbv3HNmfs/3eWbmN+/XH/ecz3/f4fC8+YNbpwJAsuqs+TF9+urVvg8BUKyWLevUqQ5A + o0a+zwFQlIULKyrWfPNaAFq0+G8QAJS3Nd98/foEAEjSmm++Xj0CACRpzTdfty4BAJJEAICEEYAMXjqm + gv82RZSOfvF/3zwBKBEBQKwIgAACgFgRAAEEALEiAAIIAGJFAASYAnBUv26+zwJqePnskdomAAIIAGJB + AHJgDEBfAoDwvHwOARBHABALApADUwCOJAAI0AACIM8YgIcJAMIz4FwCII4AIBYEIAfGADxEABCeAecR + AHEEALEgADkwBeCIBwkAwvNKbwIgjgAgFgQgBwQAsSAAOTAG4AECgPC8cj4BEEcAEAsCkANTAA6/v7vv + s4AaBl4wQtsEQAABQCwIQA6MAbiPACA8Ay8kAOIIAGJBAHJAABALApADUwAOu5cAIDyDLiIA4ggAYkEA + cmAMwD0EAOEZdDEBEEcAEAsCkANjAO4mAAjPoEsIgDhTAA4lAAjQYAIgjwAgFgQgB8YA3EUAEJ7BlxIA + cQQAsSAAOTAG4E4CgPAMvowAiDMF4BACgAANIQDyjAG4gwAgPEMuJwDiCABiQQByQAAQCwKQA2MAbicA + CM+QKwiAOAKAWBCAHJgC0Os2AoDwDL2SAIgjAIgFAciBMQC3EgCEZ+hVBEAcAUAsCEAOCABiQQByYApA + z1sIAMIz7GoCII4AIBYEIAfGANxMABCeYdcQAHEEALEgADkwBuAmAoDwDLuWAIgzBaAHAUCAhhMAeQQA + sSAAOTAG4EYCgPAMv44AiCMAiAUByIExADcQAIRneB8CIM4UgO439PB9FlDDiD7DtU0ABBgDcD0BQHhG + XE8AxBEAxIIA5IAAIBYEIAfGAPQhAAjPiBsIgDhTALoRAARoJAGQZwzAdQQA4Rl5IwEQRwAQCwKQA2MA + riUACM/ImwiAOAKAWBCAHJgCcDABQIBGEQB5xgBcQwAQnlE3EwBxBACxIAA5MAbgagKA8Iy6hQCIIwCI + BQHIgSkAB11FABCe0bcSAHEEALEgADkgAIgFAciBMQBXEgCEZ/RtBEAcAUAsCEAOTAE48AoCgPCMuZ0A + iCMAiAUByIExAJcTAIRnzB0EQBwBQCwIQA6MAbiMACA8Y+4kAOJMAehKABCgsQRAHgFALAhADowBuJQA + IDxj7yIA4ggAYkEAcmAMwCUEAOEZezcBEGcKQBcCgABVEgB5xgBcTAAQnsp7CIA4AoBYEIAcEADEggDk + wBiAiwgAwlN5LwEQZwpA54t6+j4LqGHcvcO0TQAEGANwIQFAeMbdRwDEEQDEggDkwBiACwgAwjPufgIg + jgAgFiUHoL+q+ZccFRUnzKioYwpAJwKAAI0nALJqDcD5BADhGf8AARBFABATAiCs1gD0JgAIz/gHCYAo + AoCYiAagyza+n1O8yi/1XVsAOp5HABCeCQ9JBmBr388pXuVX+iYAiIloADonGIBxBAAREw1ApwQDMN42 + AOcSAIRnwsOCAei4le/nFG/C1/omAIgJAcjINgAdziEACM/EvoIB6JBgACYSAERMNADtEwxAlW0AziYA + CM/EfpIBaO77OcWr+kbfBAAxEQ1AuwQDMIkAIGKiAWibYAAm2wbgLAKA8Ex8RDIAzXw/p3iTZ+m7tgC0 + JwAIUJVkANokGIAptgE4kwAgPFWPCgagdYIBmEoAEDHRALTa0vdzijdttr5rDcAZBADhqXqMAGRCABAz + 0QC0TDAA0y0D0I4AIECTJAOgtvD9nOLN+FbftQbgdAKA8Ex6nABkQgAQM9EAHJBgAF61DcBpBADhmfSE + YAD239z3c4r32nf6JgCICQHIyDYAbU8lAAjP5CcFA7BfggF4nQAgYqIB2Hcz388p3htz9E0AEBMCkJF1 + AE4hAAjP5KcEA7BPggF4kwAgYqIB2DvBALxlGYA2J/fyfSpQw5Snh2o7UwD2aur7OcV7e66+CQBiQgAy + sg7ASQQA4ZnyjGAA9kwwAO8QAERMNAB7NPH9nOK9O0/fBAAxIQAZ2Qag9YkEAOGZ+qxgAHZPMADvEQBE + TDQAuzX2/ZzivT9f37UG4AQCgPBM7U8AMiEAiJloAHZNMAAf2AbgeAKA8Ex9TjAAu2zq+znF+/B7fdcW + gFYEAAGaRgCyIQCImWgAWiQYgJm2ATiOACA8054XDMDOm/h+TvE+WqBvAoCYiAZgpwQD8LFtAI4lAAjP + tBcIQCa2AQBikCkAOzbyfX7xPlmobwKAmGUKwA4JBuBTAoAyQgAcEQCUk0wB2D7BAHxmCIDvm4CsSgrA + dhv7Prt4ny/SNwFAOSAAlggAylFJAdg2wQB8QQBQhkoKwDYNfZ9dvC8X65sAoByUFICtEwzAVwQAZYgA + WCIAKEclBWCrBr7PLt7XS/RtGwClTud3BcrQjBmPl8U/ACUFoHmCAfiGAGAtBCAxBABrSzoAzTbyfXbx + Zv2gbwKQtqQDsGWCAZhNALAWApAYyQDMOLy37+fAkRr4oLaTDsAWG/o+u3jfLtV3pgAcdp7v58CRGvSQ + tpMOwOYJBuA7yQAcSgBiowYTgGoEIGMADjnX93PgSA15WNtJB2CzDXyfXbw5P+o7UwB6neP7OXCkhvbV + dtIBaJpgAOZKBqDn2b6fA0dqWD9tJx2AJgkGYJ5kAHoQgNio4QSgGgHIGIDuZ/l+DhypEY9oO+kANK7v + ++zizV+m70wB6Ham7+fAkRr5qLaTDsCmCQbge8kAHHyG7+fAkRr1mLYJQGIIQNoIwFo2Wd/32cVbsFzf + mQJw0Om+nwNHavTj2k46AI0SDMBCyQAceJrv58CRGvOEtglAYkQD0PVU38+BIzX2SW0nHYCN1/N9dvEW + /aTvTAHoQgBioyoJQLWGCQZgsWQAOp/i+zlwpMY9pW0CkBjRAHQ62fdz4EiNf1rbSQegQT3fZxdvyQp9 + ZwpAx5N8PweO1IRntJ10ADZKMAA/SAagAwGIjZpIAKptmGAAlkoGoP2Jvp8DR6rqWW2nHYC6vs8u3tKV + +s4UgHYn+H4OHKlJ/bWddAA2SDAAP0oGoO3xvp8DR2ryc9pOOgD1EwzAMskAtCEAsVFTCEC1+uv6Prt4 + y1bpO1MAWh/n+zlwpKY+r+2kA7B+ggFYLhmAVsf6fg4cqWkvaDvpAKyXYAB+kgxAy2N8PweO1PQXtU0A + EiMaAEUAYqNmEIBq9dbxfXbxVvys70wBOOCPvp8DR+rVP2k76QDUTTAAKyUDsP8ffD8HjtRrf9Z20gFY + N8EArJIMwH6/9/0cOFKv/0XbaQegLJ7uZtX//SlkCsC+BCA26g0CUG2dsni6m58lA7DP73w/B47Um3/V + dtIBQMYA7P1b3+fDkXrrb9omAInLFIC9fuP7fDhSb/9d2wQgcZkCsCcBiI16hwBgLZkCsMevfZ8PR+rd + f2g7mQAgG2MAdv+V77PgSL33T20TAFgxBmC3X/o+C47U+//SNgGAFQJQHggASmIMwK5H+z4LjtQHL2mb + AMCKMQC7/ML3WXCkPvy3tgkArBgD0OIo32fBkZr5srYJAKwYA7AzAYiN+ogAoATGAOx0pO+z4Eh9PEDb + BABWjAHY8QjfZ8GR+uQVbRMAWDEGYIfDfZ8FR+rTgdomALBiDMD2BCA26jMCgBIYA7DdYb7PgiP1+SBt + EwBYMQZg20N9nwVH6ovB2iYAsGIMwDaH+D4LjtSXQ7RNAGDFGICtCUBs1FcEACUwBmCrXr7PgiP19VBt + EwBYMQageU/fZ8GR+maYtgkArBgD0KyH77PgSM0arm0CACvGAGxJAGKjZhMAlMAYgC26+z4LjtS3I7RN + AGDFGIDNu/k+C47UdyO1TQBgxRiAzQ72fRYcqTmjtE0AYMUYgKYEIDZqLgFACYwBaHKQ77PgSM0brW0C + ACvGADQ+0PdZcKTmj9E2AYAVYwA27er7LDhS34/VNgGAFWMANunq+yw4UgvGapsAwIoxAI26+D4LjtTC + Sm0TAFgxBmDjzr7PgiO1aJy2CQCsGAPQsJPvs+BILR6vbQIAKwSgPBAAlMQYgAYdfZ8FR2rJBG0TAFgx + BmCjDr7PgiP1w0RtEwBYMQZgw/a+z4IjtbRK2wQAVowB2IAAxEb9SABQAmMA6rfzfRYcqWWTtE0AYMUY + gPXb+j4LjtTyydomALBiDMB6bXyfBUfqpynaJgCwYgxAPQIQG7WCAKAExgDUbe37LDhSK6dqmwDAijEA + 67byfRYcqVXTtE0AYMUUAMSPAMAKAShPBABWCEB5IgCwQgDKEwEAED0CACSMAAAJIwBAwggAkDACACSM + AAAJIwBAwmoEwPdBAIpVHYA1Zs5cvXr58oqKVasqKsgBkIb/AA/38rf1PkgbAAAAAElFTkSuQmCCKAAA + ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46 + AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/ + AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD + AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ + AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O + AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT + ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ + ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd + ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i + AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2 + qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo + AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3 + qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt + AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA + AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy + AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7 + swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53 + AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8 + ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ + ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA + vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD + xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4 + 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8 + 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH + zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8 + 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI + 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM + 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD + 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8 + 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI + zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI + 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO + 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT + 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba + 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe + 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl + +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp + +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw + /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u//// + ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O + 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA + AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO + 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO + 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf + OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr + SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw + UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8 + YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA + NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI + cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA + O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP + eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA + QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A + W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI + uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA + R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI + vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H + wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA + 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 + SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH + au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI + fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz + fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z + pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi + bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na + JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA + JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO + RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy + IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA + /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A + /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA + /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA + /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA + /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A + /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA + /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA + /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A + /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A + /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA + AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA + /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao + /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA + /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer + /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A + /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv + /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA + /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz + /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA + /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA + /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA + AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA + /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66 + /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+ + /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB + /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF + /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL + egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM + /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ + /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU + /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY + /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM + egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP + /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM + ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL + ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM + ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O + 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA + AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO + 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 + PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh + /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m + /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV + /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza + /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp + /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH + b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs + /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv + /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv + /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv + /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz + /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2 + /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9 + wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38 + /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38 + /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM + AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL + VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB + pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA + Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA + AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl + o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz + /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh + /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer + /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2 + /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB + /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh + /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv + /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo + /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr + /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu + /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy + /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31 + /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4 + /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7 + /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs + QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ== + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj new file mode 100644 index 000000000..eeba5aae8 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj @@ -0,0 +1,98 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C} + WinExe + Properties + ProcessAnalyzer + ProcessAnalyzer + v2.0 + 512 + fake_base.ico + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + AnyCPU + + + + + + + + + + + + Form + + + MainWindow.cs + + + + + MainWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + {8A448157-E1A7-4DDF-954E-287F1117832B} + ProcessHacker.Native + + + + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs new file mode 100644 index 000000000..72127b455 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace ProcessAnalyzer +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ca8c088ab --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Process Analyzer")] +[assembly: AssemblyDescription("Process Analyzer")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("Process Analyzer")] +[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c98807ff-33c2-465b-a70c-8b0ac257ff96")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs new file mode 100644 index 000000000..dacacce16 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ProcessAnalyzer.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProcessAnalyzer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx new file mode 100644 index 000000000..ffecec851 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs new file mode 100644 index 000000000..8a1647c9e --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ProcessAnalyzer.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico new file mode 100644 index 0000000000000000000000000000000000000000..e724a6b20ddf3b93d6b90f644c04728d9eca4e7a GIT binary patch literal 47017 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%0D80KV3h%## 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 mr~?c2?MN9!OwRoj + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tabControl = new System.Windows.Forms.TabControl(); + this.tabEvent = new System.Windows.Forms.TabPage(); + this.groupArguments = new System.Windows.Forms.GroupBox(); + this.listArguments = new System.Windows.Forms.ListView(); + this.columnIndex = new System.Windows.Forms.ColumnHeader(); + this.columnValue = new System.Windows.Forms.ColumnHeader(); + this.columnExtendedValue = new System.Windows.Forms.ColumnHeader(); + this.columnType = new System.Windows.Forms.ColumnHeader(); + this.groupBasic = new System.Windows.Forms.GroupBox(); + this.textSystemCall = new System.Windows.Forms.TextBox(); + this.textMode = new System.Windows.Forms.TextBox(); + this.textTime = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.labelMode = new System.Windows.Forms.Label(); + this.tabProcess = new System.Windows.Forms.TabPage(); + this.tabStackTrace = new System.Windows.Forms.TabPage(); + this.listStackTrace = new System.Windows.Forms.ListView(); + this.columnAddress = new System.Windows.Forms.ColumnHeader(); + this.columnSymbol = new System.Windows.Forms.ColumnHeader(); + this.buttonClose = new System.Windows.Forms.Button(); + this.tabControl.SuspendLayout(); + this.tabEvent.SuspendLayout(); + this.groupArguments.SuspendLayout(); + this.groupBasic.SuspendLayout(); + this.tabStackTrace.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl + // + this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl.Controls.Add(this.tabEvent); + this.tabControl.Controls.Add(this.tabProcess); + this.tabControl.Controls.Add(this.tabStackTrace); + this.tabControl.Location = new System.Drawing.Point(12, 12); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(510, 409); + this.tabControl.TabIndex = 0; + // + // tabEvent + // + this.tabEvent.Controls.Add(this.groupArguments); + this.tabEvent.Controls.Add(this.groupBasic); + this.tabEvent.Location = new System.Drawing.Point(4, 22); + this.tabEvent.Name = "tabEvent"; + this.tabEvent.Padding = new System.Windows.Forms.Padding(3); + this.tabEvent.Size = new System.Drawing.Size(502, 383); + this.tabEvent.TabIndex = 0; + this.tabEvent.Text = "Event"; + this.tabEvent.UseVisualStyleBackColor = true; + // + // groupArguments + // + this.groupArguments.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupArguments.Controls.Add(this.listArguments); + this.groupArguments.Location = new System.Drawing.Point(6, 138); + this.groupArguments.Name = "groupArguments"; + this.groupArguments.Size = new System.Drawing.Size(490, 239); + this.groupArguments.TabIndex = 2; + this.groupArguments.TabStop = false; + this.groupArguments.Text = "Arguments"; + // + // listArguments + // + this.listArguments.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnIndex, + this.columnValue, + this.columnExtendedValue, + this.columnType}); + this.listArguments.Dock = System.Windows.Forms.DockStyle.Fill; + this.listArguments.FullRowSelect = true; + this.listArguments.HideSelection = false; + this.listArguments.Location = new System.Drawing.Point(3, 16); + this.listArguments.Name = "listArguments"; + this.listArguments.ShowItemToolTips = true; + this.listArguments.Size = new System.Drawing.Size(484, 220); + this.listArguments.TabIndex = 1; + this.listArguments.UseCompatibleStateImageBehavior = false; + this.listArguments.View = System.Windows.Forms.View.Details; + // + // columnIndex + // + this.columnIndex.Text = "Index"; + this.columnIndex.Width = 40; + // + // columnValue + // + this.columnValue.Text = "Value"; + this.columnValue.Width = 100; + // + // columnExtendedValue + // + this.columnExtendedValue.Text = "Extended Value"; + this.columnExtendedValue.Width = 260; + // + // columnType + // + this.columnType.Text = "Type"; + this.columnType.Width = 80; + // + // groupBasic + // + this.groupBasic.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBasic.Controls.Add(this.textSystemCall); + this.groupBasic.Controls.Add(this.textMode); + this.groupBasic.Controls.Add(this.textTime); + this.groupBasic.Controls.Add(this.label1); + this.groupBasic.Controls.Add(this.labelMode); + this.groupBasic.Location = new System.Drawing.Point(6, 6); + this.groupBasic.Name = "groupBasic"; + this.groupBasic.Size = new System.Drawing.Size(490, 126); + this.groupBasic.TabIndex = 1; + this.groupBasic.TabStop = false; + this.groupBasic.Text = "Basic"; + // + // textSystemCall + // + this.textSystemCall.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textSystemCall.BackColor = System.Drawing.SystemColors.Window; + this.textSystemCall.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textSystemCall.Location = new System.Drawing.Point(6, 19); + this.textSystemCall.Name = "textSystemCall"; + this.textSystemCall.ReadOnly = true; + this.textSystemCall.Size = new System.Drawing.Size(478, 13); + this.textSystemCall.TabIndex = 4; + // + // textMode + // + this.textMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textMode.BackColor = System.Drawing.SystemColors.Window; + this.textMode.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textMode.Location = new System.Drawing.Point(49, 57); + this.textMode.Name = "textMode"; + this.textMode.ReadOnly = true; + this.textMode.Size = new System.Drawing.Size(435, 13); + this.textMode.TabIndex = 3; + // + // textTime + // + this.textTime.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textTime.BackColor = System.Drawing.SystemColors.Window; + this.textTime.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textTime.Location = new System.Drawing.Point(45, 38); + this.textTime.Name = "textTime"; + this.textTime.ReadOnly = true; + this.textTime.Size = new System.Drawing.Size(439, 13); + this.textTime.TabIndex = 3; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 38); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(33, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Time:"; + // + // labelMode + // + this.labelMode.AutoSize = true; + this.labelMode.Location = new System.Drawing.Point(6, 57); + this.labelMode.Name = "labelMode"; + this.labelMode.Size = new System.Drawing.Size(37, 13); + this.labelMode.TabIndex = 1; + this.labelMode.Text = "Mode:"; + // + // tabProcess + // + this.tabProcess.Location = new System.Drawing.Point(4, 22); + this.tabProcess.Name = "tabProcess"; + this.tabProcess.Padding = new System.Windows.Forms.Padding(3); + this.tabProcess.Size = new System.Drawing.Size(502, 383); + this.tabProcess.TabIndex = 1; + this.tabProcess.Text = "Process"; + this.tabProcess.UseVisualStyleBackColor = true; + // + // tabStackTrace + // + this.tabStackTrace.Controls.Add(this.listStackTrace); + this.tabStackTrace.Location = new System.Drawing.Point(4, 22); + this.tabStackTrace.Name = "tabStackTrace"; + this.tabStackTrace.Padding = new System.Windows.Forms.Padding(3); + this.tabStackTrace.Size = new System.Drawing.Size(502, 383); + this.tabStackTrace.TabIndex = 2; + this.tabStackTrace.Text = "Stack Trace"; + this.tabStackTrace.UseVisualStyleBackColor = true; + // + // listStackTrace + // + this.listStackTrace.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnAddress, + this.columnSymbol}); + this.listStackTrace.Dock = System.Windows.Forms.DockStyle.Fill; + this.listStackTrace.FullRowSelect = true; + this.listStackTrace.HideSelection = false; + this.listStackTrace.Location = new System.Drawing.Point(3, 3); + this.listStackTrace.Name = "listStackTrace"; + this.listStackTrace.ShowItemToolTips = true; + this.listStackTrace.Size = new System.Drawing.Size(496, 377); + this.listStackTrace.TabIndex = 0; + this.listStackTrace.UseCompatibleStateImageBehavior = false; + this.listStackTrace.View = System.Windows.Forms.View.Details; + // + // columnAddress + // + this.columnAddress.Text = "Address"; + this.columnAddress.Width = 100; + // + // columnSymbol + // + this.columnSymbol.Text = "Symbol"; + this.columnSymbol.Width = 360; + // + // buttonClose + // + this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonClose.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonClose.Location = new System.Drawing.Point(447, 427); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.Size = new System.Drawing.Size(75, 23); + this.buttonClose.TabIndex = 1; + this.buttonClose.Text = "Close"; + this.buttonClose.UseVisualStyleBackColor = true; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // EventProperties + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(534, 462); + this.Controls.Add(this.buttonClose); + this.Controls.Add(this.tabControl); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EventProperties"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Event Properties"; + this.tabControl.ResumeLayout(false); + this.tabEvent.ResumeLayout(false); + this.groupArguments.ResumeLayout(false); + this.groupBasic.ResumeLayout(false); + this.groupBasic.PerformLayout(); + this.tabStackTrace.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl; + private System.Windows.Forms.TabPage tabEvent; + private System.Windows.Forms.TabPage tabProcess; + private System.Windows.Forms.Button buttonClose; + private System.Windows.Forms.TabPage tabStackTrace; + private System.Windows.Forms.ListView listStackTrace; + private System.Windows.Forms.ColumnHeader columnAddress; + private System.Windows.Forms.ColumnHeader columnSymbol; + private System.Windows.Forms.GroupBox groupArguments; + private System.Windows.Forms.GroupBox groupBasic; + private System.Windows.Forms.Label labelMode; + private System.Windows.Forms.TextBox textMode; + private System.Windows.Forms.TextBox textTime; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textSystemCall; + private System.Windows.Forms.ListView listArguments; + private System.Windows.Forms.ColumnHeader columnIndex; + private System.Windows.Forms.ColumnHeader columnValue; + private System.Windows.Forms.ColumnHeader columnExtendedValue; + private System.Windows.Forms.ColumnHeader columnType; + } +} \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs new file mode 100644 index 000000000..375d40285 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.SsLogging; +using ProcessHacker.Native.Symbols; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Common; + +namespace SysCallHacker +{ + public partial class EventProperties : Form + { + private LogEvent _event; + private SymbolProvider _symbols; + + public EventProperties(LogEvent even) + { + InitializeComponent(); + + _event = even; + + textSystemCall.Text = MainWindow.SysCallNames.ContainsKey(even.Event.CallNumber) ? MainWindow.SysCallNames[even.Event.CallNumber] : "(unknown)"; + textTime.Text = _event.Event.Time.ToString(); + textMode.Text = _event.Event.Mode == KProcessorMode.UserMode ? "User-mode" : "Kernel-mode"; + + for (int i = 0; i < _event.Event.Arguments.Length; i++) + { + ListViewItem item = new ListViewItem(); + + item.Text = i.ToString(); + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, "0x" + _event.Event.Arguments[i].ToString("x"))); + + if (_event.Arguments[i] != null) + { + string text = ""; + SsData data = _event.Arguments[i]; + + if (data is SsSimple) + { + text = (data as SsSimple).Argument.ToString(); + } + else if (data is SsHandle) + { + SsHandle handle = data as SsHandle; + + if (!string.IsNullOrEmpty(handle.Name)) + text = handle.TypeName + ": " + handle.Name; + else + text = handle.TypeName + ": PID: " + handle.ProcessId.ToString() + + ", TID: " + handle.ThreadId.ToString(); + } + else if (data is SsUnicodeString) + { + text = (data as SsUnicodeString).String; + } + else if (data is SsObjectAttributes) + { + SsObjectAttributes oa = data as SsObjectAttributes; + text = ""; + + if (oa.RootDirectory != null) + text = oa.RootDirectory.Name; + + if (oa.ObjectName != null) + { + if (!string.IsNullOrEmpty(text)) + text = text + "\\" + oa.ObjectName.String; + else + text = oa.ObjectName.String; + } + } + else if (data is SsClientId) + { + text = "PID: " + (data as SsClientId).Original.ProcessId.ToString() + + ", TID: " + (data as SsClientId).Original.ThreadId.ToString(); + } + + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, text)); + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, _event.Arguments[i].GetType().Name.Remove(0, 2))); + } + else + { + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, "")); + item.SubItems.Add(new ListViewItem.ListViewSubItem(item, "")); + } + + listArguments.Items.Add(item); + } + + SymbolProvider.Options = SymbolOptions.DeferredLoads | SymbolOptions.UndName; + + try + { + using (var phandle = new ProcessHandle(_event.Event.ProcessId, + ProcessAccess.QueryInformation | ProcessAccess.VmRead)) + { + _symbols = new SymbolProvider(phandle); + + phandle.EnumModules((module) => + { + _symbols.LoadModule(module.FileName, module.BaseAddress, module.Size); + return true; + }); + Windows.EnumKernelModules((module) => + { + _symbols.LoadModule(module.FileName, module.BaseAddress); + return true; + }); + _symbols.PreloadModules = true; + + for (int i = 0; i < _event.Event.StackTrace.Length; i++) + { + var address = _event.Event.StackTrace[i]; + string fileName; + IntPtr baseAddress; + + fileName = _symbols.GetModuleFromAddress(address, out baseAddress); + + listStackTrace.Items.Add(new ListViewItem(new string[] + { + "0x" + address.ToString("x"), + (new System.IO.FileInfo(fileName)).Name + "+0x" + address.Decrement(baseAddress).ToString("x") + })); + + WorkQueue.GlobalQueueWorkItemTag(new Action((i_, address_) => + { + string symbol = _symbols.GetSymbolFromAddress(address_.ToUInt64()); + + if (this.IsHandleCreated) + this.BeginInvoke(new Action(() => listStackTrace.Items[i_].SubItems[1].Text = symbol)); + }), "resolve-symbol", i, address); + } + } + } + catch + { } + + listArguments.SetDoubleBuffered(true); + listStackTrace.SetDoubleBuffered(true); + } + + private void buttonClose_Click(object sender, EventArgs e) + { + this.Close(); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx new file mode 100644 index 000000000..ff31a6db5 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs new file mode 100644 index 000000000..0eba597f4 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.SsLogging; + +namespace SysCallHacker +{ + public class LogEvent + { + private SsEvent _event; + private SsData[] _arguments; + + public LogEvent(SsEvent even) + { + _event = even; + _arguments = new SsData[even.Arguments.Length]; + } + + public SsData[] Arguments + { + get { return _arguments; } + } + + public SsEvent Event + { + get { return _event; } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs new file mode 100644 index 000000000..ea3961ba7 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs @@ -0,0 +1,241 @@ +namespace SysCallHacker +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow)); + this.mainMenu = new System.Windows.Forms.MainMenu(this.components); + this.hackerMenuItem = new System.Windows.Forms.MenuItem(); + this.clearHackerMenuItem = new System.Windows.Forms.MenuItem(); + this.exitMenuItem = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.removeAllFiltersMenuItem = new System.Windows.Forms.MenuItem(); + this.addProcessFiltersMenuItem = new System.Windows.Forms.MenuItem(); + this.listEvents = new System.Windows.Forms.ListView(); + this.columnTime = new System.Windows.Forms.ColumnHeader(); + this.columnClient = new System.Windows.Forms.ColumnHeader(); + this.columnCall = new System.Windows.Forms.ColumnHeader(); + this.columnMode = new System.Windows.Forms.ColumnHeader(); + this.columnArguments = new System.Windows.Forms.ColumnHeader(); + this.imageList = new System.Windows.Forms.ImageList(this.components); + this.toolBarButtonStop = new System.Windows.Forms.ToolBarButton(); + this.toolBarButtonStart = new System.Windows.Forms.ToolBarButton(); + this.toolBar = new System.Windows.Forms.ToolBar(); + this.statusBar = new System.Windows.Forms.StatusBar(); + this.timerUpdate = new System.Windows.Forms.Timer(this.components); + this.SuspendLayout(); + // + // mainMenu + // + this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.hackerMenuItem, + this.menuItem1}); + // + // hackerMenuItem + // + this.hackerMenuItem.Index = 0; + this.hackerMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.clearHackerMenuItem, + this.exitMenuItem}); + this.hackerMenuItem.Text = "Hacker"; + // + // clearHackerMenuItem + // + this.clearHackerMenuItem.Index = 0; + this.clearHackerMenuItem.Text = "Clear"; + this.clearHackerMenuItem.Click += new System.EventHandler(this.clearHackerMenuItem_Click); + // + // exitMenuItem + // + this.exitMenuItem.Index = 1; + this.exitMenuItem.Text = "E&xit"; + this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click); + // + // menuItem1 + // + this.menuItem1.Index = 1; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.removeAllFiltersMenuItem, + this.addProcessFiltersMenuItem}); + this.menuItem1.Text = "Filters"; + // + // removeAllFiltersMenuItem + // + this.removeAllFiltersMenuItem.Index = 0; + this.removeAllFiltersMenuItem.Text = "Remove All"; + this.removeAllFiltersMenuItem.Click += new System.EventHandler(this.removeAllFiltersMenuItem_Click); + // + // addProcessFiltersMenuItem + // + this.addProcessFiltersMenuItem.Index = 1; + this.addProcessFiltersMenuItem.Text = "Add Process..."; + this.addProcessFiltersMenuItem.Click += new System.EventHandler(this.addProcessFiltersMenuItem_Click); + // + // listEvents + // + this.listEvents.AllowColumnReorder = true; + this.listEvents.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listEvents.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnTime, + this.columnClient, + this.columnCall, + this.columnMode, + this.columnArguments}); + this.listEvents.FullRowSelect = true; + this.listEvents.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listEvents.HideSelection = false; + this.listEvents.Location = new System.Drawing.Point(0, 28); + this.listEvents.Name = "listEvents"; + this.listEvents.ShowItemToolTips = true; + this.listEvents.Size = new System.Drawing.Size(813, 435); + this.listEvents.TabIndex = 1; + this.listEvents.UseCompatibleStateImageBehavior = false; + this.listEvents.View = System.Windows.Forms.View.Details; + this.listEvents.VirtualMode = true; + this.listEvents.DoubleClick += new System.EventHandler(this.listEvents_DoubleClick); + this.listEvents.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.listEvents_RetrieveVirtualItem); + // + // columnTime + // + this.columnTime.Text = "Time"; + this.columnTime.Width = 140; + // + // columnClient + // + this.columnClient.Text = "Client"; + this.columnClient.Width = 120; + // + // columnCall + // + this.columnCall.Text = "Call"; + this.columnCall.Width = 160; + // + // columnMode + // + this.columnMode.Text = "Mode"; + this.columnMode.Width = 70; + // + // columnArguments + // + this.columnArguments.Text = "Arguments"; + this.columnArguments.Width = 300; + // + // imageList + // + this.imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream"))); + this.imageList.TransparentColor = System.Drawing.Color.Transparent; + this.imageList.Images.SetKeyName(0, "control_play"); + this.imageList.Images.SetKeyName(1, "control_stop"); + // + // toolBarButtonStop + // + this.toolBarButtonStop.ImageKey = "control_stop"; + this.toolBarButtonStop.Name = "toolBarButtonStop"; + this.toolBarButtonStop.ToolTipText = "Stop"; + // + // toolBarButtonStart + // + this.toolBarButtonStart.ImageKey = "control_play"; + this.toolBarButtonStart.Name = "toolBarButtonStart"; + this.toolBarButtonStart.ToolTipText = "Start"; + // + // toolBar + // + this.toolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat; + this.toolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] { + this.toolBarButtonStart, + this.toolBarButtonStop}); + this.toolBar.DropDownArrows = true; + this.toolBar.ImageList = this.imageList; + this.toolBar.Location = new System.Drawing.Point(0, 0); + this.toolBar.Name = "toolBar"; + this.toolBar.ShowToolTips = true; + this.toolBar.Size = new System.Drawing.Size(813, 28); + this.toolBar.TabIndex = 0; + this.toolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar_ButtonClick); + // + // statusBar + // + this.statusBar.Location = new System.Drawing.Point(0, 462); + this.statusBar.Name = "statusBar"; + this.statusBar.Size = new System.Drawing.Size(813, 22); + this.statusBar.TabIndex = 2; + // + // timerUpdate + // + this.timerUpdate.Enabled = true; + this.timerUpdate.Interval = 500; + this.timerUpdate.Tick += new System.EventHandler(this.timerUpdate_Tick); + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(813, 484); + this.Controls.Add(this.statusBar); + this.Controls.Add(this.listEvents); + this.Controls.Add(this.toolBar); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Menu = this.mainMenu; + this.Name = "MainWindow"; + this.Text = "System Call Hacker"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainWindow_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MainMenu mainMenu; + private System.Windows.Forms.MenuItem hackerMenuItem; + private System.Windows.Forms.MenuItem exitMenuItem; + private System.Windows.Forms.ListView listEvents; + private System.Windows.Forms.ColumnHeader columnTime; + private System.Windows.Forms.ColumnHeader columnCall; + private System.Windows.Forms.ColumnHeader columnMode; + private System.Windows.Forms.ColumnHeader columnArguments; + private System.Windows.Forms.ImageList imageList; + private System.Windows.Forms.ToolBarButton toolBarButtonStop; + private System.Windows.Forms.ToolBarButton toolBarButtonStart; + private System.Windows.Forms.ToolBar toolBar; + private System.Windows.Forms.StatusBar statusBar; + private System.Windows.Forms.Timer timerUpdate; + private System.Windows.Forms.ColumnHeader columnClient; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem removeAllFiltersMenuItem; + private System.Windows.Forms.MenuItem addProcessFiltersMenuItem; + private System.Windows.Forms.MenuItem clearHackerMenuItem; + + + } +} + diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs new file mode 100644 index 000000000..81f04cd7a --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using ProcessHacker.Common; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.SsLogging; +using ProcessHacker.Native.Symbols; + +namespace SysCallHacker +{ + public partial class MainWindow : Form + { + private static Dictionary _sysCallNames = new Dictionary(); + + public static Dictionary SysCallNames + { + get { return _sysCallNames; } + } + + private SsLogger _logger; + private List _events = new List(); + private LogEvent _lastEvent; + private List _rules = new List(); + private Dictionary _processes; + + public MainWindow() + { + InitializeComponent(); + + Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll"); + + SymbolProvider symbols = new SymbolProvider(ProcessHandle.Current); + + SymbolProvider.Options |= SymbolOptions.PublicsOnly; + + IntPtr ntdllBase = Loader.GetDllHandle("ntdll.dll"); + FileHandle ntdllFileHandle = null; + Section section = null; + + ProcessHandle.Current.EnumModules((module) => + { + if (module.BaseName.Equals("ntdll.dll", StringComparison.InvariantCultureIgnoreCase)) + { + section = new Section( + ntdllFileHandle = new FileHandle(@"\??\" + module.FileName, + FileShareMode.ReadWrite, + FileAccess.GenericExecute | FileAccess.GenericRead + ), + true, + MemoryProtection.ExecuteRead + ); + + symbols.LoadModule(module.FileName, module.BaseAddress, module.Size); + return false; + } + + return true; + }); + + SectionView view = section.MapView((int)ntdllFileHandle.GetSize()); + + ntdllFileHandle.Dispose(); + + symbols.EnumSymbols("ntdll!Zw*", (symbol) => + { + int number = Marshal.ReadInt32( + (symbol.Address.ToIntPtr().Decrement(ntdllBase)).Increment(view.Memory).Increment(1)); + + _sysCallNames.Add( + number, + "Nt" + symbol.Name.Substring(2) + ); + + return true; + }); + + view.Dispose(); + section.Dispose(); + + symbols.Dispose(); + + KProcessHacker.Instance = new KProcessHacker(); + + _logger = new SsLogger(4096, false); + _logger.EventBlockReceived += new EventBlockReceivedDelegate(logger_EventBlockReceived); + _logger.ArgumentBlockReceived += new ArgumentBlockReceivedDelegate(logger_ArgumentBlockReceived); + _logger.AddPreviousModeRule(FilterType.Include, KProcessorMode.UserMode); + _logger.AddProcessIdRule(FilterType.Exclude, ProcessHandle.GetCurrentId()); + //_logger.Start(); + + listEvents.SetDoubleBuffered(true); + } + + private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) + { + ProcessHandle.Current.Terminate(); + } + + private void logger_EventBlockReceived(SsEvent eventBlock) + { + LogEvent logEvent = new LogEvent(eventBlock); + + lock (_events) + _events.Add(logEvent); + + _lastEvent = logEvent; + } + + private void logger_ArgumentBlockReceived(SsData argBlock) + { + if (_lastEvent != null) + { + if (argBlock.Index < _lastEvent.Arguments.Length) + _lastEvent.Arguments[argBlock.Index] = argBlock; + } + } + + private void timerUpdate_Tick(object sender, EventArgs e) + { + _processes = Windows.GetProcesses(); + + lock (_events) + listEvents.VirtualListSize = _events.Count; + + int blocksWritten, blocksDropped; + + _logger.GetStatistics(out blocksWritten, out blocksDropped); + + if (blocksWritten > 0 || blocksDropped > 0) + { + lock (_events) + { + statusBar.Text = _events.Count.ToString("N0") + " events, " + + blocksWritten.ToString("N0") + " blocks, " + + blocksDropped.ToString("N0") + " dropped (" + + ((double)blocksDropped / (blocksWritten + blocksDropped) * 100).ToString("F2") + "%)"; + } + } + } + + private void listEvents_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) + { + LogEvent logEvent; + ListViewItem item; + + lock (_events) + logEvent = _events[e.ItemIndex]; + + string objectName = ""; + + if (logEvent.Arguments.Length > 2) + { + SsObjectAttributes oa = logEvent.Arguments[2] as SsObjectAttributes; + + if (oa != null) + { + if (oa.ObjectName != null) + objectName = oa.ObjectName.String; + } + } + + item = new ListViewItem(new string[] + { + logEvent.Event.Time.ToString(), + _processes.ContainsKey(logEvent.Event.ProcessId) ? _processes[logEvent.Event.ProcessId].Name : logEvent.Event.ProcessId.ToString(), + _sysCallNames.ContainsKey(logEvent.Event.CallNumber) ? _sysCallNames[logEvent.Event.CallNumber] : "(unknown)", + logEvent.Event.Mode == KProcessorMode.UserMode ? "User" : "Kernel", + objectName + }); + e.Item = item; + } + + private void exitMenuItem_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void toolBar_ButtonClick(object sender, ToolBarButtonClickEventArgs e) + { + if (e.Button == toolBarButtonStart) + { + _logger.Start(); + } + else if (e.Button == toolBarButtonStop) + { + _logger.Stop(); + } + } + + private void ShowProperties(int index) + { + (new EventProperties(_events[index])).ShowDialog(); + } + + private void listEvents_DoubleClick(object sender, EventArgs e) + { + this.ShowProperties(listEvents.SelectedIndices[0]); + } + + private void clearHackerMenuItem_Click(object sender, EventArgs e) + { + lock (_events) + { + listEvents.VirtualListSize = 0; + _events.Clear(); + } + } + + private void removeAllFiltersMenuItem_Click(object sender, EventArgs e) + { + foreach (var rule in _rules) + _logger.RemoveRule(rule); + + _rules.Clear(); + } + + private void addProcessFiltersMenuItem_Click(object sender, EventArgs e) + { + ProcessHacker.Native.Ui.ChooseProcessDialog cpd = new ProcessHacker.Native.Ui.ChooseProcessDialog(); + + if (cpd.ShowDialog() == DialogResult.OK) + _rules.Add(_logger.AddProcessIdRule(FilterType.Include, cpd.SelectedPid)); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx new file mode 100644 index 000000000..fc7d1066f --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx @@ -0,0 +1,953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 127, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAB4 + BgAAAk1TRnQBSQFMAgEBAgEAAQQBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABEAMAAQEBAAEgBgABEP8AIwADIwE0A0cBgANaAcADYgHtA2IB7QNaAcADRwGAAyMBNCAAAyMBNANH + AYADWgHAA2IB7QNiAe0DWgHAA0cBgAMjATScAAM9AWcDXgHVA84B/wPmAf8D9QH/A/IB/wPdAf8DwAH/ + A14B1QM9AWcYAAM9AWcDXgHVA84B/wPmAf8D9QH/A/IB/wPdAf8DwAH/A14B1QM9AWeUAAM9AWcDbgHx + A+wB/wP+Af8D8wH/A+gB/wPnAf8D8QH/A/sB/wPhAf8DZwHxAz0BZxAAAz0BZwNuAfED7AH/A/4B/wPz + Af8D6AH/A+cB/wPxAf8D+wH/A+EB/wNnAfEDPQFnjAADIwE0A14B1QPwBf8D5QH/A+UB/wPvAf8D4wH/ + A+IB/wPhAf8D/gH/A+IB/wNeAdUDIwE0CAADIwE0A14B1QPwBf8D5QH/A+UB/wPkAf8D4wH/A+IB/wPh + Af8D/gH/A+IB/wNeAdUDIwE0iAADRwGAA+QB/wP+Af8D6AH/A+cB/wPmAf8DYQH/A+8B/wPkAf8D4wH/ + A+IB/wP7Af8DxgH/A0cBgAgAA0cBgAPkAf8D/gH/A+gB/wPnAf8D5gH/A+UB/wPlAf8D5AH/A+MB/wPi + Af8D+wH/A8YB/wNHAYCIAANaAcAD8gH/A/UB/wPpAf8D6QH/A+gB/wNmAf8BYgJhAf8D7wH/A+UB/wPk + Af8D8gH/A+EB/wNaAcAIAANaAcAD8gH/A/UB/wPpAf8D6QH/A4MB/wOCAf8DggH/A4EB/wPlAf8D5AH/ + A/IB/wPhAf8DWgHAiAADaAHtA/wB/wPvAf8D6wH/A+oB/wPpAf8DYwH/AWcCZgH/AWICYQH/A+8B/wPl + Af8D6AH/A/QB/wNkAe0IAANoAe0D/AH/A+8B/wPrAf8D6gH/A1wB/wNcAf8DWwH/A4IB/wPlAf8D5QH/ + A+gB/wP0Af8DZAHtiAADaAHtA/wB/wPwAf8D7QH/A+sB/wPqAf8DYwH/A2MB/wFoAmcB/wPNAf8D5gH/ + A+kB/wP6Af8DZAHtCAADaAHtA/wB/wPwAf8D7QH/A+sB/wNcAf8DXAH/A1wB/wODAf8D5wH/A+YB/wPp + Af8D+gH/A2QB7YgAA1oBwAP1Af8D9wH/A+0B/wPtAf8D7AH/AWQCYwH/AWQCYwH/A8AB/wPpAf8D6AH/ + A/QB/wPuAf8DWgHACAADWgHAA/UB/wP3Af8D7QH/A+0B/wNdAf8DXQH/A1wB/wODAf8D6QH/A+gB/wP0 + Af8D7gH/A1oBwIgAA0cBgAPrBf8D7wH/A+4B/wPtAf8BZQJkAf8DwgH/A+sB/wPpAf8D6QX/A90B/wNH + AYAIAANHAYAD6wX/A+8B/wPuAf8D7QH/A+0B/wPsAf8D6wH/A+kB/wPpBf8D3QH/A0cBgIgAAyMBNANe + AdUD+AX/A+8B/wPuAf8DwwH/A+0B/wPsAf8D6wX/A/UB/wNeAdUDIwE0CAADIwE0A14B1QP4Bf8D7wH/ + A+4B/wPtAf8D7QH/A+wB/wPrBf8D9QH/A14B1QMjATSMAAM9AWcDcgHxA/gF/wP4Af8D8gH/A/EB/wP3 + Bf8D9QH/A3AB8QM9AWcQAAM9AWcDcgHxA/gF/wP4Af8D8gH/A/EB/wP3Bf8D9QH/A3AB8QM9AWeUAAM9 + AWcDXgHVA+sB/wP1Af8D/AH/A/wB/wP0Af8D6AH/A14B1QM9AWcYAAM9AWcDXgHVA+sB/wP1Af8D/AH/ + A/wB/wP0Af8D6AH/A14B1QM9AWecAAMjATQDRwGAA1oBwANoAe0DaAHtA1oBwANHAYADIwE0IAADIwE0 + A0cBgANaAcADaAHtA2gB7QNaAcADRwGAAyMBNP8AkQABQgFNAT4HAAE+AwABKAMAAUADAAEQAwABAQEA + AQEFAAGAFwAD/wEABP8EAAHwAQ8B8AEPBAAB4AEHAeABBwQAAcABAwHAAQMEAAGAAQEBgAEBBAABgAEB + AYABAQQAAYABAQGAAQEEAAGAAQEBgAEBBAABgAEBAYABAQQAAYABAQGAAQEEAAGAAQEBgAEBBAABgAEB + AYABAQQAAcABAwHAAQMEAAHgAQcB4AEHBAAB8AEPAfABDwQABP8EAAs= + + + + 230, 17 + + + + + AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA + CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA + AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls + AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE + AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA + AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr + a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA + AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2 + tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA + AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb + 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA + AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu + p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq + 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen + p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq + eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6 + eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu + 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u + qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u + ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq + 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q + 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q + rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP// + AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/ + AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA + AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA + AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8 + AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAA + AAD//AAAAAAAAP/8AAAAAAAA///////+AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgAAAAP8AAP8AAAD//wD/AAAA/wD/AP// + AADAwMAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVlZWVkZWVAAAAAAAAAAAAGRra2tGS2TgAAAAAA + AAAABWtrZrZrZl4AAAAAAAAAAAZrZra2trZHAAAAAAAAAAAFa2trZmtmTgAAAAAAAAAABrZra2tmtk4A + AADhOBMYMAVmtmZrbbZ+AAAA8Tg4MTcGtr2727a2RwAAAOGDE4OOBW1r29vb224AAADhODgxjgRrZmZm + ZmZOAAAA6Dg4OD4FZWVlZUZWdwAAAOODg4OOAAAAAAAAAAAAAADhg4OIMziIg4g4iDiIODg+44OKg4Gn + p66np6enp6jqPug4g4g4rqenqOp6enrqeo7xo4qIOHp6eurq6up6euo34YiDgxOup6enqK6K6np6h+MT + ETgYp66nqueqenp66j4AAAAAA66np656p+p66nqOAAAAAAGnrqeqfqp6enrqNwAAAAAD6uqurqqurq6q + 6ocAAAAACK6urqrq6q6q6uo+AAAAAAOuququ6urqrq6qjgAAAAAIququqqrq6uqq6jcAAAAAA+rq6q6u + rqqurq6OAAAAAAOq6q+uqqrq6q6qjgAAAAADrqrqqq6uququrj4AAAAAA66q6urqrqrq6qqHAAAAAAiu + quqq6q6q6qrqPgAAAAADODODgzg4M4ODOD4AAAAADu7u7u7u7u7u7u7v///////gAH//4AA//+AAP//g + AD//4AA//+AAPwBgAD8AIAA/ACAAPwAgAD8AIAA/AD///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+A + AAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAAoAAAAEAAAACAA + AAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICA + gAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AADAwMAA////AAAAAAAAAAAAAAAHu2a2cAAAAAdmtrtwAOc3 + B2a2ZnAAeDh+tr22cAB4OD5HZWVwAOg4Pn7qfn6ueIOKp6jqend6iIeup6enp+eOPqenrqenAAAK6qen + rqcAAArq6uqupwAADqrqrqrqAAAKrqrq6uoAAAqq6qqqpwAADu7q7u7u//8AAPgHAAD4BwAACAcAAAAH + AAAABwAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAIlQTkcNChoKAAAADUlI + RFIAAAEAAAABAAgGAAAAXHKoZgAADTFJREFUeNrt3QuoZVUZwPF9zrlN06SNltWYSoVMSFEUex5mvp2X + j6QiiEqskIysDIQeCBUVSA8QsjIypBIrgqgwdRrH16hNzovCKKRBKtTJzNTRaZoZz6Oz5t59717n7n1n + P9baa317/X+g39lnUPZ5/Wfts8+5txMBCFZH/WvnztHI9Y4AaNaKFZ3ObACWLnW9OwCasndvFKnXvBaA + 5cungwCg3dRrfvFiAgAESb3mp6YIABAkAgAEjAAAASMANfzqgxGnTSHSu38y95onABURAEhFAAwgAJCK + ABhAACAVATCAAEAqAmBAVgAu/u4a17sFzHPLJ+7UtgmAAQQAUhAACwgApCAAFhAASEEALCAAkIIAWEAA + IAUBsIAAQAoCYEFWAN553Zqo03O9Z4COAFiQtwIYDaLDEWAyfZm/uZIAGJe3AgB8QwAsIACQggBYQAAg + BQGwgABACgJgAQGAFATAgrwADPudqDs1YjK9mAoBsIAVACRQIbjtqs3adQTAgKwAXHjtWte7BcxDACzI + C8Dk8gto2uRhAIcAFiy0AkjufKBp6edecpkVgAUcAkAKAmABAYAUBMACAgApCIAFBABSEAALCACkIAAW + EABIQQAsyAvAsK9Ov0SHp8JlLru+vPGzBMC4hQKguH7QuczlBAGwICsA538j+SDQ+AE4NJ6LUjP1gGh/ + zvVcb+l6ta1wCGDBQgHINBz/0+V6rm/o+hRWABaUDoAynFsVaA8a13O9jetn/mzj5wmAcZkB+FpOANSD + MnS9xwhSlxWAFZVWAIADBMACAgAROASwo9QhAOAQAbCAAEAKAmABAYAUBMACAgApCIAFWQFYf83aqNuN + oiGn/OCRTVcTAONYAUAC9ZcRAbAgbwUA+EStSDkEsIAAQApWABYQAEhBACzIC8CoP75Dx3cqk+nDVAiA + BawAIAUBsCArAOu+QgDgnzu+SACMWygAPiz9mMxkEgALWAFAAhUBDgEsIACQghWABQQAUhAACwgApCAA + FhAA+G406ESd3ogA2JAXAHWnAz7Z/OU7tG0CYEBWANZ+ad3h4iblZTJdT4UAWJAXACV95wOuJM9DAmDB + QgEAfEIALCAAkIIAWEAAIAUBsIAAQAoCYEFWAM77wrrZHwrKZPowlbu+SgCMWygAig8PPpOpJgGwIC8A + gG8IgAUEAFIQAAsIAKQgABYQAEhBACzIC8BooD6CGTGZXkyFAFiQFYBzr2YFAP/cfQ0BMI4AQAoCYAEB + gBQEwAICACkIgAUEAFIQAAuyAnDO5wgA/HPP1wmAcQsFwIfTP0xmMgmABawAIAUBsIAAQAoCYAEBgBQE + wAICACkIgAUEAFIQAAuyAnD2ZwgA/HPvNwmAcQQAUhAACwgApCAAFhAASEEALCAAkIIAWJAXgOEgirq9 + iMn0YioEwAICwJQy77uWABiXFYAzr+IQAP4hABYQAEhBACzICsAZn17vereAWZ3e9FOUAFiw0ApAHXsN + DnWi3qIRk+l83v+tTdrzlAAYwAoAUhAACxYKgOviM5lGVgA/juc/yRFFH9oVdVgBQAL1PkDl9wAIQDYC + AElYARhGACAJATCMAEASAmAYAYAkRgNw1kmub07ztjyqbxMASGI2ACe6vjnN2/KYvp0XgNM/RQDgnwe+ + TQBqIQCQzGgATj/B9c1p3gOP69sEAJIQgJq27omiYeqeIACQhADUVHQF8I4rCAD887vrCUAtZQPg+rfB + MpnpSQBqYgUAyYwG4NTjp+dUN4r6wzAmAYBkxlcA6oURkgf/qW8TAEhiZQWg+PC3cxNz+xPFzgIQAPjI + WAC64z9Ztcz1zWkeKwBIZnQFsOp4fXv8l2Q0bPlUAVDxS1YBBACSGF0BrAhwBbC9xApg2B/fT+M7lcn0 + YSpWVwAhKBqAt1/OCgD++f0NBgOw4tXjsnRd36RmDGfOduz8l349hwCQQK0CFKMBUCuAYUCnAVXsthd8 + D0CtAJLlF+AL4yuA0BRdAXAIAB8RgJoIACQjADURAEhmNABvfaXrm9O8P/5b3yYAkIQVQEXJdx6KBuDU + ywgA/JF8HfjBG1kB1PLQU8W+C5AEwPX3v5nM9DQWAHUq7E3HRVHP9SuyYWUOAXx4wJlMNRV1mfcAaiq7 + AlBGw07U6Y6YTKdTMXoI8ObAAqBWO7wHAGnU3/zDF6YjQABq+nPBFcDqj2xwvavArGQlsO2Hv9WuJwAl + /angCiAJQLL0AlxQL/o0AlBT2QAAPjEaAHUWIDTqECCNAEASAlATAYBkBKAmAgDJCEBNBACSGQ3AKS93 + fXOa9/DT+nbRAAwHUdTtMZluJwGoqWgAVl66YfaOT/jwBGCGPQlABYPxLe/N3BNlAqBMRgBwacdNBgOw + /FjXN6c5KgAqBLuf0a8/UgAAnxgLgPo24MnHzL0w2j4TBACSsQKo6ZFni30XYOUlGyIvfpURk5maBKCm + UisATx50ZuAzmrk8tuNmgwFQhwChUSuAIr8X4PAKAPAMAahJBSAtLwCABASgJAKANiEAJREAtImRAAyG + UdTrhjH//px+HxAASFYrAK97mevdb15WAFzvE1AXASiIAKCNCEBBBABtVCsA/fGfTHXCmHv2zf8koOsH + D6irUgBOPNr1bjfvsef1bQKANiAABbECQBsRgIJYAaCNKgXghKPG/0FgT38VgMnvAhT57+L4cj4r0EK7 + dt3QildA5QCE5vF9+jYBCBsBCAwBQFrQAVj2Ute73bwn/qtvE4CwEYDAPLm/2lkAAtBOBCAwJlcAuy66 + 0vXNQUnxrddp2wQgMCoAps4CEAB5CEBKqAFIIwBhIQApSQD6wyia6oYxn/qffh8YCcBw6PrxR0Hx7d/R + toMOwHEvcb3bzTMagAs+OffTRiACAUgJMQBPHzB3FmA2ABAj3nS9tk0AAmN0BbD+Ctc3B2WMV2qsAFJC + DYCxswAEQI6ZBzze/D3tagIQGKMrgLUfd31zUBIBSDnmxa53u3nPHtS3aweAMwCixHd9X9sOOgBqBeDD + qbmmpmI0AOd9zPXjjjK6XVYAaSGuAJ47ZPAsAAEQhxVASogBMLoCOOejrm8OylArAAIwJ9QAGDsLQADE + ie/5gbZNAALDCiBsBCDlqBe53u3m7XtB364VgDMvc31zUFJ8343adrABUMvgJVPT75CHIDkLQADCRgBS + 0isA9frotngm9vcNngVIAtAfjEvaYwqY8dYf6Y9hyAFYkgqADy9SGzNNXWd0BXDah6cvJE8y+G38OLEC + SFkS4HsAB0yuAJIAQAxWACkhBmC/jRUAxCAAKZMBUB9r77b8TUEVAGOfA1AB4OcBiBJvu0nbJgDR3PdZ + 1Iu/rd9tSW7bgYlD9VoBWH2p65uFkghAyuLe/P9RmyOgEICwEYCUrAC0ndEArLzE9c1BGeq7AARgThKA + /vhPplpxNxwZAQhbvONmbZsARHMBaPtUjAYg/oDrxx15kmPZiXe1CUBKKB8DTutPvL9BAAKhTvt0O1G8 + 66fa1cEGQJ0K67bippdjNABve7/rm4MyCICOFQABCE38h59p2wQgIGoVOJy4FwhAyyW/uWlmEoCUEA8B + Zg4FzXwSUAWATwKKEj/0c2076ACgZgDe8j7Xu4+SCAA0tQOQfNc84cF33pn5kwBAYyQACQ+e4MyMmXp8 + CAA0tQLwxve63n2UFP/lF9p2MAFAPUcMQPKpM6Zfc/JxJACoIjcAIXyXukXih3+pbRMAFJIZgFPe43q3 + UIb6NiArAFRBANqBFQAqyQzAG97lerdQhloBEABUQQDaIf7rr7VtAoBCCEA7EABUkhmAky92vVsoKX7k + Fm2bAKCQzAC8/iI/znUzC38mgACgktwVAOf+RYn/dqu2TQBQSO4KAKIQAFSSG4AhX7GQJP7Hbdo2AUAh + mQF47YWudwslEQBUkhsA3gMQJX50o7ZNAFBIZgBOOt/1bqGMQSeK99yuXUUAUEhmAF5zwcwl9UMnekzv + Z0QAUE12ANa73i2U0iMAqCZ/BTCo8H+DK/GeTdo2AUAhmQFYts71bqEM9ZuBCACqIADCzZytiZ+8U7ua + AKCQzAC8ao3r3UJJBACVEIB2IACoJDcAE797jun3JACoJDMArzh37skFEeL/3K1tEwAUkhsA199xZx55 + KsnPAyAAqGLBAECM+Jl7tW0CgEIyA3Ds2a53CyURAFSSGwB+HoAo8d4t2jYBQCGZAVh6luvdQkkEAJXk + BoD3AESJn79f2yYAKCQzAEef4Xq3UIb6zUCsAFAFAWgHVgCoJDMAS05zvVsoqjfzA0EIAKogAO0Q79+q + bRMAFEIA2oEAoBIC0A4EAJXkBqDPjwQTY6pHAFBNZgAWrXa9WygpPrRN2yYAKIQAtAMBQCW5AeC7AKLE + /e3aNgFAIZkBmFrlerdQEgFAJVkBgHwEAIUQgHYiACiEALQTAUAhBKCdCAAA8QgAEDACAASMAAABIwBA + wAgAEDACAASMAAABmxcA1zsEoFmzAVB27x6NDh6Mon7f9W4BaMr/AZCxqA55eVu6AAAAAElFTkSuQmCC + KAAAADAAAABgAAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzMAmjkDAJ46AACePgMA + oj8AAKJCAwCmQwAApkcDAKpJAACpTAMArk4AAK1RAwCyUwAAsVYDALVZAAC5XQAAvmIAALxkAwDBaAAA + w24DAMZtAADHcwMAynIAAMt3AwDOdwAA0nwAAAAzoQAANKMAADSkAAA2qQAAOa4AADqyAAA8tQAAPbkA + AD+8AABbrwAAQL4AAEHBAABDxQAARMYAAEXJAABGzAAASM8AAEjQAABK1AAATNkASLnkAEi95gBHwugA + R8XqAEfI6wBHyuwARs3tAEbR7wBG0/AARtbyAEba9ABF3fUAReD3AEXj+ABF5vkAROn7AETt/ABE8P4A + rLzZALrH3wDl2eIA/uHhAACwNgAAz0AAAPBKABH/WwAx/3EAUf+HAHH/nQCR/7IAsf/JANH/3wD///8A + AAAAAAIvAAAEUAAABnAAAAiQAAAKsAAAC88AAA7wAAAg/xIAPf8xAFv/UQB5/3EAmP+RALX/sQDU/9EA + ////AAAAAAAULwAAIlAAADBwAAA9kAAATLAAAFnPAABn8AAAeP8RAIr/MQCc/1EArv9xAMD/kQDS/7EA + 5P/RAP///wAAAAAAJi8AAEBQAABacAAAdJAAAI6wAACpzwAAwvAAANH/EQDY/zEA3v9RAOP/cQDp/5EA + 7/+xAPb/0QD///8AAAAAAC8mAABQQQAAcFsAAJB0AACwjgAAz6kAAPDDAAD/0hEA/9gxAP/dUQD/5HEA + /+qRAP/wsQD/9tEA////AAAAAAAvFAAAUCIAAHAwAACQPgAAsE0AAM9bAADwaQAA/3kRAP+KMQD/nVEA + /69xAP/BkQD/0rEA/+XRAP///wAAAAAALwMAAFAEAABwBgAAkAkAALAKAADPDAAA8A4AAP8gEgD/PjEA + /1xRAP96cQD/l5EA/7axAP/U0QD///8AAAAAAC8ADgBQABcAcAAhAJAAKwCwADYAzwBAAPAASQD/EVoA + /zFwAP9RhgD/cZwA/5GyAP+xyAD/0d8A////AAAAAAAvACAAUAA2AHAATACQAGIAsAB4AM8AjgDwAKQA + /xGzAP8xvgD/UccA/3HRAP+R3AD/seUA/9HwAP///wAAAAAALAAvAEsAUABpAHAAhwCQAKUAsADEAM8A + 4QDwAPAR/wDyMf8A9FH/APZx/wD3kf8A+bH/APvR/wD///8AAAAAABsALwAtAFAAPwBwAFIAkABjALAA + dgDPAIgA8ACZEf8ApjH/ALRR/wDCcf8Az5H/ANyx/wDr0f8A////AAAAAAAIAC8ADgBQABUAcAAbAJAA + IQCwACYAzwAsAPAAPhH/AFgx/wBxUf8AjHH/AKaR/wC/sf8A2tH/AP///wBEAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwMDAwMDAwMDAwMDAwMDAwMDAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAQQFBQUFBQUFBQUFBQUFBQUFBQUFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AQYHBwcHBwcHBwcHBwcHBwcHBwcHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgJCQkJCQkJCQkJ + CQkJCQkJCQkJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoMlZWVlZWVlZWVlZWVlZWVlZWVAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQwNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAQ4PEA8PDw8PDw8PDw8PDw8PDw8PAQAAAAAAAAAAAAAAGxsbGxsbGxsbGxsbHAAA + AQ8QEBAQEBAQEBAQEBAQEBAQEBAQAQAAAAAAAAAAAAAAGxwdHR0dHR0dHR0dGwAAARASERERERERERER + ERERERERERERAQAAAAAAAAAAAAAAHB4eHh4eHh4eHh4eGwAAARITExMTExMTExMTExMTExMTExMTAQAA + AAAAAAAAAAAAGx4eHh8eHx4fHh8fGwAAARMVFRUVFRUVFRUVFRUVFRUVFRUVAQAAAAAAAAAAAAAAGx8f + Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA + ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa + GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA + AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm + JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv + Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw + MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw + MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt + LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy + MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy + MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0 + NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA + AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1 + NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3 + Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3 + Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA + AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5 + OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6 + Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA + AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9 + PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09 + PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+ + Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA + AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA + QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk + JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA + A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u + gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA + 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 + HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL + CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA + AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ + AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj + IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk + JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm + JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo + KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u + Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/ + ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA + /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A + uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA + PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA + PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA + 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 + ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46 + AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/ + AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD + AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ + AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O + AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT + ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ + ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd + ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i + AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2 + qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo + AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3 + qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt + AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA + AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy + AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7 + swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53 + AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8 + ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ + ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz + M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA + vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD + xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4 + 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8 + 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6 + 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH + zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8 + 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI + 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/ + 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB + 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM + 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD + 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8 + 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF + 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI + zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI + 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz + oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK + 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM + 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO + 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR + 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT + 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV + 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX + 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba + 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc + 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe + 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg + 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj + +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl + +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn + +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp + +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs + /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu + /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw + /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669 + 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb + rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u//// + ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O + 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA + AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO + 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO + 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA + AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf + OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr + SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw + UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8 + YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA + NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA + AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI + cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA + O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP + eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A + AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA + AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA + QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A + W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI + uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA + R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI + vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H + wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA + 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 + SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH + au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI + fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz + fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z + pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi + bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na + JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA + JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO + RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL + W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy + IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA + /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A + /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA + /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA + /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA + /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A + /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA + /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA + /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A + /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A + /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA + AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA + /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao + /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA + /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer + /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A + /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv + /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA + /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA + /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz + /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA + /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA + /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL + egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA + AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA + /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66 + /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+ + /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB + /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF + /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk + /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL + egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM + /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm + /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ + /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n + /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU + /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY + /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp + /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM + egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq + /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP + /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs + /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt + /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u + /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM + ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv + /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw + /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz + /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0 + /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72 + /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3 + /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5 + /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL + ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6 + /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7 + /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8 + /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79 + /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+ + /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL + ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM + ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL + ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O + 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA + AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO + 7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAA + AAAADu4AAAAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO + 7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wA + AAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4AAAAAAAAO7gAAAAAAAA7uKAAAACAAAABAAAAAAQAg + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Ly2/+y8s//svLPf7Lyz3+y8s9/svLPf7Lyz3+y8s9/svL + Pf7Lyz3hrq5Ay5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iY + R8uYmEfLmJhC/svLPf7Lyz3+y8s9/svLPf7Lyz3+y8tq/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGUyMjxmMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2UyMloAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+fOwD/nzsA/587AP+fOwD/nzsA/587AP+fOwD/nzsA + /587AP+fOwD/nzsA/587AP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPf7Ly1T+y8sDAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzNVZjMz/6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA + /6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9/svL + VP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM1VmMzP/q0oA/6tKAP+rSgD/q0oA + /6tKAP+rSgD/q0oA/6tKAP+rSgD/q0oA/6tKAP+rSgD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAA + AP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+wUQD/sFEA + /7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP9mMzP/ZTIygAAAAAAAAAAAAAAA + AAAAAAAAAAAA/svLPeG6xm9da7BXADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVADOhVQAyoCZmMzNVZjMz + /7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/2YzM/9lMjKAAAAA + AAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADKg + e2YzM1VmMzP/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/ZjMz + /2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh/wA2qf8ANqn/ADap/wA2qf8ANqn/ADap + /wAzof8AMqCAZjMzVWYzM//DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA + /8NpAP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPamYvaYAM6H/ADit/wA4rf8AOK3/ADit + /wA4rf8AOK3/ADOh/wAyoIBmMzNVZjMz/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA + /8hwAP/IcAD/yHAA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AO7T/ADu0 + /wA7tP8AO7T/ADu0/wA7tP8AM6H/ADKggGYzM1VmMzP/z3gA/894AP/PeAD/z3gA/894AP/PeAD/z3gA + /894AP/PeAD/z3gA/894AP/PeAD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh + /wA9uP8APbj/AD24/wA9uP8APbj/AD24/wAzof8AMqCAZjMzVWYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svL + PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv + VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl + /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl + /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh + /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m + /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV + /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho + /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza + /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp + /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh + /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr + /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH + b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs + /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv + /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u + /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv + /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv + /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv + /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy + /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz + /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1 + /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2 + /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4 + /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9 + wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5 + /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7 + /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38 + /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38 + /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM + AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL + VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB + pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA + Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA + AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl + o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz + /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz + /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh + /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer + /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2 + /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB + /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh + /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv + /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo + /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr + /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu + /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy + /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31 + /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4 + /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7 + /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv + /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs + QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ== + + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs new file mode 100644 index 000000000..fcabcf5db --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace SysCallHacker +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f48626a0f --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SysCallHacker")] +[assembly: AssemblyDescription("System Call Hacker")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("SysCallHacker")] +[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("55039872-9923-42d3-893d-2dd59e582765")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs new file mode 100644 index 000000000..d6a9b8ec8 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SysCallHacker.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SysCallHacker.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx new file mode 100644 index 000000000..ffecec851 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs new file mode 100644 index 000000000..9f286282d --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4016 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SysCallHacker.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings new file mode 100644 index 000000000..abf36c5d3 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj b/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj new file mode 100644 index 000000000..3da8365d5 --- /dev/null +++ b/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj @@ -0,0 +1,102 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715} + WinExe + Properties + SysCallHacker + SysCallHacker + v2.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + + + + + + + + + + + Form + + + EventProperties.cs + + + + Form + + + MainWindow.cs + + + + + EventProperties.cs + + + MainWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + {8A448157-E1A7-4DDF-954E-287F1117832B} + ProcessHacker.Native + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/HACKING.txt b/branches/ph-plugins/HACKING.txt new file mode 100644 index 000000000..4422509e3 --- /dev/null +++ b/branches/ph-plugins/HACKING.txt @@ -0,0 +1,19 @@ +Note to SVN users: +If you use SVN code you may encounter weird bugs. Please use releases instead. + +Process Hacker is developed using Visual Studio 2008, and +will only work with C# compilers which support C# 3.0. It has been tested on +Visual Studio 2008 and Visual C# Express Edition (free). + +To build KProcessHacker, you will need the Windows DDK. +To build NProcessHacker, you will need Visual Studio 2008 or Visual C++ +Express Edition (free). + +IMPORTANT: If you are using Visual C# Express Edition to compile/run +Process Hacker, you MUST enable "Show advanced build configurations" in +Tools > Options > Projects and Solutions > General. + +The build script relies on ILMerge being present in the default installation +location ("%PROGRAMFILES%\Microsoft\ILMerge\ILMerge.exe") or in your PATH +environment variable. In order to build the installer you must have Inno Setup +QuickStart Pack installed (v5.3.5+). \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/HACKING.txt b/branches/ph-plugins/KProcessHacker/HACKING.txt new file mode 100644 index 000000000..00c104cfa --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/HACKING.txt @@ -0,0 +1,67 @@ +==== KProcessHacker ==== + +== IMPORTANT == +KProcessHacker has been developed from either reverse engineering of +the Windows kernel or ReactOS code (http://www.reactos.org). The +following files contain "ported" ReactOS code (with modifications): + + * mm.c + * MiDoMappedCopy + * MiDoPoolCopy (added smarter buffer management) + * MiGetExceptionInfo + * ps.c + * KphOpenProcess + * KphOpenThread + * se.c + * KphOpenProcessTokenEx + +== CODE STRUCTURE == + * handle.c + - Contains handle table code. + * hook.c + - Contains hooking code. Currently you may hook any kernel-mode + function and object type open procedures. + * io.c + - Contains I/O-related code, such as device and driver functions. + * kph.c + - Contains support routines. + * kprocesshacker.c + - Contains interfacing code, mainly consisting of the I/O control + handler. + * mm.c + - Contains memory-related code, such as reading and writing. + * ob.c + - Contains object-related code, such as handle duplication. + * protect.c + - Contains process protection code. Process protection is + achieved by hooking ObOpenObjectByPointer and some object type + OpenProcedures. + * ps.c + - Contains process- and thread-related code, such as opening and + terminating. + * ref.c + - Contains the KPH object manager. + * se.c + - Contains security-related code. Only function there is + KphOpenProcessTokenEx. + * sync.c + - Various synchronization functions. + * sysservice.c + - System service logging. + * trace.c + - Stack trace code. + * version.c + - Contains Windows-version-specific data. + +== POOL TAGS == +PhAB: System service logging argument block. sysservice.h +PhCH: Client handle table. kprocesshacker.h +PhCt: System service logging argument capture temporary buffer. sysservicep.h +PhCU: Captured Unicode string. kph.h +PhEB: System service logging event block. sysservice.h +PhOb: Object manager object. refp.h +PhPC: Pool-based virtual memory copying. mm.h +PhPr: Protection entry. protect.h +PhSc: System service call entry. sysservicedata.h +PhSD: Processor lock DPC storage. sync.h +PhSt: Stack back trace. ps.h diff --git a/branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys b/branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys new file mode 100644 index 0000000000000000000000000000000000000000..9c86f56245b18c3450696c7f82774df722f9ba56 GIT binary patch literal 60416 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. + */ + +#include "include/handle.h" +#include "include/handlep.h" + +NTSTATUS KphpAllocateHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __out PKPH_HANDLE_TABLE_ENTRY *Entry + ); + +NTSTATUS KphpFreeHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __in PKPH_HANDLE_TABLE_ENTRY Entry + ); + +/* KphCreateHandleTable + * + * Creates a handle table. + * + * HandleTable: A variable which receives a pointer to the handle table. + * MaximumHandles: The maximum number of handles that can be created. + * SizeOfEntry: The size of each handle table entry. This value must be + * divisible by 4. + * Tag: The tag to use when allocating handle table resources. + */ +NTSTATUS KphCreateHandleTable( + __out PKPH_HANDLE_TABLE *HandleTable, + __in ULONG MaximumHandles, + __in ULONG SizeOfEntry, + __in ULONG Tag + ) +{ + PKPH_HANDLE_TABLE handleTable; + + /* Each handle entry must be at least the size of our + * handle table entry definition. + */ + if (SizeOfEntry < sizeof(KPH_HANDLE_TABLE_ENTRY)) + return STATUS_INVALID_PARAMETER_3; + /* Handle entries must be 4-byte aligned. */ + if (SizeOfEntry % 4 != 0) + return STATUS_INVALID_PARAMETER_3; + + /* Allocate storage for the handle table structure. */ + handleTable = ExAllocatePoolWithTag( + PagedPool, + sizeof(KPH_HANDLE_TABLE), + Tag + ); + + if (!handleTable) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Allocate storage for the handle table itself. */ + handleTable->Table = ExAllocatePoolWithTag( + PagedPool, + MaximumHandles * SizeOfEntry, + Tag + ); + + if (!handleTable->Table) + { + ExFreePoolWithTag(handleTable, Tag); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Initialize the rest of the table descriptor. */ + handleTable->Tag = Tag; + handleTable->SizeOfEntry = SizeOfEntry; + handleTable->NextHandle = (HANDLE)0; + handleTable->FreeHandle = NULL; + ExInitializeFastMutex(&handleTable->Mutex); + handleTable->TableSize = MaximumHandles * SizeOfEntry; + + /* Zero the handle table. */ + memset(handleTable->Table, 0, handleTable->TableSize); + + /* Pass the pointer to the handle table back. */ + *HandleTable = handleTable; + + return STATUS_SUCCESS; +} + +/* KphFreeHandleTable + * + * Frees all handle table resources. + */ +VOID KphFreeHandleTable( + __in PKPH_HANDLE_TABLE HandleTable + ) +{ + ULONG i; + ULONG tag; + + /* Free all handle values. */ + for (i = 0; i < HandleTable->TableSize / HandleTable->SizeOfEntry; i++) + { + KphCloseHandle(HandleTable, KphHandleFromIndex(i)); + } + + /* Save the handle table tag first. */ + tag = HandleTable->Tag; + /* Free the table. */ + ExFreePoolWithTag(HandleTable->Table, tag); + /* Free the descriptor. */ + ExFreePoolWithTag(HandleTable, tag); +} + +/* KphCloseHandle + * + * Closes a handle, dereferencing the referenced object. + */ +NTSTATUS KphCloseHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + PVOID object; + + if (!KphValidHandle(HandleTable, Handle, &entry)) + return STATUS_INVALID_HANDLE; + + /* Save a pointer to the object referenced by the handle. */ + object = entry->Object; + /* Free the handle. */ + status = KphpFreeHandleEntry(HandleTable, entry); + + if (!NT_SUCCESS(status)) + return status; + + /* Dereference the object. */ + KphDereferenceObject(object); + + return status; +} + +/* KphCreateHandle + * + * Creates a handle and references an object. + */ +NTSTATUS KphCreateHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in PVOID Object, + __out PHANDLE Handle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + + /* Allocate a handle. */ + status = KphpAllocateHandleEntry(HandleTable, &entry); + + if (!NT_SUCCESS(status)) + return status; + + /* Reference and set the object in the entry. */ + KphReferenceObject(Object); + entry->Object = Object; + + /* Pass the handle back. */ + *Handle = KphGetHandleEntry(entry); + + return status; +} + +/* KphReferenceObjectByHandle + * + * References an object from a handle. + */ +NTSTATUS KphReferenceObjectByHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry; + + if (!KphValidHandle(HandleTable, Handle, &entry)) + return STATUS_INVALID_HANDLE; + + /* Lock the entry. */ + if (!KphLockAllocatedHandleEntry(entry)) + return STATUS_INVALID_HANDLE; + + /* Check the type of object if the caller requested us + * to do that. + */ + if (ObjectType) + { + if (KphGetObjectType(entry->Object) != ObjectType) + { + /* Bad type. */ + KphUnlockHandleEntry(entry); + + return STATUS_OBJECT_TYPE_MISMATCH; + } + } + + /* Reference and pass the object back. */ + KphReferenceObject(entry->Object); + *Object = entry->Object; + + KphUnlockHandleEntry(entry); + + return status; +} + +/* KphValidHandle + * + * Checks if a handle is valid. + */ +BOOLEAN KphValidHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry + ) +{ + PKPH_HANDLE_TABLE_ENTRY entry; + BOOLEAN valid; + + entry = KphEntryFromHandle(HandleTable, Handle); + valid = + ((ULONG_PTR)entry >= (ULONG_PTR)HandleTable->Table) && + ((ULONG_PTR)entry + HandleTable->SizeOfEntry <= + (ULONG_PTR)HandleTable->Table + HandleTable->TableSize); + + if (valid) + *Entry = entry; + + return valid; +} + +/* KphpAllocateHandleEntry + * + * Allocates a handle table entry. + */ +NTSTATUS KphpAllocateHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __out PKPH_HANDLE_TABLE_ENTRY *Entry + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_HANDLE_TABLE_ENTRY entry = NULL; + + /* Prevent others from modifying the handle table. */ + ExAcquireFastMutex(&HandleTable->Mutex); + + /* Check the free list first. If we have a free entry, + * claim it and update the free list. Otherwise, create + * a new entry from the NextHandle value. + */ + if (HandleTable->FreeHandle) + { + /* We have a free entry. Update the free list. */ + entry = HandleTable->FreeHandle; + /* The next free entry goes into FreeHandle. */ + HandleTable->FreeHandle = KphGetNextFreeEntry(entry); + } + else + { + /* No free handles. We have to initialize a new one + * based on the NextHandle value. + */ + /* Make sure we don't go past the end of the table. */ + if ( + KphIndexFromHandle(HandleTable->NextHandle) * + HandleTable->SizeOfEntry <= + HandleTable->TableSize + ) + { + /* Get a pointer to the entry from the handle. */ + entry = KphEntryFromHandle(HandleTable, HandleTable->NextHandle); + /* Increment the next handle value. */ + HandleTable->NextHandle = KphIncrementHandle(HandleTable->NextHandle); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) + { + /* Set the entry's handle value. */ + entry->Handle = KphHandleFromEntry(HandleTable, entry); + KphSetAllocatedEntry(entry); + + *Entry = entry; + } + + ExReleaseFastMutex(&HandleTable->Mutex); + + return status; +} + +/* KphpFreeHandleEntry + * + * Frees a handle table entry. + */ +NTSTATUS KphpFreeHandleEntry( + __in PKPH_HANDLE_TABLE HandleTable, + __in PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + ExAcquireFastMutex(&HandleTable->Mutex); + + /* Lock the entry. */ + if (!KphLockAllocatedHandleEntry(Entry)) + { + /* Someone else has already freed the entry (or it was never allocated). */ + ExReleaseFastMutex(&HandleTable->Mutex); + return STATUS_INVALID_HANDLE; + } + + /* Mark the entry as unallocated. */ + KphClearAllocatedEntry(Entry); + + /* Add the entry to the free list. */ + KphSetNextFreeEntry(Entry, HandleTable->FreeHandle); + HandleTable->FreeHandle = Entry; + + /* Zero the entry (except for the Value). */ + memset(&Entry->Object, 0, HandleTable->SizeOfEntry - sizeof(ULONG_PTR)); + + /* Unlock the entry. */ + KphUnlockHandleEntry(Entry); + + ExReleaseFastMutex(&HandleTable->Mutex); + + return STATUS_SUCCESS; +} diff --git a/branches/ph-plugins/KProcessHacker/hook.c b/branches/ph-plugins/KProcessHacker/hook.c new file mode 100644 index 000000000..3f775db5d --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/hook.c @@ -0,0 +1,408 @@ +/* + * Process Hacker Driver - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/hook.h" +#include "include/sync.h" + +static KPH_PROCESSOR_LOCK HookProcessorLock; + +/* KphHookInit + * + * Initializes the hooking module. + */ +NTSTATUS KphHookInit() +{ + KphInitializeProcessorLock(&HookProcessorLock); + + return STATUS_SUCCESS; +} + +/* KphInitializeHook + * + * Initializes a hook structure. + */ +VOID KphInitializeHook( + __out PKPH_HOOK Hook, + __in PVOID Function, + __in PVOID Target + ) +{ + memset(Hook, 0, sizeof(KPH_HOOK)); + Hook->Function = Function; + Hook->Target = Target; +} + +/* KphHook + * + * Hooks a kernel-mode function. + * WARNING: DO NOT HOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphHook( + __inout PKPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PUCHAR function; + + status = KphpCreateMappedMdl( + Hook->Function, + 5, + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + function = (PUCHAR)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Note that this is completely safe even though we are at + * DISPATCH_LEVEL because we are using the mapped MDL. + */ + /* Copy the original five bytes (for unhooking). */ + memcpy(Hook->Bytes, function, 10); + /* Hook the function by writing a jump instruction. */ + Hook->Hooked = TRUE; + /* jmp Target */ + *function = 0xe9; + *(PULONG_PTR)(function + 1) = (ULONG_PTR)Hook->Target - (ULONG_PTR)Hook->Function - 5; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + dprintf("KphHook: Could not acquire processor lock!\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphUnhook + * + * Unhooks a kernel-mode function. + * WARNING: DO NOT UNHOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphUnhook( + __inout PKPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + + if (!Hook->Hooked) + return STATUS_UNSUCCESSFUL; + + status = KphpCreateMappedMdl( + Hook->Function, + 5, + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Unpatch the function. */ + memcpy(mappedMdl.Address, Hook->Bytes, 5); + Hook->Hooked = FALSE; + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + dprintf("KphUnhook: Could not acquire processor lock!\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphObOpenCall + * + * Calls the original open procedure for an object type. + * + * AccessMode: If this argument is unavailable, specify KernelMode. + */ +NTSTATUS NTAPI KphObOpenCall( + __in PKPH_OB_OPEN_HOOK ObOpenHook, + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + /* If there wasn't any original open procedure, exit. */ + if (!ObOpenHook->Function) + return STATUS_SUCCESS; + + if (WindowsVersion == WINDOWS_XP) + { + return ((OB_OPEN_METHOD_51)ObOpenHook->Function)( + OpenReason, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + return ((OB_OPEN_METHOD_60)ObOpenHook->Function)( + OpenReason, + AccessMode, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} + +/* KphInitializeObOpenHook + * + * Initializes a hook structure. + */ +VOID KphInitializeObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook, + __in POBJECT_TYPE ObjectType, + __in PVOID Target51, + __in PVOID Target60 + ) +{ + memset(ObOpenHook, 0, sizeof(KPH_OB_OPEN_HOOK)); + ObOpenHook->ObjectType = ObjectType; + ObOpenHook->Target51 = Target51; + ObOpenHook->Target60 = Target60; +} + +/* KphObOpenHook + * + * Hooks the open procedure for an object type. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PVOID *openProcedure; + + status = KphpCreateMappedMdl( + KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure), + sizeof(PVOID), + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + openProcedure = (PVOID *)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Save the original open procedure pointer. */ + ObOpenHook->Function = *openProcedure; + + /* Choose the correct target open procedure and hook. */ + if (WindowsVersion == WINDOWS_XP) + { + if (ObOpenHook->Target51) + *openProcedure = ObOpenHook->Target51; + else + status = STATUS_INVALID_PARAMETER; + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + if (ObOpenHook->Target60) + *openProcedure = ObOpenHook->Target60; + else + status = STATUS_INVALID_PARAMETER; + } + else + { + status = STATUS_NOT_SUPPORTED; + } + + ObOpenHook->Hooked = TRUE; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphObOpenUnhook + * + * Unhooks the open procedure for an object type. + * + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphObOpenUnhook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + MAPPED_MDL mappedMdl; + PVOID *openProcedure; + + if (!ObOpenHook->Hooked) + return STATUS_UNSUCCESSFUL; + + status = KphpCreateMappedMdl( + KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure), + sizeof(PVOID), + &mappedMdl + ); + + if (!NT_SUCCESS(status)) + return status; + + openProcedure = (PVOID *)mappedMdl.Address; + + /* Acquire a lock on all other processors. */ + if (KphAcquireProcessorLock(&HookProcessorLock)) + { + /* Restore the original open procedure pointer. */ + *openProcedure = ObOpenHook->Function; + ObOpenHook->Hooked = FALSE; + + /* Release the processor lock. */ + KphReleaseProcessorLock(&HookProcessorLock); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KphpFreeMappedMdl(&mappedMdl); + + return status; +} + +/* KphpCreateMappedMdl + * + * Creates and maps a MDL. + * + * Thread safety: Full + * IRQL: Any + */ +NTSTATUS KphpCreateMappedMdl( + __in PVOID Address, + __in ULONG Length, + __out PMAPPED_MDL MappedMdl + ) +{ + PMDL mdl; + + MappedMdl->Mdl = NULL; + MappedMdl->Address = NULL; + + mdl = IoAllocateMdl(Address, Length, FALSE, FALSE, NULL); + + if (mdl == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + MmBuildMdlForNonPagedPool(mdl); + mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA; + MappedMdl->Address = MmMapLockedPagesSpecifyCache( + mdl, + KernelMode, + MmNonCached, + NULL, + FALSE, + HighPagePriority + ); + MappedMdl->Mdl = mdl; + + if (!MappedMdl->Address) + { + KphpFreeMappedMdl(MappedMdl); + return STATUS_INSUFFICIENT_RESOURCES; + } + + return STATUS_SUCCESS; +} + +/* KphpFreeMappedMdl + * + * Unmaps and frees a MDL. + * + * Thread safety: Full + * IRQL: Any + */ +VOID KphpFreeMappedMdl( + __in PMAPPED_MDL MappedMdl + ) +{ + if (MappedMdl->Mdl != NULL) + { + if (MappedMdl->Address != NULL) + { + MmUnmapLockedPages(MappedMdl->Address, MappedMdl->Mdl); + MappedMdl->Address = NULL; + } + + IoFreeMdl(MappedMdl->Mdl); + MappedMdl->Mdl = NULL; + } +} diff --git a/branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys b/branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys new file mode 100644 index 0000000000000000000000000000000000000000..ae87f7343cd5868ec07a6c6abd2f89e7b784a5e7 GIT binary patch literal 57856 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 N_s8u=1pX%k{tXyLzA*p* literal 0 HcmV?d00001 diff --git a/branches/ph-plugins/KProcessHacker/include/debug.h b/branches/ph-plugins/KProcessHacker/include/debug.h new file mode 100644 index 000000000..0a140616b --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/debug.h @@ -0,0 +1,35 @@ +/* + * Process Hacker Driver - + * debug definitions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifdef DBG +#define dprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__) +#else +#define dprintf +#endif + +#define dfprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__) +#define dwprintf DbgPrint + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/ex.h b/branches/ph-plugins/KProcessHacker/include/ex.h new file mode 100644 index 000000000..bfd06ccbe --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/ex.h @@ -0,0 +1,262 @@ +/* + * Process Hacker Driver - + * executive + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _EX_H +#define _EX_H + +#include "types.h" + +/* HACK - version.c dependency */ +#define WINDOWS_XP 51 +#define WINDOWS_SERVER_2003 52 +#define WINDOWS_VISTA 60 +#define WINDOWS_7 61 + +extern ULONG WindowsVersion; + +/* Handles */ + +struct _HANDLE_TABLE; +struct _HANDLE_TABLE_ENTRY; + +typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)( + struct _HANDLE_TABLE_ENTRY *HandleTableEntry, + HANDLE Handle, + PVOID Context + ); + +BOOLEAN NTAPI ExEnumHandleTable( + __in struct _HANDLE_TABLE *HandleTable, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ); + +/* Push Locks */ + +/* Definition for Windows 2003 and above. This means we + * MUST use the slow path on Windows XP. + */ +typedef struct _EXI_PUSH_LOCK +{ + union + { + struct + { + ULONG_PTR Locked : 1; + ULONG_PTR Waiting : 1; + ULONG_PTR Waking : 1; + ULONG_PTR MultipleShared : 1; + ULONG_PTR Shared : sizeof(ULONG_PTR) * 8 - 4; /* ULONG_PTR bits minus 4 */ + }; + ULONG_PTR Value; + PVOID Ptr; + }; +} EXI_PUSH_LOCK, *PEXI_PUSH_LOCK; + +#define EX_PUSH_LOCK_LOCK_SHIFT 0 +#define EX_PUSH_LOCK_LOCK ((ULONG_PTR)0x1) +/* Indicates chained waiters */ +#define EX_PUSH_LOCK_WAITING ((ULONG_PTR)0x2) +/* Traversing the list */ +#define EX_PUSH_LOCK_WAKING ((ULONG_PTR)0x4) +/* Multiple owners + waiters */ +#define EX_PUSH_LOCK_MULTIPLE_SHARED ((ULONG_PTR)0x8) + +#define EX_PUSH_LOCK_SHARE_INC ((ULONG_PTR)0x10) +#define EX_PUSH_LOCK_PTR_BITS ((ULONG_PTR)0xf) + +NTKERNELAPI VOID FASTCALL ExfAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfReleasePushLock( + __inout PEX_PUSH_LOCK PushLock + ); + +/* The below functions are only exported on Vista and higher. */ + +NTKERNELAPI VOID FASTCALL ExfReleasePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfReleasePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI BOOLEAN FASTCALL ExfTryAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ); + +NTKERNELAPI VOID FASTCALL ExfTryToWakePushLock( + __inout PEX_PUSH_LOCK PushLock + ); + +/* Wrapper functions */ + +/* ExInitializePushLock + * + * Initializes a push lock. + */ +FORCEINLINE VOID ExInitializePushLock( + __out PEX_PUSH_LOCK PushLock + ) +{ + *PushLock = 0; +} + +/* ExAcquirePushLockExclusive + * + * Acquires a push lock in exclusive mode. + */ +FORCEINLINE VOID ExAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path - acquire push lock, no function call. */ + if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT)) + { + /* Slow path - call the function. */ + ExfAcquirePushLockExclusive(PushLock); + } +} + +/* ExAcquirePushLockShared + * + * Acquires a push lock in shared mode. + */ +FORCEINLINE VOID ExAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path - acquire push lock which is not held at all, no function call. */ + if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedCompareExchangePointer( + (PVOID)PushLock, + (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK), + 0 + ) != 0) + { + /* Slow path - call the function. */ + ExfAcquirePushLockShared(PushLock); + } +} + +/* ExReleasePushLock + * + * Releases a push lock (for both types). + */ +FORCEINLINE VOID ExReleasePushLock( + __inout PEX_PUSH_LOCK PushLock + ) +{ + EXI_PUSH_LOCK oldValue, newValue; + + oldValue.Value = *PushLock; + + /* If we are the last to release in shared mode or we + * are releasing in exclusive mode, we simply set + * the value to 0. + */ + + if (oldValue.Shared > 1) + { + /* One less shared holder. */ + newValue.Value = oldValue.Value - EX_PUSH_LOCK_SHARE_INC; + } + else + { + newValue.Value = 0; + } + + /* If we have chained waiters, we can't release the + * push lock using the fast path since they need to + * be unblocked. + */ + if ( + WindowsVersion < WINDOWS_SERVER_2003 || + oldValue.Waiting || + InterlockedCompareExchangePointer( + (PVOID)PushLock, + newValue.Ptr, + oldValue.Ptr + ) != oldValue.Ptr + ) + { + /* Slow path - call the function. */ + ExfReleasePushLock(PushLock); + } +} + +#ifndef NEVER_DEFINED +/* ExTryAcquirePushLockExclusive + * + * Attempts to acquire a push lock in exclusive mode. + * + * Return value: TRUE if the push lock was acquired, FALSE if + * the push lock was already acquired in exclusive mode. + */ +FORCEINLINE BOOLEAN ExTryAcquirePushLockExclusive( + __inout PEX_PUSH_LOCK PushLock + ) +{ + if (!InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/* ExTryAcquirePushLockShared + * + * Attempts to acquire a push lock in shared mode. + * + * Return value: TRUE if the push lock was acquired, FALSE if + * the push lock was already acquired in exclusive mode. + */ +FORCEINLINE BOOLEAN ExTryAcquirePushLockShared( + __inout PEX_PUSH_LOCK PushLock + ) +{ + /* Fast path with the push lock not held at all. */ + if (InterlockedCompareExchangePointer( + (PVOID)PushLock, + (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK), + 0 + ) != 0) + { + return ExfTryAcquirePushLockShared(PushLock); + } + else + { + return TRUE; + } +} +#endif + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/handle.h b/branches/ph-plugins/KProcessHacker/include/handle.h new file mode 100644 index 000000000..710205b3e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/handle.h @@ -0,0 +1,78 @@ +/* + * Process Hacker Driver - + * handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HANDLE_H +#define _HANDLE_H + +#include "kph.h" +#include "ref.h" + +struct _KPH_HANDLE_TABLE; +typedef struct _KPH_HANDLE_TABLE *PKPH_HANDLE_TABLE; + +typedef struct _KPH_HANDLE_TABLE_ENTRY +{ + union + { + HANDLE Handle; + ULONG_PTR Value; + struct _KPH_HANDLE_TABLE_ENTRY *NextFree; + }; + PVOID Object; +} KPH_HANDLE_TABLE_ENTRY, *PKPH_HANDLE_TABLE_ENTRY; + +NTSTATUS KphCreateHandleTable( + __out PKPH_HANDLE_TABLE *HandleTable, + __in ULONG MaximumHandles, + __in ULONG SizeOfEntry, + __in ULONG Tag + ); + +VOID KphFreeHandleTable( + __in PKPH_HANDLE_TABLE HandleTable + ); + +NTSTATUS KphCloseHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle + ); + +NTSTATUS KphCreateHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in PVOID Object, + __out PHANDLE Handle + ); + +NTSTATUS KphReferenceObjectByHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ); + +BOOLEAN KphValidHandle( + __in PKPH_HANDLE_TABLE HandleTable, + __in HANDLE Handle, + __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/handlep.h b/branches/ph-plugins/KProcessHacker/include/handlep.h new file mode 100644 index 000000000..a6cd445ee --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/handlep.h @@ -0,0 +1,144 @@ +/* + * Process Hacker Driver - + * handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HANDLEP_H +#define _HANDLEP_H + +#define _HANDLE_PRIVATE +#include "handle.h" +#include "sync.h" + +#define KPH_HANDLE_INCREMENT 4 +#define KPH_HANDLE_LOCKED 0x1 +#define KPH_HANDLE_LOCKED_SHIFT 0 +#define KPH_HANDLE_ALLOCATED 0x2 +#define KPH_HANDLE_FLAGS 0x3 + +#define KphGetFlagsEntry(Entry) ((Entry)->Value & KPH_HANDLE_FLAGS) +#define KphGetHandleEntry(Entry) ((HANDLE)((Entry)->Value & ~KPH_HANDLE_FLAGS)) +#define KphIncrementHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) + KPH_HANDLE_INCREMENT)) + +#define KphIsAllocatedEntry(Entry) ((Entry)->Value & KPH_HANDLE_ALLOCATED) +#define KphClearAllocatedEntry(Entry) ((Entry)->Value &= ~KPH_HANDLE_ALLOCATED) +#define KphSetAllocatedEntry(Entry) ((Entry)->Value |= KPH_HANDLE_ALLOCATED) + +#define KphGetNextFreeEntry(Entry) ((PKPH_HANDLE_TABLE_ENTRY)((Entry)->Value & ~KPH_HANDLE_FLAGS)) +#define KphSetNextFreeEntry(Entry, NextFree) ((Entry)->Value = ((ULONG_PTR)(NextFree) | KphGetFlagsEntry(Entry))) + +#define KphHandleFromIndex(Index) ((HANDLE)((ULONG_PTR)(Index) * KPH_HANDLE_INCREMENT)) +#define KphHandleFromIndexEx(Index, Flags) ((HANDLE)(((Index) * KPH_HANDLE_INCREMENT) | (Flags))) +#define KphIndexFromHandle(Handle) (((ULONG_PTR)(Handle) & ~KPH_HANDLE_FLAGS) / KPH_HANDLE_INCREMENT) + +#define KphEntryFromHandle(HandleTable, Handle) KphEntryFromIndex((HandleTable), KphIndexFromHandle(Handle)) +#define KphEntryFromIndex(HandleTable, Index) \ + ((PKPH_HANDLE_TABLE_ENTRY)((ULONG_PTR)(HandleTable)->Table + (Index) * (HandleTable)->SizeOfEntry)) +#define KphHandleFromEntry(HandleTable, Entry) KphHandleFromIndex(KphIndexFromEntry((HandleTable), (Entry))) +#define KphHandleFromEntryEx(HandleTable, Entry, Flags) \ + KphHandleFromIndexEx(KphIndexFromEntry((HandleTable), (Entry)), (Flags)) +#define KphIndexFromEntry(HandleTable, Entry) \ + (((ULONG_PTR)(Entry) - (ULONG_PTR)(HandleTable)->Table) / (HandleTable)->SizeOfEntry) + +typedef struct _KPH_HANDLE_TABLE +{ + /* The pool tag used for this descriptor and the table itself. */ + ULONG Tag; + /* The size of each handle table entry. */ + ULONG SizeOfEntry; + /* The next handle value to use. */ + HANDLE NextHandle; + /* The free list of handle table entries. */ + struct _KPH_HANDLE_TABLE_ENTRY *FreeHandle; + + /* A fast mutex guarding writes to the handle table. */ + FAST_MUTEX Mutex; + /* The size of the table, in bytes. */ + ULONG TableSize; + /* The actual handle table. */ + PVOID Table; +} KPH_HANDLE_TABLE, *PKPH_HANDLE_TABLE; + +FORCEINLINE BOOLEAN KphLockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +FORCEINLINE VOID KphUnlockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ); + +/* KphLockHandle + * + * Locks a handle table entry for exclusive access. Do not + * modify the lowest bit of the entry's value while you + * hold the lock. + * + * Return value: TRUE if the entry is allocated, otherwise FALSE. + */ +FORCEINLINE BOOLEAN KphLockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + /* Acquire the spinlock. */ + KphAcquireBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT); + + /* Return whether the entry is allocated. */ + return !!(Entry->Value & KPH_HANDLE_ALLOCATED); +} + +/* KphLockAllocatedHandle + * + * Locks a handle table entry for exclusive access. Do not + * modify the lowest bit of the entry's value while you + * hold the lock. + * The function will not lock the handle if it is unallocated. + * + * Return value: TRUE if the entry was locked, otherwise FALSE. + */ +FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + if (!KphLockHandleEntry(Entry)) + { + KphUnlockHandleEntry(Entry); + return FALSE; + } + + return TRUE; +} + +/* KphUnlockHandle + * + * Unlocks a handle table entry. + */ +FORCEINLINE VOID KphUnlockHandleEntry( + __inout PKPH_HANDLE_TABLE_ENTRY Entry + ) +{ + /* Unlock the spinlock. */ + KphReleaseBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT); +} + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/hook.h b/branches/ph-plugins/KProcessHacker/include/hook.h new file mode 100644 index 000000000..12ec3d3f9 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/hook.h @@ -0,0 +1,108 @@ +/* + * Process Hacker Driver - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HOOK_H +#define _HOOK_H + +#include "kph.h" +#include "ob.h" + +#define KPH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \ + __declspec(naked) Name(Arguments) \ + { \ + __asm lea eax, Hook \ + __asm mov eax, [eax+KPH_HOOK.Function] \ + __asm add eax, 5 \ + __asm push ebp \ + __asm mov ebp, esp \ + __asm jmp eax \ + } \ + +typedef struct _KPH_HOOK +{ + /* The address of the hooked function. + Should NOT be a function that is callable above PASSIVE_LEVEL. */ + PVOID Function; + /* The address of the new function. */ + PVOID Target; + /* Whether the function is hooked. */ + BOOLEAN Hooked; + /* The original first 10 bytes. */ + CHAR Bytes[10]; +} KPH_HOOK, *PKPH_HOOK; + +typedef struct _KPH_OB_OPEN_HOOK +{ + /* The object type that is being hooked. */ + POBJECT_TYPE ObjectType; + /* The original open procedure. */ + PVOID Function; + /* The new open procedure for NT 5.1 (XP). */ + OB_OPEN_METHOD_51 Target51; + /* The new open procedure for NT 6.1 and above (Vista, 7 or higher). */ + OB_OPEN_METHOD_60 Target60; + /* Whether the open procedure is hooked. */ + BOOLEAN Hooked; +} KPH_OB_OPEN_HOOK, *PKPH_OB_OPEN_HOOK; + +NTSTATUS KphHookInit(); + +VOID KphInitializeHook( + __out PKPH_HOOK Hook, + __in PVOID Function, + __in PVOID Target + ); + +NTSTATUS KphHook( + __inout PKPH_HOOK Hook + ); + +NTSTATUS KphUnhook( + __inout PKPH_HOOK Hook + ); + +NTSTATUS NTAPI KphObOpenCall( + __in PKPH_OB_OPEN_HOOK ObOpenHook, + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +VOID KphInitializeObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook, + __in POBJECT_TYPE ObjectType, + __in PVOID Target51, + __in PVOID Target60 + ); + +NTSTATUS KphObOpenHook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ); + +NTSTATUS KphObOpenUnhook( + __inout PKPH_OB_OPEN_HOOK ObOpenHook + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/io.h b/branches/ph-plugins/KProcessHacker/include/io.h new file mode 100644 index 000000000..bc98fb151 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/io.h @@ -0,0 +1,34 @@ +/* + * Process Hacker Driver - + * I/O manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _IO_H +#define _IO_H + +#include "types.h" + +extern POBJECT_TYPE *IoAdapterObjectType; +extern POBJECT_TYPE *IoControllerObjectType; +extern POBJECT_TYPE *IoDeviceHandlerObjectType; /* not used anymore */ +extern POBJECT_TYPE *IoDeviceObjectType; +extern POBJECT_TYPE *IoDriverObjectType; + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/ke.h b/branches/ph-plugins/KProcessHacker/include/ke.h new file mode 100644 index 000000000..741c1b00b --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/ke.h @@ -0,0 +1,95 @@ +/* + * Process Hacker Driver - + * kernel + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KE_H +#define _KE_H + +#include "types.h" + +/* APCs */ + +typedef enum _KAPC_ENVIRONMENT +{ + OriginalApcEnvironment, + AttachedApcEnvironment, + CurrentApcEnvironment, + InsertApcEnvironment +} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT; + +typedef VOID (NTAPI *PKKERNEL_ROUTINE)( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)( + PKAPC Apc + ); + +typedef VOID (NTAPI *PKNORMAL_ROUTINE)( + PVOID NormalContext, + PVOID SystemArgument1, + PVOID SystemArgument2 + ); + +NTKERNELAPI VOID NTAPI KeInitializeApc( + PKAPC Apc, + PKTHREAD Thread, + KAPC_ENVIRONMENT Environment, + PKKERNEL_ROUTINE KernelRoutine, + PKRUNDOWN_ROUTINE RundownRoutine, + PKNORMAL_ROUTINE NormalRoutine, + KPROCESSOR_MODE ProcessorMode, + PVOID NormalContext + ); + +NTKERNELAPI BOOLEAN NTAPI KeInsertQueueApc( + PRKAPC Apc, + PVOID SystemArgument1, + PVOID SystemArgument2, + KPRIORITY Increment + ); + +/* System services */ + +/* Exported by ntoskrnl as KeServiceDescriptorTable. */ +typedef struct _KSERVICE_TABLE_DESCRIPTOR +{ + /* A pointer to an array of ULONG_PTRs - addresses of + * system services. + */ + PULONG_PTR Base; + /* A pointer to an array of ULONGs which contain counters for + * the system services. + */ + PULONG Count; + /* The number of system services. */ + ULONG Limit; + /* A pointer to an array of UCHARs which contain + * the number of arguments (in bytes) for each system service. + */ + PUCHAR Number; +} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/kph.h b/branches/ph-plugins/KProcessHacker/include/kph.h new file mode 100644 index 000000000..91e0b3db4 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/kph.h @@ -0,0 +1,503 @@ +/* + * Process Hacker Driver - + * custom APIs + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KPH_H +#define _KPH_H + +#include "types.h" +#include "debug.h" +#include "ref.h" +#include "version.h" + +#include "ke.h" +#include "mm.h" +#include "ps.h" +#include "trace.h" +#include "zw.h" + +#define MAX_UINTEGER(Bits) ((1 << (Bits)) - 1) +#define BITS_UCHAR 8 +#define MAX_UCHAR MAX_UINTEGER(BITS_UCHAR) +#define BITS_USHORT 16 +#define MAX_USHORT MAX_UINTEGER(BITS_USHORT) +#define BITS_ULONG 32 +#define MAX_ULONG MAX_UINTEGER(BITS_ULONG) + +#define SYSTEM_PROCESS_ID ((HANDLE)4) +#define KERNEL_HANDLE_BIT ((ULONG_PTR)1 << (sizeof(HANDLE) * 8 - 1)) +#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0) +#define MakeKernelHandle(Handle) ((ULONG_PTR)(Handle) |= KERNEL_HANDLE_BIT) + +#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) + +#define GET_BIT(Integer, Bit) (((Integer) >> (Bit)) & 0x1) +#define SET_BIT(Integer, Bit) ((Integer) |= 1 << (Bit)) +#define CLEAR_BIT(Integer, Bit) ((Integer) &= ~(1 << (Bit))) + +#define KPH_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000) +#define KPH_REL_TIMEOUT_IN_SEC(Time) (Time * -1 * KPH_TIMEOUT_TO_SEC) + +#define TAG_CAPTURED_UNICODE_STRING ('UChP') + +#ifdef EXT +#undef EXT +#endif + +#ifdef _KPH_PRIVATE +#define EXT +#define EQNULL = NULL +#else +#define EXT extern +#define EQNULL +#endif + +EXT PKSERVICE_TABLE_DESCRIPTOR __KeServiceDescriptorTable EQNULL; +EXT PVOID __KiFastCallEntry EQNULL; +EXT _NtClose __NtClose EQNULL; +EXT _ObGetObjectType ObGetObjectType EQNULL; +EXT _PsGetProcessJob PsGetProcessJob EQNULL; +EXT _PsResumeProcess PsResumeProcess EQNULL; +EXT _PsSuspendProcess PsSuspendProcess EQNULL; +EXT _PsTerminateProcess __PsTerminateProcess EQNULL; +EXT PVOID __PspTerminateThreadByPointer EQNULL; +EXT _NtClose __ZwClose EQNULL; + +/* Driver information */ + +typedef enum _DRIVER_INFORMATION_CLASS +{ + DriverBasicInformation, + DriverNameInformation, + DriverServiceKeyNameInformation, + MaxDriverInfoClass +} DRIVER_INFORMATION_CLASS; + +typedef struct _DRIVER_BASIC_INFORMATION +{ + ULONG Flags; + PVOID DriverStart; + ULONG DriverSize; +} DRIVER_BASIC_INFORMATION, *PDRIVER_BASIC_INFORMATION; + +typedef struct _KPH_ATTACH_STATE +{ + BOOLEAN Attached; + PEPROCESS Process; + KAPC_STATE ApcState; +} KPH_ATTACH_STATE, *PKPH_ATTACH_STATE; + +typedef struct _MAPPED_MDL +{ + PMDL Mdl; + PVOID Address; +} MAPPED_MDL, *PMAPPED_MDL; + +typedef struct _PROCESS_HANDLE +{ + HANDLE Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; + ULONG HandleAttributes; +} PROCESS_HANDLE, *PPROCESS_HANDLE; + +typedef struct _PROCESS_HANDLE_INFORMATION +{ + ULONG HandleCount; + PROCESS_HANDLE Handles[1]; +} PROCESS_HANDLE_INFORMATION, *PPROCESS_HANDLE_INFORMATION; + +/* Support routines */ + +NTSTATUS KphNtInit(); + +PVOID GetSystemRoutineAddress( + WCHAR *Name + ); + +VOID KphAttachProcess( + __in PEPROCESS Process, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphAttachProcessHandle( + __in HANDLE ProcessHandle, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphAttachProcessId( + __in HANDLE ProcessId, + __out PKPH_ATTACH_STATE AttachState + ); + +NTSTATUS KphCaptureUnicodeString( + __in PUNICODE_STRING UnicodeString, + __out PUNICODE_STRING CapturedUnicodeString + ); + +VOID KphDetachProcess( + __in PKPH_ATTACH_STATE AttachState + ); + +VOID KphFreeCapturedUnicodeString( + __in PUNICODE_STRING CapturedUnicodeString + ); + +VOID KphProbeForReadUnicodeString( + __in PUNICODE_STRING UnicodeString + ); + +VOID KphProbeSystemAddressRange( + __in PVOID BaseAddress, + __in ULONG Length + ); + +NTSTATUS OpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in HANDLE ProcessId + ); + +NTSTATUS SetProcessToken( + __in HANDLE sourcePid, + __in HANDLE targetPid + ); + +/* KProcessHacker */ + +BOOLEAN KphAcquireProcessRundownProtection( + __in PEPROCESS Process + ); + +NTSTATUS KphAssignImpersonationToken( + __in HANDLE ThreadHandle, + __in HANDLE TokenHandle + ); + +NTSTATUS KphCaptureStackBackTraceThread( + __in HANDLE ThreadHandle, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphDangerousTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphDuplicateObject( + __in HANDLE SourceProcessHandle, + __in HANDLE SourceHandle, + __in_opt HANDLE TargetProcessHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ); + +BOOLEAN KphEnumProcessHandleTable( + __in PEPROCESS Process, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ); + +NTSTATUS KphGetContextThread( + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ); + +POBJECT_TYPE KphGetObjectTypeNt( + __in PVOID Object + ); + +HANDLE KphGetProcessId( + __in HANDLE ProcessHandle + ); + +HANDLE KphGetThreadId( + __in HANDLE ThreadHandle, + __out_opt PHANDLE ProcessId + ); + +NTSTATUS KphGetThreadWin32Thread( + __in HANDLE ThreadHandle, + __out PVOID *Win32Thread, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenDevice( + __out PHANDLE DeviceHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenDirectoryObject( + __out PHANDLE DirectoryObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenDriver( + __out PHANDLE DriverHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenNamedObject( + __out PHANDLE ObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcessJob( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE JobHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenProcessTokenEx( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG ObjectAttributes, + __out PHANDLE TokenHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenThread( + __out PHANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenThreadProcess( + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE ProcessHandle, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphQueryInformationDriver( + __in HANDLE DriverHandle, + __in DRIVER_INFORMATION_CLASS DriverInformationClass, + __out_bcount_opt(DriverInformationLength) PVOID DriverInformation, + __in ULONG DriverInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphQueryNameFileObject( + __in PFILE_OBJECT FileObject, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ); + +NTSTATUS KphQueryNameObject( + __in PVOID Object, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ); + +NTSTATUS KphQueryProcessHandles( + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +VOID KphReleaseProcessRundownProtection( + __in PEPROCESS Process + ); + +NTSTATUS KphResumeProcess( + __in HANDLE ProcessHandle + ); + +NTSTATUS KphSetContextThread( + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSetHandleGrantedAccess( + __in PEPROCESS Process, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ); + +NTSTATUS KphSuspendProcess( + __in HANDLE ProcessHandle + ); + +NTSTATUS KphTerminateProcess( + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ); + +NTSTATUS KphUnsafeReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphWriteVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +/* MM */ + +NTSTATUS MiDoMappedCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +NTSTATUS MiDoPoolCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +ULONG MiGetExceptionInfo( + __in PEXCEPTION_POINTERS ExceptionInfo, + __out PBOOLEAN HaveBadAddress, + __out PULONG_PTR BadAddress + ); + +NTSTATUS MmCopyVirtualMemory( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ); + +/* KProcessHacker private */ + +NTSTATUS KphpCaptureStackBackTraceThread( + __in PETHREAD Thread, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphpCreateMappedMdl( + __in PVOID Address, + __in ULONG Length, + __out PMAPPED_MDL MappedMdl + ); + +VOID KphpFreeMappedMdl( + __in PMAPPED_MDL MappedMdl + ); + +/* OB */ + +NTSTATUS ObDuplicateObject( + __in PEPROCESS SourceProcess, + __in_opt PEPROCESS TargetProcess, + __in HANDLE SourceHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ); + +PHANDLE_TABLE ObReferenceProcessHandleTable( + __in PEPROCESS Process + ); + +VOID ObDereferenceProcessHandleTable( + __in PEPROCESS Process + ); + +/* PS */ + +NTSTATUS PsTerminateProcess( + __in PEPROCESS Process, + __in NTSTATUS ExitStatus + ); + +NTSTATUS PspTerminateThreadByPointer( + __in PETHREAD Thread, + __in NTSTATUS ExitStatus + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h b/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h new file mode 100644 index 000000000..4b45f01dc --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h @@ -0,0 +1,165 @@ +/* + * Process Hacker Driver - + * main header file + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef KPROCESSHACKER_H +#define KPROCESSHACKER_H + +#include "include/kph.h" +#include "include/handle.h" +#include "include/ref.h" +#include "include/sync.h" + +/* KPH Configuration */ + +//#define KPH_REQUIRE_DEBUG_PRIVILEGE + +/* Device */ + +#define KPH_DEVICE_TYPE (0x9999) +#define KPH_DEVICE_NAME (L"\\Device\\KProcessHacker") +#define KPH_DEVICE_DOS_NAME (L"\\DosDevices\\KProcessHacker") + +/* Features */ + +#define KPHF_PSTERMINATEPROCESS 0x1 +#define KPHF_PSPTERMINATETHREADBPYPOINTER 0x2 + +/* Control Codes */ + +#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define KPH_CLOSEHANDLE KPH_CTL_CODE(0) +#define KPH_SSQUERYCLIENTENTRY KPH_CTL_CODE(1) +#define KPH_RESERVED1 KPH_CTL_CODE(2) +#define KPH_OPENPROCESS KPH_CTL_CODE(3) +#define KPH_OPENTHREAD KPH_CTL_CODE(4) +#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(5) +#define KPH_GETPROCESSPROTECTED KPH_CTL_CODE(6) +#define KPH_SETPROCESSPROTECTED KPH_CTL_CODE(7) +#define KPH_TERMINATEPROCESS KPH_CTL_CODE(8) +#define KPH_SUSPENDPROCESS KPH_CTL_CODE(9) +#define KPH_RESUMEPROCESS KPH_CTL_CODE(10) +#define KPH_READVIRTUALMEMORY KPH_CTL_CODE(11) +#define KPH_WRITEVIRTUALMEMORY KPH_CTL_CODE(12) +#define KPH_SETPROCESSTOKEN KPH_CTL_CODE(13) +#define KPH_GETTHREADSTARTADDRESS KPH_CTL_CODE(14) +#define KPH_SETHANDLEATTRIBUTES KPH_CTL_CODE(15) +#define KPH_GETHANDLEOBJECTNAME KPH_CTL_CODE(16) +#define KPH_OPENPROCESSJOB KPH_CTL_CODE(17) +#define KPH_GETCONTEXTTHREAD KPH_CTL_CODE(18) +#define KPH_SETCONTEXTTHREAD KPH_CTL_CODE(19) +#define KPH_GETTHREADWIN32THREAD KPH_CTL_CODE(20) +#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(21) +#define KPH_ZWQUERYOBJECT KPH_CTL_CODE(22) +#define KPH_GETPROCESSID KPH_CTL_CODE(23) +#define KPH_GETTHREADID KPH_CTL_CODE(24) +#define KPH_TERMINATETHREAD KPH_CTL_CODE(25) +#define KPH_GETFEATURES KPH_CTL_CODE(26) +#define KPH_SETHANDLEGRANTEDACCESS KPH_CTL_CODE(27) +#define KPH_ASSIGNIMPERSONATIONTOKEN KPH_CTL_CODE(28) +#define KPH_PROTECTADD KPH_CTL_CODE(29) +#define KPH_PROTECTREMOVE KPH_CTL_CODE(30) +#define KPH_PROTECTQUERY KPH_CTL_CODE(31) +#define KPH_UNSAFEREADVIRTUALMEMORY KPH_CTL_CODE(32) +#define KPH_SETEXECUTEOPTIONS KPH_CTL_CODE(33) +#define KPH_QUERYPROCESSHANDLES KPH_CTL_CODE(34) +#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(35) +#define KPH_CAPTURESTACKBACKTRACETHREAD KPH_CTL_CODE(36) +#define KPH_DANGEROUSTERMINATETHREAD KPH_CTL_CODE(37) +#define KPH_OPENDEVICE KPH_CTL_CODE(38) +#define KPH_OPENDRIVER KPH_CTL_CODE(39) +#define KPH_QUERYINFORMATIONDRIVER KPH_CTL_CODE(40) +#define KPH_OPENDIRECTORYOBJECT KPH_CTL_CODE(41) +#define KPH_SSREF KPH_CTL_CODE(42) +#define KPH_SSUNREF KPH_CTL_CODE(43) +#define KPH_SSCREATECLIENTENTRY KPH_CTL_CODE(44) +#define KPH_SSCREATERULESETENTRY KPH_CTL_CODE(45) +#define KPH_SSREMOVERULE KPH_CTL_CODE(46) +#define KPH_SSADDPROCESSIDRULE KPH_CTL_CODE(47) +#define KPH_SSADDTHREADIDRULE KPH_CTL_CODE(48) +#define KPH_SSADDPREVIOUSMODERULE KPH_CTL_CODE(49) +#define KPH_SSADDNUMBERRULE KPH_CTL_CODE(50) +#define KPH_SSENABLECLIENTENTRY KPH_CTL_CODE(51) + +/* Standard Driver Routines */ + +NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); +VOID DriverUnload(PDRIVER_OBJECT DriverObject); +NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp); + +/* Clients */ + +#define TAG_CLIENT_HANDLETABLE ('HChP') +#define KPH_CLIENT_SSMAXCOUNT 1000 +#define KPH_CLIENT_MAXHANDLES 100 + +typedef struct _KPH_CLIENT_ENTRY +{ + LIST_ENTRY ClientListEntry; + HANDLE ProcessId; + PKPH_HANDLE_TABLE HandleTable; + + KPH_GUARDED_LOCK SsLock; + /* The number of times the client has "started" the system service logger. */ + LONG SsStartCount; +} KPH_CLIENT_ENTRY, *PKPH_CLIENT_ENTRY; + +/* Functions */ + +VOID SsRef(LONG count); +VOID SsUnref(LONG count); + +VOID NTAPI ClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +PKPH_CLIENT_ENTRY CreateClientEntry( + __in HANDLE ProcessId + ); + +PKPH_CLIENT_ENTRY ReferenceClientEntry( + __in_opt HANDLE ProcessId + ); + +NTSTATUS CloseClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle + ); + +NTSTATUS CreateClientHandle( + __in_opt HANDLE ProcessId, + __in PVOID Object, + __out PHANDLE Handle + ); + +NTSTATUS ReferenceClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle, + __in PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/mm.h b/branches/ph-plugins/KProcessHacker/include/mm.h new file mode 100644 index 000000000..07206dc0a --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/mm.h @@ -0,0 +1,37 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _MM_H +#define _MM_H + +#define MI_MAX_TRANSFER_SIZE (0x10000) +#define MI_COPY_STACK_SIZE (0x200) +#define MI_MAPPED_COPY_PAGES (14) +#define MM_POOL_COPY_THRESHOLD (0x1ff) +#define TAG_POOL_COPY ('CPhP') + +#define MEM_EXECUTE_OPTION_DISABLE 0x1 +#define MEM_EXECUTE_OPTION_ENABLE 0x2 +#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4 +#define MEM_EXECUTE_OPTION_PERMANENT 0x8 + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/ob.h b/branches/ph-plugins/KProcessHacker/include/ob.h new file mode 100644 index 000000000..783629c12 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/ob.h @@ -0,0 +1,168 @@ +/* + * Process Hacker Driver - + * object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _OB_H +#define _OB_H + +#include "types.h" +#include "ex.h" + +#define OBJECT_TO_OBJECT_HEADER(o) \ + CONTAINING_RECORD((o), OBJECT_HEADER, Body) + +#define OBJ_PROTECT_CLOSE 0x00000001L +#define OBJ_INHERIT 0x00000002L +#define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L +#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE) + +#define ObpDecodeGrantedAccess(Access) \ + ((Access) & ~ObpAccessProtectCloseBit) +#define ObpDecodeObject(Object) \ + ((PVOID)((ULONG_PTR)(Object) & ~OBJ_HANDLE_ATTRIBUTES)) +#define ObpGetHandleAttributes(HandleTableEntry) \ + (((HandleTableEntry)->GrantedAccess & ObpAccessProtectCloseBit) ? \ + (((HandleTableEntry)->Value & OBJ_HANDLE_ATTRIBUTES) | OBJ_PROTECT_CLOSE) : \ + ((HandleTableEntry)->Value & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE))) + +/* FUNCTION DEFS */ + +struct _OBJECT_HANDLE_FLAG_INFORMATION; +typedef struct _OBJECT_TYPE_INITIALIZER OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; + +NTSTATUS NTAPI ObCreateObjectType( + __in PUNICODE_STRING TypeName, + __in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer, + __in PSECURITY_DESCRIPTOR SecurityDescriptor, + __out_opt POBJECT_TYPE *ObjectType + ); + +NTSTATUS NTAPI ObOpenObjectByName( + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE PreviousMode, + __in_opt PACCESS_STATE AccessState, + __in_opt ACCESS_MASK DesiredAccess, + __in PVOID ParseContext, + __out PHANDLE Handle + ); + +NTSTATUS NTAPI ObSetHandleAttributes( + __in HANDLE Handle, + __in struct _OBJECT_HANDLE_FLAG_INFORMATION *HandleFlags, + __in KPROCESSOR_MODE PreviousMode + ); + +/* FUNCTION TYPEDEFS */ + +/* Seven+ */ +typedef POBJECT_TYPE (NTAPI *_ObGetObjectType)( + __in PVOID Object + ); + +enum _OB_OPEN_REASON; + +typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_51)( + enum _OB_OPEN_REASON OpenReason, + PEPROCESS Process, + PVOID Object, + ACCESS_MASK GrantedAccess, + ULONG HandleCount + ); + +typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_60)( + enum _OB_OPEN_REASON OpenReason, + KPROCESSOR_MODE AccessMode, + PEPROCESS Process, + PVOID Object, + ACCESS_MASK GrantedAccess, + ULONG HandleCount + ); + +/* ENUMS */ +typedef enum _OB_OPEN_REASON +{ + ObCreateHandle, + ObOpenHandle, + ObDuplicateHandle, + ObInheritHandle, + ObMaxOpenReason +} OB_OPEN_REASON, *POB_OPEN_REASON; + +/* STRUCTS */ + +typedef struct _OBP_QUERY_PROCESS_HANDLES_DATA +{ + PVOID Buffer; + ULONG BufferLength; + ULONG CurrentIndex; + NTSTATUS Status; +} OBP_QUERY_PROCESS_HANDLES_DATA, *POBP_QUERY_PROCESS_HANDLES_DATA; + +typedef struct _OBP_SET_HANDLE_GRANTED_ACCESS_DATA +{ + HANDLE Handle; + ACCESS_MASK GrantedAccess; +} OBP_SET_HANDLE_GRANTED_ACCESS_DATA, *POBP_SET_HANDLE_GRANTED_ACCESS_DATA; + +typedef struct _OBJECT_HANDLE_FLAG_INFORMATION +{ + BOOLEAN Inherit; + BOOLEAN ProtectFromClose; +} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; + +typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +typedef struct _OBJECT_HEADER +{ + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + POBJECT_TYPE Type; + UCHAR NameInfoOffset; + UCHAR HandleInfoOffset; + UCHAR QuotaInfoOffset; + UCHAR Flags; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +typedef struct _HANDLE_TABLE_ENTRY +{ + union + { + PVOID Object; + ULONG Value; + }; + ULONG GrantedAccess; +} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; + +typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE; + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/protect.h b/branches/ph-plugins/KProcessHacker/include/protect.h new file mode 100644 index 000000000..b714ab1b0 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/protect.h @@ -0,0 +1,95 @@ +/* + * Process Hacker Driver - + * process protection + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PROTECT_H +#define _PROTECT_H + +#include "hook.h" + +#define TAG_PROTECTION_ENTRY ('rPhP') + +#define OBOPENOBJECTBYPOINTER_ARGS \ + PVOID Object, \ + ULONG HandleAttributes, \ + PACCESS_STATE PassedAccessState, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_TYPE ObjectType, \ + KPROCESSOR_MODE AccessMode, \ + PHANDLE Handle + +typedef struct _KPH_PROCESS_ENTRY +{ + LIST_ENTRY ListEntry; + PEPROCESS Process; + PEPROCESS CreatorProcess; + HANDLE Tag; + LOGICAL AllowKernelMode; + ACCESS_MASK ProcessAllowMask; + ACCESS_MASK ThreadAllowMask; +} KPH_PROCESS_ENTRY, *PKPH_PROCESS_ENTRY; + +NTSTATUS NTAPI KphNewObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS); +NTSTATUS NTAPI KphOldObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS); + +NTSTATUS NTAPI KphNewOpenProcedure51( + __in OB_OPEN_REASON OpenReason, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +NTSTATUS NTAPI KphNewOpenProcedure60( + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ); + +NTSTATUS KphProtectInit(); +NTSTATUS KphProtectDeinit(); + +PKPH_PROCESS_ENTRY KphProtectAddEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __in LOGICAL AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ); + +PKPH_PROCESS_ENTRY KphProtectFindEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy + ); + +BOOLEAN KphProtectRemoveByProcess( + __in PEPROCESS Process + ); + +ULONG KphProtectRemoveByTag( + __in HANDLE Tag + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/ps.h b/branches/ph-plugins/KProcessHacker/include/ps.h new file mode 100644 index 000000000..df68bb16e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/ps.h @@ -0,0 +1,151 @@ +/* + * Process Hacker Driver - + * processes and threads + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PS_H +#define _PS_H + +#include "types.h" +#include "ex.h" +#include "mm.h" +#include "ob.h" +#include "se.h" + +#define TAG_CAPTURE_STACK_BACKTRACE ('tShP') + +#define PROCESS_TERMINATE (0x0001) +#define PROCESS_CREATE_THREAD (0x0002) +#define PROCESS_SET_SESSIONID (0x0004) +#define PROCESS_VM_OPERATION (0x0008) +#define PROCESS_VM_READ (0x0010) +#define PROCESS_VM_WRITE (0x0020) +#define PROCESS_DUP_HANDLE (0x0040) +#define PROCESS_CREATE_PROCESS (0x0080) +#define PROCESS_SET_QUOTA (0x0100) +#define PROCESS_SET_INFORMATION (0x0200) +#define PROCESS_QUERY_INFORMATION (0x0400) +#define PROCESS_SUSPEND_RESUME (0x0800) +#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) +#ifndef PROCESS_ALL_ACCESS +#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff) +#endif + +#define THREAD_TERMINATE (0x0001) +#define THREAD_SUSPEND_RESUME (0x0002) +#define THREAD_ALERT (0x0004) +#define THREAD_GET_CONTEXT (0x0008) +#define THREAD_SET_CONTEXT (0x0010) +#define THREAD_SET_INFORMATION (0x0020) +#define THREAD_QUERY_INFORMATION (0x0040) +#define THREAD_SET_THREAD_TOKEN (0x0080) +#define THREAD_IMPERSONATE (0x0100) +#define THREAD_DIRECT_IMPERSONATION (0x0200) +#ifndef THREAD_ALL_ACCESS +#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff) +#endif + +#define JOB_OBJECT_ASSIGN_PROCESS (0x0001) +#define JOB_OBJECT_SET_ATTRIBUTES (0x0002) +#define JOB_OBJECT_QUERY (0x0004) +#define JOB_OBJECT_TERMINATE (0x0008) +#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010) +#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1f) + +extern POBJECT_TYPE *PsJobType; + +typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT +{ + BOOLEAN Local; + KAPC Apc; + KEVENT CompletedEvent; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + ULONG CapturedFrames; + ULONG BackTraceHash; +} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT; + +typedef struct _EXIT_THREAD_CONTEXT +{ + KAPC Apc; + KEVENT CompletedEvent; + NTSTATUS ExitStatus; +} EXIT_THREAD_CONTEXT, *PEXIT_THREAD_CONTEXT; + +/* FUNCTION DEFS */ + +NTSTATUS NTAPI PsGetContextThread( + __in PETHREAD Thread, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE PreviousMode + ); + +BOOLEAN NTAPI PsGetProcessExitProcessCalled( + __in PEPROCESS Process + ); + +PVOID NTAPI PsGetThreadWin32Thread( + __in PETHREAD Thread + ); + +NTSTATUS NTAPI PsLookupProcessThreadByCid( + __in PCLIENT_ID ClientId, + __out_opt PEPROCESS *Process, + __out PETHREAD *Thread + ); + +NTSTATUS NTAPI PsSetContextThread( + __in PETHREAD Thread, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE PreviousMode + ); + +/* FUNCTION TYPEDEFS */ + +typedef PVOID (NTAPI *_PsGetProcessJob)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsResumeProcess)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsSuspendProcess)( + PEPROCESS Process + ); + +typedef NTSTATUS (NTAPI *_PsTerminateProcess)( + PEPROCESS Process, + NTSTATUS ExitStatus + ); + +typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer51)( + PETHREAD Thread, + NTSTATUS ExitStatus + ); + +typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer60)( + PETHREAD Thread, + NTSTATUS ExitStatus, + BOOLEAN DirectTerminate + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/ref.h b/branches/ph-plugins/KProcessHacker/include/ref.h new file mode 100644 index 000000000..03f9fdb9e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/ref.h @@ -0,0 +1,113 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _REF_H +#define _REF_H + +#include "kph.h" + +/* Object flags */ +#define KPHOBJ_RAISE_ON_FAIL 0x00000001 +#define KPHOBJ_PAGED_POOL 0x00000002 +#define KPHOBJ_NONPAGED_POOL 0x00000004 +#define KPHOBJ_VALID_FLAGS 0x00000007 + +/* Object type flags */ +#define KPHOBJTYPE_PASSIVE_LEVEL_DELETE 0x00000001 +#define KPHOBJTYPE_VALID_FLAGS 0x00000001 + +/* Object type callbacks */ + +/* PKPH_TYPE_DELETE_PROCEDURE + * + * The delete procedure for an object type, called when + * an object of the type is being freed. + * + * Object: A pointer to the object being freed. + * Flags: The flags specified when the object was created. + * + * IRQL: = PASSIVE_LEVEL if the require passive level flag was + * specified for the object type, otherwise <= APC_LEVEL. + */ +typedef VOID (NTAPI *PKPH_TYPE_DELETE_PROCEDURE)( + __in PVOID Object, + __in ULONG Flags + ); + +struct _KPH_OBJECT_TYPE; +typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE; + +#ifndef _REF_PRIVATE +extern PKPH_OBJECT_TYPE KphObjectTypeObject; +#endif + +NTSTATUS KphRefInit(); + +NTSTATUS KphRefDeinit(); + +NTSTATUS KphCreateObject( + __out PVOID *Object, + __in SIZE_T ObjectSize, + __in ULONG Flags, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __in_opt LONG AdditionalReferences + ); + +NTSTATUS KphCreateObjectType( + __out PKPH_OBJECT_TYPE *ObjectType, + __in POOL_TYPE DefaultPoolType, + __in ULONG Flags, + __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ); + +BOOLEAN KphDereferenceObject( + __in PVOID Object + ); + +BOOLEAN KphDereferenceObjectDeferDelete( + __in PVOID Object + ); + +LONG KphDereferenceObjectEx( + __in PVOID Object, + __in LONG RefCount, + __in BOOLEAN DeferDelete + ); + +PKPH_OBJECT_TYPE KphGetObjectType( + __in PVOID Object + ); + +VOID KphReferenceObject( + __in PVOID Object + ); + +LONG KphReferenceObjectEx( + __in PVOID Object, + __in LONG RefCount + ); + +BOOLEAN KphReferenceObjectSafe( + __in PVOID Object + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/refp.h b/branches/ph-plugins/KProcessHacker/include/refp.h new file mode 100644 index 000000000..92456a857 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/refp.h @@ -0,0 +1,137 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _REFP_H +#define _REFP_H + +#define _REF_PRIVATE +#include "ref.h" +#include "sync.h" + +#define TAG_KPHOBJ ('bOhP') + +#define KphObjectToObjectHeader(Object) ((PKPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)(Object), KPH_OBJECT_HEADER, Body)) +#define KphObjectHeaderToObject(ObjectHeader) (&((PKPH_OBJECT_HEADER)(ObjectHeader))->Body) +#define KphpAddObjectHeaderSize(Size) ((Size) + sizeof(KPH_OBJECT_HEADER) - sizeof(QUAD)) + +typedef struct _KPH_OBJECT_HEADER *PKPH_OBJECT_HEADER; +typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE; + +typedef struct _KPH_OBJECT_HEADER +{ + /* The reference count of the object. */ + LONG RefCount; + /* The flags that were used to create the object. */ + ULONG Flags; + union + { + /* The size of the object, excluding the header. */ + SIZE_T Size; + /* A pointer to the object header of the next object to free. */ + PKPH_OBJECT_HEADER NextToFree; + }; + /* The type of the object. */ + PKPH_OBJECT_TYPE Type; + /* A linked list entry for an optional object manager object list. + * For example, this may be used to free all objects when the + * driver exits. + */ + LIST_ENTRY GlobalObjectListEntry; + + /* The body of the object. For use by the KphObject(Header)ToObject(Header) macros. */ + QUAD Body; +} KPH_OBJECT_HEADER, *PKPH_OBJECT_HEADER; + +typedef struct _KPH_OBJECT_TYPE +{ + /* The default pool type for objects of this type, used when the + * pool type is not specified when an object is created. */ + POOL_TYPE DefaultPoolType; + /* The flags that were used to create the object type. */ + ULONG Flags; + /* An optional procedure called when objects of this type are freed. */ + PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure; + + /* The total number of objects of this type that are alive. */ + ULONG NumberOfObjects; +} KPH_OBJECT_TYPE, *PKPH_OBJECT_TYPE; + +/* KphpInterlockedIncrementSafe + * + * Increments a reference count, but will never increment + * from 0 to 1. + */ +FORCEINLINE BOOLEAN KphpInterlockedIncrementSafe( + __inout PLONG RefCount + ) +{ + LONG refCount; + + /* Here we will attempt to increment the reference count, + * making sure that it is not 0. + */ + + while (TRUE) + { + refCount = *RefCount; + + /* Check if the reference count is 0. If it is, the + * object is being or about to be deleted. + */ + if (refCount == 0) + return FALSE; + + /* Try to increment the reference count. */ + if (InterlockedCompareExchange( + RefCount, + refCount + 1, + refCount + ) == refCount) + { + /* Success. */ + return TRUE; + } + + /* Someone else changed the reference count before we did. + * Go back and try again. + */ + } +} + +PKPH_OBJECT_HEADER KphpAllocateObject( + __in SIZE_T ObjectSize, + __in POOL_TYPE PoolType + ); + +VOID KphpDeferDeleteObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ); + +VOID KphpDeferDeleteObjectRoutine( + __in PVOID Parameter + ); + +VOID KphpFreeObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/se.h b/branches/ph-plugins/KProcessHacker/include/se.h new file mode 100644 index 000000000..947d59f5f --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/se.h @@ -0,0 +1,55 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SE_H +#define _SE_H + +#include "types.h" + +extern POBJECT_TYPE *SeTokenObjectType; + +/* Was 0x38 on Vista, appears to be 0xc8 on 7. */ +#define AUX_ACCESS_DATA_SIZE (0xc8) + +typedef PVOID PAUX_ACCESS_DATA; + +/* FUNCTION DEFS */ + +NTKERNELAPI NTSTATUS NTAPI SeCreateAccessState( + PACCESS_STATE AccessState, + PAUX_ACCESS_DATA AuxData, + ACCESS_MASK DesiredAccess, + PGENERIC_MAPPING Mapping + ); + +NTKERNELAPI VOID NTAPI SeDeleteAccessState( + PACCESS_STATE AccessState + ); + +/* STRUCTS */ + +typedef struct _SE_AUDIT_PROCESS_CREATION_INFO +{ + POBJECT_NAME_INFORMATION ImageFileName; +} SE_AUDIT_PROCESS_CREATION_INFO, *PSE_AUDIT_PROCESS_CREATION_INFO; + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/sync.h b/branches/ph-plugins/KProcessHacker/include/sync.h new file mode 100644 index 000000000..e344ea75e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/sync.h @@ -0,0 +1,320 @@ +/* + * Process Hacker Driver - + * synchronization code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYNC_H +#define _SYNC_H + +#include "kph.h" +#include "ex.h" + +/* General synchronization macros */ + +/* KphEqualSpin + * + * Spins until the first value is equal to the second + * value. + */ +FORCEINLINE VOID KphSpinUntilEqual( + __inout PLONG Value, + __in LONG Value2 + ) +{ + while (InterlockedCompareExchange( + Value, + Value2, + Value2 + ) != Value2) + YieldProcessor(); +} + +/* KphNotEqualSpin + * + * Spins until the first value is not equal to the second + * value. + */ +FORCEINLINE VOID KphSpinUntilNotEqual( + __inout PLONG Value, + __in LONG Value2 + ) +{ + while (InterlockedCompareExchange( + Value, + Value2, + Value2 + ) == Value2) + YieldProcessor(); +} + +/* Spin Locks */ + +/* KphAcquireBitSpinLock + * + * Uses the specified bit as a spinlock and acquires the + * lock in the given value. + */ +FORCEINLINE VOID KphAcquireBitSpinLock( + __inout PLONG Value, + __in LONG Bit + ) +{ + while (InterlockedBitTestAndSet(Value, Bit)) + YieldProcessor(); +} + +/* KphReleaseBitSpinLock + * + * Uses the specified bit as a spinlock and releases the + * lock in the given value. + */ +FORCEINLINE VOID KphReleaseBitSpinLock( + __inout PLONG Value, + __in LONG Bit + ) +{ + InterlockedBitTestAndReset(Value, Bit); +} + +/* Guarded Locks */ +/* Guarded locks are small spinlocks. Code within + * synchronized regions run at APC_LEVEL. They also contain + * a signal which can used to implement rundown routines. + */ + +#define KPH_GUARDED_LOCK_ACTIVE 0x80000000 +#define KPH_GUARDED_LOCK_ACTIVE_SHIFT 31 +#define KPH_GUARDED_LOCK_SIGNALED 0x40000000 +#define KPH_GUARDED_LOCK_SIGNALED_SHIFT 30 +#define KPH_GUARDED_LOCK_FLAGS 0xc0000000 + +typedef struct _KPH_GUARDED_LOCK +{ + LONG Value; +} KPH_GUARDED_LOCK, *PKPH_GUARDED_LOCK; + +#define KphAcquireGuardedLock KphfAcquireGuardedLock +VOID FASTCALL KphfAcquireGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ); + +#define KphReleaseGuardedLock KphfReleaseGuardedLock +VOID FASTCALL KphfReleaseGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ); + +/* KphInitializeGuardedLock + * + * Initializes a guarded lock. + * + * IRQL: Any + */ +FORCEINLINE VOID KphInitializeGuardedLock( + __out PKPH_GUARDED_LOCK Lock, + __in BOOLEAN Signaled + ) +{ + Lock->Value = 0; + + if (Signaled) + Lock->Value |= KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphClearGuardedLock + * + * Clears the signal state of a guarded lock, assuming + * that the current thread has acquired it. + * + * IRQL: Any + */ +FORCEINLINE VOID KphClearGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + Lock->Value &= ~KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphSignalGuardedLock + * + * Signals a guarded lock. + * + * IRQL: Any + */ +FORCEINLINE VOID KphSignalGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + Lock->Value |= KPH_GUARDED_LOCK_SIGNALED; +} + +/* KphSignaledGuardedLock + * + * Determines whether a guarded lock is signaled. + * + * IRQL: Any + */ +FORCEINLINE BOOLEAN KphSignaledGuardedLock( + __in PKPH_GUARDED_LOCK Lock + ) +{ + return !!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED); +} + +/* KphAcquireAndClearGuardedLock + * + * Acquires a guarded lock, clear its signal, and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +FORCEINLINE VOID KphAcquireAndClearGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + KphClearGuardedLock(Lock); +} + +/* KphAcquireAndSignalGuardedLock + * + * Acquires a guarded lock, signals it, and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +FORCEINLINE VOID KphAcquireAndSignalGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + KphSignalGuardedLock(Lock); +} + +/* KphAcquireNonSignaledGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL, + * making sure the lock is not signaled. If it is, the + * lock is not acquired. + * + * Return value: whether the lock was acquired. + * IRQL: <= APC_LEVEL + */ +FORCEINLINE BOOLEAN KphAcquireNonSignaledGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + + if (Lock->Value & KPH_GUARDED_LOCK_SIGNALED) + { + KphReleaseGuardedLock(Lock); + return FALSE; + } + + return TRUE; +} + +/* KphAcquireSignaledGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL, + * making sure the lock is signaled. If it is not, the + * lock is not acquired. + * + * Return value: whether the lock was acquired. + * IRQL: <= APC_LEVEL + */ +FORCEINLINE BOOLEAN KphAcquireSignaledGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphAcquireGuardedLock(Lock); + + if (!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED)) + { + KphReleaseGuardedLock(Lock); + return FALSE; + } + + return TRUE; +} + +/* KphReleaseAndClearGuardedLock + * + * Releases a guarded lock, clears its signal, and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +FORCEINLINE VOID KphReleaseAndClearGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphClearGuardedLock(Lock); + KphReleaseGuardedLock(Lock); +} + +/* KphReleaseAndSignalGuardedLock + * + * Releases a guarded lock, signals it, and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +FORCEINLINE VOID KphReleaseAndSignalGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KphSignalGuardedLock(Lock); + KphReleaseGuardedLock(Lock); +} + +/* Processor Locks */ +/* Processor locks prevent code from executing on all other + * processors. Code within synchronized regions run at + * DISPATCH_LEVEL. + */ + +#define TAG_SYNC_DPC ('DShP') + +typedef struct _KPH_PROCESSOR_LOCK +{ + /* Synchronizes access to the processor lock. */ + KPH_GUARDED_LOCK Lock; + /* Storage allocated for DPCs. */ + PKDPC Dpcs; + /* The number of currently acquired processors. */ + LONG AcquiredProcessors; + /* The signal for acquired processors to be released. */ + LONG ReleaseSignal; + /* The old IRQL. */ + KIRQL OldIrql; + /* Whether the processor lock has been acquired. */ + BOOLEAN Acquired; +} KPH_PROCESSOR_LOCK, *PKPH_PROCESSOR_LOCK; + +BOOLEAN KphAcquireProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ); + +VOID KphInitializeProcessorLock( + __out PKPH_PROCESSOR_LOCK ProcessorLock + ); + +VOID KphReleaseProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/sysservice.h b/branches/ph-plugins/KProcessHacker/include/sysservice.h new file mode 100644 index 000000000..89e7b8dc7 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/sysservice.h @@ -0,0 +1,278 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICE_H +#define _SYSSERVICE_H + +#include "kph.h" +#include "sysservicedata.h" + +/* Define opaque object types */ + +struct _KPHSS_CLIENT_ENTRY; +typedef struct _KPHSS_CLIENT_ENTRY *PKPHSS_CLIENT_ENTRY; +struct _KPHSS_RULESET_ENTRY; +typedef struct _KPHSS_RULESET_ENTRY *PKPHSS_RULESET_ENTRY; +struct _KPHSS_RULE_ENTRY; +typedef struct _KPHSS_RULE_ENTRY *PKPHSS_RULE_ENTRY; + +/* Information types */ + +typedef struct _KPHSS_CLIENT_INFORMATION +{ + HANDLE ProcessId; + PVOID BufferBase; + ULONG BufferSize; + + ULONG NumberOfBlocksWritten; + ULONG NumberOfBlocksDropped; +} KPHSS_CLIENT_INFORMATION, *PKPHSS_CLIENT_INFORMATION; + +/* Object types */ + +#ifndef _SYSSERVICE_PRIVATE +extern PKPH_OBJECT_TYPE KphSsClientEntryType; +extern PKPH_OBJECT_TYPE KphSsRuleSetEntryType; +extern PKPH_OBJECT_TYPE KphSsRuleEntryType; +#endif + +/* Ruleset types */ + +typedef enum _KPHSS_RULESET_ACTION +{ + LogRuleSetAction, + MaxRuleSetAction +} KPHSS_RULESET_ACTION; + +/* Rule types */ + +typedef enum _KPHSS_FILTER_TYPE +{ + IncludeFilterType, + ExcludeFilterType, + MaxFilterType +} KPHSS_FILTER_TYPE; + +typedef enum _KPHSS_RULE_TYPE +{ + ProcessIdRuleType = 0, + ThreadIdRuleType, + PreviousModeRuleType, + NumberRuleType, + MaxRuleType +} KPHSS_RULE_TYPE; + +/* Block types */ + +#define KPHSS_BLOCK_SUCCESS(Status) (NT_SUCCESS(Status) && (Status) != STATUS_TIMEOUT) + +typedef enum _KPHSS_BLOCK_TYPE +{ + ResetBlockType, + EventBlockType, + ArgumentBlockType, + ProcessBlockType, + ModuleBlockType +} KPHSS_BLOCK_TYPE; + +typedef struct _KPHSS_BLOCK_HEADER +{ + USHORT Size; /* a.k.a. NextEntryOffset */ + USHORT Type; +} KPHSS_BLOCK_HEADER, *PKPHSS_BLOCK_HEADER; + +typedef struct _KPHSS_RESET_BLOCK +{ + KPHSS_BLOCK_HEADER Header; +} KPHSS_RESET_BLOCK, *PKPHSS_RESET_BLOCK; + +#define TAG_EVENT_BLOCK ('BEhP') + +#define KPHSS_EVENT_PROBE_ARGUMENTS_FAILED 0x00000001 +#define KPHSS_EVENT_COPY_ARGUMENTS_FAILED 0x00000002 +#define KPHSS_EVENT_KERNEL_MODE 0x00000004 +#define KPHSS_EVENT_USER_MODE 0x00000008 + +typedef struct _KPHSS_EVENT_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + USHORT Flags; + LARGE_INTEGER Time; + CLIENT_ID ClientId; + + /* The system service number. */ + ULONG Number; + /* The number of ULONG arguments to the system service. */ + USHORT NumberOfArguments; + USHORT ArgumentsOffset; /* ULONG[] */ + + /* The number of PVOIDs in the trace. */ + USHORT TraceCount; + USHORT TraceOffset; /* PVOID[] */ +} KPHSS_EVENT_BLOCK, *PKPHSS_EVENT_BLOCK; + +/* Argument Blocks + * + * These blocks provide additional information about + * arguments. + */ + +#define TAG_ARGUMENT_BLOCK ('BAhP') + +#define KPHSS_ARGUMENT_BLOCK_OVERHEAD \ + FIELD_OFFSET(KPHSS_ARGUMENT_BLOCK, Normal) +#define KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize) \ + (KPHSS_ARGUMENT_BLOCK_OVERHEAD + (InnerSize)) + +typedef struct _KPHSS_ARGUMENT_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + UCHAR Index; + UCHAR Type; /* KPHSS_ARGUMENT_TYPE */ + + union + { + ULONG Normal; + + LARGE_INTEGER Simple; + KPHSS_HANDLE Handle; + KPHSS_STRING String; + KPHSS_WSTRING WString; + KPHSS_ANSI_STRING AnsiString; + KPHSS_UNICODE_STRING UnicodeString; + KPHSS_OBJECT_ATTRIBUTES ObjectAttributes; + CLIENT_ID ClientId; + CONTEXT Context; + KPHSS_INITIAL_TEB InitialTeb; + GUID Guid; + }; +} KPHSS_ARGUMENT_BLOCK, *PKPHSS_ARGUMENT_BLOCK; + +/* Process Blocks + * + * These blocks notify the client of a new process. + */ + +#define TAG_PROCESS_BLOCK ('BPhP') + +typedef struct _KPHSS_PROCESS_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + + HANDLE ProcessId; + USHORT NameOffset; /* KPHSS_WSTRING */ + USHORT ImageFileNameOffset; /* KPHSS_WSTRING */ +} KPHSS_PROCESS_BLOCK, *PKPHSS_PROCESS_BLOCK; + +/* Module Blocks + * + * These blocks provide information about modules + * loaded by a process. + */ + +#define TAG_MODULE_BLOCK ('BMhP') + +typedef struct _KPHSS_MODULE_BLOCK +{ + KPHSS_BLOCK_HEADER Header; + + HANDLE ProcessId; + PVOID ModuleBase; + ULONG ModuleSize; + USHORT FileNameOffset; /* KPHSS_WSTRING */ +} KPHSS_MODULE_BLOCK, *PKPHSS_MODULE_BLOCK; + +/* Functions */ + +NTSTATUS KphSsLogInit(); +NTSTATUS KphSsLogDeinit(); +NTSTATUS KphSsLogStart(); +NTSTATUS KphSsLogStop(); + +NTSTATUS KphSsCreateClientEntry( + __out PKPHSS_CLIENT_ENTRY *ClientEntry, + __in HANDLE ProcessHandle, + __in HANDLE ReadSemaphoreHandle, + __in HANDLE WriteSemaphoreHandle, + __in PVOID BufferBase, + __in ULONG BufferSize, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSsEnableClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in BOOLEAN Enable + ); + +NTSTATUS KphSsQueryClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation, + __in ULONG ClientInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action + ); + +HANDLE KphSsGetHandleRule( + __in PKPHSS_RULE_ENTRY RuleEntry + ); + +NTSTATUS KphSsRemoveRule( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in HANDLE RuleEntryHandle + ); + +NTSTATUS KphSsAddProcessIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ProcessId + ); + +NTSTATUS KphSsAddThreadIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ThreadId + ); + +NTSTATUS KphSsAddPreviousModeRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphSsAddNumberRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in ULONG Number + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/sysservicedata.h b/branches/ph-plugins/KProcessHacker/include/sysservicedata.h new file mode 100644 index 000000000..80f2e870b --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/sysservicedata.h @@ -0,0 +1,166 @@ +/* + * Process Hacker Driver - + * system service logging (data) + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICEDATA_H +#define _SYSSERVICEDATA_H + +#include "kph.h" + +#define TAG_CALL_ENTRY ('cShP') + +typedef enum _KPHSS_ARGUMENT_TYPE +{ + /* Having argument info for out variables is very rare + * because usually the caller does not fill in anything + * in the variable. In some cases, however, the caller + * does specify a length (usually Length, or MaximumLength). + * + * Note that with the exception of a few types such as + * HANDLE, all types listed here are POINTER TYPES + * (although a handle is the size of a pointer). This + * is because non-pointer arguments are already recorded + * in the event block. + */ + + /* Anything passed by value */ + NormalArgument = 0, + + /* PBOOLEAN */ + Int8Argument, + /* P(U)SHORT */ + Int16Argument, + /* P(U)LONG */ + Int32Argument, + /* P(U)LARGE_INTEGER */ + Int64Argument, + /* HANDLE */ + /* Only object manager handles, no fake handles. */ + HandleArgument, + /* PSTR */ + StringArgument, + /* PWSTR */ + WStringArgument, + /* PANSI_STRING */ + AnsiStringArgument, + /* PUNICODE_STRING */ + UnicodeStringArgument, + /* POBJECT_ATTRIBUTES */ + ObjectAttributesArgument, + /* PCLIENT_ID */ + ClientIdArgument, + /* PCONTEXT */ + ContextArgument, + /* PINITIAL_TEB */ + InitialTebArgument, + /* PGUID */ + GuidArgument +} KPHSS_ARGUMENT_TYPE; + +typedef struct _KPHSS_HANDLE +{ + CLIENT_ID ClientId; + USHORT TypeNameOffset; /* KPHSS_WSTRING */ + USHORT NameOffset; /* KPHSS_WSTRING */ +} KPHSS_HANDLE, *PKPHSS_HANDLE; + +typedef struct _KPHSS_STRING +{ + USHORT Length; + CHAR Buffer[1]; +} KPHSS_STRING, *PKPHSS_STRING; + +typedef struct _KPHSS_WSTRING +{ + USHORT Length; + WCHAR Buffer[1]; +} KPHSS_WSTRING, *PKPHSS_WSTRING; + +typedef struct _KPHSS_ANSI_STRING +{ + USHORT Length; + USHORT MaximumLength; + PSTR Pointer; + CHAR Buffer[1]; +} KPHSS_ANSI_STRING, *PKPHSS_ANSI_STRING; + +typedef struct _KPHSS_UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Pointer; + WCHAR Buffer[1]; +} KPHSS_UNICODE_STRING, *PKPHSS_UNICODE_STRING; + +typedef struct _KPHSS_OBJECT_ATTRIBUTES +{ + union + { + OBJECT_ATTRIBUTES ObjectAttributes; + struct + { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; + }; + }; + + USHORT RootDirectoryOffset; /* KPHSS_HANDLE */ + USHORT ObjectNameOffset; /* KPHSS_UNICODE_STRING */ +} KPHSS_OBJECT_ATTRIBUTES, *PKPHSS_OBJECT_ATTRIBUTES; + +typedef struct _KPHSS_INITIAL_TEB +{ + struct + { + PVOID OldStackBase; + PVOID OldStackLimit; + } OldInitialTeb; + PVOID StackBase; + PVOID StackLimit; + PVOID StackAllocationBase; +} KPHSS_INITIAL_TEB, *PKPHSS_INITIAL_TEB; + +#ifndef _SYSSERVICEDATA_PRIVATE +extern RTL_GENERIC_TABLE KphSsCallTable; +#endif + +#define KPHSS_MAXIMUM_ARGUMENT_BLOCKS 20 + +typedef struct _KPHSS_CALL_ENTRY +{ + PULONG Number; + PSTR Name; + ULONG NumberOfArguments; + KPHSS_ARGUMENT_TYPE Arguments[KPHSS_MAXIMUM_ARGUMENT_BLOCKS]; +} KPHSS_CALL_ENTRY, *PKPHSS_CALL_ENTRY; + +VOID KphSsDataInit(); +VOID KphSsDataDeinit(); + +PKPHSS_CALL_ENTRY KphSsLookupCallEntry( + __in ULONG Number + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/include/sysservicep.h b/branches/ph-plugins/KProcessHacker/include/sysservicep.h new file mode 100644 index 000000000..50f297c33 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/sysservicep.h @@ -0,0 +1,414 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SYSSERVICEP_H +#define _SYSSERVICEP_H + +#define _SYSSERVICE_PRIVATE +#include "sysservice.h" +#include "ex.h" +#include "ref.h" + +/* PKPHPSS_KIFASTCALLENTRYPROC + * + * Represents a function called by KphpSsNewKiFastCallEntry. + */ +typedef VOID (NTAPI *PKPHPSS_KIFASTCALLENTRYPROC)( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ); + +/* Client entries + * + * Client entries describe a process and a circular buffer which + * receives logging events. + */ + +typedef struct _KPHSS_CLIENT_ENTRY +{ + PEPROCESS Process; + BOOLEAN Enabled; + + /* Buffer */ + PKSEMAPHORE ReadSemaphore; + PKSEMAPHORE WriteSemaphore; + FAST_MUTEX BufferMutex; + PVOID BufferBase; + ULONG BufferSize; + ULONG BufferCursor; + + /* Statistics */ + ULONG NumberOfBlocksWritten; /* excludes reset blocks */ + ULONG NumberOfBlocksDropped; +} KPHSS_CLIENT_ENTRY, *PKPHSS_CLIENT_ENTRY; + +/* Rulesets + * + * Rulesets contain a list of rules and an action to take if a + * system service matches the set of rules. + */ + +#define KPHSS_RULESET_ENTRY(ListEntry) \ + CONTAINING_RECORD((ListEntry), KPHSS_RULESET_ENTRY, RuleSetListEntry) +#define KPHSS_RULESET_ENTRY_LIMIT 10 +#define KPHSS_RULE_HANDLE_INCREMENT 4 + +typedef struct _KPHSS_RULESET_ENTRY +{ + LIST_ENTRY RuleSetListEntry; + /* The client is referenced. */ + PKPHSS_CLIENT_ENTRY Client; + + KPHSS_RULESET_ACTION Action; + KPHSS_FILTER_TYPE DefaultFilterType; + + ULONG NextRuleHandle; + EX_PUSH_LOCK RuleListPushLock; + /* A list of rules. Each rule is referenced when stored. */ + LIST_ENTRY RuleListHead; +} KPHSS_RULESET_ENTRY, *PKPHSS_RULESET_ENTRY; + +/* Rules */ + +#define KPHSS_RULE_ENTRY(ListEntry) \ + CONTAINING_RECORD((ListEntry), KPHSS_RULE_ENTRY, RuleListEntry) + +typedef struct _KPHSS_RULE_ENTRY +{ + BOOLEAN Initialized; + HANDLE Handle; + LIST_ENTRY RuleListEntry; + + KPHSS_FILTER_TYPE FilterType; + KPHSS_RULE_TYPE RuleType; + + union + { + struct + { + HANDLE ProcessId; + } ProcessIdRule; + struct + { + HANDLE ThreadId; + } ThreadIdRule; + struct + { + KPROCESSOR_MODE PreviousMode; + } PreviousModeRule; + struct + { + ULONG Number; + } NumberRule; + }; +} KPHSS_RULE_ENTRY, *PKPHSS_RULE_ENTRY; + +typedef enum _KPHSS_SEQUENCE_MODE +{ + NoSequence, + StartSequence, + InSequence, + EndSequence +} KPHSS_SEQUENCE_MODE; + +#define TAG_CAPTURE_TEMP_BUFFER ('tChP') +#define CAPTURE_HANDLE_BUFFER_SIZE 0x400 +#define CAPTURE_UNICODE_STRING_MAX_SIZE 0x400 + +/* KphpSsMatchRuleSetEntry + * + * Determines if a ruleset is relevant to an event. + * + * Note: This function is inlined for performance reasons. + */ +FORCEINLINE BOOLEAN KphpSsMatchRuleSetEntry( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread, + __in KPROCESSOR_MODE PreviousMode + ) +{ + PLIST_ENTRY currentListEntry; + ULONG i; + BOOLEAN ruleTypeUsedArray[MaxRuleType]; + BOOLEAN ruleTypeIncludeArray[MaxRuleType]; + BOOLEAN ruleTypeExcludeArray[MaxRuleType]; + BOOLEAN ruleTypeFailedArray[MaxRuleType]; + BOOLEAN isRuleSetMatch; + + /* Due to the lack of proper boolean expression support, + * we are going to have these rules: + * + * * Each rule type has four arrays. The standard + * filtering rules apply to each rule type, + * except that on an include we increment the value + * in the include array and on an exclude we + * increment the value in the exclude array. On a + * failed include we increment the value in the + * failed array. + * * When we're done matching the rules, we'll look + * at the default filter type. If it's Include, + * we assume the ruleset matches. If it's Exclude, + * we assume the ruleset fails. + * * We will go through each rule type and look at + * the two arrays. See the code for further + * information. + */ + + /* Initialize the arrays. */ + for (i = 0; i < MaxRuleType; i++) + { + ruleTypeUsedArray[i] = FALSE; + ruleTypeIncludeArray[i] = FALSE; + ruleTypeExcludeArray[i] = FALSE; + ruleTypeFailedArray[i] = FALSE; + } + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&RuleSetEntry->RuleListPushLock); + + currentListEntry = RuleSetEntry->RuleListHead.Flink; + + while (currentListEntry != &RuleSetEntry->RuleListHead) + { + PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry); + BOOLEAN isRuleMatch = FALSE; + + /* Check if the rule is initialized, and if + * the rule type has already been failed - + * Exclude filter types take precedence. + */ + if ( + !ruleEntry->Initialized || + ruleTypeExcludeArray[ruleEntry->RuleType] + ) + { + currentListEntry = currentListEntry->Flink; + continue; + } + + /* Attempt to match the rule. All rule types are + * considered in this one function. + */ + switch (ruleEntry->RuleType) + { + case ProcessIdRuleType: + if (PsGetProcessId(IoThreadToProcess(Thread)) == + ruleEntry->ProcessIdRule.ProcessId) + isRuleMatch = TRUE; + break; + case ThreadIdRuleType: + if (PsGetThreadId(Thread) == ruleEntry->ThreadIdRule.ThreadId) + isRuleMatch = TRUE; + break; + case PreviousModeRuleType: + if (PreviousMode == ruleEntry->PreviousModeRule.PreviousMode) + isRuleMatch = TRUE; + break; + case NumberRuleType: + if (Number == ruleEntry->NumberRule.Number) + isRuleMatch = TRUE; + break; + } + + /* Now that we have attempted to match the rule, we + * must look at the rule filter type to determine + * what to do. + */ + if (isRuleMatch) + { + if (ruleEntry->FilterType == IncludeFilterType) + { + ruleTypeIncludeArray[ruleEntry->RuleType] = TRUE; + } + else if (ruleEntry->FilterType == ExcludeFilterType) + { + ruleTypeExcludeArray[ruleEntry->RuleType] = TRUE; + } + } + else + { + if (ruleEntry->FilterType == IncludeFilterType) + { + ruleTypeFailedArray[ruleEntry->RuleType] = TRUE; + } + } + + /* Declare that we have used the rule type. */ + ruleTypeUsedArray[ruleEntry->RuleType] = TRUE; + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + /* Look at the default filter type. If it's Include, + * we assume the ruleset matches. Otherwise, we + * assume it fails. + */ + if (RuleSetEntry->DefaultFilterType == IncludeFilterType) + { + isRuleSetMatch = TRUE; + } + else if (RuleSetEntry->DefaultFilterType == ExcludeFilterType) + { + isRuleSetMatch = FALSE; + } + + /* Go through the rule type match/failed arrays. */ + + for (i = 0; i < MaxRuleType; i++) + { + /* Make sure this rule type has been used. */ + if (!ruleTypeUsedArray[i]) + continue; + + /* The ordering of these if statements are + * extremely important. The order of precedence + * is: exclude, include, failed include. Failed include + * doesn't apply if we're using the Include default + * filter type, though. + */ + if (ruleTypeExcludeArray[i]) + { + isRuleSetMatch = FALSE; + break; + } + else if (ruleTypeIncludeArray[i]) + { + isRuleSetMatch = TRUE; + } + else if ( + ruleTypeFailedArray[i] && + RuleSetEntry->DefaultFilterType != IncludeFilterType + ) + { + isRuleSetMatch = FALSE; + break; + } + } + + return isRuleSetMatch; +} + +/* Functions */ + +VOID NTAPI KphpSsClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +NTSTATUS KphpSsAddRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPHSS_RULE_TYPE RuleType + ); + +NTSTATUS KphpSsCreateEventBlock( + __out PKPHSS_EVENT_BLOCK *EventBlock, + __in PKTHREAD Thread, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments + ); + +VOID KphpSsFreeEventBlock( + __in PKPHSS_EVENT_BLOCK EventBlock + ); + +NTSTATUS KphpSsCaptureSimpleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in KPHSS_ARGUMENT_TYPE Type, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureHandleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in HANDLE Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureUnicodeStringArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PUNICODE_STRING Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureObjectAttributesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in POBJECT_ATTRIBUTES Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCaptureClientIdArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PCLIENT_ID Argument, + __in KPROCESSOR_MODE PreviousMode + ); + +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index + ); + +PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock( + __in ULONG InnerSize, + __in KPHSS_ARGUMENT_TYPE Type + ); + +VOID KphpSsFreeArgumentBlock( + __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock + ); + +NTSTATUS KphpSsWriteBlock( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode + ); + +VOID NTAPI KphpSsLogSystemServiceCall( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ); + +VOID NTAPI KphpSsNewKiFastCallEntry(); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/test.h b/branches/ph-plugins/KProcessHacker/include/test.h new file mode 100644 index 000000000..49dc96095 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/test.h @@ -0,0 +1,30 @@ +/* + * Process Hacker Driver - + * testing code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _TEST_H +#define _TEST_H + +#include "kph.h" + +VOID KphTestPushLock(); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/trace.h b/branches/ph-plugins/KProcessHacker/include/trace.h new file mode 100644 index 000000000..6b707ede5 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/trace.h @@ -0,0 +1,188 @@ +/* + * Process Hacker Driver - + * stack tracing + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _TRACE_H +#define _TRACE_H + +#include "types.h" + +/* Stack Tracing */ + +/* Sensible limit that may or may not correspond to the actual Windows value. */ +#define MAX_STACK_DEPTH 64 + +#define RTL_WALK_USER_MODE_STACK 0x00000001 +#define RTL_WALK_VALID_FLAGS 0x00000001 + +/* RtlWalkFrameChain + * + * Walks an EBP chain and fills out an array of addresses. + * + * Return value: the number of frames found. + */ +NTSYSAPI ULONG NTAPI RtlWalkFrameChain( + __out PVOID *Callers, + __in ULONG Count, + __in ULONG Flags + ); + +/* Trace Database */ + +#define RTL_TRACE_IN_USER_MODE 0x00000001 +#define RTL_TRACE_IN_KERNEL_MODE 0x00000002 +#define RTL_TRACE_USE_NONPAGED_POOL 0x00000004 +#define RTL_TRACE_USE_PAGED_POOL 0x00000008 + +typedef struct _RTL_TRACE_BLOCK +{ + ULONG Magic; + ULONG Count; /* Reference count */ + ULONG Size; /* Size, in PVOIDs, of the trace */ + + SIZE_T UserCount; + SIZE_T UserSize; + PVOID UserContext; + + struct _RTL_TRACE_BLOCK *Next; + PVOID *Trace; +} RTL_TRACE_BLOCK, *PRTL_TRACE_BLOCK; + +typedef struct _RTL_TRACE_DATABASE *PRTL_TRACE_DATABASE; + +/* Enumeration context. */ +typedef struct _RTL_TRACE_ENUMERATE +{ + PRTL_TRACE_DATABASE Database; + ULONG Index; + PRTL_TRACE_BLOCK Block; +} RTL_TRACE_ENUMERATE, *PRTL_TRACE_ENUMERATE; + +typedef ULONG (*RTL_TRACE_HASH_FUNCTION)( + ULONG Count, + PVOID *Trace + ); + +PRTL_TRACE_DATABASE RtlTraceDatabaseCreate( + __in ULONG Buckets, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, /* optional in user-mode */ + __in ULONG Tag, /* optional in user-mode */ + __in_opt RTL_TRACE_HASH_FUNCTION HashFunction + ); + +BOOLEAN RtlTraceDatabaseDestroy( + __in PRTL_TRACE_DATABASE Database + ); + +BOOLEAN RtlTraceDatabaseValidate( + __in PRTL_TRACE_DATABASE Database + ); + +BOOLEAN RtlTraceDatabaseAdd( + __in PRTL_TRACE_DATABASE Database, + __in ULONG Count, + __in PVOID *Trace, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +/* RtlTraceDatabaseEnumerate + * + * Enumerates the trace blocks in the specified trace database. + * + * Database: The trace database to process. + * Enumerate: A context structure for the enumeration. Zero the + * structure if you are using it for the first time. + * TraceBlock: The trace block that was found by the function. + * + * Return value: TRUE if a trace block was found, FALSE if there + * are no more trace blocks. + */ +BOOLEAN RtlTraceDatabaseEnumerate( + __in PRTL_TRACE_DATABASE Database, + __inout PRTL_TRACE_ENUMERATE Enumerate, + __out PRTL_TRACE_BLOCK *TraceBlock + ); + +BOOLEAN RtlTraceDatabaseFind( + __in PRTL_TRACE_DATABASE Database, + __in ULONG Count, + __in PVOID *Trace, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +/* Note: locking/unlocking is only needed when trace blocks are modified. + * It is not needed for adding/enumerating/finding. */ +VOID RtlTraceDatabaseLock( + __in PRTL_TRACE_DATABASE Database + ); + +VOID RtlTraceDatabaseUnlock( + __in PRTL_TRACE_DATABASE Database + ); + +/* KPH trace interface */ + +typedef enum _KPH_CAPTURE_AND_ADD_STACK_TYPE +{ + KphCaptureAndAddKModeStack, + KphCaptureAndAddUModeStack, + KphCaptureAndAddBothStacks, + KphCaptureAndAddMaximum +} KPH_CAPTURE_AND_ADD_STACK_TYPE, *PKPH_CAPTURE_AND_ADD_STACK_TYPE; + +typedef struct _KPH_TRACE_DATABASE +{ + PRTL_TRACE_DATABASE Database; +} KPH_TRACE_DATABASE, *PKPH_TRACE_DATABASE; + +typedef struct _KPH_TRACEDB_INFORMATION +{ + ULONG NextEntryOffset; + ULONG Count; + ULONG TraceSize; + PVOID Trace[1]; +} KPH_TRACEDB_INFORMATION, *PKPH_TRACEDB_INFORMATION; + +NTSTATUS KphTraceDatabaseInitialization(); + +BOOLEAN KphCaptureAndAddStack( + __in PKPH_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +ULONG KphCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __in_opt ULONG Flags, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG BackTraceHash + ); + +NTSTATUS KphCreateTraceDatabase( + __out PKPH_TRACE_DATABASE *Database, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, + __in ULONG Tag + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/types.h b/branches/ph-plugins/KProcessHacker/include/types.h new file mode 100644 index 000000000..e1ca291c1 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/types.h @@ -0,0 +1,7 @@ +#ifndef _TYPES_H +#define _TYPES_H + +#include +#include "version.h" + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/util.h b/branches/ph-plugins/KProcessHacker/include/util.h new file mode 100644 index 000000000..b62b0d22e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/util.h @@ -0,0 +1,133 @@ +/* + * Process Hacker Driver - + * utility functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include "kph.h" + +/* Streams + * + * Streams are small buffer management structures. They + * automatically raise an exception if the buffer is overrun. + */ + +typedef struct _KPH_STREAM +{ + PVOID Buffer; + ULONG Length; + ULONG Position; +} KPH_STREAM, *PKPH_STREAM; + +typedef enum _KPH_STREAM_ORIGIN +{ + StartOrigin, + CurrentOrigin, + EndOrigin +} KPH_STREAM_ORIGIN; + +VOID KphInitializeStream( + __out PKPH_STREAM Stream, + __in PVOID Buffer, + __in ULONG Length + ); + +ULONG KphWriteDataStream( + __inout PKPH_STREAM Stream, + __in PVOID Data, + __in ULONG Length + ); + +/* KphCheckStreamPosition + * + * Checks a stream position and raises an exception if + * appropriate. + */ +FORCEINLINE VOID KphCheckStreamPosition( + __in PKPH_STREAM Stream, + __in ULONG Position + ) +{ + if (Position > Stream->Length) + ExRaiseStatus(STATUS_BUFFER_TOO_SMALL); +} + +/* KphPositionStream + * + * Gets the current position of the specified stream. + */ +FORCEINLINE ULONG KphPositionStream( + __in PKPH_STREAM Stream + ) +{ + return Stream->Position; +} + +/* KphWriteInt8Stream + * + * Writes a 1-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt8Stream( + __inout PKPH_STREAM Stream, + __in BOOLEAN Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(BOOLEAN)); +} + +/* KphWriteInt16Stream + * + * Writes a 2-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt16Stream( + __inout PKPH_STREAM Stream, + __in SHORT Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(SHORT)); +} + +/* KphWriteInt32Stream + * + * Writes a 4-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt32Stream( + __inout PKPH_STREAM Stream, + __in LONG Value + ) +{ + KphWriteDataStream(Stream, &Value, sizeof(LONG)); +} + +/* KphWriteInt64Stream + * + * Writes a 8-byte value to a stream. + */ +FORCEINLINE VOID KphWriteInt64Stream( + __inout PKPH_STREAM Stream, + __in PLARGE_INTEGER Value + ) +{ + KphWriteDataStream(Stream, Value, sizeof(LARGE_INTEGER)); +} + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/version.h b/branches/ph-plugins/KProcessHacker/include/version.h new file mode 100644 index 000000000..00d6ba5ab --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/version.h @@ -0,0 +1,241 @@ +/* + * Process Hacker Driver - + * Windows version-specific data + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _VERSION_H +#define _VERSION_H + +#include "kph.h" + +#define WINDOWS_XP 51 +#define WINDOWS_SERVER_2003 52 +#define WINDOWS_VISTA 60 +#define WINDOWS_7 61 + +#define KVOFF(object, offset) ((PCHAR)(object) + offset) +#define SCAN_LENGTH 0x100000 +#define INIT_SCAN(scan, bytes, length, address, scanLength, displacement) \ + ( \ + ((scan).Initialized = TRUE), \ + ((scan).Bytes = (bytes)), \ + ((scan).Length = (length)), \ + ((scan).StartAddress = (address)), \ + ((scan).ScanLength = (scanLength)), \ + ((scan).Displacement = (displacement)), \ + bytes \ + ) + +typedef struct _KV_SCANPROC +{ + BOOLEAN Initialized; + PUCHAR Bytes; + ULONG Length; + ULONG_PTR StartAddress; + ULONG ScanLength; + LONG Displacement; +} KV_SCANPROC, *PKV_SCANPROC; + +NTSTATUS KvInit(); + +PVOID KvScanProc( + PKV_SCANPROC ScanProc + ); + +PVOID KvVerifyPrologue( + PVOID Address + ); + +#ifdef EXT +#undef EXT +#endif + +#ifdef _VERSION_PRIVATE +#define EXT +#define SCANNULL = { FALSE, NULL, 0, 0, 0, 0 } +#else +#define EXT extern +#define SCANNULL +#endif + +EXT ULONG WindowsVersion; +EXT RTL_OSVERSIONINFOEXW RtlWindowsVersion; +EXT ACCESS_MASK ProcessAllAccess; +EXT ACCESS_MASK ThreadAllAccess; + +/* Offsets */ +/* Structures + * Et: ETHREAD + * Ep: EPROCESS + * Ot: OBJECT_TYPE + * Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE + */ +EXT ULONG OffEtClientId; +EXT ULONG OffEtSpareByteForSs; +EXT ULONG OffEtStartAddress; +EXT ULONG OffEtWin32StartAddress; +EXT ULONG OffEpJob; +EXT ULONG OffEpObjectTable; +EXT ULONG OffEpProtectedProcessOff; +EXT ULONG OffEpProtectedProcessBit; +EXT ULONG OffEpRundownProtect; +EXT ULONG OffOhBody; +EXT ULONG OffOtName; +EXT ULONG OffOtiGenericMapping; +EXT ULONG OffOtiOpenProcedure; + +/* Functions + */ +EXT KV_SCANPROC KiFastCallEntryScan SCANNULL; +EXT KV_SCANPROC PsExitSpecialApcScan SCANNULL; +EXT KV_SCANPROC PsTerminateProcessScan SCANNULL; +EXT KV_SCANPROC PspTerminateThreadByPointerScan SCANNULL; + +/* System Call Numbers + */ +EXT ULONG SsNtAddAtom; +EXT ULONG SsNtAlertResumeThread; +EXT ULONG SsNtAlertThread; +EXT ULONG SsNtAllocateLocallyUniqueId; +EXT ULONG SsNtAllocateUserPhysicalPages; +EXT ULONG SsNtAllocateUuids; +EXT ULONG SsNtAllocateVirtualMemory; +EXT ULONG SsNtApphelpCacheControl; +EXT ULONG SsNtAreMappedFilesTheSame; +EXT ULONG SsNtAssignProcessToJobObject; +EXT ULONG SsNtCallbackReturn; +EXT ULONG SsNtCancelDeviceWakeupRequest; +EXT ULONG SsNtCancelIoFile; +EXT ULONG SsNtCancelTimer; +EXT ULONG SsNtClearEvent; +EXT ULONG SsNtClose; +EXT ULONG SsNtContinue; +EXT ULONG SsNtCreateDebugObject; +EXT ULONG SsNtCreateDirectoryObject; +EXT ULONG SsNtCreateEvent; +EXT ULONG SsNtCreateEventPair; +EXT ULONG SsNtCreateFile; +EXT ULONG SsNtCreateIoCompletion; +EXT ULONG SsNtCreateJobObject; +EXT ULONG SsNtCreateJobSet; +EXT ULONG SsNtCreateKey; +EXT ULONG SsNtCreateKeyedEvent; +EXT ULONG SsNtCreateMailslotFile; +EXT ULONG SsNtCreateMutant; +EXT ULONG SsNtCreateNamedPipeFile; +EXT ULONG SsNtCreatePagingFile; +EXT ULONG SsNtCreatePort; +EXT ULONG SsNtCreatePrivateNamespace; +EXT ULONG SsNtCreateProcess; +EXT ULONG SsNtCreateProcessEx; +EXT ULONG SsNtCreateProfile; +EXT ULONG SsNtCreateSection; +EXT ULONG SsNtCreateSemaphore; +EXT ULONG SsNtCreateSymbolicLinkObject; +EXT ULONG SsNtCreateThread; +EXT ULONG SsNtCreateTimer; +EXT ULONG SsNtCreateToken; +EXT ULONG SsNtCreateUserProcess; +EXT ULONG SsNtCreateWaitablePort; +EXT ULONG SsNtDebugActiveProcess; +EXT ULONG SsNtDebugContinue; +EXT ULONG SsNtDelayExecution; +EXT ULONG SsNtDeleteAtom; +EXT ULONG SsNtDeleteBootEntry; +EXT ULONG SsNtDeleteDriverEntry; +EXT ULONG SsNtDeleteFile; +EXT ULONG SsNtDeleteKey; +EXT ULONG SsNtDeleteObjectAuditAlarm; +EXT ULONG SsNtDeletePrivateNamespace; +EXT ULONG SsNtDeleteValueKey; +EXT ULONG SsNtDeviceIoControlFile; +EXT ULONG SsNtDisplayString; +EXT ULONG SsNtDuplicateObject; +EXT ULONG SsNtDuplicateToken; +EXT ULONG SsNtEnumerateBootEntries; +EXT ULONG SsNtEnumerateDriverEntries; +EXT ULONG SsNtEnumerateKey; +EXT ULONG SsNtEnumerateSystemEnvironmentValuesEx; +EXT ULONG SsNtEnumerateValueKey; +EXT ULONG SsNtExtendSection; +EXT ULONG SsNtFilterToken; +EXT ULONG SsNtFindAtom; +EXT ULONG SsNtFlushBuffersFile; +EXT ULONG SsNtFlushInstructionCache; +EXT ULONG SsNtFlushKey; +EXT ULONG SsNtFlushProcessWriteBuffers; +EXT ULONG SsNtFlushVirtualMemory; +EXT ULONG SsNtFlushWriteBuffer; +EXT ULONG SsNtFreeUserPhysicalPages; +EXT ULONG SsNtFreeVirtualMemory; +EXT ULONG SsNtFsControlFile; +EXT ULONG SsNtGetContextThread; +EXT ULONG SsNtGetCurrentProcessorNumber; +EXT ULONG SsNtGetDevicePowerState; +EXT ULONG SsNtGetNextProcess; +EXT ULONG SsNtGetNextThread; +EXT ULONG SsNtGetPlugPlayEvent; +EXT ULONG SsNtGetWriteWatch; +EXT ULONG SsNtImpersonateAnonymousToken; +EXT ULONG SsNtImpersonateClientOfPort; +EXT ULONG SsNtImpersonateThread; +EXT ULONG SsNtInitiatePowerAction; +EXT ULONG SsNtIsProcessInJob; +EXT ULONG SsNtIsSystemResumeAutomatic; +EXT ULONG SsNtListenPort; +EXT ULONG SsNtLoadDriver; +EXT ULONG SsNtLoadKey; +EXT ULONG SsNtLoadKey2; +EXT ULONG SsNtLockFile; +EXT ULONG SsNtLockVirtualMemory; +EXT ULONG SsNtMakePermanentObject; +EXT ULONG SsNtMakeTemporaryObject; +EXT ULONG SsNtMapUserPhysicalPages; +EXT ULONG SsNtMapUserPhysicalPagesScatter; +EXT ULONG SsNtMapViewOfSection; +EXT ULONG SsNtModifyBootEntry; +EXT ULONG SsNtModifyDriverEntry; +EXT ULONG SsNtNotifyChangeDirectoryFile; +EXT ULONG SsNtNotifyChangeKey; +EXT ULONG SsNtNotifyChangeMultipleKeys; +EXT ULONG SsNtOpenDirectoryObject; +EXT ULONG SsNtOpenEvent; +EXT ULONG SsNtOpenEventPair; +EXT ULONG SsNtOpenFile; +EXT ULONG SsNtOpenIoCompletion; +EXT ULONG SsNtOpenJobObject; +EXT ULONG SsNtOpenKey; +EXT ULONG SsNtOpenKeyedEvent; +EXT ULONG SsNtOpenMutant; +EXT ULONG SsNtOpenObjectAuditAlarm; +EXT ULONG SsNtOpenProcess; +EXT ULONG SsNtOpenProcessToken; +EXT ULONG SsNtOpenProcessTokenEx; +EXT ULONG SsNtOpenSection; +EXT ULONG SsNtOpenSemaphore; +EXT ULONG SsNtOpenSymbolicLinkObject; +EXT ULONG SsNtOpenThread; +EXT ULONG SsNtOpenThreadToken; +EXT ULONG SsNtOpenThreadTokenEx; +EXT ULONG SsNtOpenTimer; +EXT ULONG SsNtReadFile; +EXT ULONG SsNtWriteFile; + +#endif diff --git a/branches/ph-plugins/KProcessHacker/include/zw.h b/branches/ph-plugins/KProcessHacker/include/zw.h new file mode 100644 index 000000000..e6ab70f4e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/include/zw.h @@ -0,0 +1,45 @@ +/* + * Process Hacker Driver - + * system calls + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _ZW_H +#define _ZW_H + +#include "types.h" + +NTSTATUS NTAPI ZwOpenProcessToken( + HANDLE ProcessHandle, + ACCESS_MASK DesiredAccess, + PHANDLE TokenHandle + ); + +NTSTATUS NTAPI ZwSetInformationProcess( + HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength + ); + +typedef NTSTATUS (NTAPI *_NtClose)( + HANDLE Handle + ); + +#endif diff --git a/branches/ph-plugins/KProcessHacker/io.c b/branches/ph-plugins/KProcessHacker/io.c new file mode 100644 index 000000000..ddd8a779d --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/io.c @@ -0,0 +1,284 @@ +/* + * Process Hacker Driver - + * I/O manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/io.h" + +VOID KphpCopyInfoUnicodeString( + __out PVOID Information, + __in PUNICODE_STRING UnicodeString + ); + +/* KphOpenDevice + * + * Opens a device object. + */ +NTSTATUS KphOpenDevice( + __out PHANDLE DeviceHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + DeviceHandle, + 0, + ObjectAttributes, + *IoDeviceObjectType, + AccessMode + ); +} + +/* KphOpenDriver + * + * Opens a driver object. + */ +NTSTATUS KphOpenDriver( + __out PHANDLE DriverHandle, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + DriverHandle, + 0, + ObjectAttributes, + *IoDriverObjectType, + AccessMode + ); +} + +/* KphQueryInformationDriver + * + * Queries information about a driver object. + */ +NTSTATUS KphQueryInformationDriver( + __in HANDLE DriverHandle, + __in DRIVER_INFORMATION_CLASS DriverInformationClass, + __out_bcount_opt(DriverInformationLength) PVOID DriverInformation, + __in ULONG DriverInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PDRIVER_OBJECT driverObject; + + if ( + DriverInformationClass < DriverBasicInformation || + DriverInformationClass >= MaxDriverInfoClass + ) + return STATUS_INVALID_INFO_CLASS; + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + if (DriverInformation) + ProbeForWrite(DriverInformation, DriverInformationLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + DriverHandle, + 0, + *IoDriverObjectType, + KernelMode, + &driverObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + __try + { + switch (DriverInformationClass) + { + /* DriverBasicInformation + * + * Basic information such as flags, driver base and driver size. + */ + case DriverBasicInformation: + { + if (DriverInformation) + { + /* Check buffer length. */ + if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION)) + { + PDRIVER_BASIC_INFORMATION basicInfo; + + basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation; + basicInfo->Flags = driverObject->Flags; + basicInfo->DriverStart = driverObject->DriverStart; + basicInfo->DriverSize = driverObject->DriverSize; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(DRIVER_BASIC_INFORMATION); + } + break; + + /* DriverNameInformation + * + * The name of the driver - e.g. \Driver\KProcessHacker. + */ + case DriverNameInformation: + { + if (DriverInformation) + { + /* Check buffer length. */ + if ( + sizeof(UNICODE_STRING) + + driverObject->DriverName.Length <= + DriverInformationLength + ) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + /* Pass the ReturnLength. */ + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length; + } + break; + + /* DriverServiceKeyNameInformation + * + * The name of the driver's service key - e.g. \REGISTRY\... + */ + case DriverServiceKeyNameInformation: + { + if (driverObject->DriverExtension) + { + if (DriverInformation) + { + if ( + sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length <= + DriverInformationLength + ) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverExtension->ServiceKeyName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length; + } + else + { + if (DriverInformation) + { + if (sizeof(UNICODE_STRING) <= DriverInformationLength) + { + /* Zero the information buffer. */ + KphpCopyInfoUnicodeString( + DriverInformation, + NULL + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING); + } + } + break; + + default: + { + status = STATUS_INVALID_INFO_CLASS; + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + ObDereferenceObject(driverObject); + + return status; +} + +/* KphpCopyInfoUnicodeString + * + * Copies a UNICODE_STRING to an information buffer. If + * the given string is NULL, the function zeros the + * destination UNICODE_STRING. + */ +VOID KphpCopyInfoUnicodeString( + __out PVOID Information, + __in PUNICODE_STRING UnicodeString + ) +{ + PUNICODE_STRING targetUnicodeString = (PUNICODE_STRING)Information; + + if (UnicodeString) + { + targetUnicodeString->Length = UnicodeString->Length; + targetUnicodeString->MaximumLength = targetUnicodeString->Length; + targetUnicodeString->Buffer = (PWSTR)((PCHAR)Information + sizeof(UNICODE_STRING)); + memcpy( + targetUnicodeString->Buffer, + UnicodeString->Buffer, + targetUnicodeString->Length + ); + } + else + { + targetUnicodeString->Length = 0; + targetUnicodeString->MaximumLength = 0; + targetUnicodeString->Buffer = NULL; + } +} diff --git a/branches/ph-plugins/KProcessHacker/kph.c b/branches/ph-plugins/KProcessHacker/kph.c new file mode 100644 index 000000000..0fb19d84d --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/kph.c @@ -0,0 +1,373 @@ +/* + * Process Hacker Driver - + * custom APIs + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _KPH_PRIVATE +#include "include/kph.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, GetSystemRoutineAddress) +#pragma alloc_text(PAGE, KphNtInit) +#pragma alloc_text(PAGE, OpenProcess) +#pragma alloc_text(PAGE, SetProcessToken) +#endif + +/* GetSystemRoutineAddress + * + * Gets the address of a function exported by ntoskrnl or hal. + */ +PVOID GetSystemRoutineAddress(WCHAR *Name) +{ + UNICODE_STRING routineName; + PVOID routineAddress = NULL; + + RtlInitUnicodeString(&routineName, Name); + + /* Wrap in SEH because MmGetSystemRoutineAddress is known to cause + some BSODs. */ + try + { + routineAddress = MmGetSystemRoutineAddress(&routineName); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + routineAddress = NULL; + } + + return routineAddress; +} + +/* KphNtInit + * + * Initializes the KProcessHacker NT component. + */ +NTSTATUS KphNtInit() +{ + NTSTATUS status = STATUS_SUCCESS; + /* Confuse those damn AVs... */ + PWCHAR keService = L"KeService"; // length 9, 18 bytes + PWCHAR descriptorTable = L"DescriptorTable"; // 15, 30 bytes + WCHAR keServiceDescriptorTable[9 + 15 + 1]; + + /* Reconstruct the string. */ + memcpy(keServiceDescriptorTable, keService, 18); + memcpy(keServiceDescriptorTable + 9, descriptorTable, 30); + keServiceDescriptorTable[9 + 15] = L'\0'; + + /* Dynamically get function pointers. */ + __KeServiceDescriptorTable = GetSystemRoutineAddress(keServiceDescriptorTable); + dfprintf("KeServiceDescriptorTable: %#x\n", __KeServiceDescriptorTable); + PsGetProcessJob = GetSystemRoutineAddress(L"PsGetProcessJob"); + dfprintf("PsGetProcessJob: %#x\n", PsGetProcessJob); + PsResumeProcess = GetSystemRoutineAddress(L"PsResumeProcess"); + dfprintf("PsResumeProcess: %#x\n", PsResumeProcess); + PsSuspendProcess = GetSystemRoutineAddress(L"PsSuspendProcess"); + dfprintf("PsSuspendProcess: %#x\n", PsSuspendProcess); + + if (WindowsVersion >= WINDOWS_7) + { + ObGetObjectType = GetSystemRoutineAddress(L"ObGetObjectType"); + dfprintf("ObGetObjectType: %#x\n", ObGetObjectType); + } + + /* Scan for functions. */ + if (KiFastCallEntryScan.Initialized) + { + __KiFastCallEntry = KvScanProc(&KiFastCallEntryScan); + dfprintf("KiFastCallEntry+x: %#x\n", __KiFastCallEntry); + } + if (PsTerminateProcessScan.Initialized) + { + __PsTerminateProcess = KvScanProc(&PsTerminateProcessScan); + dfprintf("PsTerminateProcess: %#x\n", __PsTerminateProcess); + } + if (PspTerminateThreadByPointerScan.Initialized) + { + __PspTerminateThreadByPointer = KvScanProc(&PspTerminateThreadByPointerScan); + dfprintf("PspTerminateThreadByPointer: %#x\n", __PspTerminateThreadByPointer); + } + + return status; +} + +/* KphAttachProcess + * + * Attaches to a process represented by the specified EPROCESS. + */ +VOID KphAttachProcess( + __in PEPROCESS Process, + __out PKPH_ATTACH_STATE AttachState + ) +{ + AttachState->Attached = FALSE; + + /* Don't attach if we are already attached to the target. */ + if (Process != PsGetCurrentProcess()) + { + KeStackAttachProcess(Process, &AttachState->ApcState); + AttachState->Attached = TRUE; + AttachState->Process = Process; + } +} + +/* KphAttachProcessHandle + * + * Attaches to a process represented by the specified handle. + */ +NTSTATUS KphAttachProcessHandle( + __in HANDLE ProcessHandle, + __out PKPH_ATTACH_STATE AttachState + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + AttachState->Attached = FALSE; + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + KphAttachProcess(processObject, AttachState); + ObDereferenceObject(processObject); + + return status; +} + +/* KphAttachProcessId + * + * Attaches to a process represented by the specified process ID. + */ +NTSTATUS KphAttachProcessId( + __in HANDLE ProcessId, + __out PKPH_ATTACH_STATE AttachState + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + AttachState->Attached = FALSE; + + status = PsLookupProcessByProcessId(ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + return status; + + KphAttachProcess(processObject, AttachState); + ObDereferenceObject(processObject); + + return status; +} + +/* KphCaptureUnicodeString + * + * Captures a UNICODE_STRING. This function will not throw exceptions. + */ +NTSTATUS KphCaptureUnicodeString( + __in PUNICODE_STRING UnicodeString, + __out PUNICODE_STRING CapturedUnicodeString + ) +{ + __try + { + CapturedUnicodeString->Length = UnicodeString->Length; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + CapturedUnicodeString->MaximumLength = CapturedUnicodeString->Length; + CapturedUnicodeString->Buffer = ExAllocatePoolWithTag( + PagedPool, + CapturedUnicodeString->Length, + TAG_CAPTURED_UNICODE_STRING + ); + + if (!CapturedUnicodeString->Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + __try + { + memcpy( + CapturedUnicodeString->Buffer, + UnicodeString->Buffer, + CapturedUnicodeString->Length + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphFreeCapturedUnicodeString(CapturedUnicodeString); + return GetExceptionCode(); + } + + return STATUS_SUCCESS; +} + +/* KphDetachProcess + * + * Detaches from the currently attached process. + */ +VOID KphDetachProcess( + __in PKPH_ATTACH_STATE AttachState + ) +{ + if (AttachState->Attached) + KeUnstackDetachProcess(&AttachState->ApcState); +} + +/* KphFreeCapturedUnicodeString + * + * Frees a UNICODE_STRING captured by KphCaptureUnicodeString. + */ +VOID KphFreeCapturedUnicodeString( + __in PUNICODE_STRING CapturedUnicodeString + ) +{ + ExFreePoolWithTag( + CapturedUnicodeString->Buffer, + TAG_CAPTURED_UNICODE_STRING + ); +} + +/* KphProbeForReadUnicodeString + * + * Probes a UNICODE_STRING structure for reading. + */ +VOID KphProbeForReadUnicodeString( + __in PUNICODE_STRING UnicodeString + ) +{ + ProbeForRead(UnicodeString, sizeof(UNICODE_STRING), 1); + ProbeForRead(UnicodeString->Buffer, UnicodeString->Length, 1); +} + +/* KphProbeSystemAddressRange + * + * Probes an address range in kernel-mode memory for reading. + */ +VOID KphProbeSystemAddressRange( + __in PVOID BaseAddress, + __in ULONG Length + ) +{ + ULONG_PTR page, pageEnd; + + /* HACK HACK HACK HACK HACK HACK */ + /* Check the address range by checking each page. */ + /* Round down the base address to the page size. Note: please make sure you are + * not using a dumbass compiler which optimizes the following line by removing + * the divide and multiply. + */ + page = (ULONG_PTR)BaseAddress / PAGE_SIZE * PAGE_SIZE; + /* BaseAddress + Length - 1 is the last address we will be reading. */ + pageEnd = ((ULONG_PTR)BaseAddress + Length - 1) / PAGE_SIZE * PAGE_SIZE; + + for (; page <= pageEnd; page += PAGE_SIZE) + { + /* Check the page. */ + if (!MmIsAddressValid((PVOID)page)) + ExRaiseStatus(STATUS_ACCESS_VIOLATION); + } +} + +/* OpenProcess + * + * Opens the process with the specified PID. + */ +NTSTATUS OpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in HANDLE ProcessId + ) +{ + OBJECT_ATTRIBUTES objAttr = { 0 }; + CLIENT_ID clientId; + + objAttr.Length = sizeof(objAttr); + clientId.UniqueThread = 0; + clientId.UniqueProcess = ProcessId; + + return KphOpenProcess(ProcessHandle, DesiredAccess, &objAttr, &clientId, KernelMode); +} + +/* SetProcessToken + * + * Assigns the primary token of the target process from the + * primary token of source process. + */ +NTSTATUS SetProcessToken( + __in HANDLE sourcePid, + __in HANDLE targetPid + ) +{ + NTSTATUS status; + HANDLE source; + + if (NT_SUCCESS(status = OpenProcess(&source, PROCESS_QUERY_INFORMATION, sourcePid))) + { + HANDLE target; + + if (NT_SUCCESS(status = OpenProcess(&target, PROCESS_QUERY_INFORMATION | + PROCESS_SET_INFORMATION, targetPid))) + { + HANDLE sourceToken; + + if (NT_SUCCESS(status = KphOpenProcessTokenEx(source, TOKEN_DUPLICATE, 0, + &sourceToken, UserMode))) + { + HANDLE dupSourceToken; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + + objectAttributes.Length = sizeof(objectAttributes); + + if (NT_SUCCESS(status = ZwDuplicateToken(sourceToken, TOKEN_ASSIGN_PRIMARY, &objectAttributes, + FALSE, TokenPrimary, &dupSourceToken))) + { + PROCESS_ACCESS_TOKEN token; + + token.Token = dupSourceToken; + token.Thread = 0; + + status = ZwSetInformationProcess(target, ProcessAccessToken, &token, sizeof(token)); + } + + ZwClose(dupSourceToken); + } + + ZwClose(sourceToken); + } + + ZwClose(target); + } + + ZwClose(source); + + return status; +} diff --git a/branches/ph-plugins/KProcessHacker/kprocesshacker.c b/branches/ph-plugins/KProcessHacker/kprocesshacker.c new file mode 100644 index 000000000..6b2249298 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/kprocesshacker.c @@ -0,0 +1,2367 @@ +/* + * Process Hacker Driver - + * main driver code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kprocesshacker.h" +#include "include/debug.h" + +#include "include/kph.h" +#include "include/protect.h" +#include "include/ps.h" +#include "include/sysservice.h" +#include "include/version.h" + +#define CHECK_IN_LENGTH \ + if (inLength < sizeof(*args)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } +#define CHECK_OUT_LENGTH \ + if (outLength < sizeof(*ret)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } +#define CHECK_IN_OUT_LENGTH \ + if (inLength < sizeof(*args) || outLength < sizeof(*ret)) \ + { \ + status = STATUS_BUFFER_TOO_SMALL; \ + goto IoControlEnd; \ + } + +PDRIVER_OBJECT KphDriverObject; + +static PKPH_OBJECT_TYPE ClientEntryType; +static LIST_ENTRY ClientListHead; +static EX_PUSH_LOCK ClientListLock; + +static BOOLEAN ProtectionInitialized = FALSE; +static FAST_MUTEX ProtectionMutex; + +static ULONG SsStartCount = 0; +static FAST_MUTEX SsMutex; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, DriverUnload) +#pragma alloc_text(PAGE, KphDispatchCreate) +#pragma alloc_text(PAGE, KphDispatchClose) +#pragma alloc_text(PAGE, KphDispatchDeviceControl) +#pragma alloc_text(PAGE, KphDispatchRead) +#pragma alloc_text(PAGE, KphUnsupported) +#endif + +NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + NTSTATUS status = STATUS_SUCCESS; + int i; + PDEVICE_OBJECT deviceObject = NULL; + UNICODE_STRING deviceName, dosDeviceName; + + KphDriverObject = DriverObject; + + /* Initialize version information. */ + status = KvInit(); + + if (!NT_SUCCESS(status)) + { + if (status == STATUS_NOT_SUPPORTED) + dprintf("Your operating system is not supported by KProcessHacker\n"); + + return status; + } + + /* Initialize NT KPH. */ + status = KphNtInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize hooking. */ + status = KphHookInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the KPH object manager. */ + status = KphRefInit(); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize system service logging. */ + status = KphSsLogInit(); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize trace databases. */ + status = KphTraceDatabaseInitialization(); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize client list structures. */ + InitializeListHead(&ClientListHead); + ExInitializePushLock(&ClientListLock); + + status = KphCreateObjectType( + &ClientEntryType, + PagedPool, + 0, + ClientEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + { + KphRefDeinit(); + return status; + } + + /* Initialize process protection. */ + ExInitializeFastMutex(&ProtectionMutex); + /* Initialize the system service logging mutex. */ + ExInitializeFastMutex(&SsMutex); + + RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME); + RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME); + + /* Create the KProcessHacker device. */ + status = IoCreateDevice(DriverObject, 0, &deviceName, + FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); + + /* Set up the major functions. */ + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + DriverObject->MajorFunction[i] = NULL; + + DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose; + DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_READ] = KphDispatchRead; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl; + DriverObject->DriverUnload = DriverUnload; + + deviceObject->Flags |= DO_BUFFERED_IO; + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + IoCreateSymbolicLink(&dosDeviceName, &deviceName); + + dprintf("Driver loaded\n"); + + return STATUS_SUCCESS; +} + +VOID DriverUnload(PDRIVER_OBJECT DriverObject) +{ + UNICODE_STRING dosDeviceName; + + RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME); + IoDeleteSymbolicLink(&dosDeviceName); + IoDeleteDevice(DriverObject->DeviceObject); + + ExAcquireFastMutex(&ProtectionMutex); + + if (ProtectionInitialized) + { + KphProtectDeinit(); + ProtectionInitialized = FALSE; + } + + ExReleaseFastMutex(&ProtectionMutex); + + /* Make sure system service logging is disabled. */ + if (SsStartCount > 0) + SsUnref(SsStartCount); + + /* Free system service logging structures. */ + KphSsLogDeinit(); + + /* Free all objects in the object manager. */ + KphRefDeinit(); + + dprintf("Driver unloaded\n"); +} + +NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + +#ifdef KPH_REQUIRE_DEBUG_PRIVILEGE + if (!SeSinglePrivilegeCheck(SeExports->SeDebugPrivilege, UserMode)) + { + dprintf("Client (PID %d) was refused\n", PsGetCurrentProcessId()); + Irp->IoStatus.Status = STATUS_PRIVILEGE_NOT_HELD; + + return STATUS_PRIVILEGE_NOT_HELD; + } +#endif + + /* Add a client entry. Note that we don't dereference it because + * we keep one reference for it being on the client list. + */ + if (!CreateClientEntry(NULL)) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + return STATUS_INSUFFICIENT_RESOURCES; + } + + dprintf("Client (PID %d) connected\n", PsGetCurrentProcessId()); + dprintf("Base IOCTL is 0x%08x\n", KPH_CTL_CODE(0)); + + return status; +} + +NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_CLIENT_ENTRY clientEntry; + + ExAcquireFastMutex(&ProtectionMutex); + + if (ProtectionInitialized) + { + ULONG count = KphProtectRemoveByTag(PsGetCurrentProcessId()); + dprintf("Removed %d protection entries\n", count); + } + + ExReleaseFastMutex(&ProtectionMutex); + + /* Get the current client entry and dereference it twice to remove it. */ + clientEntry = ReferenceClientEntry(NULL); + + if (clientEntry) + KphDereferenceObjectEx(clientEntry, 2, FALSE); + + dprintf("Client (PID %d) disconnected\n", PsGetCurrentProcessId()); + + return status; +} + +VOID InitProtection() +{ + ExAcquireFastMutex(&ProtectionMutex); + + if (!ProtectionInitialized) + { + if (NT_SUCCESS(KphProtectInit())) + ProtectionInitialized = TRUE; + } + + ExReleaseFastMutex(&ProtectionMutex); +} + +VOID SsRef(LONG count) +{ + LONG oldRefCount; + + ASSERT(count >= 0); + + if (count == 0) + return; + + ExAcquireFastMutex(&SsMutex); + + /* Add references. */ + oldRefCount = InterlockedExchangeAdd(&SsStartCount, count); + ASSERT(oldRefCount >= 0); + + /* Start system service logging if this was the first bunch of references. */ + if (oldRefCount == 0) + KphSsLogStart(); + + ExReleaseFastMutex(&SsMutex); +} + +VOID SsUnref(LONG count) +{ + LONG oldRefCount; + + ASSERT(count >= 0); + + if (count == 0) + return; + + ExAcquireFastMutex(&SsMutex); + + oldRefCount = InterlockedExchangeAdd(&SsStartCount, -count); + ASSERT(oldRefCount > 0); + + if (oldRefCount - count == 0) + KphSsLogStop(); + + ExReleaseFastMutex(&SsMutex); +} + +VOID NTAPI ClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPH_CLIENT_ENTRY entry = (PKPH_CLIENT_ENTRY)Object; + + /* Lower the SS start count. */ + SsUnref(entry->SsStartCount); + + /* Free the handle table. */ + KphFreeHandleTable(entry->HandleTable); + + /* Remove the entry from the client list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&ClientListLock); + RemoveEntryList(&entry->ClientListEntry); + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); +} + +PKPH_CLIENT_ENTRY CreateClientEntry( + __in_opt HANDLE ProcessId + ) +{ + PKPH_CLIENT_ENTRY entry; + PKPH_HANDLE_TABLE handleTable; + + /* If the PID wasn't specified, use the current one. */ + if (!ProcessId) + ProcessId = PsGetCurrentProcessId(); + + if (!NT_SUCCESS(KphCreateHandleTable( + &handleTable, + KPH_CLIENT_MAXHANDLES, + sizeof(KPH_HANDLE_TABLE_ENTRY), + TAG_CLIENT_HANDLETABLE + ))) + return NULL; + + if (!NT_SUCCESS(KphCreateObject( + &entry, + sizeof(KPH_CLIENT_ENTRY), + 0, + ClientEntryType, + 0 + ))) + { + KphFreeHandleTable(handleTable); + return NULL; + } + + /* Initialize the entry. */ + entry->ProcessId = ProcessId; + entry->HandleTable = handleTable; + KphInitializeGuardedLock(&entry->SsLock, FALSE); + entry->SsStartCount = 0; + + /* Insert the entry into the client list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&ClientListLock); + InsertHeadList(&ClientListHead, &entry->ClientListEntry); + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return entry; +} + +PKPH_CLIENT_ENTRY ReferenceClientEntry( + __in_opt HANDLE ProcessId + ) +{ + PLIST_ENTRY entry = ClientListHead.Flink; + + /* If the PID wasn't specified, use the current one. */ + if (!ProcessId) + ProcessId = PsGetCurrentProcessId(); + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&ClientListLock); + + /* Find the client entry. */ + while (entry != &ClientListHead) + { + PKPH_CLIENT_ENTRY clientEntry = + CONTAINING_RECORD(entry, KPH_CLIENT_ENTRY, ClientListEntry); + + if (clientEntry->ProcessId == ProcessId) + { + PKPH_CLIENT_ENTRY returnEntry = NULL; + + /* Reference and return the entry. */ + if (KphReferenceObjectSafe(clientEntry)) + { + returnEntry = clientEntry; + } + + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return returnEntry; + } + + entry = entry->Flink; + } + + ExReleasePushLock(&ClientListLock); + KeLeaveCriticalRegion(); + + return NULL; +} + +NTSTATUS CloseClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphCloseHandle(clientEntry->HandleTable, Handle); + KphDereferenceObject(clientEntry); + + return status; +} + +NTSTATUS CreateClientHandle( + __in_opt HANDLE ProcessId, + __in PVOID Object, + __out PHANDLE Handle + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphCreateHandle(clientEntry->HandleTable, Object, Handle); + KphDereferenceObject(clientEntry); + + return status; +} + +NTSTATUS ReferenceClientHandle( + __in_opt HANDLE ProcessId, + __in HANDLE Handle, + __in PKPH_OBJECT_TYPE ObjectType, + __out PVOID *Object + ) +{ + NTSTATUS status; + PKPH_CLIENT_ENTRY clientEntry; + + clientEntry = ReferenceClientEntry(ProcessId); + + if (!clientEntry) + return STATUS_UNSUCCESSFUL; + + status = KphReferenceObjectByHandle( + clientEntry->HandleTable, + Handle, + ObjectType, + Object + ); + KphDereferenceObject(clientEntry); + + return status; +} + +PCHAR GetIoControlName(ULONG ControlCode) +{ + switch (ControlCode) + { + case KPH_CLOSEHANDLE: + return "Client Close Handle"; + case KPH_SSQUERYCLIENTENTRY: + return "SsQueryClientEntry"; + case KPH_OPENPROCESS: + return "KphOpenProcess"; + case KPH_OPENTHREAD: + return "KphOpenThread"; + case KPH_OPENPROCESSTOKEN: + return "KphOpenProcessTokenEx"; + case KPH_GETPROCESSPROTECTED: + return "Get Process Protected"; + case KPH_SETPROCESSPROTECTED: + return "Set Process Protected"; + case KPH_TERMINATEPROCESS: + return "KphTerminateProcess"; + case KPH_SUSPENDPROCESS: + return "KphSuspendProcess"; + case KPH_RESUMEPROCESS: + return "KphResumeProcess"; + case KPH_READVIRTUALMEMORY: + return "KphReadVirtualMemory"; + case KPH_WRITEVIRTUALMEMORY: + return "KphWriteVirtualMemory"; + case KPH_SETPROCESSTOKEN: + return "Set Process Token"; + case KPH_GETTHREADSTARTADDRESS: + return "Get Thread Start Address"; + case KPH_SETHANDLEATTRIBUTES: + return "Set Handle Attributes"; + case KPH_GETHANDLEOBJECTNAME: + return "Get Handle Object Name"; + case KPH_OPENPROCESSJOB: + return "KphOpenProcessJob"; + case KPH_GETCONTEXTTHREAD: + return "KphGetContextThread"; + case KPH_SETCONTEXTTHREAD: + return "KphSetContextThread"; + case KPH_GETTHREADWIN32THREAD: + return "KphGetThreadWin32Thread"; + case KPH_DUPLICATEOBJECT: + return "KphDuplicateObject"; + case KPH_ZWQUERYOBJECT: + return "ZwQueryObject"; + case KPH_GETPROCESSID: + return "KphGetProcessId"; + case KPH_GETTHREADID: + return "KphGetThreadId"; + case KPH_TERMINATETHREAD: + return "KphTerminateThread"; + case KPH_GETFEATURES: + return "Get Features"; + case KPH_SETHANDLEGRANTEDACCESS: + return "KphSetHandleGrantedAccess"; + case KPH_ASSIGNIMPERSONATIONTOKEN: + return "KphAssignImpersonationToken"; + case KPH_PROTECTADD: + return "Add Process Protection"; + case KPH_PROTECTREMOVE: + return "Remove Process Protection"; + case KPH_PROTECTQUERY: + return "Query Process Protection"; + case KPH_UNSAFEREADVIRTUALMEMORY: + return "KphUnsafeReadVirtualMemory"; + case KPH_SETEXECUTEOPTIONS: + return "Set Execute Options"; + case KPH_QUERYPROCESSHANDLES: + return "KphQueryProcessHandles"; + case KPH_OPENTHREADPROCESS: + return "KphOpenThreadProcess"; + case KPH_CAPTURESTACKBACKTRACETHREAD: + return "KphCaptureStackBackTraceThread"; + case KPH_DANGEROUSTERMINATETHREAD: + return "KphDangerousTerminateThread"; + case KPH_OPENDEVICE: + return "KphOpenDevice"; + case KPH_OPENDRIVER: + return "KphOpenDriver"; + case KPH_QUERYINFORMATIONDRIVER: + return "KphQueryInformationDriver"; + case KPH_OPENDIRECTORYOBJECT: + return "KphOpenDirectoryObject"; + case KPH_SSREF: + return "SsRef"; + case KPH_SSUNREF: + return "SsUnref"; + case KPH_SSCREATECLIENTENTRY: + return "SsCreateClientEntry"; + case KPH_SSCREATERULESETENTRY: + return "SsCreateRuleSetEntry"; + case KPH_SSREMOVERULE: + return "SsRemoveRule"; + case KPH_SSADDPROCESSIDRULE: + return "SsAddProcessIdRule"; + case KPH_SSADDTHREADIDRULE: + return "SsAddThreadIdRule"; + case KPH_SSADDPREVIOUSMODERULE: + return "SsAddPreviousModeRule"; + case KPH_SSADDNUMBERRULE: + return "SsAddNumberRule"; + case KPH_SSENABLECLIENTENTRY: + return "SsEnableClientEntry"; + default: + return "Unknown"; + } +} + +NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION ioStackIrp = NULL; + PVOID dataBuffer; + ULONG controlCode; + ULONG inLength = 0; + ULONG outLength = 0; + ULONG retLength = 0; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + ioStackIrp = IoGetCurrentIrpStackLocation(Irp); + + if (ioStackIrp == NULL) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + dataBuffer = Irp->AssociatedIrp.SystemBuffer; + + if (dataBuffer == NULL && (inLength != 0 || outLength != 0)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto IoControlEnd; + } + + inLength = ioStackIrp->Parameters.DeviceIoControl.InputBufferLength; + outLength = ioStackIrp->Parameters.DeviceIoControl.OutputBufferLength; + controlCode = ioStackIrp->Parameters.DeviceIoControl.IoControlCode; + + dprintf("IoControl 0x%08x (%s)\n", controlCode, GetIoControlName(controlCode)); + + /* 1-byte packing for KPH input/output structures. */ + #include + + switch (controlCode) + { + /* Client Close Handle + * + * Closes a handle opened by the client. + */ + case KPH_CLOSEHANDLE: + { + struct + { + HANDLE Handle; + } *args = dataBuffer; + PKPH_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + status = CloseClientHandle(NULL, args->Handle); + } + break; + + /* SsQueryClientEntry + * + * Queries information about a client entry. + */ + case KPH_SSQUERYCLIENTENTRY: + { + struct + { + HANDLE ClientEntryHandle; + PKPHSS_CLIENT_INFORMATION ClientInformation; + ULONG ClientInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Query the client entry. */ + status = KphSsQueryClientEntry( + clientEntry, + args->ClientInformation, + args->ClientInformationLength, + args->ReturnLength, + UserMode + ); + KphDereferenceObject(clientEntry); + } + break; + + /* KphOpenProcess + * + * Opens the specified process. This call will never fail unless: + * 1. PsLookupProcessByProcessId, ObOpenObjectByPointer or some lower-level + * function is hooked, or + * 2. The process is protected. + */ + case KPH_OPENPROCESS: + { + struct + { + HANDLE ProcessId; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ProcessHandle; + } *ret = dataBuffer; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + + CHECK_IN_OUT_LENGTH; + + clientId.UniqueThread = 0; + clientId.UniqueProcess = args->ProcessId; + status = KphOpenProcess( + &ret->ProcessHandle, + args->DesiredAccess, + &objectAttributes, + &clientId, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphOpenThread + * + * Opens the specified thread. This call will never fail unless: + * 1. PsLookupProcessThreadByCid, ObOpenObjectByPointer or some lower-level + * function is hooked, or + * 2. The thread's process is protected. + */ + case KPH_OPENTHREAD: + { + struct + { + HANDLE ThreadId; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ThreadHandle; + } *ret = dataBuffer; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + + CHECK_IN_OUT_LENGTH; + + clientId.UniqueThread = args->ThreadId; + clientId.UniqueProcess = 0; + status = KphOpenThread( + &ret->ThreadHandle, + args->DesiredAccess, + &objectAttributes, + &clientId, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphOpenProcessToken + * + * Opens the specified process' token. This call will never fail unless + * a low-level function is hooked. + */ + case KPH_OPENPROCESSTOKEN: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE TokenHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenProcessTokenEx( + args->ProcessHandle, + args->DesiredAccess, + 0, + &ret->TokenHandle, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* Get Process Protected + * + * Gets whether the process is protected. + */ + case KPH_GETPROCESSPROTECTED: + { + struct + { + HANDLE ProcessId; + } *args = dataBuffer; + struct + { + BOOLEAN IsProtected; + } *ret = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_OUT_LENGTH; + + status = PsLookupProcessByProcessId(args->ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ret->IsProtected = + (CHAR)GET_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + ObDereferenceObject(processObject); + retLength = sizeof(*ret); + } + break; + + /* Set Process Protected + * + * Sets whether the process is protected. + */ + case KPH_SETPROCESSPROTECTED: + { + struct + { + HANDLE ProcessId; + BOOLEAN IsProtected; + } *args = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_LENGTH; + + status = PsLookupProcessByProcessId(args->ProcessId, &processObject); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (args->IsProtected) + { + SET_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + } + else + { + CLEAR_BIT( + *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff), + OffEpProtectedProcessBit + ); + } + + ObDereferenceObject(processObject); + } + break; + + /* KphTerminateProcess + * + * Terminates the specified process. This call will never fail unless + * PsTerminateProcess could not be located and Zw/NtTerminateProcess + * is hooked, or an attempt was made to terminate the current process. + * In that case, the call will fail with STATUS_CANT_TERMINATE_SELF. + */ + case KPH_TERMINATEPROCESS: + { + struct + { + HANDLE ProcessHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphTerminateProcess(args->ProcessHandle, args->ExitStatus); + } + break; + + /* KphSuspendProcess + * + * Suspends the specified process. This call will fail on Windows XP + * and below. + */ + case KPH_SUSPENDPROCESS: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSuspendProcess(args->ProcessHandle); + } + break; + + /* KphResumeProcess + * + * Resumes the specified process. This call will fail on Windows XP + * and below. + */ + case KPH_RESUMEPROCESS: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphResumeProcess(args->ProcessHandle); + } + break; + + /* KphReadVirtualMemory + * + * Reads process memory. + */ + case KPH_READVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphReadVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphWriteVirtualMemory + * + * Writes to process memory. + */ + case KPH_WRITEVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphWriteVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* Set Process Token + * + * Assigns the primary token of a source process to a target process. + */ + case KPH_SETPROCESSTOKEN: + { + struct + { + HANDLE SourceProcessId; + HANDLE TargetProcessId; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = SetProcessToken(args->SourceProcessId, args->TargetProcessId); + } + break; + + /* Get Thread Start Address + * + * Gets the specified thread's start address. + */ + case KPH_GETTHREADSTARTADDRESS: + { + struct + { + HANDLE ThreadHandle; + } *args = dataBuffer; + struct + { + PVOID StartAddress; + } *ret = dataBuffer; + PETHREAD threadObject; + + CHECK_IN_OUT_LENGTH; + + status = ObReferenceObjectByHandle(args->ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, NULL); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Get the Win32StartAddress */ + if (!(ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtWin32StartAddress))) + { + /* If that failed, get the StartAddress */ + ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtStartAddress); + } + + ObDereferenceObject(threadObject); + retLength = sizeof(*ret); + } + break; + + /* Set Handle Attributes + * + * Sets handle flags in the specified process. + */ + case KPH_SETHANDLEATTRIBUTES: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + ULONG Flags; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + OBJECT_HANDLE_FLAG_INFORMATION handleFlags = { 0 }; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (args->Flags & OBJ_PROTECT_CLOSE) + handleFlags.ProtectFromClose = TRUE; + if (args->Flags & OBJ_INHERIT) + handleFlags.Inherit = TRUE; + + status = ObSetHandleAttributes(args->Handle, &handleFlags, UserMode); + KphDetachProcess(&attachState); + } + break; + + /* Get Handle Object Name + * + * Gets the name of the specified handle. The handle can be remote; in + * that case a valid process handle must be passed. Otherwise, set the + * process handle to -1 (NtCurrentProcess()). + */ + case KPH_GETHANDLEOBJECTNAME: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + PVOID object; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* See the block for KPH_ZWQUERYOBJECT for information. */ + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + status = ObReferenceObjectByHandle(args->Handle, 0, NULL, KernelMode, &object, NULL); + KphDetachProcess(&attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = KphQueryNameObject(object, (PUNICODE_STRING)dataBuffer, outLength, &retLength); + ObDereferenceObject(object); + } + break; + + /* KphOpenProcessJob + * + * Opens the job object that the process is assigned to. If the process is + * not assigned to any job object, the call will fail with STATUS_PROCESS_NOT_IN_JOB. + */ + case KPH_OPENPROCESSJOB: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE JobHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenProcessJob(args->ProcessHandle, args->DesiredAccess, &ret->JobHandle, KernelMode); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphGetContextThread + * + * Gets the context of the specified thread. + */ + case KPH_GETCONTEXTTHREAD: + { + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphGetContextThread(args->ThreadHandle, args->ThreadContext, UserMode); + } + break; + + /* KphSetContextThread + * + * Sets the context of the specified thread. + */ + case KPH_SETCONTEXTTHREAD: + { + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSetContextThread(args->ThreadHandle, args->ThreadContext, UserMode); + } + break; + + /* KphGetThreadWin32Thread + * + * Gets a pointer to the specified thread's Win32Thread structure. + */ + case KPH_GETTHREADWIN32THREAD: + { + struct + { + HANDLE ThreadHandle; + } *args = dataBuffer; + struct + { + PVOID Win32Thread; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphGetThreadWin32Thread(args->ThreadHandle, &ret->Win32Thread, KernelMode); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphDuplicateObject + * + * Duplicates the specified handle from the source process to the target process. + * Do not use this call to duplicate file handles; it may freeze indefinitely if + * the file is a named pipe. + */ + case KPH_DUPLICATEOBJECT: + { + struct + { + HANDLE SourceProcessHandle; + HANDLE SourceHandle; + HANDLE TargetProcessHandle; + PHANDLE TargetHandle; + ACCESS_MASK DesiredAccess; + ULONG HandleAttributes; + ULONG Options; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphDuplicateObject( + args->SourceProcessHandle, + args->SourceHandle, + args->TargetProcessHandle, + args->TargetHandle, + args->DesiredAccess, + args->HandleAttributes, + args->Options, + UserMode + ); + } + break; + + /* ZwQueryObject + * + * Performs ZwQueryObject in the context of another process. + */ + case KPH_ZWQUERYOBJECT: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + OBJECT_INFORMATION_CLASS ObjectInformationClass; + } *args = dataBuffer; + struct + { + NTSTATUS Status; + ULONG ReturnLength; + PVOID BufferBase; + CHAR Buffer[1]; + } *ret = dataBuffer; + NTSTATUS status2 = STATUS_SUCCESS; + KPH_ATTACH_STATE attachState; + BOOLEAN attached; + + if (inLength < sizeof(*args) || outLength < sizeof(*ret) - sizeof(CHAR)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto IoControlEnd; + } + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Are we attached to the system process? If we are, + * we must set the high bit in the handle to indicate + * that it is a kernel handle - a new check for this + * was added in Windows 7. + */ + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + status2 = ZwQueryObject( + args->Handle, + args->ObjectInformationClass, + ret->Buffer, + outLength - (sizeof(*ret) - sizeof(CHAR)), + &retLength + ); + KphDetachProcess(&attachState); + + ret->ReturnLength = retLength; + ret->BufferBase = ret->Buffer; + + if (NT_SUCCESS(status2)) + retLength += sizeof(*ret) - sizeof(CHAR); + else + retLength = sizeof(*ret) - sizeof(CHAR); + + ret->Status = status2; + } + break; + + /* KphGetProcessId + * + * Gets the process ID of a process handle in the context of another process. + */ + case KPH_GETPROCESSID: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + struct + { + HANDLE ProcessId; + } *ret = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_OUT_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + ret->ProcessId = KphGetProcessId(args->Handle); + KphDetachProcess(&attachState); + retLength = sizeof(*ret); + } + break; + + /* KphGetThreadId + * + * Gets the thread ID of a thread handle in the context of another process. + */ + case KPH_GETTHREADID: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + } *args = dataBuffer; + struct + { + HANDLE ThreadId; + HANDLE ProcessId; + } *ret = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_OUT_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + if (attachState.Process == PsInitialSystemProcess) + MakeKernelHandle(args->Handle); + + ret->ThreadId = KphGetThreadId(args->Handle, &ret->ProcessId); + KphDetachProcess(&attachState); + retLength = sizeof(*ret); + } + break; + + /* KphTerminateThread + * + * Terminates the specified thread. This call will fail if + * PspTerminateThreadByPointer could not be located or if an attempt + * was made to terminate the current thread. In that case, the call + * will return STATUS_CANT_TERMINATE_SELF. + */ + case KPH_TERMINATETHREAD: + { + struct + { + HANDLE ThreadHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphTerminateThread(args->ThreadHandle, args->ExitStatus); + } + break; + + /* Get Features + * + * Gets the features supported by the driver. + */ + case KPH_GETFEATURES: + { + struct + { + ULONG Features; + } *ret = dataBuffer; + ULONG features = 0; + + CHECK_OUT_LENGTH; + + if (__PsTerminateProcess) + features |= KPHF_PSTERMINATEPROCESS; + if (__PspTerminateThreadByPointer) + features |= KPHF_PSPTERMINATETHREADBPYPOINTER; + + ret->Features = features; + retLength = sizeof(*ret); + } + break; + + /* KphSetHandleGrantedAccess + * + * Sets the granted access for a handle. + */ + case KPH_SETHANDLEGRANTEDACCESS: + { + struct + { + HANDLE Handle; + ACCESS_MASK GrantedAccess; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphSetHandleGrantedAccess( + PsGetCurrentProcess(), + args->Handle, + args->GrantedAccess + ); + } + break; + + /* KphAssignImpersonationToken + * + * Assigns an impersonation token to a thread. + */ + case KPH_ASSIGNIMPERSONATIONTOKEN: + { + struct + { + HANDLE ThreadHandle; + HANDLE TokenHandle; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphAssignImpersonationToken(args->ThreadHandle, args->TokenHandle); + } + break; + + /* Add Process Protection */ + case KPH_PROTECTADD: + { + struct + { + HANDLE ProcessHandle; + LOGICAL AllowKernelMode; + ACCESS_MASK ProcessAllowMask; + ACCESS_MASK ThreadAllowMask; + } *args = dataBuffer; + PEPROCESS processObject; + + CHECK_IN_LENGTH; + + /* We'll reference the process and then dereference it. That way + * we can get the address of the object - that's all we need. + */ + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + InitProtection(); + + /* Don't protect the same process twice. */ + if (KphProtectFindEntry(processObject, NULL, NULL)) + { + status = STATUS_NOT_SUPPORTED; + goto IoControlEnd; + } + + if (!KphProtectAddEntry( + processObject, + PsGetCurrentProcessId(), + args->AllowKernelMode, + args->ProcessAllowMask, + args->ThreadAllowMask + )) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + } + break; + + /* Remove Process Protection */ + case KPH_PROTECTREMOVE: + { + struct + { + HANDLE ProcessHandle; + } *args = dataBuffer; + PEPROCESS processObject; + + /* Can't remove anything if process protection hasn't been initialized - + there isn't anything to remove. */ + if (!ProtectionInitialized) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + CHECK_IN_LENGTH; + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + if (!KphProtectRemoveByProcess(processObject)) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + } + break; + + /* Query Process Protection */ + case KPH_PROTECTQUERY: + { + struct + { + HANDLE ProcessHandle; + PLOGICAL AllowKernelMode; + PACCESS_MASK ProcessAllowMask; + PACCESS_MASK ThreadAllowMask; + } *args = dataBuffer; + PEPROCESS processObject; + KPH_PROCESS_ENTRY processEntry; + + /* Can't query anything if process protection hasn't been initialized - + there isn't anything to query. */ + if (!ProtectionInitialized) + { + status = STATUS_INVALID_PARAMETER; + goto IoControlEnd; + } + + CHECK_IN_LENGTH; + + __try + { + ProbeForWrite(args->AllowKernelMode, sizeof(LOGICAL), 1); + ProbeForWrite(args->ProcessAllowMask, sizeof(ACCESS_MASK), 1); + ProbeForWrite(args->ThreadAllowMask, sizeof(ACCESS_MASK), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto IoControlEnd; + } + + status = ObReferenceObjectByHandle( + args->ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + ObDereferenceObject(processObject); + + if (!KphProtectFindEntry(processObject, NULL, &processEntry)) + { + status = STATUS_UNSUCCESSFUL; + goto IoControlEnd; + } + + __try + { + *(args->AllowKernelMode) = processEntry.AllowKernelMode; + *(args->ProcessAllowMask) = processEntry.ProcessAllowMask; + *(args->ThreadAllowMask) = processEntry.ThreadAllowMask; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + break; + + /* KphUnsafeReadVirtualMemory + * + * Reads process memory or kernel memory. + */ + case KPH_UNSAFEREADVIRTUALMEMORY: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphUnsafeReadVirtualMemory( + args->ProcessHandle, + args->BaseAddress, + args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* Set Execute Options + * + * Sets NX status for a process. + */ + case KPH_SETEXECUTEOPTIONS: + { + struct + { + HANDLE ProcessHandle; + ULONG ExecuteOptions; + } *args = dataBuffer; + KPH_ATTACH_STATE attachState; + + CHECK_IN_LENGTH; + + status = KphAttachProcessHandle(args->ProcessHandle, &attachState); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = ZwSetInformationProcess( + NtCurrentProcess(), + ProcessExecuteFlags, + &args->ExecuteOptions, + sizeof(ULONG) + ); + KphDetachProcess(&attachState); + } + break; + + /* KphQueryProcessHandles + * + * Gets the handles in a process handle table. + */ + case KPH_QUERYPROCESSHANDLES: + { + struct + { + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphQueryProcessHandles( + args->ProcessHandle, + (PPROCESS_HANDLE_INFORMATION)args->Buffer, + args->BufferLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphOpenThreadProcess + * + * Opens the process associated with the specified thread. + */ + case KPH_OPENTHREADPROCESS: + { + struct + { + HANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + } *args = dataBuffer; + struct + { + HANDLE ProcessHandle; + } *ret = dataBuffer; + + CHECK_IN_OUT_LENGTH; + + status = KphOpenThreadProcess( + args->ThreadHandle, + args->DesiredAccess, + &ret->ProcessHandle, + KernelMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + retLength = sizeof(*ret); + } + break; + + /* KphCaptureStackBackTraceThread + * + * Captures a kernel stack trace for the specified thread. + */ + case KPH_CAPTURESTACKBACKTRACETHREAD: + { + struct + { + HANDLE ThreadHandle; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + PULONG CapturedFrames; + PULONG BackTraceHash; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphCaptureStackBackTraceThread( + args->ThreadHandle, + args->FramesToSkip, + args->FramesToCapture, + args->BackTrace, + args->CapturedFrames, + args->BackTraceHash, + UserMode + ); + } + break; + + /* KphDangerousTerminateThread + * + * Terminates the specified thread. This operation may cause a bugcheck. + */ + case KPH_DANGEROUSTERMINATETHREAD: + { + struct + { + HANDLE ThreadHandle; + NTSTATUS ExitStatus; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphDangerousTerminateThread(args->ThreadHandle, args->ExitStatus); + } + break; + + /* KphOpenDevice + * + * Opens a device object. + */ + case KPH_OPENDEVICE: + { + struct + { + PHANDLE DeviceHandle; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenDevice(args->DeviceHandle, args->ObjectAttributes, UserMode); + } + break; + + /* KphOpenDriver + * + * Opens a driver object. + */ + case KPH_OPENDRIVER: + { + struct + { + PHANDLE DriverHandle; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenDriver(args->DriverHandle, args->ObjectAttributes, UserMode); + } + break; + + /* KphQueryInformationDriver + * + * Queries information about a driver object. + */ + case KPH_QUERYINFORMATIONDRIVER: + { + struct + { + HANDLE DriverHandle; + DRIVER_INFORMATION_CLASS DriverInformationClass; + PVOID DriverInformation; + ULONG DriverInformationLength; + PULONG ReturnLength; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphQueryInformationDriver( + args->DriverHandle, + args->DriverInformationClass, + args->DriverInformation, + args->DriverInformationLength, + args->ReturnLength, + UserMode + ); + } + break; + + /* KphOpenDirectoryObject + * + * Opens a directory object. + */ + case KPH_OPENDIRECTORYOBJECT: + { + struct + { + PHANDLE DirectoryObjectHandle; + ACCESS_MASK DesiredAccess; + POBJECT_ATTRIBUTES ObjectAttributes; + } *args = dataBuffer; + + CHECK_IN_LENGTH; + + status = KphOpenDirectoryObject( + args->DirectoryObjectHandle, + args->DesiredAccess, + args->ObjectAttributes, + UserMode + ); + } + break; + + /* SsRef + * + * Adds a system service logging reference. + */ + case KPH_SSREF: + { + PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL); + + if (!clientEntry) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + KphAcquireGuardedLock(&clientEntry->SsLock); + + if (clientEntry->SsStartCount < KPH_CLIENT_SSMAXCOUNT) + { + clientEntry->SsStartCount++; + SsRef(1); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + KphReleaseGuardedLock(&clientEntry->SsLock); + + KphDereferenceObject(clientEntry); + } + break; + + /* SsUnref + * + * Removes a system service logging reference. + */ + case KPH_SSUNREF: + { + PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL); + + if (!clientEntry) + { + status = STATUS_INTERNAL_ERROR; + goto IoControlEnd; + } + + KphAcquireGuardedLock(&clientEntry->SsLock); + + if (clientEntry->SsStartCount > 0) + { + clientEntry->SsStartCount--; + SsUnref(1); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + KphReleaseGuardedLock(&clientEntry->SsLock); + + KphDereferenceObject(clientEntry); + } + break; + + /* SsCreateClientEntry + * + * Creates a system service logging client entry. + */ + case KPH_SSCREATECLIENTENTRY: + { + struct + { + HANDLE ProcessHandle; + HANDLE EventHandle; + HANDLE SemaphoreHandle; + PVOID BufferBase; + ULONG BufferSize; + } *args = dataBuffer; + struct + { + HANDLE ClientEntryHandle; + } *ret = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_OUT_LENGTH; + + status = KphSsCreateClientEntry( + &clientEntry, + args->ProcessHandle, + args->EventHandle, + args->SemaphoreHandle, + args->BufferBase, + args->BufferSize, + UserMode + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + status = CreateClientHandle(NULL, clientEntry, &ret->ClientEntryHandle); + KphDereferenceObject(clientEntry); + retLength = sizeof(*ret); + } + break; + + /* SsCreateRuleSetEntry + * + * Creates a system service logging ruleset entry. + */ + case KPH_SSCREATERULESETENTRY: + { + struct + { + HANDLE ClientEntryHandle; + KPHSS_FILTER_TYPE DefaultFilterType; + KPHSS_RULESET_ACTION Action; + } *args = dataBuffer; + struct + { + HANDLE RuleSetEntryHandle; + } *ret = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Create the ruleset entry. */ + status = KphSsCreateRuleSetEntry( + &ruleSetEntry, + clientEntry, + args->DefaultFilterType, + args->Action + ); + KphDereferenceObject(clientEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Create and return a handle to the ruleset entry. */ + status = CreateClientHandle(NULL, ruleSetEntry, &ret->RuleSetEntryHandle); + KphDereferenceObject(ruleSetEntry); + retLength = sizeof(*ret); + } + break; + + /* SsRemoveRule + * + * Removes a rule from a ruleset. + */ + case KPH_SSREMOVERULE: + { + struct + { + HANDLE RuleSetEntryHandle; + HANDLE RuleEntryHandle; + } *args = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + CHECK_IN_LENGTH; + + /* Reference the ruleset entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Remove the rule. */ + status = KphSsRemoveRule(ruleSetEntry, args->RuleEntryHandle); + KphDereferenceObject(ruleSetEntry); + } + break; + + /* SsAddProcessIdRule + * + * Adds a process ID rule to a ruleset. + */ + case KPH_SSADDPROCESSIDRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + HANDLE ProcessId; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a process ID rule. */ + status = KphSsAddProcessIdRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->ProcessId + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddThreadIdRule + * + * Adds a thread ID rule to a ruleset. + */ + case KPH_SSADDTHREADIDRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + HANDLE ThreadId; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a thread ID rule. */ + status = KphSsAddThreadIdRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->ThreadId + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddPreviousModeRule + * + * Adds a previous mode rule to a ruleset. + */ + case KPH_SSADDPREVIOUSMODERULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + KPROCESSOR_MODE PreviousMode; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a previous mode rule. */ + status = KphSsAddPreviousModeRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->PreviousMode + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsAddNumberRule + * + * Adds a system service number rule to a ruleset. + */ + case KPH_SSADDNUMBERRULE: + { + struct + { + HANDLE RuleSetEntryHandle; + KPHSS_FILTER_TYPE FilterType; + ULONG Number; + } *args = dataBuffer; + struct + { + HANDLE RuleEntryHandle; + } *ret = dataBuffer; + PKPHSS_RULESET_ENTRY ruleSetEntry; + PKPHSS_RULE_ENTRY ruleEntry; + + CHECK_IN_OUT_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->RuleSetEntryHandle, + KphSsRuleSetEntryType, + &ruleSetEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Add a number rule. */ + status = KphSsAddNumberRule( + &ruleEntry, + ruleSetEntry, + args->FilterType, + args->Number + ); + KphDereferenceObject(ruleSetEntry); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Return the rule handle. */ + ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry); + KphDereferenceObject(ruleEntry); + retLength = sizeof(*ret); + } + break; + + /* SsEnableClientEntry + * + * Enables or disables a client entry. + */ + case KPH_SSENABLECLIENTENTRY: + { + struct + { + HANDLE ClientEntryHandle; + BOOLEAN Enable; + } *args = dataBuffer; + PKPHSS_CLIENT_ENTRY clientEntry; + + CHECK_IN_LENGTH; + + /* Reference the client entry. */ + status = ReferenceClientHandle( + NULL, + args->ClientEntryHandle, + KphSsClientEntryType, + &clientEntry + ); + + if (!NT_SUCCESS(status)) + goto IoControlEnd; + + /* Enable/disable the client entry. */ + status = KphSsEnableClientEntry(clientEntry, args->Enable); + KphDereferenceObject(clientEntry); + } + break; + + default: + { + dprintf("Unrecognized IOCTL code 0x%08x\n", controlCode); + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + /* Restore the old packing. */ + #include + +IoControlEnd: + Irp->IoStatus.Information = retLength; + Irp->IoStatus.Status = status; + dprintf("IOCTL 0x%08x result was 0x%08x\n", controlCode, status); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION ioStackIrp = NULL; + ULONG retLength = 0; + + ioStackIrp = IoGetCurrentIrpStackLocation(Irp); + + if (ioStackIrp != NULL) + { + PCHAR readDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer; + ULONG readLength = ioStackIrp->Parameters.Read.Length; + + if (readDataBuffer != NULL) + { + dprintf("Client read %d bytes!\n", readLength); + + if (readLength == 4) + { + *(ULONG *)readDataBuffer = KPH_CTL_CODE(0); + retLength = 4; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + } + + Irp->IoStatus.Information = retLength; + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + dfprintf("Unsupported function called.\n"); + + return STATUS_NOT_SUPPORTED; +} diff --git a/branches/ph-plugins/KProcessHacker/makefile b/branches/ph-plugins/KProcessHacker/makefile new file mode 100644 index 000000000..05a507be4 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/branches/ph-plugins/KProcessHacker/mm.c b/branches/ph-plugins/KProcessHacker/mm.c new file mode 100644 index 000000000..84492ab34 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/mm.c @@ -0,0 +1,703 @@ +/* + * Process Hacker Driver - + * memory manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/mm.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphReadVirtualMemory) +#pragma alloc_text(PAGE, KphUnsafeReadVirtualMemory) +#pragma alloc_text(PAGE, KphWriteVirtualMemory) +#pragma alloc_text(PAGE, MiDoMappedCopy) +#pragma alloc_text(PAGE, MiDoPoolCopy) +#pragma alloc_text(PAGE, MiGetExceptionInfo) +#pragma alloc_text(PAGE, MmCopyVirtualMemory) +#endif + +/* KphReadVirtualMemory + * + * Reads virtual memory from the specified process. + */ +NTSTATUS KphReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + ULONG returnLength = 0; + + /* Probe user input if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + /* If we actually have work to do, reference the process object and + call the internal function. */ + if (BufferLength) + { + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_READ, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = MmCopyVirtualMemory( + processObject, + BaseAddress, + PsGetCurrentProcess(), + Buffer, + BufferLength, + AccessMode, + &returnLength + ); + ObDereferenceObject(processObject); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +NTSTATUS KphUnsafeReadVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength = 0; + + /* Initial probing. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + ProbeForWrite(Buffer, BufferLength, 1); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Make sure we have something to copy. */ + if (BufferLength == 0) + { + __try + { + *ReturnLength = 0; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + return STATUS_SUCCESS; + } + + /* Select the appropriate copy method. */ + if (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) + { + /* Kernel memory unsafe copy. */ + + __try + { + /* Probe the address range. */ + KphProbeSystemAddressRange(BaseAddress, BufferLength); + + /* Copy the data. */ + memcpy(Buffer, BaseAddress, BufferLength); + returnLength = BufferLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + } + else + { + /* User memory safe copy. */ + status = KphReadVirtualMemory( + ProcessHandle, + BaseAddress, + Buffer, + BufferLength, + ReturnLength, + AccessMode + ); + } + + return status; +} + +/* KphWriteVirtualMemory + * + * Writes virtual memory to the specified process. + */ +NTSTATUS KphWriteVirtualMemory( + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + ULONG returnLength = 0; + + /* Probe user input if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + { + if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) || + (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress)) + { + return STATUS_ACCESS_VIOLATION; + } + + __try + { + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + /* If we actually have work to do, reference the process object and + call the internal function. */ + if (BufferLength) + { + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_WRITE, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = MmCopyVirtualMemory( + PsGetCurrentProcess(), + Buffer, + processObject, + BaseAddress, + BufferLength, + AccessMode, + &returnLength + ); + ObDereferenceObject(processObject); + } + + if (ReturnLength) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* MiDoMappedCopy + * + * Copies virtual memory from the source process to the target process + * using a memory mapping. + */ +NTSTATUS MiDoMappedCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; + PMDL mdl = (PMDL)mdlBuffer; + /* The mapped address. */ + PVOID mappedAddress; + /* The total size allocated (mapped pages). */ + ULONG totalSize; + /* The block size. */ + ULONG blockSize; + /* The amount still left to copy. */ + ULONG stillToCopy; + /* Attach state. */ + KPH_ATTACH_STATE attachState; + /* The current source address. */ + PVOID sourceAddress; + /* The current target address. */ + PVOID targetAddress; + /* Whether the pages have been locked. */ + BOOLEAN pagesLocked; + /* Whether we are currently copying. */ + BOOLEAN copying = FALSE; + /* Whether we are currently probing. */ + BOOLEAN probing = FALSE; + /* Whether we are currently mapping. */ + BOOLEAN mapping = FALSE; + /* Whether we have the bad address. */ + BOOLEAN haveBadAddress; + /* The bad address of the exception. */ + ULONG_PTR badAddress; + + sourceAddress = FromAddress; + targetAddress = ToAddress; + + totalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE; + + if (BufferLength <= totalSize) + totalSize = BufferLength; + + stillToCopy = BufferLength; + blockSize = totalSize; + + while (stillToCopy) + { + /* If we're at the last copy block, copy the remaining bytes instead + of the whole block size. */ + if (stillToCopy < blockSize) + blockSize = stillToCopy; + + /* Reset state. */ + mappedAddress = NULL; + pagesLocked = FALSE; + copying = FALSE; + + KphAttachProcess(FromProcess, &attachState); + + __try + { + /* Probe only if this is the first time. */ + if ((sourceAddress == FromAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForRead(sourceAddress, BufferLength, 1); + probing = FALSE; + } + + /* Initialize the MDL. */ + MmInitializeMdl(mdl, sourceAddress, blockSize); + MmProbeAndLockPages(mdl, AccessMode, IoReadAccess); + pagesLocked = TRUE; + + /* Map the pages. */ + mappedAddress = MmMapLockedPagesSpecifyCache( + mdl, + KernelMode, + MmCached, + NULL, + FALSE, + HighPagePriority + ); + + if (!mappedAddress) + { + /* Insufficient resources; exit. */ + mapping = TRUE; + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + + KphDetachProcess(&attachState); + + /* Attach to the target process and copy the mapped contents. */ + KphAttachProcess(ToProcess, &attachState); + + /* Probe only if this is the first time. */ + if ((targetAddress == ToAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForWrite(targetAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the data. */ + copying = TRUE; + memcpy(targetAddress, mappedAddress, blockSize); + } + __except (MiGetExceptionInfo( + GetExceptionInformation(), + &haveBadAddress, + &badAddress + )) + { + KphDetachProcess(&attachState); + + /* If we mapped the pages, unmap them. */ + if (mappedAddress) + MmUnmapLockedPages(mappedAddress, mdl); + + /* If we locked the pages, unlock them. */ + if (pagesLocked) + MmUnlockPages(mdl); + + /* If we failed when probing or mapping, return the error code. */ + if (probing || mapping) + return GetExceptionCode(); + + /* Otherwise, give the caller the number of bytes we copied. */ + *ReturnLength = BufferLength - stillToCopy; + + /* If we were copying, we can probably get the exact + number of bytes copied. */ + if (copying && haveBadAddress) + *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); + + return STATUS_PARTIAL_COPY; + } + + KphDetachProcess(&attachState); + MmUnmapLockedPages(mappedAddress, mdl); + MmUnlockPages(mdl); + + stillToCopy -= blockSize; + sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); + targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); + } + + *ReturnLength = BufferLength; + + return STATUS_SUCCESS; +} + +/* MiDoPoolCopy + * + * Copies virtual memory from the source process to the target process + * using either a pool allocation or a stack buffer. + */ +NTSTATUS MiDoPoolCopy( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + /* The size of the pool-allocated buffer. */ + ULONG allocSize = MI_MAX_TRANSFER_SIZE; + /* The stack-based buffer. */ + CHAR stackBuffer[MI_COPY_STACK_SIZE]; + /* The buffer - could be from the pool or could be the stack buffer. */ + PVOID buffer = NULL; + /* The block size - should be the same as the allocated size. */ + ULONG blockSize; + /* The amount still left to copy. */ + ULONG stillToCopy; + /* Attach state. */ + KPH_ATTACH_STATE attachState; + /* The current source address. */ + PVOID sourceAddress; + /* The current target address. */ + PVOID targetAddress; + /* Whether we are currently copying. */ + BOOLEAN copying = FALSE; + /* Whether we are currently probing. */ + BOOLEAN probing = FALSE; + /* Whether we have the bad address. */ + BOOLEAN haveBadAddress; + /* The bad address of the exception. */ + ULONG_PTR badAddress; + + sourceAddress = FromAddress; + targetAddress = ToAddress; + + /* Don't allocate a buffer larger than the amount we're about to copy. */ + if (allocSize > BufferLength) + allocSize = BufferLength; + + /* If we're copying MI_COPY_STACK_SIZE bytes or less, use the stack buffer. */ + if (BufferLength <= MI_COPY_STACK_SIZE) + { + buffer = stackBuffer; + } + else + { + /* Keep on trying to allocate a buffer, halving the size each time + we fail. */ + while (TRUE) + { + buffer = ExAllocatePoolWithTag(NonPagedPool, allocSize, TAG_POOL_COPY); + + /* Stop trying if we got a buffer. */ + if (buffer) + break; + + /* Otherwise, halve the size and try again. */ + allocSize /= 2; + /* Could we use the stack buffer? */ + if (allocSize <= MI_COPY_STACK_SIZE) + { + buffer = stackBuffer; + break; + } + } + } + + stillToCopy = BufferLength; + blockSize = allocSize; + + /* Perform the copy in blocks of blockSize. */ + while (stillToCopy) + { + /* If we're at the last copy block, copy the remaining bytes instead + of the whole block size. */ + if (stillToCopy < blockSize) + blockSize = stillToCopy; + + copying = FALSE; + KphAttachProcess(FromProcess, &attachState); + + __try + { + /* Probe before reading the source contents. */ + /* Probe only if this is the first time. */ + if ((sourceAddress == FromAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForRead(sourceAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the source contents to the buffer. */ + memcpy(buffer, sourceAddress, blockSize); + KphDetachProcess(&attachState); + + /* Probe before writing. */ + KphAttachProcess(ToProcess, &attachState); + + /* Probe only if this is the first time. */ + if ((targetAddress == ToAddress) && (AccessMode != KernelMode)) + { + probing = TRUE; + ProbeForWrite(targetAddress, BufferLength, 1); + probing = FALSE; + } + + /* Copy the buffer contents to the destination. */ + copying = TRUE; + memcpy(targetAddress, buffer, blockSize); + } + __except (MiGetExceptionInfo( + GetExceptionInformation(), + &haveBadAddress, + &badAddress + )) + { + KphDetachProcess(&attachState); + + /* Free the allocated buffer if needed. */ + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, TAG_POOL_COPY); + + /* If we were probing an address, return the error code. */ + if (probing) + return GetExceptionCode(); + + /* Otherwise, give the caller the number of bytes we copied. */ + *ReturnLength = BufferLength - stillToCopy; + + /* If we were copying, we can probably get the exact + number of bytes copied. */ + if (copying && haveBadAddress) + *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); + + return STATUS_PARTIAL_COPY; + } + + KphDetachProcess(&attachState); + + stillToCopy -= blockSize; + sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); + targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); + } + + /* Free the buffer if it wasn't stack-allocated. */ + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, TAG_POOL_COPY); + + *ReturnLength = BufferLength; + + return STATUS_SUCCESS; +} + +ULONG MiGetExceptionInfo( + __in PEXCEPTION_POINTERS ExceptionInfo, + __out PBOOLEAN HaveBadAddress, + __out PULONG_PTR BadAddress + ) +{ + PEXCEPTION_RECORD exceptionRecord; + + *HaveBadAddress = FALSE; + exceptionRecord = ExceptionInfo->ExceptionRecord; + + if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) + { + if (exceptionRecord->NumberParameters > 1) + { + /* We have the address. */ + *HaveBadAddress = TRUE; + *BadAddress = exceptionRecord->ExceptionInformation[1]; + } + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +NTSTATUS MmCopyVirtualMemory( + __in PEPROCESS FromProcess, + __in PVOID FromAddress, + __in PEPROCESS ToProcess, + __in PVOID ToAddress, + __in ULONG BufferLength, + __in KPROCESSOR_MODE AccessMode, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processToLock = FromProcess; + + if (!BufferLength) + return STATUS_SUCCESS; + + /* If we're copying from the current process, lock the target. */ + if (processToLock == PsGetCurrentProcess()) + processToLock = ToProcess; + + /* Prevent the process from terminating. */ + if (!KphAcquireProcessRundownProtection(processToLock)) + return STATUS_PROCESS_IS_TERMINATING; + + /* If the amount we're trying to copy is over the threshold + for MiDoPoolCopy, use MiDoMappedCopy. */ + if (BufferLength > MM_POOL_COPY_THRESHOLD) + { + status = MiDoMappedCopy( + FromProcess, + FromAddress, + ToProcess, + ToAddress, + BufferLength, + AccessMode, + ReturnLength + ); + } + else + { + status = MiDoPoolCopy( + FromProcess, + FromAddress, + ToProcess, + ToAddress, + BufferLength, + AccessMode, + ReturnLength + ); + } + + /* Allow the process to terminate. */ + KphReleaseProcessRundownProtection(processToLock); + + return status; +} diff --git a/branches/ph-plugins/KProcessHacker/ob.c b/branches/ph-plugins/KProcessHacker/ob.c new file mode 100644 index 000000000..21255b0e8 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/ob.c @@ -0,0 +1,847 @@ +/* + * Process Hacker Driver - + * object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/ob.h" + +BOOLEAN KphpQueryProcessHandlesEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_QUERY_PROCESS_HANDLES_DATA Context + ); + +BOOLEAN KphpSetHandleGrantedAccessEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphDuplicateObject) +#pragma alloc_text(PAGE, ObDuplicateObject) +#endif + +/* This attribute is now stored in the GrantedAccess field. */ +ULONG ObpAccessProtectCloseBit = 0x80000000; + +/* KphDuplicateObject + * + * Duplicates a handle from the source process to the target process. + */ +NTSTATUS KphDuplicateObject( + __in HANDLE SourceProcessHandle, + __in HANDLE SourceHandle, + __in_opt HANDLE TargetProcessHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS sourceProcess = NULL; + PEPROCESS targetProcess = NULL; + HANDLE targetHandle; + + if (TargetHandle && AccessMode != KernelMode) + { + __try + { + ProbeForWrite(TargetHandle, sizeof(HANDLE), 1); + *TargetHandle = NULL; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return STATUS_ACCESS_VIOLATION; + } + } + + status = ObReferenceObjectByHandle( + SourceProcessHandle, + PROCESS_DUP_HANDLE, + *PsProcessType, + KernelMode, + &sourceProcess, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Target handle is optional. */ + if (TargetProcessHandle) + { + status = ObReferenceObjectByHandle( + TargetProcessHandle, + PROCESS_DUP_HANDLE, + *PsProcessType, + KernelMode, + &targetProcess, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + } + + /* Fix the source handle if the source process is + * the system process. + */ + if (sourceProcess == PsInitialSystemProcess) + MakeKernelHandle(SourceHandle); + + /* Call the internal function. */ + status = ObDuplicateObject( + sourceProcess, + targetProcess, + SourceHandle, + &targetHandle, + DesiredAccess, + HandleAttributes, + Options, + AccessMode + ); + + if (TargetHandle) + { + __try + { + *TargetHandle = targetHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = STATUS_ACCESS_VIOLATION; + } + } + + ObDereferenceObject(sourceProcess); + if (targetProcess) + ObDereferenceObject(targetProcess); + + return status; +} + +/* KphEnumProcessHandleTable + * + * Enumerates the handles in the specified process' handle table. + */ +BOOLEAN KphEnumProcessHandleTable( + __in PEPROCESS Process, + __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + __inout PVOID Context, + __out_opt PHANDLE Handle + ) +{ + BOOLEAN result = FALSE; + PHANDLE_TABLE handleTable = NULL; + + handleTable = ObReferenceProcessHandleTable(Process); + + if (!handleTable) + return FALSE; + + result = ExEnumHandleTable( + handleTable, + EnumHandleProcedure, + Context, + Handle + ); + ObDereferenceProcessHandleTable(Process); + + return result; +} + +/* KphGetObjectTypeNt + * + * Gets the type of an object. + */ +POBJECT_TYPE KphGetObjectTypeNt( + __in PVOID Object + ) +{ + /* XP to Vista: A pointer to the object type is + * stored in the object header. + */ + if ( + WindowsVersion >= WINDOWS_XP && + WindowsVersion <= WINDOWS_VISTA + ) + { + return OBJECT_TO_OBJECT_HEADER(Object)->Type; + } + /* Seven and above: An index to an internal object type + * table is stored in the object header. Luckily we have + * a new exported function, ObGetObjectType, to get + * the object type. + */ + else if (WindowsVersion >= WINDOWS_7) + { + return ObGetObjectType(Object); + } + else + { + return NULL; + } +} + +/* KphOpenDirectoryObject + * + * Opens a directory object. + */ +NTSTATUS KphOpenDirectoryObject( + __out PHANDLE DirectoryObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in KPROCESSOR_MODE AccessMode + ) +{ + return KphOpenNamedObject( + DirectoryObjectHandle, + DesiredAccess, + ObjectAttributes, + NULL, + AccessMode + ); +} + +/* KphOpenNamedObject + * + * Opens a named object. + */ +NTSTATUS KphOpenNamedObject( + __out PHANDLE ObjectHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in POBJECT_TYPE ObjectType, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + HANDLE objectHandle; + UNICODE_STRING capturedObjectName; + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + + if (!ObjectAttributes) + return STATUS_INVALID_PARAMETER; + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ObjectHandle, sizeof(HANDLE), 1); + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1); + + if (ObjectAttributes->ObjectName) + KphProbeForReadUnicodeString(ObjectAttributes->ObjectName); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + __try + { + /* Verify parameters. */ + if (!ObjectAttributes->ObjectName) + return STATUS_INVALID_PARAMETER; + + /* Copy the object attributes structure. */ + memcpy(&objectAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Capture the ObjectName string. */ + status = KphCaptureUnicodeString( + ObjectAttributes->ObjectName, + &capturedObjectName + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Set the new string in the object attributes. */ + objectAttributes.ObjectName = &capturedObjectName; + /* Make sure the SecurityDescriptor and SecurityQualityOfService fields are NULL + * since we haven't probed them. + */ + objectAttributes.SecurityDescriptor = NULL; + objectAttributes.SecurityQualityOfService = NULL; + + /* Open the object. */ + status = ObOpenObjectByName( + &objectAttributes, + ObjectType, + KernelMode, + NULL, + DesiredAccess, + NULL, + &objectHandle + ); + + /* Free the captured ObjectName. */ + KphFreeCapturedUnicodeString(&capturedObjectName); + + /* Pass the handle back. */ + __try + { + *ObjectHandle = objectHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + return status; +} + +/* KphQueryFileObjectName + * + * Queries the name of a file object. + * + * Technique from YAPM. + */ +NTSTATUS KphQueryNameFileObject( + __in PFILE_OBJECT FileObject, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength; + PCHAR objectName; + ULONG usedLength; + ULONG subNameLength; + PFILE_OBJECT relatedFileObject; + + /* We need at least the size of UNICODE_STRING to + * continue. + */ + if (BufferLength < sizeof(UNICODE_STRING)) + { + *ReturnLength = sizeof(UNICODE_STRING); + + return STATUS_BUFFER_TOO_SMALL; + } + + /* Assume failure. */ + Buffer->Length = 0; + /* We will place the object name directly after the + * UNICODE_STRING structure in the buffer. + */ + Buffer->Buffer = (PWSTR)PTR_ADD_OFFSET(Buffer, sizeof(UNICODE_STRING)); + /* Retain a local pointer to the object name so we + * can manipulate the pointer. + */ + objectName = (PCHAR)Buffer->Buffer; + /* A variable that keeps track of how much space we + * have used. + */ + usedLength = sizeof(UNICODE_STRING); + + /* Check if the file object has an associated device + * (e.g. "\Device\NamedPipe", "\Device\Mup"). We can + * use the user-supplied buffer for this since if the + * buffer isn't big enough, we can't proceed anyway + * (we are going to use the name). + */ + if (FileObject->DeviceObject) + { + status = ObQueryNameString( + FileObject->DeviceObject, + (POBJECT_NAME_INFORMATION)Buffer, + BufferLength, + &returnLength + ); + + if (!NT_SUCCESS(status)) + { + *ReturnLength = returnLength; + + return status; + } + + /* The UNICODE_STRING in the buffer is now filled in. + * We will append to the object name later, so + * we need to fix the object name pointer by adding + * the length, in bytes, of the device name string we + * just got. + */ + objectName += Buffer->Length; + usedLength += Buffer->Length; + } + + /* Check if the file object has a file name component. If not, + * we can't do anything else, so we just return the name we + * have already. + */ + if (!FileObject->FileName.Buffer) + { + *ReturnLength = usedLength; + + return STATUS_SUCCESS; + } + + /* The file object has a name. We need to walk up the file + * object tree and append the names of the related file + * objects in reverse order. This means we need to calculate + * the total length first. + */ + + relatedFileObject = FileObject; + subNameLength = 0; + + do + { + subNameLength += relatedFileObject->FileName.Length; + + /* Avoid infinite loops. */ + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + usedLength += subNameLength; + + /* Check if we have enough space to write the whole thing. */ + if (usedLength > BufferLength) + { + *ReturnLength = usedLength; + + return STATUS_BUFFER_TOO_SMALL; + } + + /* We're ready to begin copying the names. */ + + /* Add the name length because we're copying in reverse order. */ + objectName += subNameLength; + + relatedFileObject = FileObject; + + do + { + objectName -= relatedFileObject->FileName.Length; + memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length); + + /* Avoid infinite loops. */ + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } + while (relatedFileObject); + + /* Update the length. */ + Buffer->Length += (USHORT)subNameLength; + + /* Pass the return length back. */ + *ReturnLength = usedLength; + + return STATUS_SUCCESS; +} + +/* KphQueryObjectName + * + * Queries the name of an object. + */ +NTSTATUS KphQueryNameObject( + __in PVOID Object, + __inout_bcount(BufferLength) PUNICODE_STRING Buffer, + __in ULONG BufferLength, + __out PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + POBJECT_TYPE objectType; + + objectType = KphGetObjectTypeNt(Object); + + /* Check if we are going to hang when querying the object, and use + * the special file object query function if needed. + */ + if ( + (objectType == *IoFileObjectType) && + (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters) + ) + { + status = KphQueryNameFileObject((PFILE_OBJECT)Object, Buffer, BufferLength, ReturnLength); + } + else + { + status = ObQueryNameString(Object, (POBJECT_NAME_INFORMATION)Buffer, BufferLength, ReturnLength); + } + + return status; +} + +/* KphQueryProcessHandles + * + * Queries a process handle table. + */ +NTSTATUS KphQueryProcessHandles( + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + BOOLEAN result; + PEPROCESS processObject; + OBP_QUERY_PROCESS_HANDLES_DATA context; + + /* Probe buffer contents. */ + if (AccessMode != KernelMode) + { + __try + { + if (Buffer) + ProbeForWrite(Buffer, BufferLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Reference the process object. */ + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_QUERY_INFORMATION, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the enumeration context. */ + context.Buffer = Buffer; + context.BufferLength = BufferLength; + context.CurrentIndex = 0; + context.Status = STATUS_SUCCESS; + + /* Enumerate the handles. */ + result = KphEnumProcessHandleTable( + processObject, + KphpQueryProcessHandlesEnumCallback, + &context, + NULL + ); + ObDereferenceObject(processObject); + + /* Write the number of handles (if we have a buffer). */ + if ( + Buffer && + BufferLength >= sizeof(ULONG) + ) + { + __try + { + Buffer->HandleCount = context.CurrentIndex; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Supply the return length if the caller wanted it. */ + if (ReturnLength) + { + __try + { + /* CurrentIndex should contain the number of handles, so we simply multiply it + by the size of PROCESS_HANDLE. */ + *ReturnLength = sizeof(ULONG) + context.CurrentIndex * sizeof(PROCESS_HANDLE); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + return context.Status; +} + +/* KphpQueryProcessHandlesEnumCallback + * + * The callback for KphEnumProcessHandleTable, used by + * KphQueryProcessHandles. + */ +BOOLEAN KphpQueryProcessHandlesEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_QUERY_PROCESS_HANDLES_DATA Context + ) +{ + PROCESS_HANDLE handleInfo; + PPROCESS_HANDLE_INFORMATION buffer = Context->Buffer; + ULONG i; + + handleInfo.Handle = Handle; + handleInfo.Object = ObpDecodeObject(HandleTableEntry->Object); + handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess); + handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry); + + /* Increment the index regardless of whether the information will be written; + this will allow KphQueryProcessHandles to report the correct return length. */ + i = Context->CurrentIndex++; + + /* Only write if we have a buffer and have not exceeded the buffer length. */ + if ( + buffer && + (sizeof(ULONG) + Context->CurrentIndex * sizeof(PROCESS_HANDLE)) <= Context->BufferLength + ) + { + __try + { + buffer->Handles[i] = handleInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* Report an error. */ + if (Context->Status == STATUS_SUCCESS) + Context->Status = GetExceptionCode(); + } + } + else + { + /* Report that the buffer is too small. */ + if (Context->Status == STATUS_SUCCESS) + Context->Status = STATUS_BUFFER_TOO_SMALL; + } + + return FALSE; +} + +/* KphSetHandleGrantedAccess + * + * Sets the granted access of a handle. + */ +NTSTATUS KphSetHandleGrantedAccess( + __in PEPROCESS Process, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ) +{ + BOOLEAN result; + OBP_SET_HANDLE_GRANTED_ACCESS_DATA context; + + context.Handle = Handle; + context.GrantedAccess = GrantedAccess; + + result = KphEnumProcessHandleTable( + Process, + KphpSetHandleGrantedAccessEnumCallback, + &context, + NULL + ); + + return result ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +/* KphpSetHandleGrantedAccessEnumCallback + * + * The callback for KphEnumProcessHandleTable, used by + * KphSetHandleGrantedAccess. + */ +BOOLEAN KphpSetHandleGrantedAccessEnumCallback( + __inout PHANDLE_TABLE_ENTRY HandleTableEntry, + __in HANDLE Handle, + __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context + ) +{ + if (Handle != Context->Handle) + return FALSE; + + HandleTableEntry->GrantedAccess = Context->GrantedAccess; + + return TRUE; +} + +/* ObDereferenceProcessHandleTable + * + * Allows the process to terminate. + */ +VOID ObDereferenceProcessHandleTable( + __in PEPROCESS Process + ) +{ + KphReleaseProcessRundownProtection(Process); +} + +/* ObDuplicateObject + * + * Duplicates a handle from the source process to the target process. + * WARNING: This does not actually duplicate a handle. It simply + * re-opens an object in another process. + */ +NTSTATUS ObDuplicateObject( + __in PEPROCESS SourceProcess, + __in_opt PEPROCESS TargetProcess, + __in HANDLE SourceHandle, + __out_opt PHANDLE TargetHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG HandleAttributes, + __in ULONG Options, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN sourceAttached = FALSE; + BOOLEAN targetAttached = FALSE; + KAPC_STATE apcState; + PVOID object; + HANDLE objectHandle; + + /* Validate the parameters */ + if (!TargetProcess || !TargetHandle) + { + if (!(Options & DUPLICATE_CLOSE_SOURCE)) + return STATUS_INVALID_PARAMETER; + } + + /* Check if we need to attach to the source process */ + if (SourceProcess != PsGetCurrentProcess()) + { + KeStackAttachProcess(SourceProcess, &apcState); + sourceAttached = TRUE; + } + + /* If the caller wants us to close the source handle, do it now */ + if (Options & DUPLICATE_CLOSE_SOURCE) + { + status = NtClose(SourceHandle); + if (sourceAttached) + KeUnstackDetachProcess(&apcState); + + return status; + } + + /* Reference the object and detach from the source process */ + status = ObReferenceObjectByHandle( + SourceHandle, + 0, + NULL, + KernelMode, + &object, + NULL + ); + if (sourceAttached) + KeUnstackDetachProcess(&apcState); + + if (!NT_SUCCESS(status)) + return status; + + /* Check if we need to attach to the target process */ + if (TargetProcess != PsGetCurrentProcess()) + { + KeStackAttachProcess(TargetProcess, &apcState); + targetAttached = TRUE; + } + + /* Open the object and detach from the target process */ + { + POBJECT_TYPE objectType = KphGetObjectTypeNt(object); + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + if (!objectType && AccessMode != KernelMode) + { + status = STATUS_INVALID_HANDLE; + goto OpenObjectEnd; + } + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(objectType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + goto OpenObjectEnd; + + accessState.PreviouslyGrantedAccess |= 0xffffffff; /* HACK, doesn't work properly */ + accessState.RemainingDesiredAccess = 0; + + status = ObOpenObjectByPointer( + object, + HandleAttributes, + &accessState, + DesiredAccess, + objectType, + KernelMode, + &objectHandle + ); + SeDeleteAccessState(&accessState); + } + +OpenObjectEnd: + ObDereferenceObject(object); + + if (targetAttached) + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + *TargetHandle = objectHandle; + else + *TargetHandle = NULL; + + return status; +} + +/* ObReferenceProcessHandleTable + * + * Prevents the process from terminating and returns a pointer + * to its handle table. + */ +PHANDLE_TABLE ObReferenceProcessHandleTable( + __in PEPROCESS Process + ) +{ + PHANDLE_TABLE handleTable = NULL; + + if (KphAcquireProcessRundownProtection(Process)) + { + handleTable = *(PHANDLE_TABLE *)KVOFF(Process, OffEpObjectTable); + + if (!handleTable) + KphReleaseProcessRundownProtection(Process); + } + + return handleTable; +} diff --git a/branches/ph-plugins/KProcessHacker/protect.c b/branches/ph-plugins/KProcessHacker/protect.c new file mode 100644 index 000000000..406c32019 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/protect.c @@ -0,0 +1,457 @@ +/* + * Process Hacker Driver - + * process protection + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/protect.h" + +BOOLEAN KphpIsAccessAllowed( + __in PVOID Object, + __in KPROCESSOR_MODE AccessMode, + __in ACCESS_MASK DesiredAccess + ); + +BOOLEAN KphpIsCurrentProcessProtected(); + +VOID KphpProtectRemoveEntry( + __in PKPH_PROCESS_ENTRY Entry + ); + +/* ProtectedProcessRundownProtect + * + * Rundown protection making sure this module doesn't deinitialize before all hook targets + * have finished executing and no one is accessing the lookaside list. + */ +static EX_RUNDOWN_REF ProtectedProcessRundownProtect; +/* ProtectedProcessListHead + * + * The head of the process protection linked list. Each entry stores protection + * information for a process. + */ +static LIST_ENTRY ProtectedProcessListHead; +/* ProtectedProcessListLock + * + * The spinlock which protects all accesses to the protected process list (even + * the individual entries) + */ +static KSPIN_LOCK ProtectedProcessListLock; +/* ProtectedProcessLookasideList + * + * The lookaside list for protected process entries. + */ +static NPAGED_LOOKASIDE_LIST ProtectedProcessLookasideList; + +static KPH_OB_OPEN_HOOK ProcessOpenHook = { 0 }; +static KPH_OB_OPEN_HOOK ThreadOpenHook = { 0 }; + +/* KphProtectInit + * + * Initializes process protection. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphProtectInit() +{ + NTSTATUS status; + + /* Initialize rundown protection. */ + ExInitializeRundownProtection(&ProtectedProcessRundownProtect); + /* Initialize list structures. */ + InitializeListHead(&ProtectedProcessListHead); + KeInitializeSpinLock(&ProtectedProcessListLock); + ExInitializeNPagedLookasideList( + &ProtectedProcessLookasideList, + NULL, + NULL, + 0, + sizeof(KPH_PROCESS_ENTRY), + TAG_PROTECTION_ENTRY, + 0 + ); + + /* Hook various functions. */ + /* Hooking the open procedure calls for processes and threads allows + * us to intercept handle creation/duplication/inheritance. */ + KphInitializeObOpenHook(&ProcessOpenHook, *PsProcessType, KphNewOpenProcedure51, KphNewOpenProcedure60); + if (!NT_SUCCESS(status = KphObOpenHook(&ProcessOpenHook))) + return status; + KphInitializeObOpenHook(&ThreadOpenHook, *PsThreadType, KphNewOpenProcedure51, KphNewOpenProcedure60); + if (!NT_SUCCESS(status = KphObOpenHook(&ThreadOpenHook))) + return status; + + return STATUS_SUCCESS; +} + +/* KphProtectDeinit + * + * Removes process protection and frees associated structures. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphProtectDeinit() +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + LARGE_INTEGER waitLi; + + /* Unhook. */ + status = KphObOpenUnhook(&ProcessOpenHook); + status = KphObOpenUnhook(&ThreadOpenHook); + + /* Wait for all activity to finish. */ + ExWaitForRundownProtectionRelease(&ProtectedProcessRundownProtect); + /* Wait for a bit (some regions of hook target functions + are NOT guarded by rundown protection, e.g. + prologues and epilogues). */ + waitLi.QuadPart = KPH_REL_TIMEOUT_IN_SEC(1); + KeDelayExecutionThread(KernelMode, FALSE, &waitLi); + + /* Free all process protection entries. */ + ExDeleteNPagedLookasideList(&ProtectedProcessLookasideList); + + return status; +} + +/* KphNewOpenProcedure51 + * + * New process/thread open procedure for NT 5.1. + */ +NTSTATUS NTAPI KphNewOpenProcedure51( + __in OB_OPEN_REASON OpenReason, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + /* Simply call the 6.0 open procedure. */ + /* NOTE: GrantedAccess is always 0 on XP... */ + return KphNewOpenProcedure60( + OpenReason, + /* Assume worst case. */ + UserMode, + Process, + Object, + GrantedAccess, + HandleCount + ); +} + +/* KphNewOpenProcedure60 + * + * New process/thread open procedure for NT 6.0 and 6.1. + */ +NTSTATUS NTAPI KphNewOpenProcedure60( + __in OB_OPEN_REASON OpenReason, + __in KPROCESSOR_MODE AccessMode, + __in PEPROCESS Process, + __in PVOID Object, + __in ACCESS_MASK GrantedAccess, + __in ULONG HandleCount + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN accessAllowed = TRUE; + + /* Prevent the driver from unloading while this routine is executing. */ + if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect)) + { + /* Should never happen. */ + return STATUS_INTERNAL_ERROR; + } + + accessAllowed = KphpIsAccessAllowed( + Object, + AccessMode, + /* Assume worst case if granted access not available. */ + !GrantedAccess ? (ACCESS_MASK)-1 : GrantedAccess + ); + + if (accessAllowed) + { + POBJECT_TYPE objectType = KphGetObjectTypeNt(Object); + + /* Call the original open procedure. There shouldn't be any for Windows XP, + * while on Windows Vista and 7 it is used for implementing protected + * processes (Big Content's DRM protection, not KProcessHacker's protection). + */ + status = KphObOpenCall( + objectType == *PsProcessType ? &ProcessOpenHook : &ThreadOpenHook, + OpenReason, + AccessMode, + Process, + Object, + GrantedAccess, + HandleCount + ); + } + else + { + dprintf("KphNewOpenProcedure60: Access denied.\n"); + status = STATUS_ACCESS_DENIED; + } + + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + return status; +} + +/* KphProtectAddEntry + * + * Protects the specified process. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +PKPH_PROCESS_ENTRY KphProtectAddEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __in LOGICAL AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ) +{ + KIRQL oldIrql; + PKPH_PROCESS_ENTRY entry; + + /* Prevent the lookaside list from being freed. */ + if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect)) + return NULL; + + entry = ExAllocateFromNPagedLookasideList(&ProtectedProcessLookasideList); + /* Lookaside list no longer needed. */ + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + if (!entry) + return NULL; + + entry->Process = Process; + entry->CreatorProcess = PsGetCurrentProcess(); + entry->Tag = Tag; + entry->AllowKernelMode = AllowKernelMode; + entry->ProcessAllowMask = ProcessAllowMask; + entry->ThreadAllowMask = ThreadAllowMask; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + InsertHeadList(&ProtectedProcessListHead, &entry->ListEntry); + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return entry; +} + +/* KphProtectFindEntry + * + * Finds process protection data. + * + * Thread safety: Full/Limited. The returned pointer is not guaranteed to + * point to a valid process entry. However, the copied entry is safe to + * read. + * IRQL: <= DISPATCH_LEVEL + */ +PKPH_PROCESS_ENTRY KphProtectFindEntry( + __in PEPROCESS Process, + __in HANDLE Tag, + __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy + ) +{ + KIRQL oldIrql; + PLIST_ENTRY entry = ProtectedProcessListHead.Flink; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + + while (entry != &ProtectedProcessListHead) + { + PKPH_PROCESS_ENTRY processEntry = + CONTAINING_RECORD(entry, KPH_PROCESS_ENTRY, ListEntry); + + if ( + (Process != NULL && processEntry->Process == Process) || + (Tag != NULL && processEntry->Tag == Tag) + ) + { + /* Copy the entry if requested. */ + if (ProcessEntryCopy) + memcpy(ProcessEntryCopy, processEntry, sizeof(KPH_PROCESS_ENTRY)); + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return processEntry; + } + + entry = entry->Flink; + } + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); + + return NULL; +} + +/* KphProtectRemoveByProcess + * + * Removes protection from the specified process. + * + * Thread safety: Limited. Callers must synchronize remove calls such + * as KphProtectRemoveByProcess and KphProtectRemoveByTag. + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphProtectRemoveByProcess( + __in PEPROCESS Process + ) +{ + PKPH_PROCESS_ENTRY entry = KphProtectFindEntry(Process, NULL, NULL); + + if (!entry) + return FALSE; + + KphpProtectRemoveEntry(entry); + + return TRUE; +} + +/* KphProtectRemoveByTag + * + * Removes protection from all processes with the specified tag. + * + * Thread safety: Limited. Callers must synchronize remove calls such + * as KphProtectRemoveByProcess and KphProtectRemoveByTag. + * IRQL: <= DISPATCH_LEVEL + */ +ULONG KphProtectRemoveByTag( + __in HANDLE Tag + ) +{ + KIRQL oldIrql; + ULONG count = 0; + PKPH_PROCESS_ENTRY entry; + + /* Keep removing entries until we can't find any more. */ + while (entry = KphProtectFindEntry(NULL, Tag, NULL)) + { + KphpProtectRemoveEntry(entry); + count++; + } + + return count; +} + +/* KphpIsAccessAllowed + * + * Checks if the specified access is allowed, according to process + * protection rules. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphpIsAccessAllowed( + __in PVOID Object, + __in KPROCESSOR_MODE AccessMode, + __in ACCESS_MASK DesiredAccess + ) +{ + POBJECT_TYPE objectType; + PEPROCESS processObject; + BOOLEAN isThread = FALSE; + + objectType = KphGetObjectTypeNt(Object); + /* It doesn't matter if it isn't actually a process because we won't be + dereferencing it. */ + processObject = (PEPROCESS)Object; + isThread = objectType == *PsThreadType; + + /* If this is a thread, get its parent process. */ + if (isThread) + processObject = IoThreadToProcess((PETHREAD)Object); + + if ( + processObject != PsGetCurrentProcess() && /* let the caller open its own processes/threads */ + (objectType == *PsProcessType || objectType == *PsThreadType) /* only protect processes and threads */ + ) + { + KPH_PROCESS_ENTRY processEntry; + + /* Search for and copy the corresponding process protection entry. */ + if (KphProtectFindEntry(processObject, NULL, &processEntry)) + { + ACCESS_MASK mask = + isThread ? processEntry.ThreadAllowMask : processEntry.ProcessAllowMask; + + /* The process/thread is protected. Check if the requested access is allowed. */ + if ( + /* check if kernel-mode is exempt from protection */ + !(processEntry.AllowKernelMode && AccessMode == KernelMode) && + /* allow the creator of the rule to bypass protection */ + processEntry.CreatorProcess != PsGetCurrentProcess() && + (DesiredAccess & mask) != DesiredAccess + ) + { + /* Access denied. */ + dprintf( + "%d: Access denied: 0x%08x (%s)\n", + PsGetCurrentProcessId(), + DesiredAccess, + isThread ? "Thread" : "Process" + ); + + return FALSE; + } + } + } + + return TRUE; +} + +/* KphpIsCurrentProcessProtected + * + * Determines whether the current process is protected. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +BOOLEAN KphpIsCurrentProcessProtected() +{ + return KphProtectFindEntry(PsGetCurrentProcess(), NULL, NULL) != NULL; +} + +/* KphpProtectRemoveEntry + * + * Removes and frees process protection data. + * + * Thread safety: Full + * IRQL: <= DISPATCH_LEVEL + */ +VOID KphpProtectRemoveEntry( + __in PKPH_PROCESS_ENTRY Entry + ) +{ + KIRQL oldIrql; + + KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql); + RemoveEntryList(&Entry->ListEntry); + + /* Prevent the lookaside list from being destroyed. */ + ExAcquireRundownProtection(&ProtectedProcessRundownProtect); + ExFreeToNPagedLookasideList( + &ProtectedProcessLookasideList, + Entry + ); + ExReleaseRundownProtection(&ProtectedProcessRundownProtect); + + KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql); +} diff --git a/branches/ph-plugins/KProcessHacker/ps.c b/branches/ph-plugins/KProcessHacker/ps.c new file mode 100644 index 000000000..ac7502747 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/ps.c @@ -0,0 +1,1217 @@ +/* + * Process Hacker Driver - + * processes and threads + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/ke.h" +#include "include/ps.h" + +VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +VOID NTAPI KphpExitSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphAssignImpersonationToken) +#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc) +#pragma alloc_text(PAGE, KphDangerousTerminateThread) +#pragma alloc_text(PAGE, KphpExitSpecialApc) +#pragma alloc_text(PAGE, KphGetContextThread) +#pragma alloc_text(PAGE, KphGetProcessId) +#pragma alloc_text(PAGE, KphGetThreadId) +#pragma alloc_text(PAGE, KphGetThreadWin32Thread) +#pragma alloc_text(PAGE, KphOpenProcess) +#pragma alloc_text(PAGE, KphOpenProcessJob) +#pragma alloc_text(PAGE, KphOpenThread) +#pragma alloc_text(PAGE, KphOpenThreadProcess) +#pragma alloc_text(PAGE, KphResumeProcess) +#pragma alloc_text(PAGE, KphSetContextThread) +#pragma alloc_text(PAGE, KphSuspendProcess) +#pragma alloc_text(PAGE, KphResumeProcess) +#pragma alloc_text(PAGE, KphTerminateProcess) +#pragma alloc_text(PAGE, KphTerminateThread) +#pragma alloc_text(PAGE, PsTerminateProcess) +#pragma alloc_text(PAGE, PspTerminateThreadByPointer) +#endif + +/* KphAcquireProcessRundownProtection + * + * Prevents the process from terminating. + */ +BOOLEAN KphAcquireProcessRundownProtection( + __in PEPROCESS Process + ) +{ + return ExAcquireRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect)); +} + +/* KphAssignImpersonationToken + * + * Assigns an impersonation token to the specified thread. + */ +NTSTATUS KphAssignImpersonationToken( + __in HANDLE ThreadHandle, + __in HANDLE TokenHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PsAssignImpersonationToken(threadObject, TokenHandle); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphCaptureStackBackTraceThread + * + * Captures a kernel-mode stack backtrace for the specified thread. + */ +NTSTATUS KphCaptureStackBackTraceThread( + __in HANDLE ThreadHandle, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + /* Reference the thread. */ + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_QUERY_INFORMATION, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Get the stack trace. */ + status = KphpCaptureStackBackTraceThread( + threadObject, + FramesToSkip, + FramesToCapture, + BackTrace, + CapturedFrames, + BackTraceHash, + AccessMode + ); + /* Dereference the thread. */ + ObDereferenceObject(threadObject); + + return status; +} + +/* KphpCaptureStackBackTraceThread + * + * Captures a kernel-mode stack backtrace for the specified thread. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphpCaptureStackBackTraceThread( + __in PETHREAD Thread, + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG CapturedFrames, + __out_opt PULONG BackTraceHash, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + CAPTURE_BACKTRACE_THREAD_CONTEXT context; + ULONG backTraceSize; + PVOID *backTrace; + + backTraceSize = FramesToCapture * sizeof(PVOID); + + /* Probe user input. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(BackTrace, backTraceSize, 1); + + if (CapturedFrames) + ProbeForWrite(CapturedFrames, sizeof(ULONG), 1); + if (BackTraceHash) + ProbeForWrite(BackTraceHash, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Allocate storage for the stack trace. */ + backTrace = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, backTraceSize, TAG_CAPTURE_STACK_BACKTRACE); + + if (!backTrace) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize the context structure. */ + context.FramesToSkip = FramesToSkip; + context.FramesToCapture = FramesToCapture; + context.BackTrace = backTrace; + + /* Check if we're trying to get a stack trace of the current thread. */ + if (Thread == PsGetCurrentThread()) + { + PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context; + PVOID dummy = NULL; + KIRQL oldIrql; + + context.Local = TRUE; + /* Raise the IRQL to APC_LEVEL to simulate an APC environment. */ + KeRaiseIrql(APC_LEVEL, &oldIrql); + /* Call the APC routine directly. */ + KphpCaptureStackBackTraceThreadSpecialApc( + &context.Apc, + NULL, + NULL, + &contextPtr, + &dummy + ); + /* Lower the IRQL back. */ + KeLowerIrql(oldIrql); + } + else + { + context.Local = FALSE; + /* Initialize the stack trace completed event. */ + KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); + /* Initialize the APC. */ + KeInitializeApc( + &context.Apc, + (PKTHREAD)Thread, + OriginalApcEnvironment, + KphpCaptureStackBackTraceThreadSpecialApc, + NULL, + NULL, + KernelMode, + NULL + ); + /* Queue the APC. */ + if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) + { + /* Wait for the APC to complete. */ + status = KeWaitForSingleObject( + &context.CompletedEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) + { + ASSERT(context.CapturedFrames <= FramesToCapture); + + /* Write the information. */ + __try + { + memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); + + if (CapturedFrames) + *CapturedFrames = context.CapturedFrames; + if (BackTraceHash) + *BackTraceHash = context.BackTraceHash; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + /* Free the allocated stack trace storage. */ + ExFreePoolWithTag(backTrace, TAG_CAPTURE_STACK_BACKTRACE); + + return status; +} + +/* KphpCaptureStackBackTraceThreadSpecialApc + * + * The special APC routine which captures a thread stack trace. + */ +VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ) +{ + PCAPTURE_BACKTRACE_THREAD_CONTEXT context = + (PCAPTURE_BACKTRACE_THREAD_CONTEXT)*SystemArgument1; + + /* Capture a stack trace. */ + context->CapturedFrames = KphCaptureStackBackTrace( + context->FramesToSkip, + context->FramesToCapture, + 0, + context->BackTrace, + &context->BackTraceHash + ); + + if (!context->Local) + { + /* Signal the completed event. */ + KeSetEvent(&context->CompletedEvent, 0, FALSE); + } +} + +/* KphDangerousTerminateThread + * + * Terminates the specified thread by queueing an APC. + */ +NTSTATUS KphDangerousTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + if (!__PspTerminateThreadByPointer) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_TERMINATE, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (threadObject != PsGetCurrentThread()) + { + EXIT_THREAD_CONTEXT context; + + /* Initialize the context structure. */ + context.ExitStatus = ExitStatus; + /* Initialize the completion event. */ + KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); + /* Initialize the APC. */ + KeInitializeApc( + &context.Apc, + (PKTHREAD)threadObject, + OriginalApcEnvironment, + KphpExitSpecialApc, + NULL, + NULL, + KernelMode, + NULL + ); + + /* Queue the APC. */ + if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) + { + /* Wait for the APC to initialize. */ + status = KeWaitForSingleObject( + &context.CompletedEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + ObDereferenceObject(threadObject); + } + else + { + /* Can't terminate self. */ + ObDereferenceObject(threadObject); + return STATUS_CANT_TERMINATE_SELF; + } + + return status; +} + +VOID NTAPI KphpExitSpecialApc( + PKAPC Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ) +{ + PEXIT_THREAD_CONTEXT context = + (PEXIT_THREAD_CONTEXT)*SystemArgument1; + NTSTATUS exitStatus; + + /* Get the exit status. */ + exitStatus = context->ExitStatus; + /* That's the best we can do. Once we exit the current thread we can't + * signal the event, so just signal it now. */ + KeSetEvent(&context->CompletedEvent, 0, FALSE); + /* Exit the thread by calling PspTerminateThreadByPointer. */ + PspTerminateThreadByPointer(PsGetCurrentThread(), exitStatus); + /* Should never happen. */ + dfprintf( + "WARNING: Thread was not terminated by PspTerminateThreadByPointer: %d, %#x\n", + PsGetCurrentThreadId(), + PsGetCurrentThread() + ); +} + +/* KphGetContextThread + * + * Gets the context of the specified thread. + */ +NTSTATUS KphGetContextThread( + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_GET_CONTEXT, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PsGetContextThread(threadObject, ThreadContext, AccessMode); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphGetProcessId + * + * Gets the ID of the process referenced by the specified handle. + */ +HANDLE KphGetProcessId( + __in HANDLE ProcessHandle + ) +{ + PEPROCESS processObject; + HANDLE processId; + + if (!NT_SUCCESS(ObReferenceObjectByHandle(ProcessHandle, 0, + *PsProcessType, KernelMode, &processObject, NULL))) + return 0; + + processId = PsGetProcessId(processObject); + ObDereferenceObject(processObject); + + return processId; +} + +/* KphGetThreadId + * + * Gets the ID of the thread referenced by the specified handle, + * and optionally the ID of the thread's process. + */ +HANDLE KphGetThreadId( + __in HANDLE ThreadHandle, + __out_opt PHANDLE ProcessId + ) +{ + PETHREAD threadObject; + CLIENT_ID clientId; + + if (!NT_SUCCESS(ObReferenceObjectByHandle(ThreadHandle, 0, + *PsThreadType, KernelMode, &threadObject, NULL))) + return 0; + + clientId = *(PCLIENT_ID)KVOFF(threadObject, OffEtClientId); + + ObDereferenceObject(threadObject); + + if (ProcessId) + { + *ProcessId = clientId.UniqueProcess; + } + + return clientId.UniqueThread; +} + +/* KphGetThreadWin32Thread + * + * Gets a pointer to the WIN32THREAD structure of the specified thread. + */ +NTSTATUS KphGetThreadWin32Thread( + __in HANDLE ThreadHandle, + __out PVOID *Win32Thread, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + PVOID win32Thread; + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(Win32Thread, sizeof(PVOID), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + KernelMode, + &threadObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + win32Thread = PsGetThreadWin32Thread(threadObject); + ObDereferenceObject(threadObject); + + __try + { + *Win32Thread = win32Thread; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + return status; +} + +/* KphOpenProcess + * + * Opens a process. + */ +NTSTATUS KphOpenProcess( + __out PHANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ) +{ + BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL; + ULONG attributes = ObjectAttributes->Attributes; + NTSTATUS status = STATUS_SUCCESS; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + PEPROCESS processObject = NULL; + PETHREAD threadObject = NULL; + HANDLE processHandle = NULL; + + if (hasObjectName && ClientId) + return STATUS_INVALID_PARAMETER_MIX; + + /* ReactOS code cleared this bit up for me :) */ + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + /* Let's hope our client isn't a virus... */ + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ProcessAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + if (hasObjectName) + { + status = ObOpenObjectByName( + ObjectAttributes, + *PsProcessType, + AccessMode, + &accessState, + 0, + NULL, + &processHandle + ); + SeDeleteAccessState(&accessState); + } + else if (ClientId) + { + if (ClientId->UniqueThread) + { + status = PsLookupProcessThreadByCid(ClientId, &processObject, &threadObject); + } + else + { + status = PsLookupProcessByProcessId(ClientId->UniqueProcess, &processObject); + } + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + status = ObOpenObjectByPointer( + processObject, + attributes, + &accessState, + 0, + *PsProcessType, + AccessMode, + &processHandle + ); + + SeDeleteAccessState(&accessState); + ObDereferenceObject(processObject); + + if (threadObject) + ObDereferenceObject(threadObject); + } + else + { + SeDeleteAccessState(&accessState); + return STATUS_INVALID_PARAMETER_MIX; + } + + if (NT_SUCCESS(status)) + { + *ProcessHandle = processHandle; + } + + return status; +} + +/* KphOpenProcessJob + * + * Opens the specified process' job object. If the process has + * not been assigned to a job object, the function returns + * STATUS_PROCESS_NOT_IN_JOB. + */ +NTSTATUS KphOpenProcessJob( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE JobHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + PVOID jobObject; + HANDLE jobHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsJobType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= JOB_OBJECT_ALL_ACCESS; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, &processObject, 0); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + /* If we have PsGetProcessJob, use it. Otherwise, read the EPROCESS structure. */ + if (PsGetProcessJob) + { + jobObject = PsGetProcessJob(processObject); + } + else + { + jobObject = *(PVOID *)((PCHAR)processObject + OffEpJob); + } + + ObDereferenceObject(processObject); + + if (jobObject == NULL) + { + /* No such job. Output a NULL handle and exit. */ + SeDeleteAccessState(&accessState); + *JobHandle = NULL; + return STATUS_PROCESS_NOT_IN_JOB; + } + + ObReferenceObject(jobObject); + status = ObOpenObjectByPointer( + jobObject, + 0, + &accessState, + 0, + *PsJobType, + AccessMode, + &jobHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(jobObject); + + if (NT_SUCCESS(status)) + *JobHandle = jobHandle; + + return status; +} + +/* KphOpenThread + * + * Opens a thread. + */ +NTSTATUS KphOpenThread( + __out PHANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __in POBJECT_ATTRIBUTES ObjectAttributes, + __in_opt PCLIENT_ID ClientId, + __in KPROCESSOR_MODE AccessMode + ) +{ + BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL; + ULONG attributes = ObjectAttributes->Attributes; + NTSTATUS status = STATUS_SUCCESS; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + PETHREAD threadObject = NULL; + HANDLE threadHandle = NULL; + + if (hasObjectName && ClientId) + return STATUS_INVALID_PARAMETER_MIX; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsThreadType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ThreadAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + if (hasObjectName) + { + status = ObOpenObjectByName( + ObjectAttributes, + *PsThreadType, + AccessMode, + &accessState, + 0, + NULL, + &threadHandle + ); + SeDeleteAccessState(&accessState); + } + else if (ClientId) + { + if (ClientId->UniqueProcess) + { + status = PsLookupProcessThreadByCid(ClientId, NULL, &threadObject); + } + else + { + status = PsLookupThreadByThreadId(ClientId->UniqueThread, &threadObject); + } + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + status = ObOpenObjectByPointer( + threadObject, + attributes, + &accessState, + 0, + *PsThreadType, + AccessMode, + &threadHandle + ); + + SeDeleteAccessState(&accessState); + ObDereferenceObject(threadObject); + } + else + { + SeDeleteAccessState(&accessState); + return STATUS_INVALID_PARAMETER_MIX; + } + + if (NT_SUCCESS(status)) + { + *ThreadHandle = threadHandle; + } + + return status; +} + +/* KphOpenThreadProcess + * + * Opens a thread's process. + */ +NTSTATUS KphOpenThreadProcess( + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess, + __out PHANDLE ProcessHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + PEPROCESS processObject; + HANDLE processHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= ProcessAllAccess; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle(ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, 0); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + /* Get the process object. */ + processObject = IoThreadToProcess(threadObject); + ObDereferenceObject(threadObject); + + if (processObject == NULL) + { + /* Thread does not have a process (?). */ + SeDeleteAccessState(&accessState); + *ProcessHandle = NULL; + return STATUS_UNSUCCESSFUL; + } + + ObReferenceObject(processObject); + status = ObOpenObjectByPointer( + processObject, + 0, + &accessState, + 0, + *PsProcessType, + AccessMode, + &processHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(processObject); + + if (NT_SUCCESS(status)) + *ProcessHandle = processHandle; + + return status; +} + +/* KphReleaseProcessRundownProtection + * + * Allows the process to terminate. + */ +VOID KphReleaseProcessRundownProtection( + __in PEPROCESS Process + ) +{ + ExReleaseRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect)); +} + +/* KphResumeProcess + * + * Resumes the specified process. + */ +NTSTATUS KphResumeProcess( + __in HANDLE ProcessHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + if (!PsResumeProcess) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_SUSPEND_RESUME, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsResumeProcess(processObject); + ObDereferenceObject(processObject); + + return status; +} + +/* KphSetContextThread + * + * Sets the context of the specified thread. + */ +NTSTATUS KphSetContextThread( + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_SET_CONTEXT, + *PsThreadType, + KernelMode, + &threadObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsSetContextThread(threadObject, ThreadContext, AccessMode); + ObDereferenceObject(threadObject); + + return status; +} + +/* KphSuspendProcess + * + * Suspends the specified process. + */ +NTSTATUS KphSuspendProcess( + __in HANDLE ProcessHandle + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + if (!PsSuspendProcess) + return STATUS_NOT_SUPPORTED; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_SUSPEND_RESUME, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + status = PsSuspendProcess(processObject); + ObDereferenceObject(processObject); + + return status; +} + +/* KphTerminateProcess + * + * Terminates the specified process. + */ +NTSTATUS KphTerminateProcess( + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_TERMINATE, + *PsProcessType, + KernelMode, + &processObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + /* Can't terminate ourself. Get user-mode to do it. */ + if (processObject == PsGetCurrentProcess()) + { + ObDereferenceObject(processObject); + return STATUS_CANT_TERMINATE_SELF; + } + + /* If we have located PsTerminateProcess/PspTerminateProcess, + call it. */ + if (__PsTerminateProcess) + { + status = PsTerminateProcess(processObject, ExitStatus); + ObDereferenceObject(processObject); + } + else + { + /* Otherwise, we'll have to call ZwTerminateProcess - most hooks on this function + allow kernel-mode callers through. */ + OBJECT_ATTRIBUTES objectAttributes = { 0 }; + CLIENT_ID clientId; + HANDLE newProcessHandle; + + /* We have to open it again because ZwTerminateProcess only accepts kernel handles. */ + clientId.UniqueThread = 0; + clientId.UniqueProcess = PsGetProcessId(processObject); + status = KphOpenProcess(&newProcessHandle, 0x1, &objectAttributes, &clientId, KernelMode); + ObDereferenceObject(processObject); + + if (NT_SUCCESS(status)) + { + status = ZwTerminateProcess(newProcessHandle, ExitStatus); + ZwClose(newProcessHandle); + } + } + + return status; +} + +/* KphTerminateThread + * + * Terminates the specified thread. + */ +NTSTATUS KphTerminateThread( + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD threadObject; + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_TERMINATE, + *PsThreadType, + KernelMode, + &threadObject, + NULL); + + if (!NT_SUCCESS(status)) + return status; + + if (threadObject != PsGetCurrentThread()) + { + status = PspTerminateThreadByPointer(threadObject, ExitStatus); + ObDereferenceObject(threadObject); + } + else + {/* + ObDereferenceObject(threadObject); + status = PspTerminateThreadByPointer(PsGetCurrentThread(), ExitStatus); */ + /* Leads to bugs, so don't terminate self. */ + ObDereferenceObject(threadObject); + return STATUS_CANT_TERMINATE_SELF; + } + + return status; +} + +/* PsTerminateProcess + * + * Terminates the specified process. If PsTerminateProcess or + * PspTerminateProcess could not be located, the call will fail + * with STATUS_NOT_SUPPORTED. + */ +NTSTATUS PsTerminateProcess( + __in PEPROCESS Process, + __in NTSTATUS ExitStatus + ) +{ + PVOID psTerminateProcess = __PsTerminateProcess; + NTSTATUS status; + + if (!psTerminateProcess) + return STATUS_NOT_SUPPORTED; + +#ifdef _X86_ + if (WindowsVersion == WINDOWS_XP) + { + /* PspTerminateProcess on XP is stdcall. */ + __asm + { + push [ExitStatus] + push [Process] + call [psTerminateProcess] + mov [status], eax + } + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + /* PsTerminateProcess on Vista and above is thiscall. */ + __asm + { + push [ExitStatus] + mov ecx, [Process] + call [psTerminateProcess] + mov [status], eax + } + } + else + { + return STATUS_NOT_SUPPORTED; + } +#else + status = __PsTerminateProcess(Process, ExitStatus); +#endif + + return status; +} + +/* PspTerminateThreadByPointer + * + * Terminates the specified thread. If PspTerminateThreadByPointer + * could not be located, the call will fail with STATUS_NOT_SUPPORTED. + */ +NTSTATUS PspTerminateThreadByPointer( + __in PETHREAD Thread, + __in NTSTATUS ExitStatus + ) +{ + PVOID pspTerminateThreadByPointer = __PspTerminateThreadByPointer; + + if (!pspTerminateThreadByPointer) + return STATUS_NOT_SUPPORTED; + + if (WindowsVersion == WINDOWS_XP) + { + return ((_PspTerminateThreadByPointer51)pspTerminateThreadByPointer)( + Thread, + ExitStatus + ); + } + else if ( + WindowsVersion == WINDOWS_VISTA || + WindowsVersion == WINDOWS_7 + ) + { + return ((_PspTerminateThreadByPointer60)pspTerminateThreadByPointer)( + Thread, + ExitStatus, + Thread == PsGetCurrentThread() + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} diff --git a/branches/ph-plugins/KProcessHacker/ref.c b/branches/ph-plugins/KProcessHacker/ref.c new file mode 100644 index 000000000..eeb0f917a --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/ref.c @@ -0,0 +1,574 @@ +/* + * Process Hacker Driver - + * internal object manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/refp.h" + +/* A list of all objects created by the object manager. */ +LIST_ENTRY KphObjectListHead; +/* A mutex protecting global data structures. */ +FAST_MUTEX KphObjectListMutex; +/* The object type type. */ +PKPH_OBJECT_TYPE KphObjectTypeObject = NULL; + +/* Whether the object manager is destroying all objects. */ +BOOLEAN KphObjectDeinitializing = FALSE; +/* The work item for deferred object deletes. */ +WORK_QUEUE_ITEM KphObjectDeferDeleteWorkItem; +/* The next object to delete. */ +PKPH_OBJECT_HEADER KphObjectNextToFree = NULL; + +/* KphRefInit + * + * Initializes the KPH object manager. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphRefInit() +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Initialize the object list. */ + InitializeListHead(&KphObjectListHead); + /* Initialize the object list mutex. */ + ExInitializeFastMutex(&KphObjectListMutex); + + /* Initialize the deferred delete work item. */ + ExInitializeWorkItem( + &KphObjectDeferDeleteWorkItem, + KphpDeferDeleteObjectRoutine, + NULL + ); + + /* Create the fundamental object type. */ + status = KphCreateObjectType( + &KphObjectTypeObject, + NonPagedPool, + 0, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Now that the fundamental object type exists, fix it up. */ + KphObjectToObjectHeader(KphObjectTypeObject)->Type = KphObjectTypeObject; + KphObjectTypeObject->NumberOfObjects = 1; + + return status; +} + +/* KphRefDeinit + * + * Frees all objects created by the KPH object manager. + * + * IRQL: = PASSIVE_LEVEL + */ +NTSTATUS KphRefDeinit() +{ + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY currentEntry; + + KphObjectDeinitializing = TRUE; + + /* Acquire the object list mutex to make sure no one else + * modifies the list. */ + ExAcquireFastMutex(&KphObjectListMutex); + + /* Remove and free all objects in the list. */ + while ((currentEntry = RemoveHeadList(&KphObjectListHead)) != &KphObjectListHead) + { + PKPH_OBJECT_HEADER objectHeader = + CONTAINING_RECORD(currentEntry, KPH_OBJECT_HEADER, GlobalObjectListEntry); + + /* Free the object, ignoring its reference count. */ + KphpFreeObject(objectHeader); + } + + /* Release the object list mutex and restore the IRQL. */ + ExReleaseFastMutex(&KphObjectListMutex); + + return STATUS_SUCCESS; +} + +/* KphCreateObject + * + * Allocates a object. + * + * Object: A variable which receives a pointer to the newly allocated object. + * ObjectSize: The size of the object. + * Flags: A combination of flags specifying how the object is to be allocated. + * * KPHOBJ_RAISE_ON_FAIL: An exception will be raised if the object could + * not be allocated. + * * KPHOBJ_PAGED_POOL: The object will be allocated in the paged pool. If + * this flag is specified, KPHOBJ_NONPAGED_POOL cannot be specified. + * * KPHOBJ_NONPAGED_POOL: The object will be allocated in the non-paged pool. + * If this flag is specified, KPHOBJ_PAGED_POOL cannot be specified. + * ObjectType: The type of the object. + * AdditionalReferences: The number of references to add to the object. The + * object will have a reference count of 1 + AdditionalReferences. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphCreateObject( + __out PVOID *Object, + __in SIZE_T ObjectSize, + __in ULONG Flags, + __in_opt PKPH_OBJECT_TYPE ObjectType, + __in_opt LONG AdditionalReferences + ) +{ + PKPH_OBJECT_HEADER objectHeader; + POOL_TYPE poolType; + + /* Check the flags. */ + if ((Flags & KPHOBJ_VALID_FLAGS) != Flags) /* Valid flag mask */ + return STATUS_INVALID_PARAMETER_3; + if ((Flags & KPHOBJ_PAGED_POOL) && (Flags & KPHOBJ_NONPAGED_POOL)) /* Can't be both pools */ + return STATUS_INVALID_PARAMETER_3; + /* The object type is only optional if the fundamental object type + * hasn't been created. */ + if (!ObjectType && KphObjectTypeObject) + return STATUS_INVALID_PARAMETER_4; + /* Make sure the additional reference count isn't negative. */ + if (AdditionalReferences < 0) + return STATUS_INVALID_PARAMETER_5; + + /* Figure out the pool type. If it wasn't specified in Flags, + * get the pool type from the object type. */ + if (Flags & KPHOBJ_PAGED_POOL) + poolType = PagedPool; + else if (Flags & KPHOBJ_NONPAGED_POOL) + poolType = NonPagedPool; + else if (ObjectType) /* May be null if we're creating the fundamental type */ + poolType = ObjectType->DefaultPoolType; + else + poolType = NonPagedPool; + + /* Allocate storage for the object. Note that this includes + * the object header followed by the object body. */ + objectHeader = KphpAllocateObject(ObjectSize, poolType); + + if (!objectHeader) + { + if (Flags & KPHOBJ_RAISE_ON_FAIL) + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + else + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Object type statistics. */ + if (ObjectType) + { + InterlockedIncrement(&ObjectType->NumberOfObjects); + } + + /* Initialize the object header. */ + objectHeader->RefCount = 1 + AdditionalReferences; + objectHeader->Flags = Flags; + objectHeader->Size = ObjectSize; + objectHeader->Type = ObjectType; + + /* Insert the object into the global object list. */ + ExAcquireFastMutex(&KphObjectListMutex); + InsertHeadList(&KphObjectListHead, &objectHeader->GlobalObjectListEntry); + ExReleaseFastMutex(&KphObjectListMutex); + + /* Pass a pointer to the object body back to the caller. */ + *Object = KphObjectHeaderToObject(objectHeader); + + return STATUS_SUCCESS; +} + +/* KphCreateObjectType + * + * Creates an object type. + * + * IRQL: <= APC_LEVEL + */ +NTSTATUS KphCreateObjectType( + __out PKPH_OBJECT_TYPE *ObjectType, + __in POOL_TYPE DefaultPoolType, + __in ULONG Flags, + __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPH_OBJECT_TYPE objectType; + + /* Check the flags. */ + if ((Flags & KPHOBJTYPE_VALID_FLAGS) != Flags) /* Valid flag mask */ + return STATUS_INVALID_PARAMETER_3; + + /* Create the type object. */ + status = KphCreateObject( + &objectType, + sizeof(KPH_OBJECT_TYPE), + 0, + KphObjectTypeObject, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the type object. */ + objectType->DefaultPoolType = DefaultPoolType; + objectType->Flags = Flags; + objectType->DeleteProcedure = DeleteProcedure; + objectType->NumberOfObjects = 0; + + *ObjectType = objectType; + + return status; +} + +/* KphDereferenceObject + * + * Dereferences the specified object. The object will be freed if + * its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * + * Return value: TRUE if the object was freed, otherwise FALSE. + * + * IRQL: <= APC_LEVEL + */ +BOOLEAN KphDereferenceObject( + __in PVOID Object + ) +{ + return KphDereferenceObjectEx(Object, 1, FALSE) == 0; +} + +/* KphDereferenceObjectDeferDelete + * + * Dereferences the specified object. The object will be freed in + * a worker thread if its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * + * Return value: TRUE if the object was freed, otherwise FALSE. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +BOOLEAN KphDereferenceObjectDeferDelete( + __in PVOID Object + ) +{ + return KphDereferenceObjectEx(Object, 1, TRUE) == 0; +} + +/* KphDereferenceObjectEx + * + * Dereferences the specified object. The object will be freed if + * its reference count reaches 0. + * + * Object: A pointer to the object to dereference. + * RefCount: The number of references to remove. + * + * Return value: The new reference count of the object. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool and deletion is being deferred, otherwise <= APC_LEVEL. + */ +LONG KphDereferenceObjectEx( + __in PVOID Object, + __in LONG RefCount, + __in BOOLEAN DeferDelete + ) +{ + PKPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + + /* Make sure we're not subtracting a negative reference count. */ + if (RefCount < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + objectHeader = KphObjectToObjectHeader(Object); + + /* Decrease the reference count. */ + oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount); + + /* Free the object if it has 0 references. */ + if (oldRefCount - RefCount == 0) + { + /* If we are at DISPATCH_LEVEL or higher, the type requests + * us to do so, or the caller requests us to do so, defer + * the deletion. + */ + if ( + DeferDelete || + (objectHeader->Type->Flags & KPHOBJTYPE_PASSIVE_LEVEL_DELETE) || + (KeGetCurrentIrql() > APC_LEVEL) + ) + { + KphpDeferDeleteObject(objectHeader); + } + else + { + /* Free the object. */ + KphpFreeObject(objectHeader); + } + } + + return oldRefCount - RefCount; +} + +/* KphGetObjectType + * + * Gets an object's type. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +PKPH_OBJECT_TYPE KphGetObjectType( + __in PVOID Object + ) +{ + return KphObjectToObjectHeader(Object)->Type; +} + +/* KphReferenceObject + * + * References the specified object. + * + * Object: A pointer to the object to reference. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +VOID KphReferenceObject( + __in PVOID Object + ) +{ + PKPH_OBJECT_HEADER objectHeader; + + objectHeader = KphObjectToObjectHeader(Object); + /* Increment the reference count. */ + InterlockedIncrement(&objectHeader->RefCount); +} + +/* KphReferenceObjectEx + * + * References the specified object. + * + * Object: A pointer to the object to reference. + * RefCount: The number of references to add. + * + * Return value: The new reference count of the object. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +LONG KphReferenceObjectEx( + __in PVOID Object, + __in LONG RefCount + ) +{ + PKPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + + /* Make sure we're not adding a negative reference count. */ + if (RefCount < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + objectHeader = KphObjectToObjectHeader(Object); + /* Increase the reference count. */ + oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, RefCount); + + return oldRefCount + RefCount; +} + +/* KphReferenceObjectSafe + * + * Attempts to reference an object and fails if it is being + * destroyed. + * + * Object: The object to reference if it is not being deleted. + * + * Return value: TRUE if the object was referenced, FALSE if + * it was being deleted and was not referenced. + * + * Remarks: + * This function is useful if a reference to an object is + * held, protected by a mutex, and the delete procedure of + * the object's type attempts to acquire the mutex. If this + * function is called while the mutex is owned, you can + * avoid referencing an object that is being destroyed. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +BOOLEAN KphReferenceObjectSafe( + __in PVOID Object + ) +{ + PKPH_OBJECT_HEADER objectHeader; + BOOLEAN result; + + objectHeader = KphObjectToObjectHeader(Object); + /* Increase the reference count only if it isn't 0 (atomically). */ + result = KphpInterlockedIncrementSafe(&objectHeader->RefCount); + + return result; +} + +/* KphpAllocateObject + * + * Allocates storage for an object. + * + * ObjectSize: The size of the object, excluding the header. + * PoolType: The pool in which to allocate the object. + */ +PKPH_OBJECT_HEADER KphpAllocateObject( + __in SIZE_T ObjectSize, + __in POOL_TYPE PoolType + ) +{ + return ExAllocatePoolWithTag( + PoolType, + KphpAddObjectHeaderSize(ObjectSize), + TAG_KPHOBJ + ); +} + +/* KphpDeferDeleteObject + * + * Queues an object for deletion. + * + * IRQL: <= DISPATCH_LEVEL if the object was allocated using the + * non-paged pool, otherwise <= APC_LEVEL. + */ +VOID KphpDeferDeleteObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ) +{ + PKPH_OBJECT_HEADER nextToFree; + + /* Add the object to the list while saving the old value, atomically. + * Note that it is first-in, last-out. + */ + while (TRUE) + { + nextToFree = KphObjectNextToFree; + ObjectHeader->NextToFree = nextToFree; + + /* Attempt to set the global next-to-free variable. */ + if (InterlockedCompareExchangePointer( + &KphObjectNextToFree, + ObjectHeader, + nextToFree + ) == nextToFree) + { + /* Success. */ + break; + } + + /* Someone else changed the next-to-free variable. + * Go back and try again. + */ + } + + /* Was the to-free list empty before? If so, we need to queue + * the work item. + */ + if (!nextToFree) + { + ExQueueWorkItem(&KphObjectDeferDeleteWorkItem, CriticalWorkQueue); + } +} + +/* KphpDeferDeleteObjectRoutine + * + * Removes and frees objects from the to-free list. + * + * IRQL: PASSIVE_LEVEL + */ +VOID KphpDeferDeleteObjectRoutine( + __in PVOID Parameter + ) +{ + PKPH_OBJECT_HEADER objectHeader = NULL; + + while (TRUE) + { + /* Get the next object to free while replacing the global variable with + * what we needed to free next. + */ + objectHeader = InterlockedExchangePointer(&KphObjectNextToFree, objectHeader); + + /* If we have an object to free, free it and move on to the + * next object. Otherwise, stop. + */ + if (objectHeader) + { + KphpFreeObject(objectHeader); + objectHeader = objectHeader->NextToFree; + } + else + { + break; + } + } +} + +/* KphpFreeObject + * + * Calls the delete procedure for an object and frees its + * allocated storage. + * + * ObjectHeader: A pointer to the object header of an allocated object. + */ +VOID KphpFreeObject( + __in PKPH_OBJECT_HEADER ObjectHeader + ) +{ + /* Object type statistics. */ + InterlockedDecrement(&ObjectHeader->Type->NumberOfObjects); + + /* Remove the object from the global object list. + * If the object manager is being destroyed, don't do this - + * we will deadlock because the deinitialization function + * holds the mutex. + */ + if (!KphObjectDeinitializing) + { + ExAcquireFastMutex(&KphObjectListMutex); + RemoveEntryList(&ObjectHeader->GlobalObjectListEntry); + ExReleaseFastMutex(&KphObjectListMutex); + } + + /* Call the delete procedure if we have one. */ + if (ObjectHeader->Type->DeleteProcedure) + { + ObjectHeader->Type->DeleteProcedure( + KphObjectHeaderToObject(ObjectHeader), + ObjectHeader->Flags + ); + } + + ExFreePoolWithTag( + ObjectHeader, + TAG_KPHOBJ + ); +} diff --git a/branches/ph-plugins/KProcessHacker/resource.rc b/branches/ph-plugins/KProcessHacker/resource.rc new file mode 100644 index 000000000..e02647e28 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/resource.rc @@ -0,0 +1,53 @@ +#include + +#define VER_COMMA 1,6,0,0 +#define VER_STR "1.6\0" + +#define VER_FILEVERSION VER_COMMA +#define VER_FILEVERSION_STR VER_STR +#define VER_PRODUCTVERSION VER_COMMA +#define VER_PRODUCTVERSION_STR VER_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#define VER_PRIVATEBUILD 0 +#define VER_PRERELEASE 0 + +#define VER_COMPANYNAME_STR "wj32\0" +#define VER_FILEDESCRIPTION_STR "KProcessHacker\0" +#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2009 wj32. Licensed under the GNU GPL, v3.\0" +#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0" +#define VER_PRODUCTNAME_STR "KProcessHacker\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DRV +FILESUBTYPE VFT2_DRV_SYSTEM +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/branches/ph-plugins/KProcessHacker/se.c b/branches/ph-plugins/KProcessHacker/se.c new file mode 100644 index 000000000..ce09c6607 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/se.c @@ -0,0 +1,102 @@ +/* + * Process Hacker Driver - + * security + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" +#include "include/se.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphOpenProcessTokenEx) +#endif + +/* KphOpenProcessTokenEx + * + * Opens the primary token of the specified process. + */ +NTSTATUS KphOpenProcessTokenEx( + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess, + __in ULONG ObjectAttributes, + __out PHANDLE TokenHandle, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS processObject; + PACCESS_TOKEN tokenObject; + HANDLE tokenHandle; + ACCESS_STATE accessState; + CHAR auxData[AUX_ACCESS_DATA_SIZE]; + + status = SeCreateAccessState( + &accessState, + (PAUX_ACCESS_DATA)auxData, + DesiredAccess, + (PGENERIC_MAPPING)KVOFF(*SeTokenObjectType, OffOtiGenericMapping) + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) + accessState.PreviouslyGrantedAccess |= TOKEN_ALL_ACCESS; + else + accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess; + + accessState.RemainingDesiredAccess = 0; + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + KernelMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + { + SeDeleteAccessState(&accessState); + return status; + } + + tokenObject = PsReferencePrimaryToken(processObject); + ObDereferenceObject(processObject); + + status = ObOpenObjectByPointer( + tokenObject, + ObjectAttributes, + &accessState, + 0, + *SeTokenObjectType, + AccessMode, + &tokenHandle + ); + SeDeleteAccessState(&accessState); + ObDereferenceObject(tokenObject); + + if (NT_SUCCESS(status)) + *TokenHandle = tokenHandle; + + return status; +} diff --git a/branches/ph-plugins/KProcessHacker/sources b/branches/ph-plugins/KProcessHacker/sources new file mode 100644 index 000000000..ff6e07b5e --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/sources @@ -0,0 +1,29 @@ +TARGETNAME=kprocesshacker +TARGETTYPE=DRIVER +TARGETPATH=.\ + +INCLUDES=$(DDK_INC_PATH) +LIBS=%BUILD%\lib + +SOURCES= \ + kprocesshacker.c \ + version.c \ + \ + kph.c \ + handle.c \ + hook.c \ + protect.c \ + ref.c \ + sync.c \ + sysservice.c \ + sysservicedata.c \ + test.c \ + trace.c \ + util.c \ + \ + io.c \ + mm.c \ + ob.c \ + ps.c \ + se.c \ + resource.rc diff --git a/branches/ph-plugins/KProcessHacker/sync.c b/branches/ph-plugins/KProcessHacker/sync.c new file mode 100644 index 000000000..99bd46bf8 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/sync.c @@ -0,0 +1,312 @@ +/* + * Process Hacker Driver - + * synchronization code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/sync.h" +#include "include/debug.h" + +ULONG KphpCountBits( + __in ULONG_PTR Number + ); + +VOID KphpProcessorLockDpc( + __in PKDPC Dpc, + __in PVOID DeferredContext, + __in PVOID SystemArgument1, + __in PVOID SystemArgument2 + ); + +/* KphfAcquireGuardedLock + * + * Acquires a guarded lock and raises the IRQL to APC_LEVEL. + * + * IRQL: <= APC_LEVEL + */ +VOID FASTCALL KphfAcquireGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KIRQL oldIrql; + + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + /* Raise to APC_LEVEL. */ + oldIrql = KeRaiseIrql(APC_LEVEL, &oldIrql); + + /* Acquire the spinlock. */ + KphAcquireBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT); + + /* Now that we have the lock, we must save the old IRQL. */ + /* Clear the old IRQL. */ + Lock->Value &= KPH_GUARDED_LOCK_FLAGS; + /* Set the new IRQL. */ + Lock->Value |= oldIrql; +} + +/* KphfReleaseGuardedLock + * + * Releases a guarded lock and restores the old IRQL. + * + * IRQL: >= APC_LEVEL + */ +VOID FASTCALL KphfReleaseGuardedLock( + __inout PKPH_GUARDED_LOCK Lock + ) +{ + KIRQL oldIrql; + + ASSERT(KeGetCurrentIrql() >= APC_LEVEL); + + /* Get the old IRQL. */ + oldIrql = (KIRQL)(Lock->Value & ~KPH_GUARDED_LOCK_FLAGS); + /* Unlock the spinlock. */ + KphReleaseBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT); + /* Restore the old IRQL. */ + KeLowerIrql(oldIrql); +} + +/* KphAcquireProcessorLock + * + * Raises the IRQL to DISPATCH_LEVEL and prevents threads from + * executing on other processors until the processor lock is released. + * Blocks if the supplied processor lock is already in use. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * Comments: + * Here is how the processor lock works: + * 1. Tries to acquire the mutex in the processor lock, and + * blocks until it can be obtained. + * 2. Initializes a DPC for each processor on the computer. + * 3. Raises the IRQL to DISPATCH_LEVEL to make sure the + * code is not interrupted by a context switch. + * 4. Queues each of the previously-initialized DPCs, except if + * it is targeted at the current processor. + * 5. Since DPCs run at DISPATCH_LEVEL, they have exclusive + * control of the processor. As each runs, they increment + * a counter in the processor lock. They then enter a loop. + * 6. The routine waits for the counter to become n - 1, + * signaling that all (other) processors have been acquired + * (where n is the number of processors). + * 7. It returns. Any code from here will be running in + * DISPATCH_LEVEL and will be the only code running on the + * machine. + * Thread safety: Full + * IRQL: <= APC_LEVEL + */ +BOOLEAN KphAcquireProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + ULONG i; + ULONG numberProcessors; + ULONG currentProcessor; + + /* Acquire the processor lock guarded lock. */ + KphAcquireGuardedLock(&ProcessorLock->Lock); + + /* Reset some state. */ + ASSERT(ProcessorLock->AcquiredProcessors == 0); + ProcessorLock->AcquiredProcessors = 0; + ProcessorLock->ReleaseSignal = 0; /* IMPORTANT */ + + /* Get the number of processors. */ + numberProcessors = KphpCountBits(KeQueryActiveProcessors()); + + /* If there's only one processor we can simply raise the IRQL and exit. */ + if (numberProcessors == 1) + { + dprintf("KphAcquireProcessorLock: Only one processor, raising IRQL and exiting...\n"); + KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql); + ProcessorLock->Acquired = TRUE; + + return TRUE; + } + + /* Allocate storage for the DPCs. */ + ProcessorLock->Dpcs = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(KDPC) * numberProcessors, + TAG_SYNC_DPC + ); + + if (!ProcessorLock->Dpcs) + { + dprintf("KphAcquireProcessorLock: Could not allocate storage for DPCs!\n"); + KphReleaseGuardedLock(&ProcessorLock->Lock); + return FALSE; + } + + /* Initialize the DPCs. */ + for (i = 0; i < numberProcessors; i++) + { + KeInitializeDpc(&ProcessorLock->Dpcs[i], KphpProcessorLockDpc, NULL); + KeSetTargetProcessorDpc(&ProcessorLock->Dpcs[i], (CCHAR)i); + KeSetImportanceDpc(&ProcessorLock->Dpcs[i], HighImportance); + } + + /* Raise the IRQL to DISPATCH_LEVEL to prevent context switching. */ + KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql); + /* Get the current processor number. */ + currentProcessor = KeGetCurrentProcessorNumber(); + + /* Queue the DPCs (except on the current processor). */ + for (i = 0; i < numberProcessors; i++) + if (i != currentProcessor) + KeInsertQueueDpc(&ProcessorLock->Dpcs[i], ProcessorLock, NULL); + + /* Spinwait for all (other) processors to be acquired. */ + KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, numberProcessors - 1); + + dprintf("KphAcquireProcessorLock: All processors acquired.\n"); + ProcessorLock->Acquired = TRUE; + + return TRUE; +} + +/* KphInitializeProcessorLock + * + * Initializes a processor lock. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * IRQL: Any + */ +VOID KphInitializeProcessorLock( + __out PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + KphInitializeGuardedLock(&ProcessorLock->Lock, FALSE); + ProcessorLock->Dpcs = NULL; + ProcessorLock->AcquiredProcessors = 0; + ProcessorLock->ReleaseSignal = 0; + ProcessorLock->OldIrql = PASSIVE_LEVEL; + ProcessorLock->Acquired = FALSE; +} + +/* KphReleaseProcessorLock + * + * Allows threads to execute on other processors and restores the IRQL. + * + * ProcessorLock: A processor lock structure that is present in + * non-paged memory. + * + * Comments: + * Here is how the processor lock is released: + * 1. Sets the signal to release the processors. The DPCs that are + * currently waiting for the signal will return and decrement + * the acquired processors counter. + * 2. Waits for the acquired processors counter to become zero. + * 3. Restores the old IRQL. This will always be APC_LEVEL due to + * the mutex. + * 4. Frees the storage allocated for the DPCs. + * 5. Releases the processor lock mutex. This will restore the IRQL + * back to normal. + * Thread safety: Full + * IRQL: DISPATCH_LEVEL + */ +VOID KphReleaseProcessorLock( + __inout PKPH_PROCESSOR_LOCK ProcessorLock + ) +{ + if (!ProcessorLock->Acquired) + return; + + /* Signal for the acquired processors to be released. */ + InterlockedExchange(&ProcessorLock->ReleaseSignal, 1); + + /* Spinwait for all acquired processors to be released. */ + KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, 0); + + dprintf("KphReleaseProcessorLock: All processors released.\n"); + + /* Restore the old IRQL (should always be APC_LEVEL due to the + * fast mutex). */ + KeLowerIrql(ProcessorLock->OldIrql); + + /* Free the DPCs if necessary. */ + if (ProcessorLock->Dpcs != NULL) + { + ExFreePoolWithTag(ProcessorLock->Dpcs, TAG_SYNC_DPC); + ProcessorLock->Dpcs = NULL; + } + + ProcessorLock->Acquired = FALSE; + + /* Release the processor lock guarded lock. This will restore the + * IRQL back to what it was before the processor lock was + * acquired. + */ + KphReleaseGuardedLock(&ProcessorLock->Lock); +} + +/* KphpCountBits + * + * Counts the number of bits set in an integer. + */ +ULONG KphpCountBits( + __in ULONG_PTR Number + ) +{ + ULONG count = 0; + + while (Number) + { + count++; + Number &= Number - 1; + } + + return count; +} + +/* KphpProcessorLockDpc + * + * The DPC routine which "locks" processors. + * + * Thread safety: Full + * IRQL: DISPATCH_LEVEL + */ +VOID KphpProcessorLockDpc( + __in PKDPC Dpc, + __in PVOID DeferredContext, + __in PVOID SystemArgument1, + __in PVOID SystemArgument2 + ) +{ + PKPH_PROCESSOR_LOCK processorLock = (PKPH_PROCESSOR_LOCK)SystemArgument1; + + ASSERT(processorLock != NULL); + + dprintf("KphpProcessorLockDpc: Acquiring processor %d.\n", KeGetCurrentProcessorNumber()); + + /* Increase the number of acquired processors. */ + InterlockedIncrement(&processorLock->AcquiredProcessors); + + /* Spin until we get the signal to release the processor. */ + KphSpinUntilNotEqual(&processorLock->ReleaseSignal, 0); + + /* Decrease the number of acquired processors. */ + InterlockedDecrement(&processorLock->AcquiredProcessors); + + dprintf("KphpProcessorLockDpc: Releasing processor %d.\n", KeGetCurrentProcessorNumber()); +} diff --git a/branches/ph-plugins/KProcessHacker/sysservice.c b/branches/ph-plugins/KProcessHacker/sysservice.c new file mode 100644 index 000000000..0568e7d07 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/sysservice.c @@ -0,0 +1,2050 @@ +/* + * Process Hacker Driver - + * system service logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* ================ IMPORTANT ================ + * Please read the comments in KphpSsNewKiFastCallEntry to find out how + * KiFastCallEntry can be hooked. + * + * Note that the ONLY SUPPORTED METHOD of hooking is KiFastCallEntry, + * which means you MUST be using a CPU which supports sysenter. + * =========================================== + */ + +#include "include/sysservicep.h" +#include "include/hook.h" +#include "include/sync.h" +#include "include/trace.h" + +extern PDRIVER_OBJECT KphDriverObject; + +/* A fast mutex guarding starting/stopping system service logging. */ +FAST_MUTEX KphSsMutex; +/* Whether system service logging has been initialized. */ +BOOLEAN KphSsInitialized = FALSE; +/* The KiFastCallEntry hook. */ +KPH_HOOK KphSsKiFastCallEntryHook; +/* The number of active loggers. */ +ULONG KphSsNumberOfActiveLoggers = 0; + +/* The object type for client entries. */ +PKPH_OBJECT_TYPE KphSsClientEntryType; +/* The object type for ruleset entries. */ +PKPH_OBJECT_TYPE KphSsRuleSetEntryType; +/* The object type for rule entries. */ +PKPH_OBJECT_TYPE KphSsRuleEntryType; + +/* The list of ruleset entries. */ +LIST_ENTRY KphSsRuleSetListHead; +/* A push lock guarding accesses to the ruleset list. */ +EX_PUSH_LOCK KphSsRuleSetListPushLock; + +/* KphSsLogInit + * + * Initializes system service logging. + */ +NTSTATUS KphSsLogInit() +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Initialize the system service call data. */ + KphSsDataInit(); + + /* Initialize the ruleset list. */ + InitializeListHead(&KphSsRuleSetListHead); + ExInitializeFastMutex(&KphSsMutex); + ExInitializePushLock(&KphSsRuleSetListPushLock); + + /* Initialize the object types. */ + status = KphCreateObjectType( + &KphSsClientEntryType, + NonPagedPool, + 0, + KphpSsClientEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + return status; + + status = KphCreateObjectType( + &KphSsRuleSetEntryType, + NonPagedPool, + 0, + KphpSsRuleSetEntryDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + { + KphDereferenceObject(KphSsClientEntryType); + return status; + } + + status = KphCreateObjectType( + &KphSsRuleEntryType, + NonPagedPool, + 0, + NULL + ); + + if (!NT_SUCCESS(status)) + { + KphDereferenceObject(KphSsClientEntryType); + KphDereferenceObject(KphSsRuleSetEntryType); + return status; + } + + return status; +} + +/* KphSsLogDeinit + * + * Frees system service logging data. + */ +NTSTATUS KphSsLogDeinit() +{ + KphSsDataDeinit(); + + return STATUS_SUCCESS; +} + +/* KphSsLogStart + * + * Starts system service logging. + */ +NTSTATUS KphSsLogStart() +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + + /* Make sure we have the KiFastCallEntry+x address. */ + if (!__KiFastCallEntry) + return STATUS_NOT_SUPPORTED; + + ExAcquireFastMutex(&KphSsMutex); + + if (KphSsInitialized) + { + ExReleaseFastMutex(&KphSsMutex); + return STATUS_UNSUCCESSFUL; + } + + /* Hook KiFastCallEntry. Logging will start from now. */ + KphInitializeHook( + &KphSsKiFastCallEntryHook, + __KiFastCallEntry, + KphpSsNewKiFastCallEntry + ); + status = KphHook(&KphSsKiFastCallEntryHook); + + if (!NT_SUCCESS(status)) + { + ExReleaseFastMutex(&KphSsMutex); + return status; + } + + KphSsInitialized = TRUE; + + ExReleaseFastMutex(&KphSsMutex); + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphSsLogStop + * + * Stops system service logging. + */ +NTSTATUS KphSsLogStop() +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + + ExAcquireFastMutex(&KphSsMutex); + + if (!KphSsInitialized) + { + ExReleaseFastMutex(&KphSsMutex); + return STATUS_UNSUCCESSFUL; + } + + status = KphUnhook(&KphSsKiFastCallEntryHook); + + if (!NT_SUCCESS(status)) + { + ExReleaseFastMutex(&KphSsMutex); + return status; + } + + /* Spin until the logger count reaches 0. */ + KphSpinUntilEqual(&KphSsNumberOfActiveLoggers, 0); + + KphSsInitialized = FALSE; + + ExReleaseFastMutex(&KphSsMutex); + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphSsCreateClientEntry + * + * Creates a client entry which describes a client of the + * system service logger. Clients receive system service log events. + * Note that a client may have several ruleset entries associated + * with it. + * + * ClientEntry: A variable which receives a pointer to the client entry. + * ProcessHandle: A handle to the client process, with PROCESS_VM_WRITE + * access. + * ReadSemaphoreHandle: A handle to a semaphore which is released when an + * event is written to the client buffer. The client must wait for the + * semaphore when it is about to read a block. + * WriteSemaphoreHandle: A handle to a semaphore which is acquired when an + * event is about to be written to the client buffer. If the semaphore + * cannot be acquired immediately, the event is dropped. The client must + * continually read the buffer and release the semaphore. + * BufferBase: A pointer to a buffer in the client process. + * BufferSize: The size of the buffer, in bytes. + * AccessMode: The mode to use when probing arguments. + */ +NTSTATUS KphSsCreateClientEntry( + __out PKPHSS_CLIENT_ENTRY *ClientEntry, + __in HANDLE ProcessHandle, + __in HANDLE ReadSemaphoreHandle, + __in HANDLE WriteSemaphoreHandle, + __in PVOID BufferBase, + __in ULONG BufferSize, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_CLIENT_ENTRY clientEntry; + PEPROCESS processObject; + PKSEMAPHORE readSemaphore; + PKSEMAPHORE writeSemaphore; + + /* Probe. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(BufferBase, BufferSize, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Reference the client process. */ + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_WRITE, + *PsProcessType, + AccessMode, + &processObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Reference the read semaphore. */ + status = ObReferenceObjectByHandle( + ReadSemaphoreHandle, + SEMAPHORE_MODIFY_STATE, + *ExSemaphoreObjectType, + AccessMode, + &readSemaphore, + NULL + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + return status; + } + + /* Reference the write semaphore. */ + status = ObReferenceObjectByHandle( + WriteSemaphoreHandle, + SEMAPHORE_MODIFY_STATE, + *ExSemaphoreObjectType, + AccessMode, + &writeSemaphore, + NULL + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + ObDereferenceObject(readSemaphore); + return status; + } + + /* Create the client entry object. */ + status = KphCreateObject( + &clientEntry, + sizeof(KPHSS_CLIENT_ENTRY), + 0, + KphSsClientEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + { + ObDereferenceObject(processObject); + ObDereferenceObject(readSemaphore); + ObDereferenceObject(writeSemaphore); + + return status; + } + + clientEntry->Process = processObject; + clientEntry->Enabled = TRUE; + clientEntry->ReadSemaphore = readSemaphore; + clientEntry->WriteSemaphore = writeSemaphore; + ExInitializeFastMutex(&clientEntry->BufferMutex); + clientEntry->BufferBase = BufferBase; + clientEntry->BufferSize = BufferSize; + clientEntry->BufferCursor = 0; + clientEntry->NumberOfBlocksWritten = 0; + clientEntry->NumberOfBlocksDropped = 0; + + *ClientEntry = clientEntry; + + return status; +} + +/* KphSsEnableClientEntry + * + * Enables or disables a client entry. + */ +NTSTATUS KphSsEnableClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in BOOLEAN Enable + ) +{ + if (Enable) + ClientEntry->Enabled = TRUE; + else + ClientEntry->Enabled = FALSE; + + return STATUS_SUCCESS; +} + +/* KphSsQueryClientEntry + * + * Queries information about a client entry. + */ +NTSTATUS KphSsQueryClientEntry( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation, + __in ULONG ClientInformationLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + /* Probe the return length if necessary. */ + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* Check the length. */ + if (ClientInformationLength >= sizeof(KPHSS_CLIENT_INFORMATION)) + { + if (ClientInformation) + { + __try + { + /* Probe the buffer if we're not from kernel-mode. */ + if (AccessMode != KernelMode) + ProbeForWrite(ClientInformation, sizeof(KPHSS_CLIENT_INFORMATION), 1); + + ClientInformation->ProcessId = PsGetProcessId(ClientEntry->Process); + ClientInformation->BufferBase = ClientEntry->BufferBase; + ClientInformation->BufferSize = ClientEntry->BufferSize; + ClientInformation->NumberOfBlocksWritten = ClientEntry->NumberOfBlocksWritten; + ClientInformation->NumberOfBlocksDropped = ClientEntry->NumberOfBlocksDropped; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + + /* Pass the return length back if requested. */ + if (ReturnLength) + { + __try + { + *ReturnLength = sizeof(KPHSS_CLIENT_INFORMATION); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* KphpSsClientEntryDeleteProcedure + * + * Performs cleanup for a client entry. + */ +VOID NTAPI KphpSsClientEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPHSS_CLIENT_ENTRY clientEntry = (PKPHSS_CLIENT_ENTRY)Object; + + ObDereferenceObject(clientEntry->Process); + ObDereferenceObject(clientEntry->ReadSemaphore); + ObDereferenceObject(clientEntry->WriteSemaphore); +} + +/* KphSsCreateRuleSetEntry + * + * Creates a ruleset entry which contains a list of rules + * and an action to perform. + */ +NTSTATUS KphSsCreateRuleSetEntry( + __out PKPHSS_RULESET_ENTRY *RuleSetEntry, + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in KPHSS_FILTER_TYPE DefaultFilterType, + __in KPHSS_RULESET_ACTION Action + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULESET_ENTRY ruleSetEntry; + + /* Make sure the action is valid. */ + if (Action < LogRuleSetAction || Action >= MaxRuleSetAction) + return STATUS_INVALID_PARAMETER_3; + + /* Create the ruleset object. */ + status = KphCreateObject( + &ruleSetEntry, + sizeof(KPHSS_RULESET_ENTRY), + 0, + KphSsRuleSetEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the ruleset object. */ + KphReferenceObject(ClientEntry); + ruleSetEntry->Client = ClientEntry; + ruleSetEntry->DefaultFilterType = DefaultFilterType; + ruleSetEntry->Action = Action; + ruleSetEntry->NextRuleHandle = 4; + ExInitializePushLock(&ruleSetEntry->RuleListPushLock); + InitializeListHead(&ruleSetEntry->RuleListHead); + + /* Add the ruleset to the list. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock); + InsertHeadList(&KphSsRuleSetListHead, &ruleSetEntry->RuleSetListEntry); + ExReleasePushLock(&KphSsRuleSetListPushLock); + KeLeaveCriticalRegion(); + + *RuleSetEntry = ruleSetEntry; + + return status; +} + +/* KphpSsRuleSetEntryDeleteProcedure + * + * Performs cleanup for a ruleset entry. + */ +VOID NTAPI KphpSsRuleSetEntryDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPHSS_RULESET_ENTRY ruleSetEntry = (PKPHSS_RULESET_ENTRY)Object; + PLIST_ENTRY currentRuleListEntry; + + /* Dereference the client entry. */ + KphDereferenceObject(ruleSetEntry->Client); + + KeEnterCriticalRegion(); + + /* Dereference all rules in the ruleset. */ + ExAcquirePushLockExclusive(&ruleSetEntry->RuleListPushLock); + + currentRuleListEntry = ruleSetEntry->RuleListHead.Flink; + + while (currentRuleListEntry != &ruleSetEntry->RuleListHead) + { + PLIST_ENTRY nextEntry; + + /* Save the next entry pointer since currentRuleListEntry may + * be deallocated due to the dereference. + */ + nextEntry = currentRuleListEntry->Flink; + KphDereferenceObject(KPHSS_RULE_ENTRY(currentRuleListEntry)); + currentRuleListEntry = nextEntry; + } + + ExReleasePushLock(&ruleSetEntry->RuleListPushLock); + + /* Remove the ruleset from the list. */ + ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock); + RemoveEntryList(&ruleSetEntry->RuleSetListEntry); + ExReleasePushLock(&KphSsRuleSetListPushLock); + + KeLeaveCriticalRegion(); +} + +/* KphSsAddProcessIdRule + * + * Adds a process ID rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddProcessIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ProcessId + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ProcessIdRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->ProcessIdRule.ProcessId = ProcessId; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddThreadIdRule + * + * Adds a thread ID rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddThreadIdRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in HANDLE ThreadId + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ThreadIdRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->ThreadIdRule.ThreadId = ThreadId; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddPreviousModeRule + * + * Adds a previous mode rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddPreviousModeRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, PreviousModeRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->PreviousModeRule.PreviousMode = PreviousMode; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsAddNumberRule + * + * Adds a system service number rule entry to a ruleset entry. + */ +NTSTATUS KphSsAddNumberRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in ULONG Number + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Add the rule. */ + status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, NumberRuleType); + + if (!NT_SUCCESS(status)) + return status; + + ruleEntry->NumberRule.Number = Number; + ruleEntry->Initialized = TRUE; + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphSsGetHandleRule + * + * Gets the handle of a rule. + */ +HANDLE KphSsGetHandleRule( + __in PKPHSS_RULE_ENTRY RuleEntry + ) +{ + return RuleEntry->Handle; +} + +/* KphSsRemoveRule + * + * Removes a rule entry from a ruleset entry. + */ +NTSTATUS KphSsRemoveRule( + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in HANDLE RuleEntryHandle + ) +{ + PLIST_ENTRY currentListEntry; + + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock); + + /* Find the rule in the ruleset. */ + + currentListEntry = RuleSetEntry->RuleListHead.Flink; + + while (currentListEntry != &RuleSetEntry->RuleListHead) + { + PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry); + + if (ruleEntry->Handle == RuleEntryHandle) + { + /* Remove the rule from the list. */ + RemoveEntryList(&ruleEntry->RuleListEntry); + /* Dereference the rule (it was referenced when it + * got added to the list). + */ + KphDereferenceObject(ruleEntry); + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + return STATUS_SUCCESS; + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + + return STATUS_INVALID_PARAMETER_2; +} + +/* KphpSsAddRule + * + * Adds a rule entry to a ruleset entry. + */ +NTSTATUS KphpSsAddRule( + __out PKPHSS_RULE_ENTRY *RuleEntry, + __in PKPHSS_RULESET_ENTRY RuleSetEntry, + __in KPHSS_FILTER_TYPE FilterType, + __in KPHSS_RULE_TYPE RuleType + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_RULE_ENTRY ruleEntry; + + /* Make sure the filter/rule type is valid. */ + if (FilterType < IncludeFilterType || FilterType >= MaxFilterType) + return STATUS_INVALID_PARAMETER_3; + if (RuleType < ProcessIdRuleType || RuleType >= MaxRuleType) + return STATUS_INVALID_PARAMETER_4; + + /* Create the rule entry object. */ + status = KphCreateObject( + &ruleEntry, + sizeof(KPHSS_RULE_ENTRY), + 0, + KphSsRuleEntryType, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Initialize the object. */ + ruleEntry->Initialized = FALSE; + ruleEntry->FilterType = FilterType; + ruleEntry->RuleType = RuleType; + + /* Get a handle for the rule. */ + ruleEntry->Handle = (HANDLE)(ULONG_PTR)InterlockedExchangeAdd( + &RuleSetEntry->NextRuleHandle, + KPHSS_RULE_HANDLE_INCREMENT + ); + + /* Add the rule to the ruleset. */ + KeEnterCriticalRegion(); + ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock); + InsertTailList(&RuleSetEntry->RuleListHead, &ruleEntry->RuleListEntry); + ExReleasePushLock(&RuleSetEntry->RuleListPushLock); + KeLeaveCriticalRegion(); + /* Add a reference for the rule being on the list. */ + KphReferenceObject(ruleEntry); + + *RuleEntry = ruleEntry; + + return status; +} + +/* KphpSsCreateEventBlock + * + * Allocates and initializes an event block. + * + * EventBlock: A variable which receives a pointer to the event block. + * Thread: The thread for which the event is being generated. + * Number: The system service number. + * Arguments: A pointer to the caller-supplied arguments. + * NumberOfArguments: The number of arguments, in ULONGs. + */ +NTSTATUS KphpSsCreateEventBlock( + __out PKPHSS_EVENT_BLOCK *EventBlock, + __in PKTHREAD Thread, + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments + ) +{ + PKPHSS_EVENT_BLOCK eventBlock; + KPROCESSOR_MODE previousMode; + ULONG eventBlockSize; + ULONG argumentsSize; + ULONG traceSize; + PVOID stackTrace[MAX_STACK_DEPTH * 2]; + ULONG capturedFrames; + + /* Make sure the argument count isn't too large. */ + if (NumberOfArguments > MAX_USHORT) + return STATUS_INVALID_PARAMETER; + + previousMode = ExGetPreviousMode(); + + /* Capture kernel-mode and user-mode stack traces. + * We do this before we allocate the event block so + * we can calculate how large the block should be. + */ + + /* Get a kernel-mode stack trace. */ + capturedFrames = KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + 0, + stackTrace, + NULL + ); + + if (PsGetCurrentProcess() != PsInitialSystemProcess) + { + /* Get a user-mode stack trace. */ + capturedFrames += KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + RTL_WALK_USER_MODE_STACK, + &stackTrace[capturedFrames], + NULL + ); + } + + /* Calculate the size of the event block. */ + argumentsSize = NumberOfArguments * sizeof(ULONG); + traceSize = capturedFrames * sizeof(PVOID); + eventBlockSize = sizeof(KPHSS_EVENT_BLOCK) + argumentsSize + traceSize; + + /* Make sure the block size isn't too large. */ + if (eventBlockSize > MAX_USHORT) + return STATUS_INVALID_PARAMETER; + + /* Allocate the event block. */ + eventBlock = ExAllocatePoolWithTag(PagedPool, eventBlockSize, TAG_EVENT_BLOCK); + + if (!eventBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize the event block. */ + eventBlock->Header.Size = (USHORT)eventBlockSize; + eventBlock->Header.Type = EventBlockType; + eventBlock->Flags = 0; + KeQuerySystemTime(&eventBlock->Time); + eventBlock->ClientId.UniqueThread = PsGetThreadId(Thread); + eventBlock->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess(Thread)); + eventBlock->Number = Number; + eventBlock->NumberOfArguments = (USHORT)NumberOfArguments; + eventBlock->ArgumentsOffset = sizeof(KPHSS_EVENT_BLOCK); + eventBlock->TraceCount = (USHORT)capturedFrames; + eventBlock->TraceOffset = (USHORT)(sizeof(KPHSS_EVENT_BLOCK) + argumentsSize); + + /* Set the flags according to the previous mode. */ + if (previousMode == UserMode) + eventBlock->Flags |= KPHSS_EVENT_USER_MODE; + else if (previousMode == KernelMode) + eventBlock->Flags |= KPHSS_EVENT_KERNEL_MODE; + + /* Probe and copy the arguments. */ + if (previousMode != KernelMode) + { + __try + { + ProbeForRead(Arguments, argumentsSize, 4); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + eventBlock->Flags |= KPHSS_EVENT_PROBE_ARGUMENTS_FAILED; + } + } + + __try + { + /* Copy the arguments to the space immediately after the event block. */ + memcpy((PCHAR)eventBlock + eventBlock->ArgumentsOffset, Arguments, argumentsSize); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + eventBlock->Flags |= KPHSS_EVENT_COPY_ARGUMENTS_FAILED; + } + + /* Copy the stack trace. */ + memcpy((PCHAR)eventBlock + eventBlock->TraceOffset, stackTrace, traceSize); + + /* Pass the pointer to the event block back. */ + *EventBlock = eventBlock; + + return STATUS_SUCCESS; +} + +/* KphpSsFreeEventBlock + * + * Frees an event block created by KphpSsCreateEventBlock. + */ +VOID KphpSsFreeEventBlock( + __in PKPHSS_EVENT_BLOCK EventBlock + ) +{ + ExFreePoolWithTag(EventBlock, TAG_EVENT_BLOCK); +} + +/* KphpSsCaptureSimpleArgument + * + * Captures a simple (1-, 2-, 4- or 8-byte) argument. + */ +NTSTATUS KphpSsCaptureSimpleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PVOID Argument, + __in KPHSS_ARGUMENT_TYPE Type, + __in KPROCESSOR_MODE PreviousMode + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + LARGE_INTEGER value; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Get the proper argument size based on the argument type. */ + switch (Type) + { + case Int8Argument: + size = sizeof(BOOLEAN); + break; + case Int16Argument: + size = sizeof(SHORT); + break; + case Int32Argument: + size = sizeof(LONG); + break; + case Int64Argument: + size = sizeof(LARGE_INTEGER); + break; + default: + return STATUS_INVALID_PARAMETER_3; + } + + /* Probe and read the value. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, size, 1); + + memcpy(&value, Argument, size); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock(size, Type); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Copy the value into the argument block. */ + memcpy(&argumentBlock->Simple, &value, size); + *ArgumentBlock = argumentBlock; + + return STATUS_SUCCESS; +} + +/* KphpSsCaptureHandleArgument + * + * Captures a handle argument. + */ +NTSTATUS KphpSsCaptureHandleArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in HANDLE Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG bufferLength; + PVOID object; + POBJECT_TYPE objectType; + PUNICODE_STRING objectTypeName; + PUNICODE_STRING objectNameInfo; + ULONG returnLength; + PKPHSS_HANDLE handleInfo; + PKPHSS_WSTRING wString; + + /* Return if we have a NULL handle. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Make sure the handle isn't a kernel handle if we're + * from user-mode. We need exceptions for the process + * and thread pseudo-handles. + */ + if (PreviousMode != KernelMode) + { + if ( + IsKernelHandle(Argument) && + Argument != NtCurrentProcess() && + Argument != NtCurrentThread() + ) + return STATUS_INVALID_HANDLE; + } + + /* Reference the object. */ + status = ObReferenceObjectByHandle( + Argument, + 0, + NULL, + KernelMode, + &object, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + /* Get a pointer to the UNICODE_STRING containing the + * object type name. + */ + objectType = KphGetObjectTypeNt(object); + objectTypeName = (PUNICODE_STRING)KVOFF(objectType, OffOtName); + + /* Allocate a buffer for name information. */ + objectNameInfo = (PUNICODE_STRING)ExAllocatePoolWithTag( + PagedPool, + CAPTURE_HANDLE_BUFFER_SIZE, + TAG_CAPTURE_TEMP_BUFFER + ); + + if (!objectNameInfo) + goto CleanupObject; + + /* Query the name of the object. */ + status = KphQueryNameObject( + object, + objectNameInfo, + CAPTURE_HANDLE_BUFFER_SIZE, + &returnLength + ); + + if (!NT_SUCCESS(status)) + goto CleanupName; + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_HANDLE) + sizeof(KPHSS_WSTRING) + sizeof(KPHSS_WSTRING) + + objectTypeName->Length + objectNameInfo->Length, + HandleArgument + ); + + if (!argumentBlock) + goto CleanupName; + + handleInfo = &argumentBlock->Handle; + /* Calculate the offsets. */ + handleInfo->TypeNameOffset = sizeof(KPHSS_HANDLE); + handleInfo->NameOffset = + handleInfo->TypeNameOffset + sizeof(KPHSS_WSTRING) + + objectTypeName->Length; + + /* Copy the object type name into the block. */ + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->TypeNameOffset); + wString->Length = objectTypeName->Length; + memcpy(&wString->Buffer, objectTypeName->Buffer, wString->Length); + + /* Copy the object name into the block. */ + wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->NameOffset); + wString->Length = objectNameInfo->Length; + memcpy(&wString->Buffer, objectNameInfo->Buffer, wString->Length); + + /* We may be able to get additional information for the + * object. + */ + + handleInfo->ClientId.UniqueProcess = NULL; + handleInfo->ClientId.UniqueThread = NULL; + + if (objectType == *PsProcessType) + { + handleInfo->ClientId.UniqueProcess = PsGetProcessId((PEPROCESS)object); + } + else if (objectType == *PsThreadType) + { + handleInfo->ClientId.UniqueThread = PsGetThreadId((PETHREAD)object); + handleInfo->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess((PETHREAD)object)); + } + + *ArgumentBlock = argumentBlock; + +CleanupName: + ExFreePoolWithTag(objectNameInfo, TAG_CAPTURE_TEMP_BUFFER); +CleanupObject: + ObDereferenceObject(object); + + return status; +} + +/* KphpSsCaptureUnicodeStringArgument + * + * Captures a UNICODE_STRING argument. + */ +NTSTATUS KphpSsCaptureUnicodeStringArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PUNICODE_STRING Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + UNICODE_STRING unicodeString; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the UNICODE_STRING structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(UNICODE_STRING), 1); + + memcpy(&unicodeString, Argument, sizeof(UNICODE_STRING)); + + /* Probe the buffer, if present. */ + if (unicodeString.Buffer && PreviousMode != KernelMode) + { + ProbeForRead(unicodeString.Buffer, unicodeString.Length, 1); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Check if the string is too large. */ + if (unicodeString.Length > CAPTURE_UNICODE_STRING_MAX_SIZE) + return STATUS_UNSUCCESSFUL; + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_UNICODE_STRING) + unicodeString.Length, + UnicodeStringArgument + ); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Copy the string into the argument block. */ + argumentBlock->UnicodeString.Length = unicodeString.Length; + argumentBlock->UnicodeString.MaximumLength = unicodeString.MaximumLength; + argumentBlock->UnicodeString.Pointer = unicodeString.Buffer; + + if (unicodeString.Buffer) + { + __try + { + memcpy(argumentBlock->UnicodeString.Buffer, unicodeString.Buffer, unicodeString.Length); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphpSsFreeArgumentBlock(argumentBlock); + return GetExceptionCode(); + } + } + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCaptureObjectAttributesArgument + * + * Captures an OBJECT_ATTRIBUTES argument. + */ +NTSTATUS KphpSsCaptureObjectAttributesArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in POBJECT_ATTRIBUTES Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + OBJECT_ATTRIBUTES objectAttributes; + PKPHSS_ARGUMENT_BLOCK rootDirectoryArgumentBlock = NULL; + ULONG rootDirectoryArgumentBlockSize = 0; + PKPHSS_ARGUMENT_BLOCK objectNameArgumentBlock = NULL; + ULONG objectNameArgumentBlockSize = 0; + + /* Return if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the OBJECT_ATTRIBUTES structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(OBJECT_ATTRIBUTES), 1); + + memcpy(&objectAttributes, Argument, sizeof(OBJECT_ATTRIBUTES)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* If we have a root directory, create an argument block from it + * and copy it to our argument block. + */ + if (objectAttributes.RootDirectory) + { + status = KphpSsCaptureHandleArgument( + &rootDirectoryArgumentBlock, + objectAttributes.RootDirectory, + PreviousMode + ); + + /* If we created the argument block, we need to calculate + * the size of the KPHSS_HANDLE structure. + */ + if (NT_SUCCESS(status)) + { + rootDirectoryArgumentBlockSize = + rootDirectoryArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD; + } + else + { + rootDirectoryArgumentBlock = NULL; + } + } + + /* If we have a object name, create an argument block from it and + * copy it to our argument block. + */ + if (objectAttributes.ObjectName) + { + status = KphpSsCaptureUnicodeStringArgument( + &objectNameArgumentBlock, + objectAttributes.ObjectName, + PreviousMode + ); + + /* If we created the argument block, we need to calculate + * the size of the KPHSS_UNICODE_STRING structure. + */ + if (NT_SUCCESS(status)) + { + objectNameArgumentBlockSize = + objectNameArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD; + } + else + { + objectNameArgumentBlock = NULL; + } + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize + objectNameArgumentBlockSize, + ObjectAttributesArgument + ); + + argumentBlock->ObjectAttributes.RootDirectoryOffset = 0; + argumentBlock->ObjectAttributes.ObjectNameOffset = 0; + + /* Copy the object attributes fields. */ + memcpy( + &argumentBlock->ObjectAttributes.ObjectAttributes, + &objectAttributes, + sizeof(OBJECT_ATTRIBUTES) + ); + + /* Copy the root directory structure, if we have one. */ + if (rootDirectoryArgumentBlock) + { + ULONG rootDirectoryOffset; + + /* It will go directly after the KPHSS_OBJECT_ATTRIBUTES structure. */ + rootDirectoryOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES); + argumentBlock->ObjectAttributes.RootDirectoryOffset = (USHORT)rootDirectoryOffset; + /* Copy it. */ + memcpy( + PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, rootDirectoryOffset), + &rootDirectoryArgumentBlock->Handle, + rootDirectoryArgumentBlockSize + ); + /* Free the block. */ + KphpSsFreeArgumentBlock(rootDirectoryArgumentBlock); + } + + /* Copy the object name structure, if we have one. */ + if (objectNameArgumentBlock) + { + ULONG objectNameOffset; + + /* We'll place the structure after the root directory structure, + * if present. + */ + objectNameOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize; + + /* Make sure the offset isn't too large. */ + if (objectNameOffset <= MAX_USHORT) + { + argumentBlock->ObjectAttributes.ObjectNameOffset = (USHORT)objectNameOffset; + /* Copy it. */ + memcpy( + PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, objectNameOffset), + &objectNameArgumentBlock->UnicodeString, + objectNameArgumentBlockSize + ); + } + + /* Free the block. */ + KphpSsFreeArgumentBlock(objectNameArgumentBlock); + } + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCaptureClientIdArgument + * + * Captures a CLIENT_ID argument. + */ +NTSTATUS KphpSsCaptureClientIdArgument( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in PCLIENT_ID Argument, + __in KPROCESSOR_MODE PreviousMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + CLIENT_ID clientId; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + + /* Check if we have a NULL pointer. */ + if (!Argument) + return STATUS_INVALID_PARAMETER_2; + + /* Probe and copy the CLIENT_ID structure. */ + __try + { + if (PreviousMode != KernelMode) + ProbeForRead(Argument, sizeof(CLIENT_ID), 1); + + memcpy(&clientId, Argument, sizeof(CLIENT_ID)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + /* Allocate an argument block. */ + argumentBlock = KphpSsAllocateArgumentBlock( + sizeof(CLIENT_ID), + ClientIdArgument + ); + + if (!argumentBlock) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Fill in the argument block. */ + memcpy(&argumentBlock->ClientId, &clientId, sizeof(CLIENT_ID)); + + *ArgumentBlock = argumentBlock; + + return status; +} + +/* KphpSsCreateArgumentBlock + * + * Allocates and initializes an argument block. + */ +NTSTATUS KphpSsCreateArgumentBlock( + __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock, + __in ULONG Number, + __in ULONG Argument, + __in ULONG Index + ) +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + PKPHSS_ARGUMENT_BLOCK argumentBlock; + KPROCESSOR_MODE previousMode; + PKPHSS_CALL_ENTRY callEntry; + KPHSS_ARGUMENT_TYPE argumentType; + + previousMode = ExGetPreviousMode(); + + /* Get a pointer to the call entry for the system service. + * If we don't have one, we can't proceed. + */ + callEntry = KphSsLookupCallEntry(Number); + + if (!callEntry) + return STATUS_INVALID_PARAMETER_2; + + /* Validate the argument index. */ + if (Index >= callEntry->NumberOfArguments) + return STATUS_INVALID_PARAMETER_3; + + /* Is this a normal argument? If so, there's no point + * creating an argument block since the data is already + * in the event block. + */ + argumentType = callEntry->Arguments[Index]; + + if (argumentType == NormalArgument) + return STATUS_UNSUCCESSFUL; + + /* Capture the argument. */ + + switch (argumentType) + { + case Int8Argument: + case Int16Argument: + case Int32Argument: + case Int64Argument: + status = KphpSsCaptureSimpleArgument( + &argumentBlock, + (PVOID)Argument, + argumentType, + previousMode + ); + break; + case HandleArgument: + status = KphpSsCaptureHandleArgument( + &argumentBlock, + (HANDLE)Argument, + previousMode + ); + break; + case UnicodeStringArgument: + status = KphpSsCaptureUnicodeStringArgument( + &argumentBlock, + (PUNICODE_STRING)Argument, + previousMode + ); + break; + case ObjectAttributesArgument: + status = KphpSsCaptureObjectAttributesArgument( + &argumentBlock, + (POBJECT_ATTRIBUTES)Argument, + previousMode + ); + break; + case ClientIdArgument: + status = KphpSsCaptureClientIdArgument( + &argumentBlock, + (PCLIENT_ID)Argument, + previousMode + ); + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (!NT_SUCCESS(status)) + return status; + + /* Put the index in. */ + argumentBlock->Index = (UCHAR)Index; + + *ArgumentBlock = argumentBlock; + + return status; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +/* KphpSsAllocateArgumentBlock + * + * Allocates an argument block and initializes some fields. + */ +PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock( + __in ULONG InnerSize, + __in KPHSS_ARGUMENT_TYPE Type + ) +{ + PKPHSS_ARGUMENT_BLOCK argumentBlock; + ULONG size; + + size = KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize); + + /* Make sure the size isn't too large. */ + if (size > MAX_USHORT) + return NULL; + + argumentBlock = ExAllocatePoolWithTag( + PagedPool, + size, + TAG_ARGUMENT_BLOCK + ); + + if (!argumentBlock) + return NULL; + + argumentBlock->Header.Type = ArgumentBlockType; + argumentBlock->Header.Size = (USHORT)size; + argumentBlock->Type = Type; + + return argumentBlock; +} + +/* KphpSsFreeArgumentBlock + * + * Frees an argument block created by KphpSsCreateArgumentBlock. + */ +VOID KphpSsFreeArgumentBlock( + __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock + ) +{ + ExFreePoolWithTag(ArgumentBlock, TAG_ARGUMENT_BLOCK); +} + +/* KphpSsWriteBlock + * + * Writes a block into client memory. + */ +NTSTATUS KphpSsWriteBlock( + __in PKPHSS_CLIENT_ENTRY ClientEntry, + __in_opt PKPHSS_BLOCK_HEADER Block, + __in KPHSS_SEQUENCE_MODE SequenceMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + LARGE_INTEGER zeroTimeout; + KPH_ATTACH_STATE attachState; + ULONG availableSpace; + HANDLE dupHandleInClient = NULL; + + zeroTimeout.QuadPart = 0; + + /* Take care of the sequence mode. If it isn't + * NoSequence, it is effectively a way for the caller + * to control the buffer mutex. + */ + if (SequenceMode == StartSequence) + { + ExAcquireFastMutex(&ClientEntry->BufferMutex); + return STATUS_SUCCESS; + } + else if (SequenceMode == EndSequence) + { + ExReleaseFastMutex(&ClientEntry->BufferMutex); + return STATUS_SUCCESS; + } + else + { + /* If we aren't manipulating the mutex, we need + * a block to write. + */ + if (!Block) + return STATUS_INVALID_PARAMETER_2; + + /* If we're in a sequence, don't acquire the mutex + * because the caller would have acquired it using + * StartSequence already. + */ + if (SequenceMode != InSequence) + ExAcquireFastMutex(&ClientEntry->BufferMutex); + } + + /* Try to acquire the write semaphore. If we can't acquire + * it immediately, drop the block. + */ + status = KeWaitForSingleObject( + ClientEntry->WriteSemaphore, + Executive, + KernelMode, + FALSE, + &zeroTimeout + ); + + if (!KPHSS_BLOCK_SUCCESS(status)) + { + if (status == STATUS_TIMEOUT) + { + dprintf("Ss: WARNING: Dropped block (server %#x).\n", ClientEntry->BufferCursor); + ClientEntry->NumberOfBlocksDropped++; + } + + goto CleanupBufferMutex; + } + + availableSpace = ClientEntry->BufferSize - ClientEntry->BufferCursor; + + /* Blocks are recorded in a circular buffer. + * In the case that there is not enough space for an entire block, + * we will record a reset block that tells the client to reset + * its read cursor to 0. In the case that there is not enough + * space for a block header, it is implied that the client will + * reset its read cursor. + */ + + /* Check if we have enough space for a block header. */ + if (availableSpace < sizeof(KPHSS_BLOCK_HEADER)) + { + /* Not enough space. Reset the cursor. */ + dprintf("Ss: Implicit cursor reset (server %#x).\n", ClientEntry->BufferCursor); + ClientEntry->BufferCursor = 0; + availableSpace = ClientEntry->BufferSize; + } + /* Check if we have enough space for the block. */ + else if (availableSpace < Block->Size) + { + KPHSS_RESET_BLOCK resetBlock; + + /* Not enough space for the block, but enough space + * for a reset block. Write the reset block and reset + * the cursor. + */ + resetBlock.Header.Size = sizeof(KPHSS_RESET_BLOCK); + resetBlock.Header.Type = ResetBlockType; + + /* Attach to the client process and copy the block. */ + KphAttachProcess(ClientEntry->Process, &attachState); + + __try + { + memcpy( + PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor), + &resetBlock, + resetBlock.Header.Size + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + KphDetachProcess(&attachState); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + dprintf("Ss: Wrote reset block (server %#x).\n", ClientEntry->BufferCursor); + KphDetachProcess(&attachState); + ClientEntry->BufferCursor = 0; + availableSpace = ClientEntry->BufferSize; + } + + /* Now that we have dealt with any end-of-buffer issues, + * we still have to check if we have enough space for the + * event. We may have a huge event or the client may have a + * tiny buffer. + */ + if (availableSpace < Block->Size) + { + dfprintf("Ss: WARNING: Insufficient buffer size (server %#x).\n", ClientEntry->BufferCursor); + status = STATUS_BUFFER_TOO_SMALL; + goto CleanupBufferMutex; + } + + /* Time to copy the block into the buffer. + */ + KphAttachProcess(ClientEntry->Process, &attachState); + + __try + { + memcpy( + PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor), + Block, + Block->Size + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + dfprintf("Ss: ERROR: Could not write to the client buffer (server %#x)!\n", ClientEntry->BufferCursor); + KphDetachProcess(&attachState); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + KphDetachProcess(&attachState); + + /* Now that we have succesfully copied the block, we need to + * release the read semaphore to notify to the client that they have + * a block to read. We also need to advance our cursor. + */ + + /* May cause an exception (STATUS_SEMAPHORE_LIMIT_EXCEEDED). */ + __try + { + KeReleaseSemaphore(ClientEntry->ReadSemaphore, 2, 1, FALSE); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + dfprintf("Ss: ERROR: Could not release read semaphore (server %#x)!\n", ClientEntry->BufferCursor); + status = GetExceptionCode(); + goto CleanupBufferMutex; + } + + ClientEntry->BufferCursor += Block->Size; + ClientEntry->NumberOfBlocksWritten++; + + dprintf("Ss: Wrote block (server %#x).\n", ClientEntry->BufferCursor); + +CleanupBufferMutex: + if (SequenceMode != InSequence) + ExReleaseFastMutex(&ClientEntry->BufferMutex); + + return status; +} + +/* KphpSsLogSystemServiceCall + * + * Logs a system service. + * + * WARNING: This function CANNOT make any system calls. + * + * IRQL: <= APC_LEVEL + */ +VOID NTAPI KphpSsLogSystemServiceCall( + __in ULONG Number, + __in ULONG *Arguments, + __in ULONG NumberOfArguments, + __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable, + __in PKTHREAD Thread + ) +{ +#ifdef _X86_ + NTSTATUS status = STATUS_SUCCESS; + KPROCESSOR_MODE previousMode; + PLIST_ENTRY currentListEntry; + PKPHSS_RULESET_ENTRY ruleSetEntryArray[KPHSS_RULESET_ENTRY_LIMIT]; + ULONG ruleSetEntryCount; + PKPHSS_EVENT_BLOCK eventBlock; + PKPHSS_ARGUMENT_BLOCK argumentBlockArray[KPHSS_MAXIMUM_ARGUMENT_BLOCKS]; + ULONG i, j; + + previousMode = ExGetPreviousMode(); + /* Ignore the Thread argument. Replace it with our own. */ + Thread = KeGetCurrentThread(); + + /* First, some checks. + * * We can't operate at IRQL > APC_LEVEL because + * of restrictions on logging. + * * We can't operate on unknown service tables like the + * shadow service table (yet). + * * We have to make sure we aren't attempting to log + * a call to ZwContinue because we caused an exception + * last time we were logging something. This will cause + * a deadlock! + */ + + if (KeGetCurrentIrql() > APC_LEVEL) + return; + if ( + ServiceTable->Base != __KeServiceDescriptorTable->Base || + ServiceTable->Number != __KeServiceDescriptorTable->Number || + ServiceTable->Limit != __KeServiceDescriptorTable->Limit + ) + return; + + /* Make sure we aren't logging ZwContinue if it's because + * we caused an exception somewhere. */ + if ( + ServiceTable->Base == __KeServiceDescriptorTable->Base && + Number == SsNtContinue && + NumberOfArguments == 2 && + previousMode == KernelMode + ) + { + /* "Reverse probe" the arguments. */ + if ( + (ULONG_PTR)Arguments > (ULONG_PTR)MmHighestUserAddress && + Arguments[0] > (ULONG_PTR)MmHighestUserAddress + ) + { + CONTEXT context; + + /* The first argument contains the context. */ + memcpy(&context, (PCONTEXT)Arguments[0], sizeof(CONTEXT)); + /* Check if the context Eip points into the KPH module. + * If so, abort the logging. + */ + if ( + context.Eip >= (ULONG_PTR)KphDriverObject->DriverStart && + context.Eip < (ULONG_PTR)KphDriverObject->DriverStart + KphDriverObject->DriverSize + ) + return; + } + } + + /* Build the ruleset entry array by going through the ruleset + * list, referencing each relevant one and copying them into + * the local array. This we way don't hold the lock for too + * long. + */ + + KeEnterCriticalRegion(); + ExAcquirePushLockShared(&KphSsRuleSetListPushLock); + + currentListEntry = KphSsRuleSetListHead.Flink; + ruleSetEntryCount = 0; + + while ( + currentListEntry != &KphSsRuleSetListHead && + ruleSetEntryCount < KPHSS_RULESET_ENTRY_LIMIT + ) + { + PKPHSS_RULESET_ENTRY ruleSetEntry = KPHSS_RULESET_ENTRY(currentListEntry); + + if (KphpSsMatchRuleSetEntry( + ruleSetEntry, + Number, + Arguments, + NumberOfArguments, + ServiceTable, + Thread, + previousMode + )) + { + /* Reference and store the ruleset entry in the local array. */ + if (KphReferenceObjectSafe(ruleSetEntry)) + { + /* Make sure the client is enabled. */ + if (ruleSetEntry->Client->Enabled) + { + ruleSetEntryArray[ruleSetEntryCount] = ruleSetEntry; + ruleSetEntryCount++; + } + else + { + /* We need to use defer delete here because we hold the + * ruleset list lock. + */ + KphDereferenceObjectDeferDelete(ruleSetEntry); + } + } + } + + currentListEntry = currentListEntry->Flink; + } + + ExReleasePushLock(&KphSsRuleSetListPushLock); + KeLeaveCriticalRegion(); + + /* If we didn't find any ruleset entries, don't bother creating the + * event block. + */ + if (ruleSetEntryCount == 0) + return; + + /* We have work to do. Create an event block first. */ + if (!NT_SUCCESS(KphpSsCreateEventBlock( + &eventBlock, + Thread, + Number, + Arguments, + NumberOfArguments + ))) + { + dfprintf("Ss: ERROR: Unable to create an event block!\n"); + return; + } + + /* Create the argument blocks. If we fail to create one, + * set the array entry to NULL and we'll skip it later. + */ + + for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++) + { + ULONG argument; + + __try + { + /* We'll assume the arguments have already been probed + * since we created the event block successfully. + */ + argument = Arguments[i]; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* Silently skip this argument. Even though it is 99% likely + * that we will fail to read the next argument, continue + * anyway. + */ + argumentBlockArray[i] = NULL; + continue; + } + + status = KphpSsCreateArgumentBlock( + &argumentBlockArray[i], + Number, + argument, + i + ); + + if (!NT_SUCCESS(status)) + argumentBlockArray[i] = NULL; + } + + /* Go through the ruleset entry array and write the blocks to each + * client. While we're doing that we can also dereference each + * ruleset entry. + */ + for (i = 0; i < ruleSetEntryCount; i++) + { + /* Begin a sequence. */ + status = KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, StartSequence); + + if (NT_SUCCESS(status)) + { + /* Write the event block. */ + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &eventBlock->Header, InSequence); + + /* Write the argument blocks. */ + for (j = 0; j < NumberOfArguments && j < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; j++) + { + if (argumentBlockArray[j]) + { + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &argumentBlockArray[j]->Header, InSequence); + } + } + + /* End the sequence. */ + KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, EndSequence); + } + + KphDereferenceObject(ruleSetEntryArray[i]); + } + + /* Free the event block. */ + KphpSsFreeEventBlock(eventBlock); + + /* Free the argument blocks. */ + for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++) + { + if (argumentBlockArray[i]) + KphpSsFreeArgumentBlock(argumentBlockArray[i]); + } +#else + KeBugCheck(STATUS_NOT_SUPPORTED); +#endif +} + +#ifdef _X86_ + +/* KphpSsNewKiFastCallEntry + * + * The hook function called from within the hooked KiFastCallEntry. + */ +__declspec(naked) VOID NTAPI KphpSsNewKiFastCallEntry() +{ + /* KiFastCallEntry handles system service calls. User-mode applications + * will perform system calls like this: + * + * Nt*: + * mov eax, SystemServiceNumber + * mov edx, 0x7ffe0300 <-- at 0x7ffe0300 we have a pointer to KiFastSystemCall + * call [edx] + * ret + * + * At KiFastSystemCall: + * mov edx, esp + * sysenter + */ + /* This means that in KiFastCallEntry, eax will contain the system service + * number while edx will contain a pointer to the arguments for the system + * service. KiFastCallEntry will fill in edi with the service table, and + * esi will contain the caller KTHREAD. + * + * We cannot hook KiFastCallEntry from the beginning because it starts on the DPC + * stack. KiFastCallEntry switches to the proper thread stack, and we want to + * hook it just after it switches to the stack. That way we can avoid having to + * manually switch the thread stack ourselves. + * + * At this point: + * * eax contains the system service number. + * * edx contains a pointer to the user-supplied arguments for + * the system service. + * * edi contains a pointer to the service table associated with + * the system service number. + * * esi contains a pointer to the KTHREAD of the caller. + */ + /* Some context: + * + * push edx + * push eax + * call [_KeGdiFlushUserBatch] + * pop eax + * pop edx + * inc dword ptr fs:[PbSystemCalls] <-- this gets overwritten with a jmp to here + * mov edi, edx + * mov ebx, [edi+...] + * ... + */ + __asm + { + /* Save all registers first. */ + push ebp + push edi + push esi + push edx + push ecx + push ebx + push eax + + /* Since we overwrite the inc instruction when we did the hook, + * perform the job now - we have to increment the system calls + * counter. + */ + lea ebx, KphSsKiFastCallEntryHook /* get a pointer to the hook structure */ + mov ebx, dword ptr [ebx+KPH_HOOK.Bytes+3] /* get the PbSystemCalls offset from the original inc instruction */ + inc dword ptr fs:[ebx] /* increment PbSystemCalls in the PRCB */ + + /* Get the number of arguments for this system service. */ + mov ebx, dword ptr [edi+KSERVICE_TABLE_DESCRIPTOR.Number] /* ebx = a pointer to the argument table */ + xor ecx, ecx + mov cl, [ebx+eax] /* ecx = size of the arguments, in bytes. */ + shr ecx, 2 /* divide by 2 to get the number of arguments (all ULONGs) */ + + /* Call the KiFastCallEntry proc while maintaining the logger count + * so that the driver doesn't get unloaded while we're executing. + */ + push esi /* Thread */ + push edi /* ServiceTable */ + push ecx /* NumberOfArguments */ + push edx /* Arguments */ + push eax /* Number */ + lock inc dword ptr KphSsNumberOfActiveLoggers + call KphpSsLogSystemServiceCall + lock dec dword ptr KphSsNumberOfActiveLoggers + + /* Restore the registers and resume execution in KiFastCallEntry. */ + pop eax + pop ebx + pop ecx + pop edx + pop esi + pop edi + pop ebp + + /* Luckily, KiFastCallEntry will overwrite ebx when we jump back, so it's safe to use it. */ + lea ebx, __KiFastCallEntry + mov ebx, [ebx] /* ebx = KiFastCallEntry at the inc instruction */ + add ebx, 7 /* skip the inc instruction */ + jmp ebx /* jump back */ + } +} + +#else + +VOID NTAPI KphpSsNewKiFastCallEntry() +{ + KeBugCheck(STATUS_NOT_SUPPORTED); +} + +#endif diff --git a/branches/ph-plugins/KProcessHacker/sysservicedata.c b/branches/ph-plugins/KProcessHacker/sysservicedata.c new file mode 100644 index 000000000..c30c36917 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/sysservicedata.c @@ -0,0 +1,513 @@ +/* + * Process Hacker Driver - + * system service logging (data) + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _SYSSERVICEDATA_PRIVATE +#include "include/sysservicedata.h" + +PVOID KphpSsCallEntryAllocateRoutine( + __in PRTL_GENERIC_TABLE Table, + __in CLONG ByteSize + ); + +RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID FirstStruct, + __in PVOID SecondStruct + ); + +VOID KphpSsCallEntryFreeRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID Buffer + ); + +KPHSS_CALL_ENTRY SsEntries[] = +{ + /* NTSTATUS NtAddAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */ + { &SsNtAddAtom, "NtAddAtom", 3, { WStringArgument, 0, Int16Argument } }, + /* NTSTATUS NtAlertResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount) */ + { &SsNtAlertResumeThread, "NtAlertResumeThread", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtAlertThread(HANDLE ThreadHandle) */ + { &SsNtAlertThread, "NtAlertThread", 1, { HandleArgument } }, + /* NTSTATUS NtAllocateLocallyUniqueId(PLUID Luid) */ + { &SsNtAllocateLocallyUniqueId, "NtAllocateLocallyUniqueId", 1, { 0 } }, + /* NTSTATUS NtAllocateUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtAllocateUserPhysicalPages, "NtAllocateUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } }, + /* NTSTATUS NtAllocateUuids(PLARGE_INTEGER UuidLastTimeAllocated, PULONG UuidDeltaTime, PULONG UuidSequenceNumber, + * PUCHAR UuidSeed) */ + { &SsNtAllocateUuids, "NtAllocateUuids", 4, { Int64Argument, 0, 0, 0 } }, + /* NTSTATUS NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits, + * PULONG AllocationSize, ULONG AllocationType, ULONG Protect) */ + { &SsNtAllocateVirtualMemory, "NtAllocateVirtualMemory", 6, { HandleArgument, Int32Argument, 0, Int32Argument, 0, 0 } }, + /* NTSTATUS NtApphelpCacheControl(APPHELPCACHECONTROL ApphelpCacheControl, PUNICODE_STRING ApphelpCacheObject) */ + { &SsNtApphelpCacheControl, "NtApphelpCacheControl", 2, { 0, UnicodeStringArgument } }, + /* NTSTATUS NtAreMappedFilesTheSame(PVOID Address1, PVOID Address2) */ + { &SsNtAreMappedFilesTheSame, "NtAreMappedFilesTheSame", 2, { 0, 0 } }, + /* NTSTATUS NtAssignProcessToJobObject(HANDLE JobHandle, HANDLE ProcessHandle) */ + { &SsNtAssignProcessToJobObject, "NtAssignProcessToJobObject", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtCallbackReturn(PVOID Result, ULONG ResultLength, NTSTATUS Status) */ + { &SsNtCallbackReturn, "NtCallbackReturn", 3, { 0, 0, 0 } }, + /* NTSTATUS NtCancelDeviceWakeupRequest(HANDLE DeviceHandle) */ + { &SsNtCancelDeviceWakeupRequest, "NtCancelDeviceWakeupRequest", 1, { HandleArgument } }, + /* NTSTATUS NtCancelIoFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtCancelIoFile, "NtCancelIoFile", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtCancelTimer(HANDLE TimerHandle, PBOOLEAN CurrentState) */ + { &SsNtCancelTimer, "NtCancelTimer", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtClearEvent(HANDLE EventHandle) */ + { &SsNtClearEvent, "NtClearEvent", 1, { HandleArgument } }, + /* NTSTATUS NtClose(HANDLE Handle) */ + { &SsNtClose, "NtClose", 1, { HandleArgument } }, + /* NTSTATUS NtContinue(PCONTEXT Context, BOOLEAN TestAlert) */ + { &SsNtContinue, "NtContinue", 2, { ContextArgument, 0 } }, + /* NTSTATUS NtCreateDebugObject(PHANDLE DebugObjectHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG Flags) */ + { &SsNtCreateDebugObject, "NtCreateDebugObject", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateDirectoryObject, "NtCreateDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * EVENT_TYPE EventType, BOOLEAN InitialState) */ + { &SsNtCreateEvent, "NtCreateEvent", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } }, + /* NTSTATUS NtCreateEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateEventPair, "NtCreateEventPair", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, + * ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, + * PVOID EaBuffer, ULONG EaLength) */ + { &SsNtCreateFile, "NtCreateFile", 11, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG NumberOfConcurrentThreads) */ + { &SsNtCreateIoCompletion, "NtCreateIoCompletion", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtCreateJobObject, "NtCreateJobObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtCreateJobSet(ULONG NumJob, IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags) */ + { &SsNtCreateJobSet, "NtCreateJobSet", 3, { 0, 0, 0 } }, + /* NTSTATUS NtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, + * PULONG Disposition) */ + { &SsNtCreateKey, "NtCreateKey", 7, { 0, 0, ObjectAttributesArgument, 0, UnicodeStringArgument, 0, 0 } }, + /* NTSTATUS NtCreateKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * ULONG Flags) */ + { &SsNtCreateKeyedEvent, "NtCreateKeyedEvent", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateMailslotFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG CreateOptions, ULONG MailslotQuota, + * ULONG MaximumMessageSize, PLARGE_INTEGER ReadTimeout) */ + { &SsNtCreateMailslotFile, "NtCreateMailslotFile", 8, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, Int64Argument } }, + /* NTSTATUS NtCreateMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * BOOLEAN InitialOwner) */ + { &SsNtCreateMutant, "NtCreateMutant", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateNamedPipeFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG CreateDisposition, + * ULONG CreateOptions, BOOLEAN TypeMessage, BOOLEAN ReadmodeMessage, + * BOOLEAN Nonblocking, ULONG MaxInstances, ULONG InBufferSize, + * ULONG OutBufferSize, PLARGE_INTEGER DefaultTimeout) */ + { &SsNtCreateNamedPipeFile, "NtCreateNamedPipeFile", 14, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Int64Argument } }, + /* NTSTATUS NtCreatePagingFile(PUNICODE_STRING FileName, PULARGE_INTEGER MinimumSize, PULARGE_INTEGER MaximumSize, + * ULONG Priority) */ + { &SsNtCreatePagingFile, "NtCreatePagingFile", 4, { UnicodeStringArgument, Int64Argument, Int64Argument, 0 } }, + /* NTSTATUS NtCreatePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength, + * ULONG MaxMessageLength, ULONG MaxPoolUsage) */ + { &SsNtCreatePort, "NtCreatePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtCreatePrivateNamespace(PHANDLE PrivateNamespaceHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PBOUNDARY_DESCRIPTOR BoundaryDescriptor) */ + { &SsNtCreatePrivateNamespace, "NtCreatePrivateNamespace", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE InheritFromProcessHandle, BOOLEAN InheritHandles, HANDLE SectionHandle, + * HANDLE DebugPort, HANDLE ExceptionPort) */ + { &SsNtCreateProcess, "NtCreateProcess", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument } }, + /* NTSTATUS NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle, + * HANDLE DebugPort, HANDLE ExceptionPort, ULONG JobMemberLevel */ + { &SsNtCreateProcessEx, "NtCreateProcessEx", 9, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument, 0 } }, + /* NTSTATUS NtCreateProfile(PHANDLE ProfileHandle, HANDLE ProcessHandle, PVOID Base, + * ULONG Size, ULONG BucketShift, PULONG Buffer, + * ULONG BufferLength, KPROFILE_SOURCE Source, ULONG ProcessorMask) */ + { &SsNtCreateProfile, "NtCreateProfile", 9, { 0, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PLARGE_INTEGER SectionSize, ULONG Protect, ULONG Attributes, + * HANDLE FileHandle) */ + { &SsNtCreateSection, "NtCreateSection", 7, { 0, 0, ObjectAttributesArgument, Int64Argument, 0, 0, HandleArgument } }, + /* NTSTATUS NtCreateSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * LONG InitialCount, LONG MaximumCount) */ + { &SsNtCreateSemaphore, "NtCreateSemaphore", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } }, + /* NTSTATUS NtCreateSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PUNICODE_STRING TargetName) */ + { &SsNtCreateSymbolicLinkObject, "NtCreateSymbolicLinkObject", 4, { 0, 0, ObjectAttributesArgument, UnicodeStringArgument } }, + /* NTSTATUS NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext, + * PINITIAL_TEB UserStack, BOOLEAN CreateSuspended) */ + { &SsNtCreateThread, "NtCreateThread", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, ContextArgument, InitialTebArgument, 0 } }, + /* NTSTATUS NtCreateTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * TIMER_TYPE TimerType) */ + { &SsNtCreateTimer, "NtCreateTimer", 4, { 0, 0, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtCreateToken(PHANDLE TokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * TOKEN_TYPE Type, PLUID AuthenticationId, PLARGE_INTEGER ExpirationTime, + * PTOKEN_USER User, PTOKEN_GROUPS Groups, PTOKEN_PRIVILEGES Privileges, + * PTOKEN_OWNER Owner, PTOKEN_PRIMARY_GROUP PrimaryGroup, PTOKEN_DEFAULT_DACL DefaultDacl, + * PTOKEN_SOURCE Source) */ + { &SsNtCreateToken, "NtCreateToken", 13, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, Int64Argument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtCreateWaitablePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength, + * ULONG MaxMessageLength, ULONG MaxPoolUsage) */ + { &SsNtCreateWaitablePort, "NtCreateWaitablePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtDebugActiveProcess(HANDLE ProcessHandle, HANDLE DebugObjectHandle) */ + { &SsNtDebugActiveProcess, "NtDebugActiveProcess", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtDebugContinue(HANDLE DebugObjectHandle, PCLIENT_ID ClientId, NTSTATUS ContinueStatus) */ + { &SsNtDebugContinue, "NtDebugContinue", 3, { HandleArgument, ClientIdArgument, 0 } }, + /* NTSTATUS NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER Interval) */ + { &SsNtDelayExecution, "NtDelayExecution", 2, { 0, Int64Argument } }, + /* NTSTATUS NtDeleteAtom(USHORT Atom) */ + { &SsNtDeleteAtom, "NtDeleteAtom", 1, { 0 } }, + /* NTSTATUS NtDeleteBootEntry(ULONG Id) */ + { &SsNtDeleteBootEntry, "NtDeleteBootEntry", 1, { 0 } }, + /* NTSTATUS NtDeleteDriverEntry(ULONG Id) */ + { &SsNtDeleteDriverEntry, "NtDeleteDriverEntry", 1, { 0 } }, + /* NTSTATUS NtDeleteFile(POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtDeleteFile, "NtDeleteFile", 1, { ObjectAttributesArgument } }, + /* NTSTATUS NtDeleteKey(HANDLE KeyHandle) */ + { &SsNtDeleteKey, "NtDeleteKey", 1, { HandleArgument } }, + /* NTSTATUS NtDeleteObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID HandleId, BOOLEAN GenerateOnClose) */ + { &SsNtDeleteObjectAuditAlarm, "NtDeleteObjectAuditAlarm", 3, { UnicodeStringArgument, 0, 0 } }, + /* NTSTATUS NtDeletePrivateNamespace(HANDLE PrivateNamespaceHandle) */ + { &SsNtDeletePrivateNamespace, "NtDeletePrivateNamespace", 1, { HandleArgument } }, + /* NTSTATUS NtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName) */ + { &SsNtDeleteValueKey, "NtDeleteValueKey", 2, { HandleArgument, UnicodeStringArgument } }, + /* NTSTATUS NtDeviceIoControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, + * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, + * ULONG OutputBufferLength) */ + { &SsNtDeviceIoControlFile, "NtDeviceIoControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtDisplayString(PUNICODE_STRING String) */ + { &SsNtDisplayString, "NtDisplayString", 1, { UnicodeStringArgument } }, + /* NTSTATUS NtDuplicateObject(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, + * PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, + * ULONG Options) */ + { &SsNtDuplicateObject, "NtDuplicateObject", 7, { HandleArgument, HandleArgument, HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtDuplicateToken(HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, PHANDLE NewTokenHandle) */ + { &SsNtDuplicateToken, "NtDuplicateToken", 6, { HandleArgument, 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtEnumerateBootEntries(PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateBootEntries, "NtEnumerateBootEntries", 2, { 0, Int32Argument } }, + /* NTSTATUS NtEnumerateDriverEntries(PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateDriverEntries, "NtEnumerateDriverEntries", 2, { 0, Int32Argument } }, + /* NTSTATUS NtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, + * PVOID KeyInformation, ULONG KeyInformationLength, PULONG ResultLength) */ + { &SsNtEnumerateKey, "NtEnumerateKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtEnumerateSystemEnvironmentValuesEx(ULONG InformationClass, PVOID Buffer, PULONG BufferLength) */ + { &SsNtEnumerateSystemEnvironmentValuesEx, "NtEnumerateSystemEnvironmentValuesEx", 3, { 0, 0, Int32Argument } }, + /* NTSTATUS NtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + * PVOID KeyValueInformation, ULONG KeyValueInformationLength, PULONG ResultLength) */ + { &SsNtEnumerateValueKey, "NtEnumerateValueKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtExtendSection(HANDLE SectionHandle, PLARGE_INTEGER SectionSize) */ + { &SsNtExtendSection, "NtExtendSection", 2, { HandleArgument, Int64Argument } }, + /* NTSTATUS NtFilterToken(HANDLE ExistingTokenHandle, ULONG Flags, PTOKEN_GROUPS SidsToDisable, + * PTOKEN_PRIVILEGES PrivilegesToDelete, PTOKEN_GROUPS SidsToRestricted, PHANDLE NewTokenHandle) */ + { &SsNtFilterToken, "NtFilterToken", 6, { HandleArgument, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtFindAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */ + { &SsNtFindAtom, "NtFindAtom", 3, { WStringArgument, 0, 0 } }, + /* NTSTATUS NtFlushBuffersFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtFlushBuffersFile, "NtFlushBuffersFile", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtFlushInstructionCache(HANDLE ProcessHandle, PVOID BaseAddress, ULONG FlushSize) */ + { &SsNtFlushInstructionCache, "NtFlushInstructionCache", 3, { HandleArgument, 0, 0 } }, + /* NTSTATUS NtFlushKey(HANDLE KeyHandle) */ + { &SsNtFlushKey, "NtFlushKey", 1, { HandleArgument } }, + /* NTSTATUS NtFlushProcessWriteBuffers() */ + { &SsNtFlushProcessWriteBuffers, "NtFlushProcessWriteBuffers", 0 }, + /* NTSTATUS NtFlushVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FlushSize, + * PIO_STATUS_BLOCK IoStatusBlock) */ + { &SsNtFlushVirtualMemory, "NtFlushVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtFlushWriteBuffer() */ + { &SsNtFlushWriteBuffer, "NtFlushWriteBuffer", 0 }, + /* NTSTATUS NtFreeUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtFreeUserPhysicalPages, "NtFreeUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } }, + /* NTSTATUS NtFreeVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FreeSize, + * ULONG FreeType) */ + { &SsNtFreeVirtualMemory, "NtFreeVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, + * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, + * ULONG OutputBufferLength) */ + { &SsNtFsControlFile, "NtFsControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context) */ + { &SsNtGetContextThread, "NtGetContextThread", 2, { HandleArgument, ContextArgument } }, + /* NTSTATUS NtGetCurrentProcessorNumber() */ + { &SsNtGetCurrentProcessorNumber, "NtGetCurrentProcessorNumber", 0 }, + /* NTSTATUS NtGetDevicePowerState(HANDLE DeviceHandle, PDEVICE_POWER_STATE DevicePowerState) */ + { &SsNtGetDevicePowerState, "NtGetDevicePowerState", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtGetNextProcess(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, + * ULONG Flags, PHANDLE NewProcessHandle) */ + { &SsNtGetNextProcess, "NtGetNextProcess", 5, { HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetNextThread(HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, + * ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle) */ + { &SsNtGetNextThread, "NtGetNextThread", 6, { HandleArgument, HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtGetPlugPlayEvent(HANDLE EventHandle, PVOID Context, PVOID Buffer, + * ULONG BufferLength) */ + { &SsNtGetPlugPlayEvent, "NtGetPlugPlayEvent", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtGetWriteWatch(HANDLE ProcessHandle, ULONG Flags, PVOID BaseAddress, + * ULONG RegionSize, PULONG Buffer, PULONG BufferEntries, + * PULONG Granularity) */ + { &SsNtGetWriteWatch, "NtGetWriteWatch", 7, { HandleArgument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtImpersonateAnonymousToken(HANDLE ThreadHandle) */ + { &SsNtImpersonateAnonymousToken, "NtImpersonateAnonymousToken", 1, { HandleArgument } }, + /* NTSTATUS NtImpersonateClientOfPort(HANDLE PortHandle, PPORT_MESSAGE Message) */ + { &SsNtImpersonateClientOfPort, "SsNtImpersonateClientOfPort", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtImpersonateThread(HANDLE ThreadHandle, HANDLE TargetThreadHandle, PSECURITY_QUALITY_OF_SERVICE SecurityQos) */ + { &SsNtImpersonateThread, "NtImpersonateThread", 3, { HandleArgument, HandleArgument, 0 } }, + /* NTSTATUS NtInitiatePowerAction(POWER_ACTION SystemAction, SYSTEM_POWER_STATE MinSystemState, ULONG Flags, + * BOOLEAN Asynchronous) */ + { &SsNtInitiatePowerAction, "NtInitiatePowerAction", 4, { 0, 0, 0, 0 } }, + /* NTSTATUS NtIsProcessInJob(HANDLE ProcessHandle, HANDLE JobHandle) */ + { &SsNtIsProcessInJob, "NtIsProcessInJob", 2, { HandleArgument, HandleArgument } }, + /* NTSTATUS NtIsSystemResumeAutomatic() */ + { &SsNtIsSystemResumeAutomatic, "NtIsSystemResumeAutomatic", 0 }, + /* NTSTATUS NtListenPort(HANDLE PortHandle, PPORT_MESSAGE Message) */ + { &SsNtListenPort, "NtListenPort", 2, { HandleArgument, 0 } }, + /* NTSTATUS NtLoadDriver(PUNICODE_STRING DriverServiceName) */ + { &SsNtLoadDriver, "NtLoadDriver", 1, { UnicodeStringArgument } }, + /* NTSTATUS NtLoadKey(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes) */ + { &SsNtLoadKey, "NtLoadKey", 2, { ObjectAttributesArgument, ObjectAttributesArgument } }, + /* NTSTATUS NtLoadKey2(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes, ULONG Flags) */ + { &SsNtLoadKey2, "NtLoadKey2", 3, { ObjectAttributesArgument, ObjectAttributesArgument, 0 } }, + /* NTSTATUS NtLockFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PULARGE_INTEGER LockOffset, + * PULARGE_INTEGER LockLength, ULONG Key, BOOLEAN FailImmediately, + * BOOLEAN ExclusiveLock) */ + { &SsNtLockFile, "NtLockFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, Int64Argument, Int64Argument, 0, 0, 0 } }, + /* NTSTATUS NtLockVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG LockSize, + * ULONG LockType) */ + { &SsNtLockVirtualMemory, "NtLockVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } }, + /* NTSTATUS NtMakePermanentObject(HANDLE Handle) */ + { &SsNtMakePermanentObject, "NtMakePermanentObject", 1, { HandleArgument } }, + /* NTSTATUS NtMakeTemporaryObject(HANDLE Handle) */ + { &SsNtMakeTemporaryObject, "NtMakeTemporaryObject", 1, { HandleArgument } }, + /* NTSTATUS NtMapUserPhysicalPages(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtMapUserPhysicalPages, "NtMapUserPhysicalPages", 3, { 0, Int32Argument, 0 } }, + /* NTSTATUS NtMapUserPhysicalPagesScatter(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */ + { &SsNtMapUserPhysicalPagesScatter, "NtMapUserPhysicalPagesScatter", 3, { 0, Int32Argument, 0 } }, + /* NTSTATUS NtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID *BaseAddress, + * ULONG ZeroBits, ULONG CommitSize, PLARGE_INTEGER SectionOffset, + * PULONG ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, + * ULONG Protect) */ + { &SsNtMapViewOfSection, "NtMapViewOfSection", 10, { HandleArgument, HandleArgument, Int32Argument, 0, 0, Int64Argument, Int32Argument, 0, 0, 0 } }, + /* NTSTATUS NtModifyBootEntry(PBOOT_ENTRY BootEntry) */ + { &SsNtModifyBootEntry, "NtModifyBootEntry", 1, { 0 } }, + /* NTSTATUS NtModifyDriverEntry(PEFI_DRIVER_ENTRY DriverEntry) */ + { &SsNtModifyDriverEntry, "NtModifyDriverEntry", 1, { 0 } }, + /* NTSTATUS NtNotifyChangeDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PFILE_NOTIFY_INFORMATION Buffer, + * ULONG BufferLength, ULONG NotifyFilter, BOOLEAN WatchSubtree) */ + { &SsNtNotifyChangeDirectoryFile, "NtNotifyChangeDirectoryFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtNotifyChangeKey(HANDLE KeyHandle, HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter, + * BOOLEAN WatchSubtree, PVOID Buffer, ULONG BufferLength, + * BOOLEAN Asynchronous) */ + { &SsNtNotifyChangeKey, "NtNotifyChangeKey", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtNotifyChangeMultipleKeys(HANDLE KeyHandle, ULONG Flags, POBJECT_ATTRIBUTES KeyObjectAttributes, + * HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter, BOOLEAN WatchSubtree, + * PVOID Buffer, ULONG BufferLength, BOOLEAN Asynchronous) */ + { &SsNtNotifyChangeMultipleKeys, "NtNotifyChangeMultipleKeys", 12, { HandleArgument, 0, ObjectAttributesArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenDirectoryObject, "NtOpenDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenEvent, "NtOpenEvent", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenEventPair, "NtOpenEventPair", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions) */ + { &SsNtOpenFile, "NtOpenFile", 6, { 0, 0, ObjectAttributesArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenIoCompletion, "NtOpenIoCompletion", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenJobObject, "NtOpenJobObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenKey, "NtOpenKey", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenKeyedEvent, "NtOpenKeyedEvent", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenMutant, "NtOpenMutant", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID *HandleId, PUNICODE_STRING ObjectTypeName, + * PUNICODE_STRING ObjectName, PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE TokenHandle, + * ACCESS_MASK DesiredAccess, ACCESS_MASK GrantedAccess, PPRIVILEGE_SET Privileges, + * BOOLEAN ObjectCreation, BOOLEAN AccessGranted, PBOOLEAN GenerateOnClose) */ + { &SsNtOpenObjectAuditAlarm, "NtOpenObjectAuditAlarm", 12, { UnicodeStringArgument, Int32Argument, UnicodeStringArgument, UnicodeStringArgument, 0, HandleArgument, 0, 0, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PCLIENT_ID ClientId) */ + { &SsNtOpenProcess, "NtOpenProcess", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } }, + /* NTSTATUS NtOpenProcessToken(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, PHANDLE TokenHandle) */ + { &SsNtOpenProcessToken, "NtOpenProcessToken", 3, { HandleArgument, 0, 0 } }, + /* NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, + * PHANDLE TokenHandle) */ + { &SsNtOpenProcessTokenEx, "NtOpenProcessTokenEx", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSection, "NtOpenSection", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSemaphore, "NtOpenSemaphore", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenSymbolicLinkObject, "NtOpenSymbolicLinkObject", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtOpenThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + * PCLIENT_ID ClientId) */ + { &SsNtOpenThread, "NtOpenThread", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } }, + /* NTSTATUS NtOpenThreadToken(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf, + * PHANDLE TokenHandle) */ + { &SsNtOpenThreadToken, "NtOpenThreadToken", 4, { HandleArgument, 0, 0, 0 } }, + /* NTSTATUS NtOpenThreadTokenEx(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf, + * ULONG HandleAttributes, PHANDLE TokenHandle) */ + { &SsNtOpenThreadTokenEx, "NtOpenThreadTokenEx", 5, { HandleArgument, 0, 0, 0, 0 } }, + /* NTSTATUS NtOpenTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */ + { &SsNtOpenTimer, "NtOpenTimer", 3, { 0, 0, ObjectAttributesArgument } }, + /* NTSTATUS NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, + * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */ + { &SsNtReadFile, "NtReadFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } }, + /* NTSTATUS NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, + * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, + * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */ + { &SsNtWriteFile, "NtWriteFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } }, + + { NULL, "Dummy", 0 } +}; + +RTL_GENERIC_TABLE KphSsCallTable; +FAST_MUTEX KphSsCallTableMutex; + +/* KphSsDataInit + * + * Initializes all data structures so that system service entries + * can be looked up. + */ +VOID KphSsDataInit() +{ + ULONG i; + + RtlInitializeGenericTable( + &KphSsCallTable, + KphpSsCallEntryCompareRoutine, + KphpSsCallEntryAllocateRoutine, + KphpSsCallEntryFreeRoutine, + NULL + ); + + for (i = 0; i < sizeof(SsEntries) / sizeof(KPHSS_CALL_ENTRY); i++) + { + /* Ignore the dummy entry. */ + if (SsEntries[i].Number) + { + RtlInsertElementGenericTable( + &KphSsCallTable, + &SsEntries[i], + /* Save some space... */ + FIELD_OFFSET(KPHSS_CALL_ENTRY, Arguments) + + SsEntries[i].NumberOfArguments * sizeof(KPHSS_ARGUMENT_TYPE), + NULL + ); + } + } + + ExInitializeFastMutex(&KphSsCallTableMutex); +} + +/* KphSsDataDeinit + * + * Frees all memory associated with system service data. + */ +VOID KphSsDataDeinit() +{ + PKPHSS_CALL_ENTRY callEntry; + + while (callEntry = (PKPHSS_CALL_ENTRY)RtlGetElementGenericTable(&KphSsCallTable, 0)) + RtlDeleteElementGenericTable(&KphSsCallTable, callEntry); +} + +/* KphSsLookupCallEntry + * + * Lookups up a system service entry by system service number. + */ +PKPHSS_CALL_ENTRY KphSsLookupCallEntry( + __in ULONG Number + ) +{ + KPHSS_CALL_ENTRY callEntry; + PKPHSS_CALL_ENTRY foundEntry; + + callEntry.Number = &Number; + + ExAcquireFastMutex(&KphSsCallTableMutex); + foundEntry = (PKPHSS_CALL_ENTRY)RtlLookupElementGenericTable( + &KphSsCallTable, + &callEntry + ); + ExReleaseFastMutex(&KphSsCallTableMutex); + + return foundEntry; +} + +/* KphpSsCallEntryAllocateRoutine + * + * Allocates storage for a system service entry. + */ +PVOID KphpSsCallEntryAllocateRoutine( + __in PRTL_GENERIC_TABLE Table, + __in CLONG ByteSize + ) +{ + return ExAllocatePoolWithTag( + PagedPool, + ByteSize, + TAG_CALL_ENTRY + ); +} + +/* KphpSsCallEntryCompareRoutine + * + * Compares two system service entries. + */ +RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID FirstStruct, + __in PVOID SecondStruct + ) +{ + PKPHSS_CALL_ENTRY callEntry1, callEntry2; + + callEntry1 = (PKPHSS_CALL_ENTRY)FirstStruct; + callEntry2 = (PKPHSS_CALL_ENTRY)SecondStruct; + + if (*(callEntry1->Number) < *(callEntry2->Number)) + return GenericLessThan; + else if (*(callEntry1->Number) > *(callEntry2->Number)) + return GenericGreaterThan; + else + return GenericEqual; +} + +/* KphpSsCallEntryFreeRoutine + * + * Frees storage for a system service entry. + */ +VOID KphpSsCallEntryFreeRoutine( + __in PRTL_GENERIC_TABLE Table, + __in PVOID Buffer + ) +{ + ExFreePoolWithTag( + Buffer, + TAG_CALL_ENTRY + ); +} diff --git a/branches/ph-plugins/KProcessHacker/test.c b/branches/ph-plugins/KProcessHacker/test.c new file mode 100644 index 000000000..ffc9d6016 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/test.c @@ -0,0 +1,71 @@ +/* + * Process Hacker Driver - + * testing code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" + +static EX_PUSH_LOCK TestLock; + +VOID KphpTestPushLockThreadStart( + __in PVOID Context + ); + +VOID KphTestPushLock() +{ + ULONG i; + + ExInitializePushLock(&TestLock); + + for (i = 0; i < 10; i++) + { + HANDLE threadHandle; + OBJECT_ATTRIBUTES objectAttributes; + + InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + PsCreateSystemThread(&threadHandle, 0, &objectAttributes, NULL, NULL, KphpTestPushLockThreadStart, NULL); + } +} + +VOID KphpTestPushLockThreadStart( + __in PVOID Context + ) +{ + ULONG i, j; + + for (i = 0; i < 400000; i++) + { + ExAcquirePushLockShared(&TestLock); + + for (j = 0; j < 1000; j++) + YieldProcessor(); + + ExReleasePushLock(&TestLock); + + ExAcquirePushLockExclusive(&TestLock); + + for (j = 0; j < 9000; j++) + YieldProcessor(); + + ExReleasePushLock(&TestLock); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} diff --git a/branches/ph-plugins/KProcessHacker/trace.c b/branches/ph-plugins/KProcessHacker/trace.c new file mode 100644 index 000000000..57fc98ede --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/trace.c @@ -0,0 +1,344 @@ +/* + * Process Hacker Driver - + * stack tracing + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/kph.h" + +BOOLEAN KphpCaptureAndAddStack( + __in PRTL_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ); + +VOID KphpTraceDatabaseDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ); + +PKPH_OBJECT_TYPE KphTraceDatabaseType; + +/* KphTraceDatabaseInitialization + * + * Creates the TraceDatabase object type. + */ +NTSTATUS KphTraceDatabaseInitialization() +{ + NTSTATUS status = STATUS_SUCCESS; + + status = KphCreateObjectType( + &KphTraceDatabaseType, + PagedPool, + 0, + KphpTraceDatabaseDeleteProcedure + ); + + if (!NT_SUCCESS(status)) + return status; + + return status; +} + +/* KphCaptureStackBackTrace + * + * Walks the stack, capturing the return address from each frame. + * + * Return value: the number of captured addresses in the buffer. + */ +ULONG KphCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __in_opt ULONG Flags, + __out_ecount(FramesToCapture) PVOID *BackTrace, + __out_opt PULONG BackTraceHash + ) +{ + PVOID backTrace[MAX_STACK_DEPTH]; + ULONG framesFound; + ULONG hash; + ULONG i; + + /* Skip the current frame (for this function). */ + FramesToSkip++; + + /* Check the input. */ + /* Ensure we won't overrun the buffer. */ + if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH) + return 0; + /* Make sure the flags are correct. */ + if ((Flags & RTL_WALK_VALID_FLAGS) != Flags) + return 0; + + /* Walk the frame chain. */ + framesFound = RtlWalkFrameChain( + backTrace, + FramesToCapture + FramesToSkip, + Flags + ); + /* Return if we found fewer frames than we wanted to skip. */ + if (framesFound <= FramesToSkip) + return 0; + + /* Copy over the stack trace. + * At the same time we calculate the stack trace hash by + * summing the addresses. + */ + for (i = 0, hash = 0; i < FramesToCapture; i++) + { + if (FramesToSkip + i >= framesFound) + break; + + BackTrace[i] = backTrace[FramesToSkip + i]; + hash += PtrToUlong(BackTrace[i]); + } + + /* Pass the hash back if the caller requested it. */ + if (BackTraceHash) + *BackTraceHash = hash; + + /* Return the number of addresses we copied. */ + return i; +} + +/* KphCaptureAndAddStack + * + * Captures a stack trace and adds it to a trace database. + */ +BOOLEAN KphCaptureAndAddStack( + __in PKPH_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ) +{ + return KphpCaptureAndAddStack( + Database->Database, + Type, + TraceBlock + ); +} + +/* KphCreateTraceDatabase + * + * Creates a trace database. + */ +NTSTATUS KphCreateTraceDatabase( + __out PKPH_TRACE_DATABASE *Database, + __in_opt SIZE_T MaximumSize, + __in ULONG Flags, + __in ULONG Tag + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PRTL_TRACE_DATABASE rtlDatabase; + PKPH_TRACE_DATABASE database; + + /* Create the trace database. */ + rtlDatabase = RtlTraceDatabaseCreate( + 8, + MaximumSize, + Flags, + Tag, + NULL + ); + + if (!rtlDatabase) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Create the object. */ + status = KphCreateObject( + &database, + sizeof(KPH_TRACE_DATABASE), + 0, + KphTraceDatabaseType, + 0 + ); + + if (!NT_SUCCESS(status)) + { + /* Destroy the trace database, since we can't use it. */ + RtlTraceDatabaseDestroy(rtlDatabase); + + return status; + } + + /* Set up the trace database object. */ + database->Database = rtlDatabase; + *Database = database; + + return status; +} + +NTSTATUS KphQueryTraceDatabase( + __in PKPH_TRACE_DATABASE Database, + __out_bcount_opt(BufferLength) PKPH_TRACEDB_INFORMATION Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength, + __in KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PRTL_TRACE_DATABASE rtlDatabase = Database->Database; + PKPH_TRACEDB_INFORMATION nextEntry; + RTL_TRACE_ENUMERATE enumContext = { 0 }; + PRTL_TRACE_BLOCK currentBlock; + + /* Probe buffers. */ + if (AccessMode != KernelMode) + { + __try + { + if (Buffer) + ProbeForWrite(Buffer, BufferLength, 1); + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + /* First entry to write to. */ + /* Note that this is completely safe if Buffer is NULL. */ + nextEntry = Buffer; + + /* Enumerate the trace blocks. */ + while (RtlTraceDatabaseEnumerate(rtlDatabase, &enumContext, ¤tBlock)) + { + PKPH_TRACEDB_INFORMATION currentEntry; + + /* Save the pointer to the entry we are about to write to. */ + currentEntry = nextEntry; + /* Compute the location of the next entry. */ + nextEntry = (PKPH_TRACEDB_INFORMATION)( + (ULONG_PTR)currentEntry + /* Current entry plus */ + sizeof(KPH_TRACEDB_INFORMATION) - /* the size of the current entry minus */ + sizeof(PVOID) + /* the extra PVOID in the Trace array plus */ + currentBlock->Size * sizeof(PVOID) /* the size of the stack trace. */ + ); + + if ( + /* If we got an error last time we tried to write to the buffer, + * don't try again this time. */ + NT_SUCCESS(status) && + /* Make sure the buffer isn't NULL. */ + Buffer && + /* Make sure we don't exceed the buffer length. */ + ((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer) <= BufferLength + ) + { + __try + { + currentEntry->NextEntryOffset = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)currentEntry); + currentEntry->Count = currentBlock->Count; + currentEntry->TraceSize = currentBlock->Size; + memcpy(currentEntry->Trace, currentBlock->Trace, currentBlock->Size * sizeof(PVOID)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + { + __try + { + *ReturnLength = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + return status; +} + +/* KphCaptureAndAddStack + * + * Captures a stack trace and adds it to a trace database. + */ +BOOLEAN KphpCaptureAndAddStack( + __in PRTL_TRACE_DATABASE Database, + __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type, + __out_opt PRTL_TRACE_BLOCK *TraceBlock + ) +{ + PVOID trace[MAX_STACK_DEPTH * 2]; + ULONG kmodeFramesFound = 0; + ULONG umodeFramesFound = 0; + + /* Check input. */ + if (Type >= KphCaptureAndAddMaximum) + return FALSE; + + /* Capture the kernel-mode stack if needed. */ + if ( + Type == KphCaptureAndAddKModeStack || + Type == KphCaptureAndAddBothStacks + ) + kmodeFramesFound = KphCaptureStackBackTrace( + 1, + MAX_STACK_DEPTH - 1, + 0, + trace, + NULL + ); + /* Capture the user-mode stack if needed. */ + if ( + Type == KphCaptureAndAddUModeStack || + Type == KphCaptureAndAddBothStacks + ) + umodeFramesFound = KphCaptureStackBackTrace( + 0, + MAX_STACK_DEPTH - 1, + RTL_WALK_USER_MODE_STACK, + &trace[kmodeFramesFound], + NULL + ); + + /* Add the trace to the database. */ + return RtlTraceDatabaseAdd( + Database, + kmodeFramesFound + umodeFramesFound, + trace, + TraceBlock + ); +} + +/* KphpTraceDatabaseDeleteProcedure + * + * Destroys a trace database. + */ +VOID KphpTraceDatabaseDeleteProcedure( + __in PVOID Object, + __in ULONG Flags + ) +{ + PKPH_TRACE_DATABASE database = (PKPH_TRACE_DATABASE)Object; + + RtlTraceDatabaseDestroy(database->Database); +} diff --git a/branches/ph-plugins/KProcessHacker/util.c b/branches/ph-plugins/KProcessHacker/util.c new file mode 100644 index 000000000..61e3b3ecf --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/util.c @@ -0,0 +1,115 @@ +/* + * Process Hacker Driver - + * utility functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "include/util.h" + +/* KphInitializeStream + * + * Initializes a stream. + * + * Stream: The stream to initialize. + * Buffer: The buffer to use. + * Length: The maximum number of bytes that can be stored in + * the buffer. If an attempt is made to overrun or underrun + * the buffer, an exception will be raised. + */ +VOID KphInitializeStream( + __out PKPH_STREAM Stream, + __in PVOID Buffer, + __in ULONG Length + ) +{ + ASSERT(Length > 0); + + Stream->Buffer = Buffer; + Stream->Length = Length; + Stream->Position = 0; +} + +/* KphSeekStream + * + * Changes the position of a stream. + */ +ULONG KphSeekStream( + __inout PKPH_STREAM Stream, + __in LONG Offset, + __in KPH_STREAM_ORIGIN Origin + ) +{ + ULONG newPosition; + + switch (Origin) + { + case StartOrigin: + { + /* Can't seek to before the start of the buffer. */ + if (Offset < 0) + ExRaiseStatus(STATUS_INVALID_PARAMETER_2); + + newPosition = Offset; + } + break; + + case CurrentOrigin: + { + newPosition = Stream->Position + Offset; + } + break; + + case EndOrigin: + { + newPosition = Stream->Length - Offset - 1; + } + break; + } + + /* Check the new position and raise an exception if + * appropriate. + */ + KphCheckStreamPosition(Stream, newPosition); + Stream->Position = newPosition; + + return newPosition; +} + +/* KphWriteDataStream + * + * Writes data to a stream. + */ +ULONG KphWriteDataStream( + __inout PKPH_STREAM Stream, + __in PVOID Data, + __in ULONG Length + ) +{ + /* Check if we are going to overrun the buffer. */ + KphCheckStreamPosition(Stream, Stream->Position + Length); + /* Copy the data. */ + memcpy( + PTR_ADD_OFFSET(Stream->Buffer, Stream->Position), + Data, + Length + ); + + /* Increase the position. */ + return Stream->Position += Length; +} diff --git a/branches/ph-plugins/KProcessHacker/version.c b/branches/ph-plugins/KProcessHacker/version.c new file mode 100644 index 000000000..e8a7a8410 --- /dev/null +++ b/branches/ph-plugins/KProcessHacker/version.c @@ -0,0 +1,536 @@ +/* + * Process Hacker Driver - + * Windows version-specific data + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define _VERSION_PRIVATE +#include "include/version.h" +#include "include/debug.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KvInit) +#pragma alloc_text(PAGE, KvScanProc) +#pragma alloc_text(PAGE, KvVerifyPrologue) +#endif + +/* + * mov edi, edi + * push ebp + * mov ebp, esp + */ +static char StandardPrologue[] = { 0x8b, 0xff, 0x55, 0x8b, 0xec }; + +/* KiFastCallEntry */ +/* + * Note that this scan will get the address of + * mov esi, edx + * within KiFastCallEntry, not the start of KiFastCallEntry. + * We will then subtract 7 to get the address of + * inc dword ptr fs:PbSystemCalls + * See sysservice.c for more details. + */ +static char KiFastCallEntry51[] = +{ + 0x8b, 0xf2, 0x8b, 0x5f, 0x0c, 0x33, 0xc9, 0x8a, + 0x0c, 0x18, 0x8b, 0x3f, 0x8b, 0x1c, 0x87, 0x2b +}; +static char KiFastCallEntry60[] = +{ + 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b, + 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b +}; +static char KiFastCallEntry61[] = +{ + 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b, + 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b +}; +/* Below is the scan to find the start of KiFastCallEntry. */ +/* static char KiFastCallEntry[] = +{ + 0xb9, 0x23, 0x00, 0x00, 0x00, 0x6a, 0x30, 0x0f, + 0xa1, 0x8e, 0xd9, 0x8e, 0xc1, 0x64, 0x8b, 0x0d +}; */ + +/* PsExitSpecialApc */ +static char PsExitSpecialApc51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x64, 0xa1, 0x24, + 0x01, 0x00, 0x00, 0x8b, 0x45, 0x08, 0xf6, 0x40 +}; +static char PsExitSpecialApc60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01 +}; +static char PsExitSpecialApc61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01 +}; /* same as 6.0 */ + +/* PsTerminateProcess/PspTerminateProcess */ +static char PspTerminateProcess51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x64, 0xa1, + 0x24, 0x01, 0x00, 0x00, 0x8b, 0x75, 0x08, 0x3b +}; +static char PsTerminateProcess60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57, + 0x33, 0xd2, 0x6a, 0x08, 0x42, 0x5e, 0x8d, 0xb9 +}; +static char PsTerminateProcess61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x51, 0x51, 0x53, + 0x56, 0x64, 0x8b, 0x35, 0x24, 0x01, 0x00, 0x00, + 0x66, 0xff, 0x8e, 0x84, 0x00, 0x00, 0x00, 0x57, + 0xc7, 0x45, 0xfc +}; /* a lot of functions seem to share the first + * 16 bytes of the Windows 7 PsTerminateProcess, + * and a few even share the first 24 bytes. + */ + +/* PspTerminateThreadByPointer */ +static char PspTerminateThreadByPointer51[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xec, 0x0c, + 0x83, 0x4d, 0xf8, 0xff, 0x56, 0x57, 0x8b, 0x7d +}; +static char PspTerminateThreadByPointer60[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d, + 0xbe, 0x60, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40 +}; +static char PspTerminateThreadByPointer61[] = +{ + 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8, + 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d, + 0xbe, 0x80, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40 +}; + +/* The following offsets took me a long time to work out, so + please do not steal them. If you want to use them, please + license your project under the GNU GPL (although you are + not legally required to). + */ +NTSTATUS KvInit() +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG majorVersion, minorVersion, servicePack, buildNumber; + + /* Get Windows version information. */ + + RtlWindowsVersion.dwOSVersionInfoSize = sizeof(RtlWindowsVersion); + status = RtlGetVersion((PRTL_OSVERSIONINFOW)&RtlWindowsVersion); + + if (!NT_SUCCESS(status)) + return status; + + majorVersion = RtlWindowsVersion.dwMajorVersion; + minorVersion = RtlWindowsVersion.dwMinorVersion; + servicePack = RtlWindowsVersion.wServicePackMajor; + buildNumber = RtlWindowsVersion.dwBuildNumber; + dfprintf("Windows %d.%d, SP%d.%d, build %d\n", + majorVersion, minorVersion, servicePack, + RtlWindowsVersion.wServicePackMinor, buildNumber + ); + + __NtClose = GetSystemRoutineAddress(L"NtClose"); + + /* NtClose is used as a reference point for most addresses + dependent on where the kernel is loaded, so if we don't + have it, we can't proceed. + */ + if (!__NtClose) + return STATUS_NOT_SUPPORTED; + + /* We also need the address of ZwClose to get KiFastCallEntry. */ + __ZwClose = GetSystemRoutineAddress(L"ZwClose"); + + if (!__ZwClose) + return STATUS_NOT_SUPPORTED; + + /* Windows XP */ + if (majorVersion == 5 && minorVersion == 1) + { + ULONG_PTR searchOffset = (ULONG_PTR)__NtClose; + + WindowsVersion = WINDOWS_XP; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; + + OffEtClientId = 0x1ec; + OffEtSpareByteForSs = 0x256; /* Padding, last */ + OffEtStartAddress = 0x224; + OffEtWin32StartAddress = 0x228; + OffEpJob = 0x134; + OffEpObjectTable = 0xc4; + OffEpProtectedProcessOff = 0; + OffEpProtectedProcessBit = 0; + OffEpRundownProtect = 0x80; + OffOhBody = 0x18; + OffOtName = 0x40; + OffOtiGenericMapping = 0x60 + 0x8; + OffOtiOpenProcedure = 0x60 + 0x30; + + SsNtContinue = 0x20; + + /* INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry51, + sizeof(KiFastCallEntry51), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -6 + ); */ + /* We are scanning for PspTerminateProcess which has + the same signature as PsTerminateProcess because + PsTerminateProcess is simply a wrapper on XP. + */ + INIT_SCAN( + PsTerminateProcessScan, + PspTerminateProcess51, + sizeof(PspTerminateProcess51), + searchOffset, SCAN_LENGTH, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer51, + sizeof(PspTerminateThreadByPointer51), + searchOffset, SCAN_LENGTH, 0 + ); + + /* Windows XP SP0 and 1 are not supported */ + if (servicePack == 0) + { + return STATUS_NOT_SUPPORTED; + } + else if (servicePack == 1) + { + return STATUS_NOT_SUPPORTED; + } + else if (servicePack == 2) + { + } + else if (servicePack == 3) + { + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows XP SP%d\n", servicePack); + } + /* Windows Server 2003 */ + else if (majorVersion == 5 && minorVersion == 2) + { + WindowsVersion = WINDOWS_SERVER_2003; + + /* Not supported yet */ + return STATUS_NOT_SUPPORTED; + } + /* Windows Vista, Windows Server 2008 */ + else if (majorVersion == 6 && minorVersion == 0) + { + ULONG_PTR searchOffset = (ULONG_PTR)__NtClose; + + WindowsVersion = WINDOWS_VISTA; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff; + OffEtClientId = 0x20c; + OffEtSpareByteForSs = 0x26f; /* Padding, second-last */ + OffEtStartAddress = 0x1f8; + OffEtWin32StartAddress = 0x240; + OffEpJob = 0x10c; + OffEpObjectTable = 0xdc; + OffEpProtectedProcessOff = 0x224; + OffEpProtectedProcessBit = 0xb; + OffEpRundownProtect = 0x98; + OffOhBody = 0x18; + + INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry60, + sizeof(KiFastCallEntry60), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7 + ); + INIT_SCAN( + PsTerminateProcessScan, + PsTerminateProcess60, + sizeof(PsTerminateProcess60), + searchOffset, SCAN_LENGTH, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer60, + sizeof(PspTerminateThreadByPointer60), + searchOffset - 0x50000, SCAN_LENGTH, 0 + ); + + /* SP0 */ + if (servicePack == 0) + { + OffOtName = 0x40; + OffOtiGenericMapping = 0x60 + 0xc; + OffOtiOpenProcedure = 0x60 + 0x30; + + SsNtContinue = 0x36; + } + /* SP1 */ + else if (servicePack == 1) + { + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; /* They got rid of the Mutex (an ERESOURCE) */ + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtContinue = 0x37; + } + /* SP2 */ + else if (servicePack == 2) + { + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtAddAtom = 0x8; + SsNtAlertResumeThread = 0xd; + SsNtAlertThread = 0xe; + SsNtAllocateLocallyUniqueId = 0xf; + SsNtAllocateUserPhysicalPages = 0x10; + SsNtAllocateUuids = 0x11; + SsNtAllocateVirtualMemory = 0x12; + SsNtApphelpCacheControl = 0x28; + SsNtAreMappedFilesTheSame = 0x29; + SsNtAssignProcessToJobObject = 0x2a; + SsNtCallbackReturn = 0x2b; + SsNtCancelDeviceWakeupRequest = 0x2c; + SsNtCancelIoFile = 0x2d; + SsNtCancelTimer = 0x2e; + SsNtClearEvent = 0x2f; + SsNtClose = 0x30; + SsNtContinue = 0x37; + SsNtCreateDebugObject = 0x38; + SsNtCreateDirectoryObject = 0x39; + SsNtCreateEvent = 0x3a; + SsNtCreateEventPair = 0x3b; + SsNtCreateFile = 0x3c; + SsNtCreateIoCompletion = 0x3d; + SsNtCreateJobObject = 0x3e; + SsNtCreateJobSet = 0x3f; + SsNtCreateKey = 0x40; + SsNtCreateKeyedEvent = 0x168; + SsNtCreateMailslotFile = 0x42; + SsNtCreateMutant = 0x43; + SsNtCreateNamedPipeFile = 0x44; + SsNtCreatePagingFile = 0x46; + SsNtCreatePort = 0x47; + SsNtCreatePrivateNamespace = 0x45; + SsNtCreateProcess = 0x48; + SsNtCreateProcessEx = 0x49; + SsNtCreateProfile = 0x4a; + SsNtCreateSection = 0x4b; + SsNtCreateSemaphore = 0x4c; + SsNtCreateSymbolicLinkObject = 0x4d; + SsNtCreateThread = 0x4e; + SsNtCreateTimer = 0x4f; + SsNtCreateToken = 0x50; + SsNtCreateUserProcess = 0x17f; + SsNtCreateWaitablePort = 0x73; + SsNtDebugActiveProcess = 0x74; + SsNtDebugContinue = 0x75; + SsNtDelayExecution = 0x76; + SsNtDeleteAtom = 0x77; + SsNtDeleteBootEntry = 0x78; + SsNtDeleteDriverEntry = 0x79; + SsNtDeleteFile = 0x7a; + SsNtDeleteKey = 0x7b; + SsNtDeletePrivateNamespace = 0x7c; + SsNtDeleteObjectAuditAlarm = 0x7d; + SsNtDeleteValueKey = 0x7e; + SsNtDeviceIoControlFile = 0x7f; + SsNtDisplayString = 0x80; + SsNtDuplicateObject = 0x81; + SsNtDuplicateToken = 0x82; + SsNtEnumerateBootEntries = 0x83; + SsNtEnumerateDriverEntries = 0x84; + SsNtEnumerateKey = 0x85; + SsNtEnumerateSystemEnvironmentValuesEx = 0x86; + SsNtEnumerateValueKey = 0x88; + SsNtExtendSection = 0x89; + SsNtFilterToken = 0x8a; + SsNtFindAtom = 0x8b; + SsNtFlushBuffersFile = 0x8c; + SsNtFlushInstructionCache = 0x8d; + SsNtFlushKey = 0x8e; + SsNtFlushProcessWriteBuffers = 0x8f; + SsNtFlushVirtualMemory = 0x90; + SsNtFlushWriteBuffer = 0x91; + SsNtFreeUserPhysicalPages = 0x92; + SsNtFreeVirtualMemory = 0x93; + SsNtFsControlFile = 0x96; + SsNtGetContextThread = 0x97; + SsNtGetDevicePowerState = 0x98; + SsNtGetPlugPlayEvent = 0x9a; + SsNtGetWriteWatch = 0x9b; + SsNtImpersonateAnonymousToken = 0x9c; + SsNtImpersonateClientOfPort = 0x9d; + SsNtImpersonateThread = 0x9e; + SsNtInitiatePowerAction = 0xa1; + SsNtIsProcessInJob = 0xa2; + SsNtIsSystemResumeAutomatic = 0xa3; + SsNtListenPort = 0xa4; + SsNtLoadDriver = 0xa5; + SsNtLoadKey = 0xa6; + SsNtLoadKey2 = 0xa7; + SsNtLockFile = 0xa9; + SsNtLockVirtualMemory = 0xac; + SsNtMakePermanentObject = 0xad; + SsNtMakeTemporaryObject = 0xae; + SsNtMapUserPhysicalPages = 0xaf; + SsNtMapUserPhysicalPagesScatter = 0xb0; + SsNtMapViewOfSection = 0xb1; + SsNtModifyBootEntry = 0xb2; + SsNtModifyDriverEntry = 0xb3; + SsNtNotifyChangeDirectoryFile = 0xb4; + SsNtNotifyChangeKey = 0xb5; + SsNtNotifyChangeMultipleKeys = 0xb6; + SsNtOpenDirectoryObject = 0xb7; + SsNtOpenEvent = 0xb8; + SsNtOpenEventPair = 0xb9; + SsNtOpenFile = 0xba; + SsNtOpenIoCompletion = 0xbb; + SsNtOpenJobObject = 0xbc; + SsNtOpenKey = 0xbd; + SsNtOpenKeyedEvent = 0x169; + SsNtOpenMutant = 0xbf; + SsNtOpenObjectAuditAlarm = 0xc1; + SsNtOpenProcess = 0xc2; + SsNtOpenProcessToken = 0xc3; + SsNtOpenProcessTokenEx = 0xc4; + SsNtOpenSection = 0xc5; + SsNtOpenSemaphore = 0xc6; + SsNtOpenSymbolicLinkObject = 0xc8; + SsNtOpenThread = 0xc9; + SsNtOpenThreadToken = 0xca; + SsNtOpenThreadTokenEx = 0xcb; + SsNtOpenTimer = 0xcc; + SsNtReadFile = 0x102; + SsNtWriteFile = 0x163; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows Vista SP%d/Windows Server 2008\n", servicePack); + } + /* Windows 7 */ + else if (majorVersion == 6 && minorVersion == 1) + { + ULONG_PTR psSearchOffset = (ULONG_PTR)GetSystemRoutineAddress(L"PsSetCreateProcessNotifyRoutine"); + ULONG psScanLength = 0x200000; + + if (!psSearchOffset) + return STATUS_NOT_SUPPORTED; + + WindowsVersion = WINDOWS_7; + ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff; + ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff; + OffEtClientId = 0x22c; + OffEtSpareByteForSs = 0x2b4; /* Padding, last */ + OffEtStartAddress = 0x218; + OffEtWin32StartAddress = 0x260; + OffEpJob = 0x124; + OffEpObjectTable = 0xf4; + OffEpProtectedProcessOff = 0x26c; + OffEpProtectedProcessBit = 0xb; + OffEpRundownProtect = 0xb0; + OffOhBody = 0x18; + OffOtName = 0x8; + OffOtiGenericMapping = 0x28 + 0xc; + OffOtiOpenProcedure = 0x28 + 0x34; + + SsNtContinue = 0x3c; + + INIT_SCAN( + KiFastCallEntryScan, + KiFastCallEntry61, + sizeof(KiFastCallEntry61), + (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7 + ); + INIT_SCAN( + PsTerminateProcessScan, + PsTerminateProcess61, + sizeof(PsTerminateProcess61), + psSearchOffset, psScanLength, 0 + ); + INIT_SCAN( + PspTerminateThreadByPointerScan, + PspTerminateThreadByPointer61, + sizeof(PspTerminateThreadByPointer61), + psSearchOffset, psScanLength, 0 + ); + + /* SP0 */ + if (servicePack == 0) + { + } + else + { + return STATUS_NOT_SUPPORTED; + } + + dprintf("Initialized version-specific data for Windows 7 SP%d\n", servicePack); + } + else + { + return STATUS_NOT_SUPPORTED; + } + + return status; +} + +PVOID KvScanProc( + PKV_SCANPROC ScanProc + ) +{ + PUCHAR bytes = ScanProc->Bytes; + ULONG length = ScanProc->Length; + ULONG_PTR endAddress = ScanProc->StartAddress + ScanProc->ScanLength; + ULONG_PTR i; + + for (i = ScanProc->StartAddress; i < endAddress; i++) + { + if (memcmp((PVOID)i, bytes, length) == 0) + return (PVOID)(i + ScanProc->Displacement); + } + + return NULL; +} + +PVOID KvVerifyPrologue( + PVOID Address + ) +{ + if (memcmp(Address, StandardPrologue, 5) == 0) + return Address; + else + return NULL; +} diff --git a/branches/ph-plugins/LICENSE.txt b/branches/ph-plugins/LICENSE.txt new file mode 100644 index 000000000..89c3eac73 --- /dev/null +++ b/branches/ph-plugins/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/branches/ph-plugins/NProcessHacker/NProcessHacker.sln b/branches/ph-plugins/NProcessHacker/NProcessHacker.sln new file mode 100644 index 000000000..e43c98b54 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/NProcessHacker.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NProcessHacker", "NProcessHacker.vcproj", "{52426135-4597-4988-B5BC-BE8E93489A53}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcproj", "{A910A5C9-9BCA-47BA-AFAD-92698E512DED}" + ProjectSection(ProjectDependencies) = postProject + {52426135-4597-4988-B5BC-BE8E93489A53} = {52426135-4597-4988-B5BC-BE8E93489A53} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NProcessHackerHook", "NProcessHackerHook\NProcessHackerHook.vcproj", "{DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}" + ProjectSection(ProjectDependencies) = postProject + {52426135-4597-4988-B5BC-BE8E93489A53} = {52426135-4597-4988-B5BC-BE8E93489A53} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|Win64 = Debug|Win64 + Release|Win32 = Release|Win32 + Release|Win64 = Release|Win64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win32.ActiveCfg = Debug|x64 + {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win32.Build.0 = Debug|x64 + {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win64.ActiveCfg = Debug|x64 + {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win64.Build.0 = Debug|x64 + {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win32.ActiveCfg = Release|Win32 + {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win32.Build.0 = Release|Win32 + {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win64.ActiveCfg = Release|x64 + {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win64.Build.0 = Release|x64 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win32.ActiveCfg = Debug|Win32 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win32.Build.0 = Debug|Win32 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win64.ActiveCfg = Debug|x64 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win64.Build.0 = Debug|x64 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win32.ActiveCfg = Release|Win32 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win32.Build.0 = Release|Win32 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win64.ActiveCfg = Release|x64 + {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win64.Build.0 = Release|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win32.ActiveCfg = Debug|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win32.Build.0 = Debug|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win64.ActiveCfg = Debug|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win64.Build.0 = Debug|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win32.ActiveCfg = Release|Win32 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win32.Build.0 = Release|Win32 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win64.ActiveCfg = Release|x64 + {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj b/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj new file mode 100644 index 000000000..6834dca3a --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj new file mode 100644 index 000000000..254e1902d --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c new file mode 100644 index 000000000..c0d06ae3a --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c @@ -0,0 +1,23 @@ +#include +#include "../kphhook.h" + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved + ) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + KphHookInit(); + break; + case DLL_PROCESS_DETACH: + KphHookDeinit(); + break; + default: + break; + } + + return TRUE; +} diff --git a/branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll b/branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll new file mode 100644 index 0000000000000000000000000000000000000000..b37a9623d265d6b5cb89feafd2c79a80c14d79df GIT binary patch literal 52736 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

= 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

ZUQou<*Ly^^7Zq{6!qb5P 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 literal 0 HcmV?d00001 diff --git a/branches/ph-plugins/NProcessHacker/Test/Test.vcproj b/branches/ph-plugins/NProcessHacker/Test/Test.vcproj new file mode 100644 index 000000000..fd7dceb32 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/Test/Test.vcproj @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branches/ph-plugins/NProcessHacker/Test/test.c b/branches/ph-plugins/NProcessHacker/Test/test.c new file mode 100644 index 000000000..33abb2b12 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/Test/test.c @@ -0,0 +1,29 @@ +#ifndef UNICODE +#define UNICODE +#endif + +#include +#include +#include "../kph.h" +#include "../kphhook.h" + +int wmain(int argc, WCHAR *argv[]) +{ + ULONG pid; + HANDLE processHandle; + CHAR memory[0x1000]; + + if (argc < 2) + { + printf("Usage: test [pid to kill]\n"); + return 1; + } + + pid = _wtoi(argv[1]); + + KphHookInit(); + processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + ReadProcessMemory(processHandle, (PVOID)0x10000, memory, 0x1000, NULL); + + return 0; +} diff --git a/branches/ph-plugins/NProcessHacker/hook.c b/branches/ph-plugins/NProcessHacker/hook.c new file mode 100644 index 000000000..0ba0a55ed --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/hook.c @@ -0,0 +1,96 @@ +/* + * Process Hacker Library - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "hook.h" + +VOID PHAPI PhInitializeHook( + PPH_HOOK Hook, + PVOID Function, + PVOID Target + ) +{ + memset(Hook, 0, sizeof(PH_HOOK)); + Hook->Function = Function; + Hook->Target = Target; +} + +NTSTATUS PHAPI PhHook( + PPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG oldProtection; + PCHAR function; + + /* Change the page protection of the target page so we can write to it. */ + if (!VirtualProtect(Hook->Function, 5, PAGE_EXECUTE_READWRITE, &oldProtection)) + return STATUS_ACCESS_VIOLATION; + + __try + { + function = (PCHAR)Hook->Function; + /* Copy the original five bytes for unhooking. */ + memcpy(Hook->Bytes, function, 5); + /* Hook the function by writing a jump instruction. */ + Hook->Hooked = TRUE; + /* jmp Target */ + *function = 0xe9; + *(PULONG_PTR)(function + 1) = (ULONG_PTR)Hook->Target - (ULONG_PTR)Hook->Function - 5; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + /* Restore the old page protection. */ + VirtualProtect(Hook->Function, 5, oldProtection, NULL); + + return status; +} + +NTSTATUS PHAPI PhUnhook( + PPH_HOOK Hook + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG oldProtection; + + /* Change the page protection of the target page so we can write to it. */ + if (!VirtualProtect(Hook->Function, 5, PAGE_EXECUTE_READWRITE, &oldProtection)) + return STATUS_ACCESS_VIOLATION; + + __try + { + /* Unpatch the function by restoring the original first 5 bytes. */ + memcpy(Hook->Function, Hook->Bytes, 5); + Hook->Hooked = FALSE; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + /* Restore the old page protection. */ + VirtualProtect(Hook->Function, 5, oldProtection, NULL); + + return status; +} diff --git a/branches/ph-plugins/NProcessHacker/hook.h b/branches/ph-plugins/NProcessHacker/hook.h new file mode 100644 index 000000000..e06c74bfc --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/hook.h @@ -0,0 +1,93 @@ +/* + * Process Hacker Library - + * hooks + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _HOOK_H +#define _HOOK_H + +#include "nph.h" + +/* Almost exactly the same as the hooking code in KProcessHacker. */ + +#ifdef _X86_ + +#define PH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \ + __declspec(naked) Name(Arguments) \ + { \ + __asm lea eax, Hook \ + __asm mov eax, [eax+PH_HOOK.Function] \ + __asm add eax, 5 \ + __asm push ebp \ + __asm mov ebp, esp \ + __asm jmp eax \ + } \ + +#define PH_DEFINE_NT_HOOK_CALL(Name, Arguments, Hook) \ + __declspec(naked) Name(Arguments) \ + { \ + __asm lea eax, Hook \ + __asm mov edx, [eax+PH_HOOK.Function] \ + __asm add edx, 5 \ + /* Store the system call number in eax. */ \ + __asm mov eax, dword ptr [eax+PH_HOOK.Bytes+1] \ + __asm jmp edx \ + } \ + +#else + +#define PH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \ + Name(Arguments) \ + { \ + RaiseException(STATUS_NOT_SUPPORTED, 0, 0, NULL); \ + return 0; \ + } \ + +#define PH_DEFINE_NT_HOOK_CALL(Name, Arguments, Hook) \ + Name(Arguments) \ + { \ + RaiseException(STATUS_NOT_SUPPORTED, 0, 0, NULL); \ + return 0; \ + } \ + +#endif +typedef struct _PH_HOOK +{ + PVOID Function; + PVOID Target; + BOOLEAN Hooked; + CHAR Bytes[5]; +} PH_HOOK, *PPH_HOOK; + +NPHAPI VOID PHAPI PhInitializeHook( + PPH_HOOK Hook, + PVOID Function, + PVOID Target + ); + +NPHAPI NTSTATUS PHAPI PhHook( + PPH_HOOK Hook + ); + +NPHAPI NTSTATUS PHAPI PhUnhook( + PPH_HOOK Hook + ); + +#endif diff --git a/branches/ph-plugins/NProcessHacker/kph.c b/branches/ph-plugins/NProcessHacker/kph.c new file mode 100644 index 000000000..3f5052c26 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/kph.c @@ -0,0 +1,880 @@ +/* + * Process Hacker Library - + * KProcessHacker interface + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "kph.h" + +NTSTATUS PHAPI KphpDeviceIoControl( + HANDLE KphHandle, + ULONG KphControlCode, + PVOID InBuffer, + ULONG InBufferLength, + PVOID OutBuffer, + ULONG OutBufferLength, + PULONG ReturnLength + ); + +_NtDeviceIoControlFile NtDeviceIoControlFile = NULL; +_NtTerminateProcess NtTerminateProcess = NULL; +_NtTerminateThread NtTerminateThread = NULL; + +NTSTATUS PHAPI KphInit() +{ + if (!(NtDeviceIoControlFile = (_NtDeviceIoControlFile) + PhGetProcAddress(L"ntdll.dll", "NtDeviceIoControlFile"))) + return STATUS_PROCEDURE_NOT_FOUND; + if (!(NtTerminateProcess = (_NtTerminateProcess) + PhGetProcAddress(L"ntdll.dll", "NtTerminateProcess"))) + return STATUS_PROCEDURE_NOT_FOUND; + if (!(NtTerminateThread = (_NtTerminateThread) + PhGetProcAddress(L"ntdll.dll", "NtTerminateThread"))) + return STATUS_PROCEDURE_NOT_FOUND; + + return STATUS_SUCCESS; +} + +NTSTATUS PHAPI KphConnect( + __out PHANDLE KphHandle + ) +{ + HANDLE deviceHandle; + + deviceHandle = CreateFileW( + KPH_DEVICE_NAME, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (deviceHandle == INVALID_HANDLE_VALUE) + { + deviceHandle = NULL; + return STATUS_UNSUCCESSFUL; + } + + *KphHandle = deviceHandle; + + return STATUS_SUCCESS; +} + +NTSTATUS PHAPI KphDisconnect( + __in HANDLE KphHandle + ) +{ + if (CloseHandle(KphHandle)) + return STATUS_SUCCESS; + else + return STATUS_INVALID_HANDLE; +} + +NTSTATUS PHAPI KphpDeviceIoControl( + HANDLE KphHandle, + ULONG KphControlCode, + PVOID InBuffer, + ULONG InBufferLength, + PVOID OutBuffer, + ULONG OutBufferLength, + PULONG ReturnLength + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + + status = NtDeviceIoControlFile( + KphHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + KphControlCode, + InBuffer, + InBufferLength, + OutBuffer, + OutBufferLength + ); + + if (NT_SUCCESS(status) && ReturnLength) + *ReturnLength = ioStatusBlock.Information; + + return status; +} + +NTSTATUS PHAPI KphGetFeatures( + __in HANDLE KphHandle, + __out PULONG Features + ) +{ + NTSTATUS status; + ULONG features; + + if (NT_SUCCESS( + status = KphpDeviceIoControl( + KphHandle, + KPH_GETFEATURES, + NULL, + 0, + &features, + sizeof(ULONG), + NULL) + )) + *Features = features; + + return status; +} + +NTSTATUS PHAPI KphRead( + __in HANDLE KphHandle, + __in PVOID Address, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength + ) +{ + return KphpDeviceIoControl( + KphHandle, + KPH_READ, + &Address, + sizeof(PVOID), + Buffer, + BufferLength, + NULL + ); +} + +NTSTATUS PHAPI KphWrite( + __in HANDLE KphHandle, + __in PVOID Address, + __in_bcount(Length) PVOID Buffer, + __in ULONG Length + ) +{ + NTSTATUS status; + PVOID data = PhAlloc(Length + sizeof(PVOID)); + + *(PVOID *)data = Address; + memcpy((PCHAR)data + sizeof(PVOID), Buffer, Length); + + status = KphpDeviceIoControl( + KphHandle, + KPH_WRITE, + data, + Length + sizeof(PVOID), + NULL, + 0, + NULL + ); + PhFree(data); + + return status; +} + +NTSTATUS PHAPI KphOpenProcess( + __in HANDLE KphHandle, + __out PHANDLE ProcessHandle, + __in HANDLE ProcessId, + __in ACCESS_MASK DesiredAccess + ) +{ + NTSTATUS status; + + struct + { + HANDLE ProcessId; + ACCESS_MASK DesiredAccess; + } args; + struct + { + HANDLE ProcessHandle; + } ret; + + args.ProcessId = ProcessId; + args.DesiredAccess = DesiredAccess; + + status = KphpDeviceIoControl( + KphHandle, + KPH_OPENPROCESS, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *ProcessHandle = ret.ProcessHandle; + + return status; +} + +NTSTATUS PHAPI KphOpenThread( + __in HANDLE KphHandle, + __out PHANDLE ThreadHandle, + __in HANDLE ThreadId, + __in ACCESS_MASK DesiredAccess + ) +{ + NTSTATUS status; + + struct + { + HANDLE ThreadId; + ACCESS_MASK DesiredAccess; + } args; + struct + { + HANDLE ThreadHandle; + } ret; + + args.ThreadId = ThreadId; + args.DesiredAccess = DesiredAccess; + + status = KphpDeviceIoControl( + KphHandle, + KPH_OPENTHREAD, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *ThreadHandle = ret.ThreadHandle; + + return status; +} + +NTSTATUS PHAPI KphOpenProcessToken( + __in HANDLE KphHandle, + __out PHANDLE TokenHandle, + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess + ) +{ + NTSTATUS status; + + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } args; + struct + { + HANDLE TokenHandle; + } ret; + + args.ProcessHandle = ProcessHandle; + args.DesiredAccess = DesiredAccess; + + status = KphpDeviceIoControl( + KphHandle, + KPH_OPENPROCESSTOKEN, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *TokenHandle = ret.TokenHandle; + + return status; +} + +NTSTATUS PHAPI KphGetProcessProtected( + __in HANDLE KphHandle, + __in ULONG_PTR ProcessId, + __out PBOOLEAN IsProtected + ) +{ + NTSTATUS status; + + struct + { + HANDLE ProcessId; + } args; + struct + { + BOOLEAN IsProtected; + } ret; + + args.ProcessId = (HANDLE)ProcessId; + + status = KphpDeviceIoControl( + KphHandle, + KPH_GETPROCESSPROTECTED, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *IsProtected = ret.IsProtected; + + return status; +} + +NTSTATUS PHAPI KphSetProcessProtected( + __in HANDLE KphHandle, + __in ULONG_PTR ProcessId, + __in BOOLEAN IsProtected + ) +{ + struct + { + HANDLE ProcessId; + BOOLEAN IsProtected; + } args; + + args.ProcessId = (HANDLE)ProcessId; + args.IsProtected = IsProtected; + + return KphpDeviceIoControl( + KphHandle, + KPH_SETPROCESSPROTECTED, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphTerminateProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + struct + { + HANDLE ProcessHandle; + NTSTATUS ExitStatus; + } args; + + args.ProcessHandle = ProcessHandle; + args.ExitStatus = ExitStatus; + + status = KphpDeviceIoControl( + KphHandle, + KPH_TERMINATEPROCESS, + &args, + sizeof(args), + NULL, + 0, + NULL + ); + + /* Check if we were trying to terminate the current + * process and do it now. */ + if (status == STATUS_CANT_TERMINATE_SELF) + status = NtTerminateProcess(GetCurrentProcess(), ExitStatus); + + return status; +} + +NTSTATUS PHAPI KphSuspendProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ) +{ + struct + { + HANDLE ProcessHandle; + } args; + + args.ProcessHandle = ProcessHandle; + + return KphpDeviceIoControl( + KphHandle, + KPH_SUSPENDPROCESS, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphResumeProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ) +{ + struct + { + HANDLE ProcessHandle; + } args; + + args.ProcessHandle = ProcessHandle; + + return KphpDeviceIoControl( + KphHandle, + KPH_RESUMEPROCESS, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphReadVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } args; + + args.ProcessHandle = ProcessHandle; + args.BaseAddress = BaseAddress; + args.Buffer = Buffer; + args.BufferLength = BufferLength; + args.ReturnLength = ReturnLength; + + return KphpDeviceIoControl( + KphHandle, + KPH_READVIRTUALMEMORY, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphWriteVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } args; + + args.ProcessHandle = ProcessHandle; + args.BaseAddress = BaseAddress; + args.Buffer = Buffer; + args.BufferLength = BufferLength; + args.ReturnLength = ReturnLength; + + return KphpDeviceIoControl( + KphHandle, + KPH_WRITEVIRTUALMEMORY, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphOpenProcessJob( + __in HANDLE KphHandle, + __out PHANDLE JobHandle, + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess + ) +{ + NTSTATUS status; + + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + } args; + struct + { + HANDLE JobHandle; + } ret; + + args.ProcessHandle = ProcessHandle; + args.DesiredAccess = DesiredAccess; + + status = KphpDeviceIoControl( + KphHandle, + KPH_OPENPROCESSJOB, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *JobHandle = ret.JobHandle; + + return status; +} + +NTSTATUS PHAPI KphGetContextThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext + ) +{ + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } args; + + args.ThreadHandle = ThreadHandle; + args.ThreadContext = ThreadContext; + + return KphpDeviceIoControl( + KphHandle, + KPH_GETCONTEXTTHREAD, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphSetContextThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext + ) +{ + struct + { + HANDLE ThreadHandle; + PCONTEXT ThreadContext; + } args; + + args.ThreadHandle = ThreadHandle; + args.ThreadContext = ThreadContext; + + return KphpDeviceIoControl( + KphHandle, + KPH_SETCONTEXTTHREAD, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphTerminateThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ) +{ + NTSTATUS status = STATUS_SUCCESS; + struct + { + HANDLE ThreadHandle; + NTSTATUS ExitStatus; + } args; + + args.ThreadHandle = ThreadHandle; + args.ExitStatus = ExitStatus; + + status = KphpDeviceIoControl( + KphHandle, + KPH_TERMINATETHREAD, + &args, + sizeof(args), + NULL, + 0, + NULL + ); + + if (status == STATUS_CANT_TERMINATE_SELF) + status = NtTerminateThread(GetCurrentThread(), ExitStatus); + + return status; +} + +NTSTATUS PHAPI KphSetHandleGrantedAccess( + __in HANDLE KphHandle, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ) +{ + struct + { + HANDLE Handle; + ACCESS_MASK GrantedAccess; + } args; + + args.Handle = Handle; + args.GrantedAccess = GrantedAccess; + + return KphpDeviceIoControl( + KphHandle, + KPH_SETHANDLEGRANTEDACCESS, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphProtectAdd( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in BOOLEAN AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ) +{ + struct + { + HANDLE ProcessHandle; + LOGICAL AllowKernelMode; + ACCESS_MASK ProcessAllowMask; + ACCESS_MASK ThreadAllowMask; + } args; + + args.ProcessHandle = ProcessHandle; + args.AllowKernelMode = AllowKernelMode; + args.ProcessAllowMask = ProcessAllowMask; + args.ThreadAllowMask = ThreadAllowMask; + + return KphpDeviceIoControl( + KphHandle, + KPH_PROTECTADD, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphProtectRemove( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ) +{ + struct + { + HANDLE ProcessHandle; + } args; + + args.ProcessHandle = ProcessHandle; + + return KphpDeviceIoControl( + KphHandle, + KPH_PROTECTREMOVE, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphProtectQuery( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __out PBOOLEAN AllowKernelMode, + __out PACCESS_MASK ProcessAllowMask, + __out PACCESS_MASK ThreadAllowMask + ) +{ + NTSTATUS status = STATUS_SUCCESS; + struct + { + HANDLE ProcessHandle; + PLOGICAL AllowKernelMode; + PACCESS_MASK ProcessAllowMask; + PACCESS_MASK ThreadAllowMask; + } args; + LOGICAL allowKernelMode; + + args.ProcessHandle = ProcessHandle; + args.AllowKernelMode = &allowKernelMode; + args.ProcessAllowMask = ProcessAllowMask; + args.ThreadAllowMask = ThreadAllowMask; + + status = KphpDeviceIoControl( + KphHandle, + KPH_PROTECTQUERY, + &args, + sizeof(args), + NULL, + 0, + NULL + ); + + *AllowKernelMode = (BOOLEAN)allowKernelMode; + + return status; +} + +NTSTATUS PHAPI KphUnsafeReadVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } args; + + args.ProcessHandle = ProcessHandle; + args.BaseAddress = BaseAddress; + args.Buffer = Buffer; + args.BufferLength = BufferLength; + args.ReturnLength = ReturnLength; + + return KphpDeviceIoControl( + KphHandle, + KPH_UNSAFEREADVIRTUALMEMORY, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphSetExecuteOptions( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in ULONG ExecuteOptions + ) +{ + struct + { + HANDLE ProcessHandle; + ULONG ExecuteOptions; + } args; + + args.ProcessHandle = ProcessHandle; + args.ExecuteOptions = ExecuteOptions; + + return KphpDeviceIoControl( + KphHandle, + KPH_SETEXECUTEOPTIONS, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphQueryProcessHandles( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PVOID Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } args; + + args.ProcessHandle = ProcessHandle; + args.Buffer = Buffer; + args.BufferLength = BufferLength; + args.ReturnLength = ReturnLength; + + return KphpDeviceIoControl( + KphHandle, + KPH_QUERYPROCESSHANDLES, + &args, + sizeof(args), + NULL, + 0, + NULL + ); +} + +NTSTATUS PHAPI KphOpenThreadProcess( + __in HANDLE KphHandle, + __out PHANDLE ProcessHandle, + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess + ) +{ + NTSTATUS status; + + struct + { + HANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + } args; + struct + { + HANDLE ProcessHandle; + } ret; + + args.ThreadHandle = ThreadHandle; + args.DesiredAccess = DesiredAccess; + + status = KphpDeviceIoControl( + KphHandle, + KPH_OPENTHREADPROCESS, + &args, + sizeof(args), + &ret, + sizeof(ret), + NULL + ); + + *ProcessHandle = ret.ProcessHandle; + + return status; +} diff --git a/branches/ph-plugins/NProcessHacker/kph.h b/branches/ph-plugins/NProcessHacker/kph.h new file mode 100644 index 000000000..f66820022 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/kph.h @@ -0,0 +1,289 @@ +/* + * Process Hacker Library - + * KProcessHacker interface + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KPH_H +#define _KPH_H + +#include "nph.h" +#include "nativedefs.h" + +#define KPH_DEVICE_TYPE (0x9999) +#define KPH_DEVICE_NAME (L"\\\\.\\KProcessHacker") + +#define KPHF_PSTERMINATEPROCESS 0x1 +#define KPHF_PSPTERMINATETHREADBPYPOINTER 0x2 + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0 +#define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) +#define FILE_READ_ACCESS (0x0001) +#define FILE_WRITE_ACCESS (0x0002) +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access) ( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif + +#define KPH_READ KPH_CTL_CODE(0) +#define KPH_WRITE KPH_CTL_CODE(1) +#define KPH_GETFILEOBJECTNAME KPH_CTL_CODE(2) +#define KPH_OPENPROCESS KPH_CTL_CODE(3) +#define KPH_OPENTHREAD KPH_CTL_CODE(4) +#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(5) +#define KPH_GETPROCESSPROTECTED KPH_CTL_CODE(6) +#define KPH_SETPROCESSPROTECTED KPH_CTL_CODE(7) +#define KPH_TERMINATEPROCESS KPH_CTL_CODE(8) +#define KPH_SUSPENDPROCESS KPH_CTL_CODE(9) +#define KPH_RESUMEPROCESS KPH_CTL_CODE(10) +#define KPH_READVIRTUALMEMORY KPH_CTL_CODE(11) +#define KPH_WRITEVIRTUALMEMORY KPH_CTL_CODE(12) +#define KPH_SETPROCESSTOKEN KPH_CTL_CODE(13) +#define KPH_GETTHREADSTARTADDRESS KPH_CTL_CODE(14) +#define KPH_SETHANDLEATTRIBUTES KPH_CTL_CODE(15) +#define KPH_GETHANDLEOBJECTNAME KPH_CTL_CODE(16) +#define KPH_OPENPROCESSJOB KPH_CTL_CODE(17) +#define KPH_GETCONTEXTTHREAD KPH_CTL_CODE(18) +#define KPH_SETCONTEXTTHREAD KPH_CTL_CODE(19) +#define KPH_GETTHREADWIN32THREAD KPH_CTL_CODE(20) +#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(21) +#define KPH_ZWQUERYOBJECT KPH_CTL_CODE(22) +#define KPH_GETPROCESSID KPH_CTL_CODE(23) +#define KPH_GETTHREADID KPH_CTL_CODE(24) +#define KPH_TERMINATETHREAD KPH_CTL_CODE(25) +#define KPH_GETFEATURES KPH_CTL_CODE(26) +#define KPH_SETHANDLEGRANTEDACCESS KPH_CTL_CODE(27) +#define KPH_ASSIGNIMPERSONATIONTOKEN KPH_CTL_CODE(28) +#define KPH_PROTECTADD KPH_CTL_CODE(29) +#define KPH_PROTECTREMOVE KPH_CTL_CODE(30) +#define KPH_PROTECTQUERY KPH_CTL_CODE(31) +#define KPH_UNSAFEREADVIRTUALMEMORY KPH_CTL_CODE(32) +#define KPH_SETEXECUTEOPTIONS KPH_CTL_CODE(33) +#define KPH_QUERYPROCESSHANDLES KPH_CTL_CODE(34) +#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(35) + +#ifndef MEM_EXECUTE_OPTION_DISABLE +#define MEM_EXECUTE_OPTION_DISABLE 0x1 +#define MEM_EXECUTE_OPTION_ENABLE 0x2 +#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4 +#define MEM_EXECUTE_OPTION_PERMANENT 0x8 +#endif + +typedef struct _PROCESS_HANDLE +{ + HANDLE Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; + ULONG HandleAttributes; +} PROCESS_HANDLE, *PPROCESS_HANDLE; + +typedef struct _PROCESS_HANDLE_INFORMATION +{ + ULONG HandleCount; + PROCESS_HANDLE Handles[1]; +} PROCESS_HANDLE_INFORMATION, *PPROCESS_HANDLE_INFORMATION; + +NTSTATUS PHAPI KphInit(); + +NPHAPI NTSTATUS PHAPI KphConnect( + __out PHANDLE KphHandle + ); + +NPHAPI NTSTATUS PHAPI KphDisconnect( + __in HANDLE KphHandle + ); + +NPHAPI NTSTATUS PHAPI KphGetFeatures( + __in HANDLE KphHandle, + __out PULONG Features + ); + +NPHAPI NTSTATUS PHAPI KphRead( + __in HANDLE KphHandle, + __in PVOID Address, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength + ); + +NPHAPI NTSTATUS PHAPI KphWrite( + __in HANDLE KphHandle, + __in PVOID Address, + __in_bcount(Length) PVOID Buffer, + __in ULONG Length + ); + +NPHAPI NTSTATUS PHAPI KphOpenProcess( + __in HANDLE KphHandle, + __out PHANDLE ProcessHandle, + __in HANDLE ProcessId, + __in ACCESS_MASK DesiredAccess + ); + +NPHAPI NTSTATUS PHAPI KphOpenThread( + __in HANDLE KphHandle, + __out PHANDLE ThreadHandle, + __in HANDLE ThreadId, + __in ACCESS_MASK DesiredAccess + ); + +NPHAPI NTSTATUS PHAPI KphOpenProcessToken( + __in HANDLE KphHandle, + __out PHANDLE TokenHandle, + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess + ); + +NPHAPI NTSTATUS PHAPI KphGetProcessProtected( + __in HANDLE KphHandle, + __in ULONG_PTR ProcessId, + __out PBOOLEAN IsProtected + ); + +NPHAPI NTSTATUS PHAPI KphSetProcessProtected( + __in HANDLE KphHandle, + __in ULONG_PTR ProcessId, + __in BOOLEAN IsProtected + ); + +NPHAPI NTSTATUS PHAPI KphTerminateProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in NTSTATUS ExitStatus + ); + +NPHAPI NTSTATUS PHAPI KphSuspendProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ); + +NPHAPI NTSTATUS PHAPI KphResumeProcess( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ); + +NPHAPI NTSTATUS PHAPI KphReadVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __out_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ); + +NPHAPI NTSTATUS PHAPI KphWriteVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ); + +NPHAPI NTSTATUS PHAPI KphOpenProcessJob( + __in HANDLE KphHandle, + __out PHANDLE JobHandle, + __in HANDLE ProcessHandle, + __in ACCESS_MASK DesiredAccess + ); + +NPHAPI NTSTATUS PHAPI KphGetContextThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __inout PCONTEXT ThreadContext + ); + +NPHAPI NTSTATUS PHAPI KphSetContextThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __in PCONTEXT ThreadContext + ); + +NPHAPI NTSTATUS PHAPI KphTerminateThread( + __in HANDLE KphHandle, + __in HANDLE ThreadHandle, + __in NTSTATUS ExitStatus + ); + +NPHAPI NTSTATUS PHAPI KphSetHandleGrantedAccess( + __in HANDLE KphHandle, + __in HANDLE Handle, + __in ACCESS_MASK GrantedAccess + ); + +NPHAPI NTSTATUS PHAPI KphProtectAdd( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in BOOLEAN AllowKernelMode, + __in ACCESS_MASK ProcessAllowMask, + __in ACCESS_MASK ThreadAllowMask + ); + +NPHAPI NTSTATUS PHAPI KphProtectRemove( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle + ); + +NPHAPI NTSTATUS PHAPI KphProtectQuery( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __out PBOOLEAN AllowKernelMode, + __out PACCESS_MASK ProcessAllowMask, + __out PACCESS_MASK ThreadAllowMask + ); + +NPHAPI NTSTATUS PHAPI KphUnsafeReadVirtualMemory( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in PVOID BaseAddress, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ); + +NPHAPI NTSTATUS PHAPI KphSetExecuteOptions( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __in ULONG ExecuteOptions + ); + +NPHAPI NTSTATUS PHAPI KphQueryProcessHandles( + __in HANDLE KphHandle, + __in HANDLE ProcessHandle, + __out_bcount_opt(BufferLength) PVOID Buffer, + __in_opt ULONG BufferLength, + __out_opt PULONG ReturnLength + ); + +NPHAPI NTSTATUS PHAPI KphOpenThreadProcess( + __in HANDLE KphHandle, + __out PHANDLE ProcessHandle, + __in HANDLE ThreadHandle, + __in ACCESS_MASK DesiredAccess + ); + +#endif diff --git a/branches/ph-plugins/NProcessHacker/kphhook.c b/branches/ph-plugins/NProcessHacker/kphhook.c new file mode 100644 index 000000000..243bb5f3a --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/kphhook.c @@ -0,0 +1,180 @@ +/* + * Process Hacker Library - + * KProcessHacker transparency hooking + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "kphhook.h" + +#define STD_PREFIX NTSTATUS NTAPI +#define DECLARE_NT_HOOK(Name, Arguments) \ + static _##Name Name; \ + static PH_HOOK Name##Hook; \ + PH_DEFINE_NT_HOOK_CALL(NTSTATUS NTAPI Old##Name, Arguments, Name##Hook); \ + STD_PREFIX New##Name(Arguments) +#define DECLARE_NEW_FUNC(Name, Arguments) \ + STD_PREFIX New##Name(Arguments) +#define INITIALIZE_NT_HOOK(Name) \ + Name = PhGetProcAddress(L"ntdll.dll", #Name); \ + PhInitializeHook(&Name##Hook, Name, New##Name); \ + PhHook(&Name##Hook) +#define DEINITIALIZE_NT_HOOK(Name) \ + PhUnhook(&Name##Hook) + +BOOLEAN KphHookInitialized = FALSE; +HANDLE KphHandle = NULL; +DECLARE_NT_HOOK(NtGetContextThread, NTGETCONTEXTTHREAD_ARGS); +DECLARE_NT_HOOK(NtOpenProcess, NTOPENPROCESS_ARGS); +DECLARE_NT_HOOK(NtOpenProcessToken, NTOPENPROCESSTOKEN_ARGS); +DECLARE_NT_HOOK(NtOpenProcessTokenEx, NTOPENPROCESSTOKENEX_ARGS); +DECLARE_NT_HOOK(NtOpenThread, NTOPENTHREAD_ARGS); +DECLARE_NT_HOOK(NtReadVirtualMemory, NTREADVIRTUALMEMORY_ARGS); +DECLARE_NT_HOOK(NtSetContextThread, NTSETCONTEXTTHREAD_ARGS); +DECLARE_NT_HOOK(NtTerminateProcess, NTTERMINATEPROCESS_ARGS); +DECLARE_NT_HOOK(NtTerminateThread, NTTERMINATETHREAD_ARGS); +DECLARE_NT_HOOK(NtWriteVirtualMemory, NTWRITEVIRTUALMEMORY_ARGS); + +VOID PHAPI KphHookInit() +{ + if (KphHookInitialized) + return; + + if (!NT_SUCCESS(KphConnect(&KphHandle))) + return; + + INITIALIZE_NT_HOOK(NtGetContextThread); + INITIALIZE_NT_HOOK(NtOpenProcess); + INITIALIZE_NT_HOOK(NtOpenProcessToken); + INITIALIZE_NT_HOOK(NtOpenProcessTokenEx); + INITIALIZE_NT_HOOK(NtOpenThread); + INITIALIZE_NT_HOOK(NtReadVirtualMemory); + INITIALIZE_NT_HOOK(NtSetContextThread); + INITIALIZE_NT_HOOK(NtTerminateProcess); + INITIALIZE_NT_HOOK(NtTerminateThread); + INITIALIZE_NT_HOOK(NtWriteVirtualMemory); + + KphHookInitialized = TRUE; +} + +VOID PHAPI KphHookDeinit() +{ + if (!KphHookInitialized) + return; + + DEINITIALIZE_NT_HOOK(NtGetContextThread); + DEINITIALIZE_NT_HOOK(NtOpenProcess); + DEINITIALIZE_NT_HOOK(NtOpenProcessToken); + DEINITIALIZE_NT_HOOK(NtOpenProcessTokenEx); + DEINITIALIZE_NT_HOOK(NtOpenThread); + DEINITIALIZE_NT_HOOK(NtReadVirtualMemory); + DEINITIALIZE_NT_HOOK(NtSetContextThread); + DEINITIALIZE_NT_HOOK(NtTerminateProcess); + DEINITIALIZE_NT_HOOK(NtTerminateThread); + DEINITIALIZE_NT_HOOK(NtWriteVirtualMemory); + + KphDisconnect(KphHandle); + + KphHookInitialized = FALSE; +} + +DECLARE_NEW_FUNC(NtGetContextThread, NTGETCONTEXTTHREAD_ARGS) +{ + return KphGetContextThread(KphHandle, ThreadHandle, Context); +} + +DECLARE_NEW_FUNC(NtOpenProcess, NTOPENPROCESS_ARGS) +{ + /* Use KPH if we only have a PID, no name or TID. */ + if (!ObjectAttributes->ObjectName && ClientId->UniqueThread == 0) + return KphOpenProcess(KphHandle, ProcessHandle, ClientId->UniqueProcess, DesiredAccess); + + return OldNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); +} + +DECLARE_NEW_FUNC(NtOpenProcessToken, NTOPENPROCESSTOKEN_ARGS) +{ + return NtOpenProcessTokenEx(ProcessHandle, DesiredAccess, 0, TokenHandle); +} + +DECLARE_NEW_FUNC(NtOpenProcessTokenEx, NTOPENPROCESSTOKENEX_ARGS) +{ + /* HandleAttributes is ignored. */ + return KphOpenProcessToken(KphHandle, TokenHandle, ProcessHandle, DesiredAccess); +} + +DECLARE_NEW_FUNC(NtOpenThread, NTOPENTHREAD_ARGS) +{ + /* Use KPH if we only have a CID, no name. */ + if (!ObjectAttributes->ObjectName) + return KphOpenThread(KphHandle, ThreadHandle, ClientId->UniqueThread, DesiredAccess); + + return OldNtOpenThread(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId); +} + +DECLARE_NEW_FUNC(NtReadVirtualMemory, NTREADVIRTUALMEMORY_ARGS) +{ + return KphReadVirtualMemory(KphHandle, ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength); +} + +DECLARE_NEW_FUNC(NtSetContextThread, NTSETCONTEXTTHREAD_ARGS) +{ + return KphSetContextThread(KphHandle, ThreadHandle, Context); +} + +DECLARE_NEW_FUNC(NtTerminateProcess, NTTERMINATEPROCESS_ARGS) +{ + NTSTATUS status; + + /* Call the original NtTerminateProcess if we are terminating self to + * avoid infinite recursion with KphTerminateProcess. + */ + if (ProcessHandle == NULL || ProcessHandle == GetCurrentProcess()) + return OldNtTerminateProcess(ProcessHandle, ExitStatus); + + status = KphTerminateProcess(KphHandle, ProcessHandle, ExitStatus); + + /* Fall back to using the original NtTerminateProcess if KPH couldn't + * do it. */ + if (status == STATUS_NOT_SUPPORTED) + status = OldNtTerminateProcess(ProcessHandle, ExitStatus); + + return status; +} + +DECLARE_NEW_FUNC(NtTerminateThread, NTTERMINATETHREAD_ARGS) +{ + NTSTATUS status; + + if (ThreadHandle == NULL || ThreadHandle == GetCurrentThread()) + return OldNtTerminateThread(ThreadHandle, ExitStatus); + + status = KphTerminateThread(KphHandle, ThreadHandle, ExitStatus); + + /* Fall back to using the original NtTerminateThread if KPH couldn't + * do it. */ + if (status == STATUS_NOT_SUPPORTED) + status = OldNtTerminateThread(ThreadHandle, ExitStatus); + + return status; +} + +DECLARE_NEW_FUNC(NtWriteVirtualMemory, NTWRITEVIRTUALMEMORY_ARGS) +{ + return KphWriteVirtualMemory(KphHandle, ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength); +} diff --git a/branches/ph-plugins/NProcessHacker/kphhook.h b/branches/ph-plugins/NProcessHacker/kphhook.h new file mode 100644 index 000000000..ac602c4f3 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/kphhook.h @@ -0,0 +1,32 @@ +/* + * Process Hacker Library - + * KProcessHacker transparency hooking + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _KPHHOOK_H +#define _KPHHOOK_H + +#include "hook.h" +#include "kph.h" + +NPHAPI VOID PHAPI KphHookInit(); +NPHAPI VOID PHAPI KphHookDeinit(); + +#endif diff --git a/branches/ph-plugins/NProcessHacker/nativedefs.h b/branches/ph-plugins/NProcessHacker/nativedefs.h new file mode 100644 index 000000000..af754046d --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/nativedefs.h @@ -0,0 +1,175 @@ +#ifndef _NATIVEDEFS_H +#define _NATIVEDEFS_H + +#include "nph.h" + +typedef enum _OBJECT_INFORMATION_CLASS +{ + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectAllInformation, + ObjectDataInformation +} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; + +typedef struct _UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _CLIENT_ID +{ + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _IO_STATUS_BLOCK +{ + union + { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _OBJECT_ATTRIBUTES +{ + ULONG Length; + PVOID RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef struct _UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _OBJECT_NAME_INFORMATION +{ + UNICODE_STRING Name; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +#define NTDEVICEIOCONTROLFILE_ARGS \ + HANDLE FileHandle, \ + HANDLE Event, \ + PVOID ApcRoutine, \ + PVOID ApcContext, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + ULONG IoControlCode, \ + PVOID InputBuffer, \ + ULONG InputBufferLength, \ + PVOID OutputBuffer, \ + ULONG OutputBufferLength + +typedef NTSTATUS (NTAPI *_NtDeviceIoControlFile)( + NTDEVICEIOCONTROLFILE_ARGS + ); + +#define NTGETCONTEXTTHREAD_ARGS \ + HANDLE ThreadHandle, \ + PCONTEXT Context + +typedef NTSTATUS (NTAPI *_NtGetContextThread)( + NTGETCONTEXTTHREAD_ARGS + ); + +#define NTOPENPROCESS_ARGS \ + PHANDLE ProcessHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + PCLIENT_ID ClientId + +typedef NTSTATUS (NTAPI *_NtOpenProcess)( + NTOPENPROCESS_ARGS + ); + +#define NTOPENPROCESSTOKEN_ARGS \ + HANDLE ProcessHandle, \ + ACCESS_MASK DesiredAccess, \ + PHANDLE TokenHandle + +typedef NTSTATUS (NTAPI *_NtOpenProcessToken)( + NTOPENPROCESSTOKEN_ARGS + ); + +#define NTOPENPROCESSTOKENEX_ARGS \ + HANDLE ProcessHandle, \ + ACCESS_MASK DesiredAccess, \ + ULONG HandleAttributes, \ + PHANDLE TokenHandle + +typedef NTSTATUS (NTAPI *_NtOpenProcessTokenEx)( + NTOPENPROCESSTOKENEX_ARGS + ); + +#define NTOPENTHREAD_ARGS \ + PHANDLE ThreadHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + PCLIENT_ID ClientId + +typedef NTSTATUS (NTAPI *_NtOpenThread)( + NTOPENTHREAD_ARGS + ); + +#define NTQUERYOBJECT_ARGS \ + HANDLE Handle, \ + OBJECT_INFORMATION_CLASS ObjectInformationClass, \ + PVOID ObjectInformation, \ + ULONG Length, \ + PULONG ReturnLength + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + NTQUERYOBJECT_ARGS + ); + +#define NTREADVIRTUALMEMORY_ARGS \ + HANDLE ProcessHandle, \ + PVOID BaseAddress, \ + PVOID Buffer, \ + ULONG BufferLength, \ + PULONG ReturnLength + +typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)( + NTREADVIRTUALMEMORY_ARGS + ); + +#define NTSETCONTEXTTHREAD_ARGS \ + HANDLE ThreadHandle, \ + PCONTEXT Context + +typedef NTSTATUS (NTAPI *_NtSetContextThread)( + NTSETCONTEXTTHREAD_ARGS + ); + +#define NTTERMINATEPROCESS_ARGS \ + HANDLE ProcessHandle, \ + NTSTATUS ExitStatus + +typedef NTSTATUS (NTAPI *_NtTerminateProcess)( + NTTERMINATEPROCESS_ARGS + ); + +#define NTTERMINATETHREAD_ARGS \ + HANDLE ThreadHandle, \ + NTSTATUS ExitStatus + +typedef NTSTATUS (NTAPI *_NtTerminateThread)( + NTTERMINATETHREAD_ARGS + ); + +#define NTWRITEVIRTUALMEMORY_ARGS \ + HANDLE ProcessHandle, \ + PVOID BaseAddress, \ + PVOID Buffer, \ + ULONG BufferLength, \ + PULONG ReturnLength + +typedef NTSTATUS (NTAPI *_NtWriteVirtualMemory)( + NTWRITEVIRTUALMEMORY_ARGS + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/NProcessHacker/nph.c b/branches/ph-plugins/NProcessHacker/nph.c new file mode 100644 index 000000000..42c4eeab7 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/nph.c @@ -0,0 +1,85 @@ +/* + * Process Hacker Library - + * common code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nph.h" +#include "kph.h" +#include "obj.h" +#include "verify.h" + +PVOID PHAPI PhAlloc(SIZE_T Size) +{ + PVOID memory; + + if (!(memory = malloc(Size))) + RaiseException(EXCEPTION_NO_MEMORY, 0, 0, NULL); + + return memory; +} + +PVOID PHAPI PhRealloc(PVOID Memory, SIZE_T Size) +{ + PVOID memory; + + if (!(memory = realloc(Memory, Size))) + RaiseException(EXCEPTION_NO_MEMORY, 0, 0, NULL); + + return memory; +} + +VOID PHAPI PhFree(PVOID Memory) +{ + free(Memory); +} + +PVOID PHAPI PhGetProcAddress(PWSTR LibraryName, PSTR ProcName) +{ + return GetProcAddress(GetModuleHandle(LibraryName), ProcName); +} + +VOID PHAPI PhVoid() +{ + return; +} + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved + ) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + if (!NT_SUCCESS(PhVerifyInit())) + return FALSE; + if (!NT_SUCCESS(PhObjInit())) + return FALSE; + if (!NT_SUCCESS(KphInit())) + return FALSE; + + break; + default: + break; + } + + return TRUE; +} diff --git a/branches/ph-plugins/NProcessHacker/nph.h b/branches/ph-plugins/NProcessHacker/nph.h new file mode 100644 index 000000000..887b94f9f --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/nph.h @@ -0,0 +1,70 @@ +/* + * Process Hacker Library - + * main header file + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _NPH_H +#define _NPH_H + +/* If the user has already included windows.h, don't include ntstatus.h + * to avoid duplicate macro definitions. */ +#ifndef _WINDOWS_ +#include +#endif + +#define WIN32_LEAN_AND_MEAN +#define WIN32_NO_STATUS /* Need ntstatus.h instead */ +#include +#include + +#ifndef LOGICAL +#define LOGICAL ULONG +#define PLOGICAL PULONG +#endif + +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS (0) +#endif + +#ifndef NTSTATUS +#define NTSTATUS LONG +#endif + +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) ((x) >= STATUS_SUCCESS) +#endif + +#ifdef NPH_EXPORTS +#define NPHAPI __declspec(dllexport) +#else +#define NPHAPI __declspec(dllimport) +#endif + +#define PHAPI __stdcall + +#define EXCEPTION_NO_MEMORY STATUS_NO_MEMORY + +NPHAPI PVOID PHAPI PhAlloc(SIZE_T Size); +NPHAPI PVOID PHAPI PhRealloc(PVOID Memory, SIZE_T Size); +NPHAPI VOID PHAPI PhFree(PVOID Memory); +PVOID PHAPI PhGetProcAddress(PWSTR LibraryName, PSTR ProcName); +NPHAPI VOID PHAPI PhVoid(); + +#endif diff --git a/branches/ph-plugins/NProcessHacker/obj.c b/branches/ph-plugins/NProcessHacker/obj.c new file mode 100644 index 000000000..2dfa285d6 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/obj.c @@ -0,0 +1,157 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "obj.h" + +ULONG PHAPI PhpQueryFileObjectThreadStart( + PVOID Parameter + ); + +_NtQueryObject NtQueryObject = NULL; + +HANDLE QueryFileObjectThreadHandle = NULL; +PVOID QueryFileObjectFiber = NULL; +CRITICAL_SECTION QueryFileObjectCs; +HANDLE QueryFileObjectStartEvent = NULL; +HANDLE QueryFileObjectCompletedEvent = NULL; +HANDLE QueryFileObjectFileHandle; +PH_QUERY_FILE_OBJECT_BUFFER QueryFileObjectBuffer; + +NTSTATUS PHAPI PhObjInit() +{ + if (!(NtQueryObject = (_NtQueryObject) + PhGetProcAddress(L"ntdll.dll", "NtQueryObject"))) + return STATUS_PROCEDURE_NOT_FOUND; + + InitializeCriticalSection(&QueryFileObjectCs); + + return STATUS_SUCCESS; +} + +NTSTATUS PHAPI PhQueryNameFileObject( + HANDLE FileHandle, + POBJECT_NAME_INFORMATION FileObjectNameInformation, + ULONG FileObjectNameInformationLength, + PULONG ReturnLength + ) +{ + ULONG waitResult; + + EnterCriticalSection(&QueryFileObjectCs); + + /* Create a query thread if we don't have one. */ + if (!QueryFileObjectThreadHandle) + { + QueryFileObjectThreadHandle = CreateThread( + NULL, 0, (LPTHREAD_START_ROUTINE)PhpQueryFileObjectThreadStart, NULL, 0, NULL); + + if (!QueryFileObjectThreadHandle) + { + LeaveCriticalSection(&QueryFileObjectCs); + return STATUS_UNSUCCESSFUL; + } + } + + /* Create the events if they don't exist. */ + if (!QueryFileObjectStartEvent) + if (!(QueryFileObjectStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL))) + return STATUS_UNSUCCESSFUL; + if (!QueryFileObjectCompletedEvent) + if (!(QueryFileObjectCompletedEvent = CreateEvent(NULL, FALSE, FALSE, NULL))) + return STATUS_UNSUCCESSFUL; + + /* Initialize the work context. */ + QueryFileObjectFileHandle = FileHandle; + QueryFileObjectBuffer.Length = FileObjectNameInformationLength; + QueryFileObjectBuffer.Name = FileObjectNameInformation; + QueryFileObjectBuffer.Initialized = TRUE; + /* Allow the worker thread to start. */ + SetEvent(QueryFileObjectStartEvent); + /* Wait for the work to complete, with a timeout of 1 second. */ + waitResult = WaitForSingleObject(QueryFileObjectCompletedEvent, 1000); + /* Set the buffer as uninitialized. */ + QueryFileObjectBuffer.Initialized = FALSE; + + /* Return normally if the work was completed. */ + if (waitResult == WAIT_OBJECT_0) + { + NTSTATUS status; + ULONG returnLength; + + /* Copy the status information before we leave the critical section. */ + status = QueryFileObjectBuffer.Status; + returnLength = QueryFileObjectBuffer.ReturnLength; + LeaveCriticalSection(&QueryFileObjectCs); + + if (ReturnLength) + *ReturnLength = returnLength; + + return status; + } + /* Kill the worker thread if it took too long. */ + /* else if (waitResult == WAIT_TIMEOUT) */ + else + { + /* Kill the thread. */ + if (TerminateThread(QueryFileObjectThreadHandle, 1)) + { + QueryFileObjectThreadHandle = NULL; + + /* Delete the fiber (and free the thread stack). */ + DeleteFiber(QueryFileObjectFiber); + QueryFileObjectFiber = NULL; + } + + LeaveCriticalSection(&QueryFileObjectCs); + return STATUS_UNSUCCESSFUL; + } +} + +ULONG PHAPI PhpQueryFileObjectThreadStart( + PVOID Parameter + ) +{ + QueryFileObjectFiber = ConvertThreadToFiber(Parameter); + + while (TRUE) + { + /* Wait for work. */ + if (WaitForSingleObject(QueryFileObjectStartEvent, INFINITE) != WAIT_OBJECT_0) + continue; + + /* Make sure we actually have work. */ + if (QueryFileObjectBuffer.Initialized) + { + QueryFileObjectBuffer.Status = NtQueryObject( + QueryFileObjectFileHandle, + ObjectNameInformation, + QueryFileObjectBuffer.Name, + QueryFileObjectBuffer.Length, + &QueryFileObjectBuffer.ReturnLength + ); + + /* Work done. */ + SetEvent(QueryFileObjectCompletedEvent); + } + } + + return 0; +} diff --git a/branches/ph-plugins/NProcessHacker/obj.h b/branches/ph-plugins/NProcessHacker/obj.h new file mode 100644 index 000000000..f98f35520 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/obj.h @@ -0,0 +1,46 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _OBJ_H +#define _OBJ_H + +#include "nph.h" +#include "nativedefs.h" + +typedef struct _PH_QUERY_FILE_OBJECT_BUFFER +{ + LOGICAL Initialized; + NTSTATUS Status; + ULONG Length; + ULONG ReturnLength; + POBJECT_NAME_INFORMATION Name; +} PH_QUERY_FILE_OBJECT_BUFFER, *PPH_QUERY_FILE_OBJECT_BUFFER; + +NTSTATUS PHAPI PhObjInit(); + +NPHAPI NTSTATUS PHAPI PhQueryNameFileObject( + HANDLE FileHandle, + POBJECT_NAME_INFORMATION FileObjectNameInformation, + ULONG FileObjectNameInformationLength, + PULONG ReturnLength + ); + +#endif diff --git a/branches/ph-plugins/NProcessHacker/process.c b/branches/ph-plugins/NProcessHacker/process.c new file mode 100644 index 000000000..52c0b35d9 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/process.c @@ -0,0 +1,117 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "process.h" + +NTSTATUS PHAPI PhQueryProcessWs( + HANDLE ProcessHandle, + WS_INFORMATION_CLASS WsInformationClass, + PVOID WsInformation, + ULONG WsInformationLength, + PULONG ReturnLength + ) +{ + switch (WsInformationClass) + { + case WsCount: + case WsPrivateCount: + case WsSharedCount: + case WsShareableCount: + if (WsInformationLength < 4) + return STATUS_BUFFER_TOO_SMALL; + goto WsCounters; + case WsAllCounts: + if (WsInformationLength < sizeof(WS_ALL_COUNTS)) + return STATUS_BUFFER_TOO_SMALL; +WsCounters: + { + PROCESS_MEMORY_COUNTERS procMem; + ULONG count = 0; + ULONG privateCount = 0; + ULONG sharedCount = 0; + ULONG shareableCount = 0; + PPSAPI_WORKING_SET_INFORMATION wsInfo; + SIZE_T wsInfoLength; + ULONG i; + + if (!GetProcessMemoryInfo(ProcessHandle, &procMem, sizeof(procMem))) + return STATUS_UNSUCCESSFUL; + + /* Assume the page size is 4kB */ + wsInfoLength = sizeof(PSAPI_WORKING_SET_INFORMATION) + + sizeof(PSAPI_WORKING_SET_BLOCK) * (procMem.WorkingSetSize / 4096); + wsInfo = (PPSAPI_WORKING_SET_INFORMATION)PhAlloc(wsInfoLength); + + if (!QueryWorkingSet(ProcessHandle, wsInfo, wsInfoLength)) + { + PhFree(wsInfo); + return STATUS_UNSUCCESSFUL; + } + + for (i = 0; i < wsInfo->NumberOfEntries; i++) + { + PSAPI_WORKING_SET_BLOCK block = wsInfo->WorkingSetInfo[i]; + + count++; + + if (block.ShareCount > 1) + sharedCount++; + if (block.ShareCount == 0) + privateCount++; + if (block.Shared) + shareableCount++; + } + + PhFree(wsInfo); + + switch (WsInformationClass) + { + case WsCount: + *(PULONG)WsInformation = count; + break; + case WsPrivateCount: + *(PULONG)WsInformation = privateCount; + break; + case WsSharedCount: + *(PULONG)WsInformation = sharedCount; + break; + case WsShareableCount: + *(PULONG)WsInformation = shareableCount; + break; + case WsAllCounts: + { + PWS_ALL_COUNTS allCounts = (PWS_ALL_COUNTS)WsInformation; + + allCounts->Count = count; + allCounts->PrivateCount = privateCount; + allCounts->SharedCount = sharedCount; + allCounts->ShareableCount = shareableCount; + break; + } + } + + return STATUS_SUCCESS; + } + break; + default: + return STATUS_INVALID_PARAMETER; + } +} diff --git a/branches/ph-plugins/NProcessHacker/process.h b/branches/ph-plugins/NProcessHacker/process.h new file mode 100644 index 000000000..6d795ebc0 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/process.h @@ -0,0 +1,53 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PROCESS_H +#define _PROCESS_H + +#include "nph.h" +#include + +typedef enum _WS_INFORMATION_CLASS +{ + WsCount = 0, + WsPrivateCount, + WsSharedCount, + WsShareableCount, + WsAllCounts +} WS_INFORMATION_CLASS, *PWS_INFORMATION_CLASS; + +typedef struct _WS_ALL_COUNTS +{ + ULONG Count; + ULONG PrivateCount; + ULONG SharedCount; + ULONG ShareableCount; +} WS_ALL_COUNTS, *PWS_ALL_COUNTS; + +NPHAPI NTSTATUS PHAPI PhQueryProcessWs( + HANDLE ProcessHandle, + WS_INFORMATION_CLASS WsInformationClass, + PVOID WsInformation, + ULONG WsInformationLength, + PULONG ReturnLength + ); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/NProcessHacker/resource.rc b/branches/ph-plugins/NProcessHacker/resource.rc new file mode 100644 index 000000000..337d53ea5 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/resource.rc @@ -0,0 +1,53 @@ +#include + +#define VER_COMMA 1,5,0,0 +#define VER_STR "1.5\0" + +#define VER_FILEVERSION VER_COMMA +#define VER_FILEVERSION_STR VER_STR +#define VER_PRODUCTVERSION VER_COMMA +#define VER_PRODUCTVERSION_STR VER_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#define VER_PRIVATEBUILD 0 +#define VER_PRERELEASE 0 + +#define VER_COMPANYNAME_STR "wj32\0" +#define VER_FILEDESCRIPTION_STR "Process Hacker Library\0" +#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2009 wj32. Licensed under the GNU GPL, v3.\0" +#define VER_ORIGINALFILENAME_STR "NProcessHacker.dll\0" +#define VER_PRODUCTNAME_STR "Process Hacker\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/branches/ph-plugins/NProcessHacker/secedit.c b/branches/ph-plugins/NProcessHacker/secedit.c new file mode 100644 index 000000000..944056ccf --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/secedit.c @@ -0,0 +1,24 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "secedit.h" + + diff --git a/branches/ph-plugins/NProcessHacker/secedit.h b/branches/ph-plugins/NProcessHacker/secedit.h new file mode 100644 index 000000000..df33f1cde --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/secedit.h @@ -0,0 +1,48 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _SECEDIT_H +#define _SECEDIT_H + +#include "nph.h" + +typedef HRESULT (__stdcall *_QueryInterface)( + PVOID This, + REFIID Id, + PVOID *Object + ); + +typedef ULONG (__stdcall *_AddRef)( + PVOID This + ); + +typedef ULONG (__stdcall *_Release)( + PVOID This + ); + +typedef struct _ISECURITY_INFORMATION +{ + _QueryInterface QueryInterface; + _AddRef AddRef; + _Release Release; +} ISECURITY_INFORMATION, *PISECURITY_INFORMATION; + +#endif diff --git a/branches/ph-plugins/NProcessHacker/verify.c b/branches/ph-plugins/NProcessHacker/verify.c new file mode 100644 index 000000000..6718a09d1 --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/verify.c @@ -0,0 +1,200 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "verify.h" + +VERIFY_RESULT PHAPI PhpStatusToVerifyResult(LONG Status); +VERIFY_RESULT PHAPI PhpVerifyFileBasic(PWSTR FileName); +VERIFY_RESULT PHAPI PhpVerifyFileCat(PWSTR FileName); + +_CryptCATAdminCalcHashFromFileHandle CryptCATAdminCalcHashFromFileHandle; +_CryptCATAdminAcquireContext CryptCATAdminAcquireContext; +_CryptCATAdminEnumCatalogFromHash CryptCATAdminEnumCatalogFromHash; +_CryptCATCatalogInfoFromContext CryptCATCatalogInfoFromContext; +_CryptCATAdminReleaseCatalogContext CryptCATAdminReleaseCatalogContext; +_CryptCATAdminReleaseContext CryptCATAdminReleaseContext; + +NTSTATUS PHAPI PhVerifyInit() +{ + LoadLibrary(L"wintrust.dll"); + + CryptCATAdminCalcHashFromFileHandle = + PhGetProcAddress(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle"); + CryptCATAdminAcquireContext = + PhGetProcAddress(L"wintrust.dll", "CryptCATAdminAcquireContext"); + CryptCATAdminEnumCatalogFromHash = + PhGetProcAddress(L"wintrust.dll", "CryptCATAdminEnumCatalogFromHash"); + CryptCATCatalogInfoFromContext = + PhGetProcAddress(L"wintrust.dll", "CryptCATCatalogInfoFromContext"); + CryptCATAdminReleaseCatalogContext = + PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseCatalogContext"); + CryptCATAdminReleaseContext = + PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseContext"); + + return STATUS_SUCCESS; +} + +VERIFY_RESULT PHAPI PhpStatusToVerifyResult(LONG Status) +{ + switch (Status) + { + case 0: + return VrTrusted; + case TRUST_E_NOSIGNATURE: + return VrNoSignature; + case CERT_E_EXPIRED: + return VrExpired; + case CERT_E_REVOKED: + return VrRevoked; + case TRUST_E_EXPLICIT_DISTRUST: + return VrDistrust; + case CRYPT_E_SECURITY_SETTINGS: + return VrSecuritySettings; + default: + return VrSecuritySettings; + } +} + +VERIFY_RESULT PHAPI PhpVerifyFileBasic(PWSTR FileName) +{ + WINTRUST_DATA trustData = { 0 }; + WINTRUST_FILE_INFO fileInfo = { 0 }; + GUID actionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; + + fileInfo.cbStruct = sizeof(fileInfo); + fileInfo.pcwszFilePath = FileName; + + trustData.cbStruct = sizeof(trustData); + trustData.dwUIChoice = WTD_UI_NONE; + trustData.dwProvFlags = WTD_SAFER_FLAG; + trustData.dwUnionChoice = WTD_CHOICE_FILE; + trustData.pFile = &fileInfo; + + return PhpStatusToVerifyResult(WinVerifyTrust(NULL, &actionGenericVerifyV2, &trustData)); +} + +VERIFY_RESULT PHAPI PhpVerifyFileCat(PWSTR FileName) +{ + LONG status = TRUST_E_NOSIGNATURE; + WINTRUST_DATA trustData = { 0 }; + WINTRUST_CATALOG_INFO catalogInfo = { 0 }; + GUID driverActionVerify = DRIVER_ACTION_VERIFY; + HANDLE fileHandle; + PBYTE fileHash = NULL; + ULONG fileHashLength; + PWSTR fileHashTag = NULL; + HANDLE catAdminHandle = NULL; + HANDLE catInfoHandle = NULL; + ULONG i; + + fileHandle = CreateFile( + FileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (fileHandle == INVALID_HANDLE_VALUE) + return VrNoSignature; + + fileHashLength = 256; + fileHash = (PBYTE)PhAlloc(fileHashLength); + + if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0)) + { + fileHash = (PBYTE)PhRealloc(fileHash, fileHashLength); + + if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0)) + { + CloseHandle(fileHandle); + PhFree(fileHash); + return VrNoSignature; + } + } + + if (!CryptCATAdminAcquireContext(&catAdminHandle, &driverActionVerify, 0)) + { + CloseHandle(fileHandle); + PhFree(fileHash); + return VrNoSignature; + } + + fileHashTag = (PWSTR)PhAlloc((fileHashLength * 2 + 1) * sizeof(WCHAR)); + + for (i = 0; i < fileHashLength; i++) + wsprintfW(&fileHashTag[i * 2], L"%02X", fileHash[i]); + + catInfoHandle = CryptCATAdminEnumCatalogFromHash( + catAdminHandle, + fileHash, + fileHashLength, + 0, + NULL + ); + + PhFree(fileHash); + + if (catInfoHandle) + { + CATALOG_INFO ci = { 0 }; + + if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0)) + { + catalogInfo.cbStruct = sizeof(catalogInfo); + catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile; + catalogInfo.pcwszMemberFilePath = FileName; + catalogInfo.pcwszMemberTag = fileHashTag; + + trustData.cbStruct = sizeof(trustData); + trustData.dwUIChoice = WTD_UI_NONE; + trustData.fdwRevocationChecks = WTD_STATEACTION_VERIFY; + trustData.dwUnionChoice = WTD_CHOICE_CATALOG; + trustData.pCatalog = &catalogInfo; + + status = WinVerifyTrust(NULL, &driverActionVerify, &trustData); + } + + CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0); + } + + PhFree(fileHashTag); + CryptCATAdminReleaseContext(catAdminHandle, 0); + CloseHandle(fileHandle); + + return PhpStatusToVerifyResult(status); +} + +VERIFY_RESULT PHAPI PhVerifyFile(PWSTR FileName) +{ + VERIFY_RESULT result = VrNoSignature; + + result = PhpVerifyFileBasic(FileName); + + if (result == VrNoSignature) + { + result = PhpVerifyFileCat(FileName); + } + + return result; +} diff --git a/branches/ph-plugins/NProcessHacker/verify.h b/branches/ph-plugins/NProcessHacker/verify.h new file mode 100644 index 000000000..4c950a1fb --- /dev/null +++ b/branches/ph-plugins/NProcessHacker/verify.h @@ -0,0 +1,88 @@ +/* + * Process Hacker Library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _VERIFY_H +#define _VERIFY_H + +#include "nph.h" +#include +#include + +typedef enum _VERIFY_RESULT +{ + VrUnknown = 0, + VrNoSignature, + VrTrusted, + VrTrustedInstaller, + VrExpired, + VrRevoked, + VrDistrust, + VrSecuritySettings +} VERIFY_RESULT, *PVERIFY_RESULT; + +typedef struct _CATALOG_INFO +{ + DWORD cbStruct; + WCHAR wszCatalogFile[MAX_PATH]; +} CATALOG_INFO, *PCATALOG_INFO; + +typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle)( + HANDLE hFile, + DWORD *pcbHash, + BYTE *pbHash, + DWORD dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminAcquireContext)( + HANDLE *phCatAdmin, + GUID *pgSubsystem, + DWORD dwFlags + ); + +typedef HANDLE (WINAPI *_CryptCATAdminEnumCatalogFromHash)( + HANDLE hCatAdmin, + BYTE *pbHash, + DWORD cbHash, + DWORD dwFlags, + HANDLE *phPrevCatInfo + ); + +typedef BOOL (WINAPI *_CryptCATCatalogInfoFromContext)( + HANDLE hCatInfo, + CATALOG_INFO *psCatInfo, + DWORD dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminReleaseCatalogContext)( + HANDLE hCatAdmin, + HANDLE hCatInfo, + DWORD dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminReleaseContext)( + HANDLE hCatAdmin, + DWORD dwFlags + ); + +NTSTATUS PHAPI PhVerifyInit(); +NPHAPI VERIFY_RESULT PHAPI PhVerifyFile(PWSTR FileName); + +#endif \ No newline at end of file diff --git a/branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll b/branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll new file mode 100644 index 0000000000000000000000000000000000000000..3e91b4c992f9ab5982bcf7eb229f6f0c2d5296a9 GIT binary patch literal 53248 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 k(|zgw^hkO%J(eC%PoyW)y. + */ + +using System; +using System.Text; + +namespace ProcessHacker.Common +{ + ///

+ /// Contains methods to parse numbers from string representations using different bases. + /// + public static class BaseConverter + { + private static int[] _reverseChars = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 52, + 53, 54, 55, 56, 57, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 65, 66, 67, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + /// + /// Reverses a string. + /// + /// The string to be reversed + /// The reversed string. + public static string ReverseString(string str) + { + StringBuilder sb = new StringBuilder(); + + for (int i = str.Length - 1; i >= 0; i--) + { + sb.Append(str[i]); + } + + return sb.ToString(); + } + + /// + /// Converts a string to a number using the specified base. + /// + /// + /// This function does not parse prefixes; to do so, use + /// + /// The string to convert + /// The base to use + /// + public static decimal ToNumber(string number, int b) + { + if (b > 70) + return 0; + + if (number == "") + return 0; + + bool negative = number[0] == '-'; + int length = number.Length; + long result = 0; + + if (negative) + { + length -= 1; + } + + number = ReverseString(number).ToLower(); + + for (int i = 0; i < length; i++) + { + result += _reverseChars[number[i]] * ((long)Math.Pow(b, i)); + } + + if (negative) + return -result; + else + return result; + } + + /// + /// Converts a string to a number, parsing prefixes to determine the base. + /// + /// The string to convert. + /// + public static decimal ToNumberParse(string number) + { + return ToNumberParse(number, true); + } + + /// + /// Converts a string to a number, parsing prefixes to determine the base. + /// + /// The string to convert. + /// Enables or disables non-standard prefixes for + /// bases 2 (b), 3 (t), 4 (q), 12 (w) and 32 (r). + /// + public static decimal ToNumberParse(string number, bool allowNonStandardExts) + { + if (number == "") + return 0; + + bool negative = number[0] == '-'; + decimal result = 0; + + if (negative) + number = number.Substring(1); + + if (number.Length > 2 && (number.Substring(0, 2) == "0x")) // hexadecimal + { + result = ToNumber(number.Substring(2), 16); + } + else if (number.Length > 1) + { + if (number[0] == '0') // octal + { + result = ToNumber(number.Substring(1), 8); + } + else if (number[0] == 'b' && allowNonStandardExts) // binary + { + result = ToNumber(number.Substring(1), 2); + } + else if (number[0] == 't' && allowNonStandardExts) // ternary + { + result = ToNumber(number.Substring(1), 3); + } + else if (number[0] == 'q' && allowNonStandardExts) // quaternary + { + result = ToNumber(number.Substring(1), 4); + } + else if (number[0] == 'w' && allowNonStandardExts) // base 12 + { + result = ToNumber(number.Substring(1), 12); + } + else if (number[0] == 'r' && allowNonStandardExts) // base 32 + { + result = ToNumber(number.Substring(1), 32); + } + else // base 10 + { + result = ToNumber(number, 10); + } + } + else // base 10 + { + result = ToNumber(number, 10); + } + + if (negative) + return -result; + else + return result; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs b/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs new file mode 100644 index 000000000..37026970a --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs @@ -0,0 +1,118 @@ +/* + * Process Hacker - + * byte stream reader + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of PNG.Net. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ProcessHacker.Common +{ + public sealed class ByteStreamReader : Stream + { + private byte[] _data; + private long _position; + + public ByteStreamReader(byte[] data) + { + _data = data; + _position = 0; + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + // don't need to flush + } + + public override long Length + { + get { return _data.LongLength; } + } + + public override long Position + { + get { return _position; } + set { _position = value; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + long length = (_position + count > _data.Length) ? _data.Length - _position - 1 : count; + + if (_position >= _data.Length) + return 0; + + for (long i = 0; i < length; i++, _position++) + buffer[offset + i] = _data[_position]; + + return (int)length; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + _position = offset; + break; + + case SeekOrigin.Current: + _position += offset; + break; + + case SeekOrigin.End: + _position = _data.Length - 1 + offset; + break; + + default: + throw new ArgumentException(); + } + + return _position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs b/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs new file mode 100644 index 000000000..546023a12 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs @@ -0,0 +1,369 @@ +/* + * Process Hacker - + * circular buffer + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; + +namespace ProcessHacker.Common +{ + /// + /// Provides methods for manipulating a circular buffer. A circular buffer + /// is a fixed-size array where old elements will be automatically deleted + /// as new elements are added. + /// + /// + /// This data structure is not thread-safe. You must provide your own + /// synchronization if more than one thread reads from or writes to the + /// buffer. + /// + /// + /// Ten-element circular buffer: + /// Data array: [4] [3] [2] [1] [0] [9] [8] [7] [6] [5] + /// ^ most recent data + /// ^ index + /// + public class CircularBuffer : IList + { + private int _size; + private int _count; + private int _index; + private T[] _data; + + /// + /// Creates a new circular buffer of the specified size. + /// + /// The size of the buffer. + public CircularBuffer(int size) + { + /* + * [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] + * ^ _index + */ + _size = size; + _count = 0; + _index = 0; + _data = new T[size]; + } + + /// + /// Gets or sets an element in the buffer. This is guaranteed to + /// never throw an exception. + /// + /// + /// A zero-based index into the buffer. Index 0 contains the + /// most recently added item, and higher positive indicies + /// access less recent items. Index -1 contains the least recently + /// added item, and lower negative indicies access more recent + /// items. + /// + public T this[int index] + { + get + { + /* + * For example, if _index = 6 and index = 5: + * + * [5] [4] [3] [2] [1] [0] [9] [8] [7] [6] + * ^ _index + * ^ (_index + index) mod _size = 11 mod 10 = 1 + */ + + // See the comment in Add for more details on modulus. + return _data[(((_index + index) % _size) + _size) % _size]; + } + set + { + // See the comment in Add for more details. + _data[(((_index + index) % _size) + _size) % _size] = value; + } + } + + /// + /// Gets the number of elements stored in the buffer. + /// + public int Count + { + get { return _count; } + } + + /// + /// Gets the maximum number of elements that can be stored in + /// the buffer. + /// + public int Size + { + get { return _size; } + } + + /// + /// Adds an element to the buffer. If the maximum buffer size + /// has been reached, the least recently added element will + /// be erased by the new element. + /// + /// The element to add. + public void Add(T value) + { + /* + * To add an item to the circular buffer the index is + * decremented and a modulus is performed on it to ensure + * it is not negative. + * + * [5] [4] [3] [2] [1] [0] [9] [8] [7] [6] + * ^ _index (6) + * When the new element x is added: + * [5] [4] [3] [2] [1] [x] [9] [8] [7] [6] + * ^ _index (5) + * + * Another example: + * [9] [8] [7] [6] [5] [4] [3] [2] [1] [0] + * ^ _index (0) + * When the new element x is added: + * [9] [8] [7] [6] [5] [4] [3] [2] [1] [x] + * ^ _index (9) + * = -1 mod 10 = 9 + */ + + /* The C# modulus operator produces a result which has the + * same sign as the dividend. For circular array access, + * we want the result to have the same sign as the divisor. + * We do this by using r = ((i % t) + t) % t where i is + * the index (possibly negative) and t is the size of the + * array. + */ + _data[_index = (((_index - 1) % _size) + _size) % _size] = value; + + if (_count < _size) + _count++; + } + + /// + /// Resizes the circular buffer. + /// + /// The new maximum buffer size. + public void Resize(int newSize) + { + // If we're not actually resizing the thing... + if (newSize == _size) + return; + + T[] newArray = new T[newSize]; + int tailSize = (_size - _index) % _size; + int headSize = _count - tailSize; + + /* + * The tail contains the most recent data. + * [3] [2] [1] [0] [ ] [8] [7] [6] [5] [4] + * [ ... head ... ] [ ..... tail ..... ] + * ^ _index (5) + * tailSize = _size - _index = 5 + * headSize = _count - tailSize = 9 - 5 = 4 + */ + + // If the new buffer is bigger than the current one. + if (newSize > _size) + { + /* + * Copy the tail, then the head. + * This means that the tail will now be at the front. + * [8] [7] [6] [5] [4] [3] [2] [1] [0] [ ] [ ] [ ] + * [ ..... tail ..... ][ ... head ... ] + * ^ _index (0) + */ + Array.Copy(_data, _index, newArray, 0, tailSize); + Array.Copy(_data, 0, newArray, tailSize, headSize); + _index = 0; + } + // If the new buffer is smaller than the current one. + else if (newSize < _size) + { + // If the new buffer is smaller than (or equal to) the tail size. + if (tailSize >= newSize) + { + /* + * Copy only a part of the tail because we don't have enough room. + * [8] [7] [6] + * [ . tail . ] + * ^ _index (0) + */ + Array.Copy(_data, _index, newArray, 0, newSize); + _index = 0; + } + // If the new buffer is bigger than the tail size. + else + { + /* + * Copy the tail in full, then copy a part of the head. + * [8] [7] [6] [5] [4] [3] [2] + * [ ..... tail ..... ][ head ] + * ^ _index (0) + */ + Array.Copy(_data, _index, newArray, 0, tailSize); + Array.Copy(_data, 0, newArray, tailSize, newSize - tailSize); + _index = 0; + } + + // The number of elements obviously can't be bigger than the + // buffer size. + if (_count > newSize) + _count = newSize; + } + + _data = newArray; + _size = newSize; + } + + /// + /// Converts the buffer to an array. + /// + /// + public T[] ToArray() + { + T[] newArray = new T[this.Count]; + + this.CopyTo(newArray, 0); + + return newArray; + } + + #region IList Members + + /// + /// Gets the index of the specified element in the array. + /// + /// The element to search for. + /// A positive index if the element was found. Otherwise, -1. + public int IndexOf(T item) + { + for (int i = 0; i < this.Count; i++) + if (this[i].Equals(item)) + return i; + + return -1; + } + + /// + /// This method is not supported. + /// + public void Insert(int index, T item) + { + throw new NotSupportedException(); + } + + /// + /// This method is not supported. + /// + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + #endregion + + #region ICollection Members + + /// + /// Clears the buffer. + /// + public void Clear() + { + // Just set the number of elements to zero. + _count = 0; + } + + /// + /// Gets whether the buffer contains the specified element. + /// + /// The element to search for. + /// Whether the element is present. + public bool Contains(T item) + { + return this.IndexOf(item) != -1; + } + + /// + /// Copies the elements of the buffer to the specified array. + /// + /// The array to copy to. + /// The index of the destination array at which to begin copying. + public void CopyTo(T[] array, int arrayIndex) + { + /* + * We have to make sure we don't copy unused elements. + * [2] [1] [0] [ ] [ ] [ ] [6] [5] [4] [3] + * [ . head . ] [ ... tail ... ] + * ^ _index (6) + * tailSize = _size - _index = 10 - 6 = 4 + * headSize = _count - tailSize = 7 - 4 = 3 + */ + int tailSize = _size - _index; + int headSize = _count - tailSize; + + // Copy the tail, then the head. + Array.Copy(_data, _index, array, arrayIndex, tailSize); + Array.Copy(_data, 0, array, arrayIndex + tailSize, headSize); + } + + /// + /// Gets whether the buffer is read-only. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// This method is not supported. + /// + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + #endregion + + #region IEnumerable Members + + /// + /// Gets an enumerator for the buffer. + /// + public IEnumerator GetEnumerator() + { + for (int i = 0; i < this.Count; i++) + yield return this[i]; + } + + #endregion + + #region IEnumerable Members + + /// + /// Gets an enumerator for the buffer. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + for (int i = 0; i < this.Count; i++) + yield return this[i]; + } + + #endregion + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Delegates.cs b/branches/ph-plugins/ProcessHacker.Common/Delegates.cs new file mode 100644 index 000000000..24862dd46 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Delegates.cs @@ -0,0 +1,19 @@ +namespace System +{ + public delegate void Action(); + //public delegate void Action(T a1); + public delegate void Action(T a1, U a2); + public delegate void Action(T a1, U a2, V a3); + public delegate void Action(T a1, U a2, V a3, W a4); + public delegate void Action(T a1, U a2, V a3, W a4, X a5); + public delegate void Action(T a1, U a2, V a3, W a4, X a5, Y a6); + public delegate void Action(T a1, U a2, V a3, W a4, X a5, Y a6, Z a7); + + public delegate T Func(); + public delegate U Func(T a1); + public delegate V Func(T a1, U a2); + public delegate W Func(T a1, U a2, V a3); + public delegate X Func(T a1, U a2, V a3, W a4); + public delegate Y Func(T a1, U a2, V a3, W a4, X a5); + public delegate Z Func(T a1, U a2, V a3, W a4, X a5, Y a6); +} diff --git a/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs b/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs new file mode 100644 index 000000000..526e9fbef --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs @@ -0,0 +1,168 @@ +/* + * Process Hacker - + * delta manager + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System.Collections.Generic; + +namespace ProcessHacker.Common +{ + /// + /// Defines subtraction for a numeric type. + /// + /// The numeric type. + public interface ISubtractor + { + /// + /// Subtracts v2 from v1, i.e., v1 - v2. + /// + T Subtract(T v1, T v2); + } + + public static class Subtractor + { + private static Int64Subtractor _int64Subtractor = new Int64Subtractor(); + private static Int32Subtractor _int32Subtractor = new Int32Subtractor(); + private static DoubleSubtractor _doubleSubtractor = new DoubleSubtractor(); + private static FloatSubtractor _floatSubtractor = new FloatSubtractor(); + + public static Int64Subtractor Int64Subtractor + { + get { return _int64Subtractor; } + } + + public static Int32Subtractor Int32Subtractor + { + get { return _int32Subtractor; } + } + + public static DoubleSubtractor DoubleSubtractor + { + get { return _doubleSubtractor; } + } + + public static FloatSubtractor FloatSubtractor + { + get { return _floatSubtractor; } + } + } + + /// + /// Provides subtraction for 64-bit integers. + /// + public class Int64Subtractor : ISubtractor + { + public long Subtract(long v1, long v2) + { + return v1 - v2; + } + } + + /// + /// Provides subtraction for 32-bit integers. + /// + public class Int32Subtractor : ISubtractor + { + public int Subtract(int v1, int v2) + { + return v1 - v2; + } + } + + /// + /// Provides subtraction for double-precision floating-point values. + /// + public class DoubleSubtractor : ISubtractor + { + public double Subtract(double v1, double v2) + { + return v1 - v2; + } + } + + /// + /// Provides subtraction for single-precision floating-point values. + /// + public class FloatSubtractor : ISubtractor + { + public float Subtract(float v1, float v2) + { + return v1 - v2; + } + } + + /// + /// Provides methods for managing deltas of discrete sets of data. + /// + public sealed class DeltaManager + { + private Dictionary _values; + private Dictionary _deltas; + private ISubtractor _subtractor; + + /// + /// Creates a delta manager using the specified subtractor. + /// + /// A subtractor for the appropriate type. + public DeltaManager(ISubtractor subtractor) + { + _subtractor = subtractor; + _values = new Dictionary(); + _deltas = new Dictionary(); + } + + public DeltaManager(ISubtractor subtractor, IEqualityComparer comparer) + { + _subtractor = subtractor; + _values = new Dictionary(comparer); + _deltas = new Dictionary(comparer); + } + + public TValue this[TKey key] + { + get { return _deltas[key]; } + set { _deltas[key] = value; } + } + + public TValue GetDelta(TKey key) + { + return _deltas[key]; + } + + public void Add(TKey key, TValue initialValue) + { + _values.Add(key, initialValue); + _deltas.Add(key, initialValue); + } + + public void SetDelta(TKey key, TValue value) + { + _deltas[key] = value; + } + + public TValue Update(TKey key, TValue value) + { + _deltas[key] = _subtractor.Subtract(value, _values[key]); + _values[key] = value; + + return _deltas[key]; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs b/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs new file mode 100644 index 000000000..ed0aa58a5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs @@ -0,0 +1,124 @@ +/* + * http://www.codeproject.com/KB/cs/EnumComparer.aspx + * + * by Omer Mor + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection.Emit; + +namespace ProcessHacker.Common +{ + public sealed class EnumComparer : IEqualityComparer + where TEnum : struct, IComparable, IConvertible, IFormattable + { + public static readonly EnumComparer Instance; + + private static readonly Func _equals; + private static readonly Func _getHashCode; + + static EnumComparer() + { + _getHashCode = generateGetHashCode(); + _equals = generateEquals(); + Instance = new EnumComparer(); + } + + private EnumComparer() + { + AssertTypeIsEnum(); + AssertUnderlyingTypeIsSupported(); + } + + public bool Equals(TEnum x, TEnum y) + { + return _equals(x, y); + } + + public int GetHashCode(TEnum obj) + { + return _getHashCode(obj); + } + + private static void AssertTypeIsEnum() + { + if (typeof(TEnum).IsEnum) + return; + + throw new NotSupportedException(); + } + + private static void AssertUnderlyingTypeIsSupported() + { + var underlyingType = Enum.GetUnderlyingType(typeof(TEnum)); + ICollection supportedTypes = + new[] + { + typeof (byte), typeof (sbyte), typeof (short), typeof (ushort), + typeof (int), typeof (uint), typeof (long), typeof (ulong) + }; + + if (supportedTypes.Contains(underlyingType)) + return; + + throw new NotSupportedException(); + } + + /// + /// Generates a comparison method similar to this: + /// + /// bool Equals(TEnum x, TEnum y) + /// { + /// return x == y; + /// } + /// + /// + /// The generated method. + private static Func generateEquals() + { + var method = new DynamicMethod(typeof(TEnum).Name + "_Equals", + typeof(bool), + new[] { typeof(TEnum), typeof(TEnum) }, + typeof(TEnum), true); + var generator = method.GetILGenerator(); + // Writing body + generator.Emit(OpCodes.Ldarg_0); // load x to stack + generator.Emit(OpCodes.Ldarg_1); // load y to stack + generator.Emit(OpCodes.Ceq); // x == y + generator.Emit(OpCodes.Ret); // return result + return (Func)method.CreateDelegate + (typeof(Func)); + } + + /// + /// Generates a GetHashCode method similar to this: + /// + /// int GetHashCode(TEnum obj) + /// { + /// return ((int)obj).GetHashCode(); + /// } + /// + /// + /// The generated method. + private static Func generateGetHashCode() + { + var method = new DynamicMethod(typeof(TEnum).Name + "_GetHashCode", + typeof(int), + new[] { typeof(TEnum) }, + typeof(TEnum), true); + var generator = method.GetILGenerator(); + var underlyingType = Enum.GetUnderlyingType(typeof(TEnum)); + var getHashCodeMethod = underlyingType.GetMethod("GetHashCode"); + var castValue = generator.DeclareLocal(underlyingType); + // Writing body + generator.Emit(OpCodes.Ldarg_0); // load obj to stack + generator.Emit(OpCodes.Stloc_0); // castValue = obj + generator.Emit(OpCodes.Ldloca_S, castValue); // load *castValue to stack + generator.Emit(OpCodes.Call, getHashCodeMethod); // castValue.GetHashCode() + generator.Emit(OpCodes.Ret); // return result + return (Func)method.CreateDelegate(typeof(Func)); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs b/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs new file mode 100644 index 000000000..0e39f6c55 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs @@ -0,0 +1,6 @@ +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public class ExtensionAttribute : Attribute + { } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/FreeList.cs b/branches/ph-plugins/ProcessHacker.Common/FreeList.cs new file mode 100644 index 000000000..6b2588b30 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/FreeList.cs @@ -0,0 +1,126 @@ +/* + * Process Hacker - + * free list + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System.Threading; + +namespace ProcessHacker.Common +{ + /// + /// Manages a list of free objects that can be re-used. + /// + public class FreeList + where T : IResettable, new() + { + private class FreeListEntry + where U : IResettable, new() + { + public U Object; + public FreeListEntry Next; + } + + private FreeListEntry _listHead = null; + private int _count = 0; + private int _maximumCount = 0; + + public int Count + { + get { return _count; } + } + + public int MaximumCount + { + get { return _maximumCount; } + set { _maximumCount = value; } + } + + public T Allocate() + { + FreeListEntry listHead; + + // Atomically pop an entry off and replace the list head + // pointer with a pointer to the next entry. + while (true) + { + listHead = _listHead; + + // If the list head pointer is null, we don't have anything + // to use from the free list. + if (listHead == null) + break; + + // Try to replace the list head pointer. + if (Interlocked.CompareExchange>( + ref _listHead, + listHead.Next, + listHead + ) == listHead) + { + // Success. + _count--; + return listHead.Object; + } + } + + return this.AllocateNew(); + } + + private T AllocateNew() + { + T obj = new T(); + obj.ResetObject(); + return obj; + } + + public void Free(T obj) + { + FreeListEntry listHead; + FreeListEntry listEntry; + + // Add the object to the free list if we haven't + // exceeded the maximum count. + if (_count < _maximumCount || _maximumCount == 0) + { + listEntry = new FreeListEntry(); + + listEntry.Object = obj; + + // Atomically add the list entry. + while (true) + { + listHead = _listHead; + listEntry.Next = listHead; + + if (Interlocked.CompareExchange>( + ref _listHead, + listEntry, + listHead + ) == listHead) + { + // Success. + _count++; + break; + } + } + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs b/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs new file mode 100644 index 000000000..a7db51960 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs @@ -0,0 +1,104 @@ +/* + * Process Hacker - + * history manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace ProcessHacker.Common +{ + public static class HistoryManager + { + private static int _globalMaxCount = 600; + + public static int GlobalMaxCount + { + get { return _globalMaxCount; } + set { _globalMaxCount = value; } + } + } + + public sealed class HistoryManager + { + private Dictionary> _history; + private Dictionary> _readOnlyCollections; + private int _maxCount = -1; + + public HistoryManager() + { + _history = new Dictionary>(); + _readOnlyCollections = new Dictionary>(); + } + + public HistoryManager(IEqualityComparer comparer) + { + _history = new Dictionary>(comparer); + _readOnlyCollections = new Dictionary>(comparer); + } + + public int MaxCount + { + get { return _maxCount; } + set { _maxCount = value; } + } + + public int EffectiveMaxCount + { + get { return _maxCount == -1 ? HistoryManager.GlobalMaxCount : _maxCount; } + } + + public ReadOnlyCollection this[TKey key] + { + get { return GetHistory(key); } + } + + public void Add(TKey key) + { + _history.Add(key, new CircularBuffer(this.EffectiveMaxCount)); + } + + public ReadOnlyCollection GetHistory(TKey key) + { + if (!_readOnlyCollections.ContainsKey(key)) + { + lock (_readOnlyCollections) + { + if (!_readOnlyCollections.ContainsKey(key)) + _readOnlyCollections.Add(key, new ReadOnlyCollection(_history[key])); + } + } + + return _readOnlyCollections[key]; + } + + public void Update(TKey key, TValue value) + { + int maxCount = this.EffectiveMaxCount; + + if (_history[key].Size != maxCount) + _history[key].Resize(maxCount); + + _history[key].Add(value); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/IResettable.cs b/branches/ph-plugins/ProcessHacker.Common/IResettable.cs new file mode 100644 index 000000000..2fa07049c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/IResettable.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common +{ + public interface IResettable + { + void ResetObject(); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs b/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs new file mode 100644 index 000000000..cae77136d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs @@ -0,0 +1,116 @@ +/* + * Process Hacker - + * unique ID generator + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; + +namespace ProcessHacker.Common +{ + /// + /// Provides a facility for generating unique IDs. + /// + public class IdGenerator + { + private int _step = 1; + private bool _sort = false; + private List _ids = new List(); + private int _id; + + /// + /// Creates a new ID generator. + /// + public IdGenerator() + : this(0) + { } + + /// + /// Creates a new ID generator. + /// + /// The starting ID. + public IdGenerator(int start) + : this(start, 1) + { } + + /// + /// Creates a new ID generator. + /// + /// The starting ID. + /// The number each ID will be divisible by. + public IdGenerator(int start, int step) + { + if (step == 0) + throw new ArgumentException("step cannot be zero."); + + _id = start; + _step = step; + } + + public bool Sort + { + get { return _sort; } + set { _sort = value; } + } + + /// + /// Generates a new ID. + /// + /// + public int Pop() + { + int id; + + lock (_ids) + { + if (_ids.Count > 0) + { + id = _ids[0]; + + _ids.Remove(_ids[0]); + + return id; + } + else + { + id = _id; + _id += _step; + } + } + + return id; + } + + /// + /// Makes an ID available for use. + /// + /// + public void Push(int id) + { + lock (_ids) + { + _ids.Add(id); + + if (_sort) + _ids.Sort(); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs new file mode 100644 index 000000000..d9b935a1c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs @@ -0,0 +1,43 @@ +//using System; +//using System.Collections.Generic; +//using System.Text; +//using System.Collections; + +//namespace System.Linq +//{ +// internal class GroupedEnumerable : IEnumerable>, IEnumerable +// { +// IEnumerable _source; +// Func _keySelector; +// Func _elementSelector; +// IEqualityComparer _comparer; + +// // Methods +// public GroupedEnumerable ( +// IEnumerable source, +// Func keySelector, +// Func elementSelector, +// IEqualityComparer comparer) +// { +// if (source == null) throw new ArgumentNullException ("source"); +// if (keySelector == null) throw new ArgumentNullException ("keySelector"); +// if (elementSelector == null) throw new ArgumentNullException ("elementSelector"); + +// _source = source; +// _keySelector = keySelector; +// _elementSelector = elementSelector; +// _comparer = comparer; +// } + +// public IEnumerator> GetEnumerator () +// { +// return Lookup.Create (_source, _keySelector, _elementSelector, _comparer).GetEnumerator (); +// } + +// IEnumerator IEnumerable.GetEnumerator () +// { +// return this.GetEnumerator (); +// } +// } + +//} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs new file mode 100644 index 000000000..836f9cc26 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; + +namespace System.Linq +{ + internal class Grouping : ReadOnlyCollection, IGrouping + { + internal IList InnerList { get { return this.Items; } } + + public TKey Key { get; private set; } + + public Grouping (TKey key) : base (new List()) + { + Key = key; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs new file mode 100644 index 000000000..2bb7a8438 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public interface IGrouping : IEnumerable + { + TKey Key { get; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs new file mode 100644 index 000000000..00ab824b5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace System.Linq +{ + public interface ILookup : IEnumerable> + { + int Count { get; } + bool Contains (TKey key); + IEnumerable this [TKey key] { get; } + } + +} \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs new file mode 100644 index 000000000..4e466b336 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System +{ + // We'll stick with Microsoft's definition of IOrderedEnumerable to minimize confusion. + + public interface IOrderedEnumerable : IEnumerable + { + IOrderedEnumerable CreateOrderedEnumerable ( + Func keySelector, + IComparer comparer, + bool descending); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt b/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt new file mode 100644 index 000000000..e440b797d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt @@ -0,0 +1,20 @@ +LINQBridge Copyright (c) 2007-2008 Joseph Albahari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs new file mode 100644 index 000000000..d91e08968 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace System.Linq +{ + public class Lookup : ILookup + { + Dictionary> _groupings; + + internal static Lookup Create ( + IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException ("source"); + if (keySelector == null) throw new ArgumentNullException ("keySelector"); + if (elementSelector == null) throw new ArgumentNullException ("elementSelector"); + + var lookup = new Lookup (comparer ?? EqualityComparer.Default); + + foreach (TSource element in source) + { + TKey key = keySelector (element); + Grouping grouping; + + if (!lookup._groupings.TryGetValue (key, out grouping)) + lookup._groupings.Add (key, grouping = new Grouping (key)); + + grouping.InnerList.Add (elementSelector (element)); + } + + return lookup; + } + + Lookup (IEqualityComparer comparer) + { + _groupings = new Dictionary> (comparer); + } + + public int Count { get { return _groupings.Count; } } + + public IEnumerable this [TKey key] + { + get + { + Grouping result; + if (_groupings.TryGetValue (key, out result)) + return result; + else + return Enumerable.Empty (); + } + } + + public bool Contains (TKey key) + { + return _groupings.ContainsKey (key); + } + + public IEnumerator> GetEnumerator () + { + foreach (var grouping in _groupings.Values) + yield return grouping; + } + + IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs new file mode 100644 index 000000000..ec2d16dec --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + class OrderByEnumerable : IOrderedEnumerable + { + public readonly IEnumerable Source; + public readonly Func KeySelector; + public readonly IComparer Comparer; + public readonly bool Descending; + + public OrderByEnumerable (IEnumerable source, Func keySelector, IComparer comparer, bool descending) + { + if (source == null) throw new ArgumentNullException ("source"); + if (keySelector == null) throw new ArgumentNullException ("keySelector"); + + Source = source; + KeySelector = keySelector; + Comparer = comparer ?? Comparer.Default; + Descending = descending; + } + + public IOrderedEnumerable CreateOrderedEnumerable ( + Func keySelector, + IComparer comparer, + bool descending) + { + return new ThenByEnumerable (this, keySelector, comparer, descending); + } + + internal virtual int CompareElements (TElement e1, TElement e2) // ThenByEnumerable will override this method. + { + int result = Comparer.Compare (KeySelector (e1), KeySelector (e2)); + return Descending ? -result : result; + } + + internal virtual IEnumerable GetElementsToSort () // ThenByEnumerable will override this method. + { + return Source; + } + + public IEnumerator GetEnumerator () + { + TElement [] array = GetElementsToSort ().ToArray(); + Array.Sort (array, CompareElements); + foreach (TElement element in array) + yield return element; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + return GetEnumerator(); + } + } + +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs new file mode 100644 index 000000000..571384bbc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + // int + + public static double Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return (double) source.Sum () / source.Count (); + } + + public static double? Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count(); + if (count == 0) return null; + return (double)source.Sum () / count; + } + + public static double Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + public static double? Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + // long + + public static double Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return (double)source.Sum () / source.Count (); + } + + public static double? Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count (); + if (count == 0) return null; + return (double)source.Sum () / count; + } + + public static double Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + public static double? Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + // float + + public static float Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return source.Sum () / source.Count (); + } + + public static float? Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count (); + if (count == 0) return null; + return source.Sum () / count; + } + + public static float Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + public static float? Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + // double + + public static double Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return source.Sum () / source.Count (); + } + + public static double? Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count (); + if (count == 0) return null; + return source.Sum () / count; + } + + public static double Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + public static double? Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + // decimal + + public static decimal Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count (); + if (count == 0) ThrowNoElements (); // decimal has no special "NaN" value, so we can't divide by zero. + return source.Sum () / count; + } + + public static decimal? Average (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int count = source.Count (); + if (count == 0) return null; + return source.Sum () / count; + } + + public static decimal Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + + public static decimal? Average (this IEnumerable source, Func selector) + { + return source.Select (selector).Average (); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs new file mode 100644 index 000000000..bc82e92dc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + // int + + public static int Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int tot = 0; + foreach (int element in source) checked { tot += element; }; + return tot; + } + + // It makes no sense to me that this returns a double? rather than a double, but that's the way the standard query operators work. + public static int? Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + int tot = 0; + foreach (int? element in source) checked { tot += element ?? 0; }; + return tot; + } + + public static int Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + public static int? Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + // long + + public static long Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + long tot = 0; + foreach (long element in source) checked { tot += element; } + return tot; + } + + public static long? Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + long tot = 0; + foreach (long? element in source) checked { tot += element ?? 0; } + return tot; + } + + public static long Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + public static long? Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + // float + + public static float Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + float tot = 0; + foreach (float element in source) tot += element; + return tot; + } + + public static float? Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + float tot = 0; + foreach (float? element in source) tot += element ?? 0; + return tot; + } + + public static float Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + public static float? Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + // double + + public static double Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + double tot = 0; + foreach (double element in source) tot += element; + return tot; + } + + public static double? Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + double tot = 0; + foreach (double? element in source) + tot += element ?? 0; + return tot; + } + + public static double Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + public static double ?Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + // decimal + + public static decimal Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + decimal tot = 0; + foreach (decimal element in source) tot += element; + return tot; + } + + public static decimal? Sum (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + decimal tot = 0; + foreach (decimal? element in source) + tot += element ?? 0; + return tot; + } + + public static decimal Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + + public static decimal? Sum (this IEnumerable source, Func selector) + { + return source.Select (selector).Sum (); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators.cs new file mode 100644 index 000000000..df33123e1 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + // Count / LongCount + + public static int Count (this IEnumerable source) + { + if (source is ICollection) return ((ICollection)source).Count; + if (source is ICollection) return ((ICollection)source).Count; + + int count = 0; + foreach (TSource element in source) count++; + return count; + } + + public static int Count (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + int count = 0; + foreach (TSource element in source) + if (predicate (element)) + count++; + + return count; + } + + public static long LongCount (this IEnumerable source) + { + if (source is ICollection) return ((ICollection)source).Count; + if (source is ICollection) return ((ICollection)source).Count; + + long count = 0; + foreach (TSource element in source) count++; + return count; + } + + public static long LongCount (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + long count = 0; + foreach (TSource element in source) + if (predicate (element)) + count++; + + return count; + } + + // Min + + public static TSource Min (this IEnumerable source) + { + if (!source.Any () && default (TSource) == null) return default (TSource); + return source.Aggregate ((accum, element) => Comparer.Default.Compare (accum, element) < 0 ? accum : element); + } + + public static TSource? Min (this IEnumerable source) + where TSource : struct + { + if (!source.Any ()) return default (TSource?); + return source.Aggregate ((accum, element) => Comparer.Default.Compare (accum, element) < 0 ? accum : element); + } + + public static TResult Min (this IEnumerable source, Func selector) + { + return source.Select (selector).Min (); + } + + // Max + + public static TSource Max (this IEnumerable source) + { + if (!source.Any () && default (TSource) == null) return default (TSource); + return source.Aggregate ((accum, element) => Comparer.Default.Compare (accum, element) > 0 ? accum : element); + } + + public static TSource? Max (this IEnumerable source) + where TSource : struct + { + if (!source.Any ()) return default (TSource?); + return source.Aggregate ((accum, element) => Comparer.Default.Compare (accum, element) > 0 ? accum : element); + } + + public static TResult Max (this IEnumerable source, Func selector) + { + return source.Select (selector).Max (); + } + + // Aggregate + + public static TSource Aggregate (this IEnumerable source, Func func) + { + if (source == null) throw new ArgumentNullException ("source"); + if (func == null) throw new ArgumentNullException ("func"); + + bool noElements = true; + TSource runningValue = default (TSource); + + foreach (TSource element in source) + { + if (noElements) + { + noElements = false; + runningValue = element; + } + else + runningValue = func (runningValue, element); + } + + if (noElements) ThrowNoElements (); + return runningValue; + } + + public static TAccumulate Aggregate ( + this IEnumerable source, + TAccumulate seed, + Func func) + { + return source.Aggregate (seed, func, x => x); + } + + public static TResult Aggregate ( + this IEnumerable source, TAccumulate seed, + Func func, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (func == null) throw new ArgumentNullException ("func"); + if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); + + TAccumulate runningValue = seed; + + foreach (TSource element in source) + runningValue = func (runningValue, element); + + return resultSelector (runningValue); + } + + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Conversion Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Conversion Operators.cs new file mode 100644 index 000000000..a24361212 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Conversion Operators.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static List ToList (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return new List (source); + } + + public static TSource [] ToArray (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + return source.ToList ().ToArray (); + } + + public static Dictionary ToDictionary (this IEnumerable source, Func keySelector) + { + return source.ToDictionary (keySelector, x => x, null); + } + + public static Dictionary ToDictionary (this IEnumerable source, Func keySelector, IEqualityComparer comparer) + { + return source.ToDictionary (keySelector, x => x, comparer); + } + + public static Dictionary ToDictionary ( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return source.ToDictionary (keySelector, elementSelector, null); + } + + public static Dictionary ToDictionary ( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException ("source"); + if (keySelector == null) throw new ArgumentNullException ("keySelector"); + if (elementSelector == null) throw new ArgumentNullException ("elementSelector"); + + Dictionary d = new Dictionary (comparer); + + foreach (TSource element in source) + d.Add (keySelector (element), elementSelector (element)); + + return d; + } + + public static ILookup ToLookup (this IEnumerable source, Func keySelector) + { + return Lookup.Create (source, keySelector, x => x, null); + } + + public static ILookup ToLookup ( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return Lookup.Create (source, keySelector, x => x, comparer); + } + + public static ILookup ToLookup ( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return Lookup.Create (source, keySelector, elementSelector, null); + } + + public static ILookup ToLookup ( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + return Lookup.Create (source, keySelector, elementSelector, comparer); + } + + public static IEnumerable AsEnumerable (this IEnumerable source) + { + return source; + } + + public static IEnumerable OfType (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + foreach (object obj in source) + if (obj is TResult) + yield return (TResult) obj; + } + + public static IEnumerable Cast (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + foreach (object obj in source) + yield return (TResult) obj; + } + + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Element Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Element Operators.cs new file mode 100644 index 000000000..f928f1de9 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Element Operators.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace System.Linq +{ + public static partial class Enumerable + { + // Single + + public static TSource Single (this IEnumerable source) + { + return source.Single (x => true, false); + } + + public static TSource Single (this IEnumerable source, Func predicate) + { + return source.Single (predicate, false); + } + + public static TSource SingleOrDefault (this IEnumerable source) + { + return source.Single (x => true, true); + } + + public static TSource SingleOrDefault (this IEnumerable source, Func predicate) + { + return source.Single (predicate, true); + } + + static TSource Single (this IEnumerable source, Func predicate, bool orDefault) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + TSource answer = default (TSource); + bool match = false; + + foreach (TSource element in source) + if (predicate (element)) + { + if (match) throw new InvalidOperationException ("Enumerable contains more than one matching element"); + match = true; + answer = element; + } + + if (!match && !orDefault) ThrowNoMatches (); + return answer; + } + + // First + + public static TSource First (this IEnumerable source) + { + return source.First (x => true, false); + } + + public static TSource First (this IEnumerable source, Func predicate) + { + return source.First (predicate, false); + } + + public static TSource FirstOrDefault (this IEnumerable source) + { + return source.First (x => true, true); + } + + public static TSource FirstOrDefault (this IEnumerable source, Func predicate) + { + return source.First (predicate, true); + } + + static TSource First (this IEnumerable source, Func predicate, bool orDefault) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + foreach (TSource element in source) + if (predicate (element)) + return element; + + if (!orDefault) ThrowNoMatches (); + return default (TSource); + } + + // Last + + public static TSource Last (this IEnumerable source) + { + return source.Last (x => true, false); + } + + public static TSource Last (this IEnumerable source, Func predicate) + { + return source.Last (predicate, false); + } + + public static TSource LastOrDefault (this IEnumerable source) + { + return source.Last (x => true, true); + } + + public static TSource LastOrDefault (this IEnumerable source, Func predicate) + { + return source.Last (predicate, true); + } + + static TSource Last (this IEnumerable source, Func predicate, bool orDefault) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + TSource answer = default (TSource); + bool match = false; + + foreach (TSource element in source) + if (predicate (element)) + { + match = true; + answer = element; + } + + if (!match && !orDefault) ThrowNoMatches (); + return answer; + } + + // ElementAt + + public static TSource ElementAt (this IEnumerable source, int index) + { + return source.ElementAt (index, false); + } + + public static TSource ElementAtOrDefault (this IEnumerable source, int index) + { + return source.ElementAt (index, true); + } + + static TSource ElementAt (this IEnumerable source, int index, bool orDefault) + { + if (source == null) throw new ArgumentNullException ("source"); + if (index < 0) throw new ArgumentOutOfRangeException ("index"); + + if (source is IList) return ((IList)source) [index]; + if (source is IList) return (TSource) ((IList)source) [index]; + + foreach (TSource element in source) + if (index-- == 0) + return element; + + if (!orDefault) throw new ArgumentOutOfRangeException ("index"); + return default (TSource); + } + + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Enumerable Private.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Enumerable Private.cs new file mode 100644 index 000000000..04f3b6e9d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Enumerable Private.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + static void ThrowNoElements () + { + throw new InvalidOperationException ("Enumerable contains no elements"); + } + + static void ThrowNoMatches () + { + throw new InvalidOperationException ("Enumerable contains no matching element"); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Filtering Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Filtering Operators.cs new file mode 100644 index 000000000..96d3af0de --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Filtering Operators.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable Distinct (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + // We can't use a HashSet here, because we don't have access to FW3.5! + var visitedElements = new Dictionary (); + + foreach (TSource element in source) + if (!visitedElements.ContainsKey (element)) + { + visitedElements.Add (element, null); + yield return element; + } + } + + public static IEnumerable Skip (this IEnumerable source, int count) + { + if (source == null) throw new ArgumentNullException ("source"); + + foreach (TSource element in source) + if (count-- <= 0) + yield return element; + } + + public static IEnumerable SkipWhile (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + bool unsatisfied = true; + foreach (TSource element in source) + { + if (unsatisfied) unsatisfied = predicate (element); + if (!unsatisfied) yield return element; + } + } + + public static IEnumerable SkipWhile (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + bool unsatisfied = true; + int i = 0; + foreach (TSource element in source) + { + if (unsatisfied) unsatisfied = predicate (element, i++); + if (!unsatisfied) yield return element; + } + } + + public static IEnumerable Take (this IEnumerable source, int count) + { + if (source == null) throw new ArgumentNullException ("source"); + + if (count <= 0) yield break; + foreach (TSource element in source) + if (count-- == 0) + break; + else + yield return element; + } + + public static IEnumerable TakeWhile (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + foreach (TSource element in source) + if (predicate (element)) + yield return element; + else + break; + } + + public static IEnumerable TakeWhile (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + int i = 0; + foreach (TSource element in source) + if (predicate (element, i++)) + yield return element; + else + break; + } + + public static IEnumerable Where (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + foreach (TSource element in source) + if (predicate (element)) + yield return element; + } + + public static IEnumerable Where (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + int i = 0; + foreach (TSource element in source) + if (predicate (element, i++)) + yield return element; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Generation Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Generation Operators.cs new file mode 100644 index 000000000..5d8e23d1c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Generation Operators.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable Range (int start, int count) + { + for (int i = 0; i < count; i++) + yield return i + start; + } + + public static IEnumerable Repeat (TResult element, int count) + { + for (int i = 0; i < count; i++) + yield return element; + } + + public static IEnumerable Empty () + { + yield break; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Grouping Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Grouping Operators.cs new file mode 100644 index 000000000..5a05ba0b7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Grouping Operators.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable> GroupBy ( + this IEnumerable source, + Func keySelector) + { + return source.GroupBy (keySelector, x => x, null); + } + + public static IEnumerable> GroupBy ( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return source.GroupBy (keySelector, x => x, comparer); + } + + public static IEnumerable> GroupBy ( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return source.GroupBy (keySelector, elementSelector, null); + } + + public static IEnumerable> GroupBy ( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + return Lookup.Create (source, keySelector, elementSelector, comparer); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Joining Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Joining Operators.cs new file mode 100644 index 000000000..67edb76a2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Joining Operators.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable Join + (this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func resultSelector) + { + if (outer == null) throw new ArgumentNullException ("outer"); + if (inner == null) throw new ArgumentNullException ("inner"); + if (outerKeySelector == null) throw new ArgumentNullException ("outerKeySelector"); + if (innerKeySelector == null) throw new ArgumentNullException ("innerKeySelector"); + if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); + + ILookup lookup = inner.ToLookup (innerKeySelector); + + // We can use LINQ to write LINQ! A SelectMany-style query over a lookup is the easiest way to + // implement a Join (see page 344, C# 3.0 in a Nutshell). + return + from outerItem in outer + from innerItem in lookup [outerKeySelector (outerItem)] + select resultSelector (outerItem, innerItem); + } + + public static IEnumerable GroupJoin ( + this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func, TResult> resultSelector) + { + if (outer == null) throw new ArgumentNullException ("outer"); + if (inner == null) throw new ArgumentNullException ("inner"); + if (outerKeySelector == null) throw new ArgumentNullException ("outerKeySelector"); + if (innerKeySelector == null) throw new ArgumentNullException ("innerKeySelector"); + if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); + + ILookup lookup = inner.ToLookup (innerKeySelector); + + // We won't make this harder than it needs to be - a GroupJoin is just a projection over a lookup! + return + from outerItem in outer + select resultSelector (outerItem, lookup [outerKeySelector (outerItem)]); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Misc Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Misc Operators.cs new file mode 100644 index 000000000..a305980a0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Misc Operators.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable Reverse (this IEnumerable source) + { + if (source == null) throw new ArgumentNullException ("source"); + + var list = source.ToList (); + + for (int i = list.Count - 1; i >= 0; i--) + yield return list [i]; + } + + public static IEnumerable DefaultIfEmpty (this IEnumerable source) + { + return source.DefaultIfEmpty (default (TSource)); + } + + public static IEnumerable DefaultIfEmpty (this IEnumerable source, TSource defaultValue) + { + if (source == null) throw new ArgumentNullException ("source"); + + bool empty = true; + foreach (TSource element in source) + { + empty = false; + yield return element; + } + if (empty) yield return defaultValue; + } + + // A bonus query operator! It allows you to do this: + // myQuery.ForEach (Console.WriteLine); + + public static void ForEach (this IEnumerable source, Action action) + { + foreach (TSource element in source) + action (element); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Ordering Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Ordering Operators.cs new file mode 100644 index 000000000..587992be3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Ordering Operators.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + // OrderBy: + + public static IOrderedEnumerable OrderBy ( + this IEnumerable source, + Func keySelector) + { + return OrderBy (source, keySelector, null); + } + + public static IOrderedEnumerable OrderBy ( + this IEnumerable source, + Func keySelector, + IComparer comparer) + { + return new OrderByEnumerable (source, keySelector, null, false); + } + + public static IOrderedEnumerable OrderByDescending ( + this IEnumerable source, + Func keySelector) + { + return OrderByDescending (source, keySelector, null); + } + + public static IOrderedEnumerable OrderByDescending ( + this IEnumerable source, + Func keySelector, + IComparer comparer) + { + return new OrderByEnumerable (source, keySelector, null, true); + } + + // ThenBy: + + public static IOrderedEnumerable ThenBy ( + this IOrderedEnumerable source, + Func keySelector) + { + return ThenBy (source, keySelector, null); + } + + public static IOrderedEnumerable ThenBy ( + this IOrderedEnumerable source, + Func keySelector, + IComparer comparer) + { + return source.CreateOrderedEnumerable (keySelector, comparer, false); + } + + public static IOrderedEnumerable ThenByDescending ( + this IOrderedEnumerable source, + Func keySelector) + { + return ThenByDescending (source, keySelector, null); + } + + public static IOrderedEnumerable ThenByDescending ( + this IOrderedEnumerable source, + Func keySelector, + IComparer comparer) + { + return source.CreateOrderedEnumerable (keySelector, comparer, true); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Projection Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Projection Operators.cs new file mode 100644 index 000000000..47375ce55 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Projection Operators.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + // Select + + public static IEnumerable Select (this IEnumerable source, Func selector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (selector == null) throw new ArgumentNullException ("selector"); + + foreach (TSource element in source) + yield return selector (element); + } + + public static IEnumerable Select (this IEnumerable source, Func selector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (selector == null) throw new ArgumentNullException ("selector"); + + int i = 0; + foreach (TSource element in source) + yield return selector (element, i++); + } + + // SelectMany + + public static IEnumerable SelectMany ( + this IEnumerable source, + Func> selector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (selector == null) throw new ArgumentNullException ("selector"); + + foreach (TSource element in source) + foreach (TResult childElement in selector (element)) + yield return childElement; + } + + public static IEnumerable SelectMany ( + this IEnumerable source, + Func> selector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (selector == null) throw new ArgumentNullException ("selector"); + + int i = 0; + foreach (TSource element in source) + { + foreach (TResult innerElement in selector (element, i)) + yield return innerElement; + i++; + } + } + + public static IEnumerable SelectMany ( + this IEnumerable source, + Func> collectionSelector, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException ("source"); + if (collectionSelector == null) throw new ArgumentNullException ("collectionSelector"); + if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); + + foreach (TSource element in source) + foreach (TCollection innerElement in collectionSelector (element)) + yield return resultSelector (element, innerElement); + } + + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Quantifiers.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Quantifiers.cs new file mode 100644 index 000000000..2e5b41df2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Quantifiers.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static bool Contains (this IEnumerable source, TSource value) + { + if (source == null) throw new ArgumentNullException ("source"); + + foreach (TSource element in source) + if (object.Equals (element, value)) + return true; + + return false; + } + + public static bool Any (this IEnumerable source) + { + return Any (source, x => true); + } + + public static bool Any (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + foreach (TSource element in source) + if (predicate (element)) + return true; + + return false; + } + + public static bool All (this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException ("source"); + if (predicate == null) throw new ArgumentNullException ("predicate"); + + foreach (TSource element in source) + if (!predicate (element)) + return false; + + return true; + } + + public static bool SequenceEqual (this IEnumerable first, IEnumerable second) + { + if (first == null) throw new ArgumentNullException ("first"); + if (second == null) throw new ArgumentNullException ("second"); + + using (var firstRator = second.GetEnumerator ()) + { + foreach (TSource secondElement in first) + { + if (!firstRator.MoveNext ()) return false; + if (!object.Equals (firstRator.Current, secondElement)) return false; + } + if (firstRator.MoveNext ()) return false; + } + return true; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Set Operators.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Set Operators.cs new file mode 100644 index 000000000..fa452981e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Set Operators.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + public static partial class Enumerable + { + public static IEnumerable Concat (this IEnumerable first, IEnumerable second) + { + if (first == null) throw new ArgumentException ("first"); + if (second == null) throw new ArgumentException ("second"); + + foreach (TSource element in first) + yield return element; + + foreach (TSource element in second) + yield return element; + } + + public static IEnumerable Union (this IEnumerable first, IEnumerable second) + { + return first.Concat (second).Distinct (); + } + + public static IEnumerable Intersect (this IEnumerable first, IEnumerable second) + { + if (first == null) throw new ArgumentException ("first"); + if (second == null) throw new ArgumentException ("second"); + + var firstDict = new Dictionary(); + + foreach (TSource element in first) + firstDict [element] = false; + + foreach (TSource element in second) + if (firstDict.ContainsKey (element)) + firstDict [element] = true; + + foreach (KeyValuePair keyValue in firstDict) + if (keyValue.Value) + yield return keyValue.Key; + } + + public static IEnumerable Except (this IEnumerable first, IEnumerable second) + { + if (first == null) throw new ArgumentException ("first"); + if (second == null) throw new ArgumentException ("second"); + + Dictionary firstDict = new Dictionary (); + + foreach (TSource element in first) + firstDict [element] = null; + + foreach (TSource element in second) + firstDict.Remove (element); + + foreach (TSource element in firstDict.Keys) + yield return element; + } + + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/ThenByEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/ThenByEnumerable.cs new file mode 100644 index 000000000..a50f1208d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Linq/ThenByEnumerable.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq +{ + class ThenByEnumerable : OrderByEnumerable + { + public ThenByEnumerable ( + OrderByEnumerable source, + Func keySelector, + IComparer comparer, + bool descending) + : base (source, keySelector, comparer, descending) + { + } + + public OrderByEnumerable OrderedSource { get { return (OrderByEnumerable) Source; } } + + internal override int CompareElements (TElement e1, TElement e2) + { + // First compare elements using the preceding OrderBy operator in the chain. (If the preceding operator is also + // an instance of ThenByEnumerable, it will, in turn, look at its previous OrderBy operator). If we get a non-zero + // result back, we can ignore our own comparison logic: + int result = OrderedSource.CompareElements (e1, e2); + if (result != 0) return result; + + // All preceding OrderBy operators have decided that the two elements are in the same sorting position. + // Now it's up to us to arbitrate! We'll call upon our normal sorting logic - as defined in the base class. + return base.CompareElements (e1, e2); + } + + internal override IEnumerable GetElementsToSort () + { + // Rather than sorting the result of the previous OrderBy, we'll sort the *original* sequence, using a + // comparer that takes all keys into account at once: + return OrderedSource.GetElementsToSort(); + } + } + +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Logging.cs b/branches/ph-plugins/ProcessHacker.Common/Logging.cs new file mode 100644 index 000000000..e2c973f84 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Logging.cs @@ -0,0 +1,78 @@ +/* + * Process Hacker - + * logging + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Common +{ + public delegate void LoggingDelegate(string message); + + public static class Logging + { + public enum Importance : int + { + Information = 0, + Warning, + Error, + Critical + } + + public static event LoggingDelegate Logged; + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern void OutputDebugString(string OutputString); + + private static object _logLock = new object(); + + [Conditional("DEBUG")] + public static void Log(Importance importance, string message) + { + lock (_logLock) + { + string debugMessage = + DateTime.Now.ToString("hh:mm:ss:fff:") + + " ProcessHacker (T" + System.Threading.Thread.CurrentThread.ManagedThreadId + + "): (" + importance.ToString() + ") " + message + "\r\n\r\n" + Environment.StackTrace; + + OutputDebugString(debugMessage); + + if (Logged != null) + Logged(debugMessage); + } + } + + [Conditional("DEBUG")] + public static void Log(Exception ex) + { + string message = ex.Message; + + if (ex.InnerException != null) + message += "\r\nInner exception:\r\n" + ex.InnerException.ToString(); + if (ex.StackTrace != null) + message += "\r\n" + ex.StackTrace; + + Log(Importance.Error, message); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Messaging/Message.cs b/branches/ph-plugins/ProcessHacker.Common/Messaging/Message.cs new file mode 100644 index 000000000..99616e719 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Messaging/Message.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Messaging +{ + public class Message + { + private object _tag; + + public object Tag + { + get { return _tag; } + set { _tag = value; } + } + } + + public class ActionMessage : Message + { + private Action _action; + + public ActionMessage(Action action) + { + _action = action; + } + + public Action Action + { + get { return _action; } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueue.cs b/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueue.cs new file mode 100644 index 000000000..9ef8adb34 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueue.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Messaging +{ + public class MessageQueue + { + private Queue _queue = new Queue(); + private List _listeners = new List(); + + public MessageQueue() + { + // Action message listener. + this.AddListener(new MessageQueueListener((action) => action.Action())); + } + + public void AddListener(MessageQueueListener listener) + { + lock (_listeners) + _listeners.Add(listener); + } + + public void Enqueue(Message message) + { + lock (_queue) + _queue.Enqueue(message); + } + + public void EnqueueAction(Action action) + { + this.Enqueue(new ActionMessage(action)); + } + + public void Listen() + { + lock (_queue) + { + // Start dequeuing. + while (_queue.Count > 0) + { + Message message = _queue.Dequeue(); + + // Look for receivers. + lock (_listeners) + { + foreach (MessageQueueListener listener in _listeners) + { + // If this listener is of the right type, execute the callback. + if (listener.Type.IsInstanceOfType(message)) + listener.Callback(message); + } + } + } + } + } + + public void RemoveListener(MessageQueueListener listener) + { + lock (_listeners) + _listeners.Remove(listener); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueueListener.cs b/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueueListener.cs new file mode 100644 index 000000000..b63b72ee0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Messaging/MessageQueueListener.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Messaging +{ + public delegate void MessageReceivedDelegate(Message message); + + public class MessageQueueListener + { + private MessageReceivedDelegate _callback; + private Type _type; + + public MessageQueueListener(MessageReceivedDelegate callback, Type type) + { + _callback = callback; + _type = type; + } + + public MessageReceivedDelegate Callback + { + get { return _callback; } + } + + public Type Type + { + get { return _type; } + } + } + + public class MessageQueueListener : MessageQueueListener + where T : Message + { + public delegate void MessageReceivedDelegate(T message); + + public MessageQueueListener(MessageReceivedDelegate callback) + : base((message) => callback((T)message), typeof(T)) + { } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Objects/BaseObject.cs b/branches/ph-plugins/ProcessHacker.Common/Objects/BaseObject.cs new file mode 100644 index 000000000..2770fcbd2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Objects/BaseObject.cs @@ -0,0 +1,504 @@ +/* + * Process Hacker - + * disposable object base functionality + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define ENABLE_STATISTICS +//#define EXTENDED_FINALIZER + +using System; +using System.ComponentModel; +using System.Threading; + +namespace ProcessHacker.Common.Objects +{ + /// + /// Provides methods for managing a disposable object or resource. + /// + /// + /// + /// Each disposable object starts with a reference count of one + /// when it is created. The object is not owned by the creator; + /// rather, it is owned by the GC (garbage collector). If the user + /// does not dispose the object, the finalizer will be called by + /// the GC, the reference count will be decremented and the object + /// will be freed. If the user chooses to call Dispose, the reference + /// count will be decremented and the object will be freed. The + /// object is no longer owned by the GC and the finalizer will be + /// suppressed. Any further calls to Dispose will have no effect. + /// + /// + /// If the user chooses to use reference counting, the object + /// functions normally with the GC. If the object's reference count + /// is incremented after it is created and becomes 2, it will be + /// decremented when it is finalized or disposed. Only after the + /// object is dereferenced will the reference count become 0 and + /// the object will be freed. + /// + /// + public abstract class BaseObject : IDisposable, IRefCounted + { + private static int _createdCount = 0; + private static int _freedCount = 0; + private static int _disposedCount = 0; + private static int _finalizedCount = 0; + private static int _referencedCount = 0; + private static int _dereferencedCount = 0; + + /// + /// Gets the number of disposable objects that have been created. + /// + public static int CreatedCount { get { return _createdCount; } } + /// + /// Gets the number of disposable objects that have been freed. + /// + public static int FreedCount { get { return _freedCount; } } + /// + /// Gets the number of disposable objects that have been Disposed with managed = true. + /// + public static int DisposedCount { get { return _disposedCount; } } + /// + /// Gets the number of disposable objects that have been Disposed with managed = false. + /// + public static int FinalizedCount { get { return _finalizedCount; } } + /// + /// Gets the number of times disposable objects have been referenced. + /// + public static int ReferencedCount { get { return _referencedCount; } } + /// + /// Gets the number of times disposable objects have been dereferenced. + /// + public static int DereferencedCount { get { return _dereferencedCount; } } + + public static T SwapRef(ref T reference, T newObj) + where T : class, IRefCounted + { + T oldObj; + + // Swap the reference. + oldObj = Interlocked.Exchange(ref reference, newObj); + // Reference the new object. + if (newObj != null) + newObj.Reference(); + // Dereference the old object. + if (oldObj != null) + oldObj.Dereference(); + + return oldObj; + } + +#if DEBUG + /// + /// A stack trace collected when the object is created. + /// + private string _creationStackTrace; +#endif + /// + /// Whether the object is owned (rather, whether this class should + /// take care of anything). + /// + private bool _owned = true; + /// + /// Whether the object is owned by the garbage collector (to ensure + /// calling Dispose more than once has no effect). + /// + private int _ownedByGc = 1; + /// + /// The reference count of the object. + /// + private int _refCount = 1; + /// + /// Whether the object has been freed. + /// + private volatile bool _disposed = false; +#if EXTENDED_FINALIZER + /// + /// Whether the finalizer will run. + /// + private int _finalizerRegistered = 1; +#endif + + /// + /// Initializes a disposable object. + /// + public BaseObject() + : this(true) + { } + + /// + /// Initializes a disposable object. + /// + /// Whether the resource is owned. + public BaseObject(bool owned) + { + _owned = owned; + + // Don't need to finalize the object if it doesn't need to be disposed. + if (!_owned) + { +#if EXTENDED_FINALIZER + this.DisableFinalizer(); +#else + GC.SuppressFinalize(this); +#endif + _ownedByGc = 0; + _refCount = 0; + } + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _createdCount); +#endif + +#if DEBUG + _creationStackTrace = Environment.StackTrace; +#endif + } + + /// + /// Ensures that the GC does not own the object. + /// + ~BaseObject() + { + // Get rid of GC ownership if still present. + this.Dispose(false); + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _finalizedCount); + // Dispose just incremented this value, but it + // shouldn't have been incremented. + Interlocked.Decrement(ref _disposedCount); +#endif + } + + /// + /// Ensures that the GC does not own the object. + /// + public void Dispose() + { + this.Dispose(true); + } + + /// + /// Ensures that the GC does not own the object. + /// + /// Whether to dispose managed resources. + public void Dispose(bool managed) + { + if (!_owned) + return; + + Thread.BeginCriticalRegion(); + + try + { + int oldOwnedByGc; + + // Only proceed if the object is owned by the GC. We can perform + // this operation without any locks by using CAS. + oldOwnedByGc = Interlocked.CompareExchange(ref _ownedByGc, 0, 1); + + if (oldOwnedByGc == 1) + { + // Decrement the reference count. + this.Dereference(managed); + + // Disable the finalizer. + if (managed) + { +#if EXTENDED_FINALIZER + this.DisableFinalizer(); +#else + GC.SuppressFinalize(this); +#endif + } + +#if ENABLE_STATISTICS + // Stats. + Interlocked.Increment(ref _disposedCount); + + // The dereferenced count should count the number of times + // the user has called Dereference, so decrement it + // because we just called it. + Interlocked.Decrement(ref _dereferencedCount); +#endif + } + } + finally + { + Thread.EndCriticalRegion(); + } + } + + /// + /// Queues the object for disposal in the current delayed release pool. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void DisposeDelayed() + { + DelayedReleasePool.CurrentPool.AddDispose(this); + } + + /// + /// Disposes the resources of the object. This method must not be + /// called directly; instead, override this method in a derived class. + /// + /// Whether or not to dispose managed objects. + protected abstract void DisposeObject(bool disposing); + + /// + /// Gets whether the object has been freed. + /// + public bool Disposed + { + get { return _disposed; } + } + + /// + /// Gets whether the object will be freed. + /// + public bool Owned + { + get { return _owned; } + } + + /// + /// Gets whether the object is owned by the garbage collector. + /// + public bool OwnedByGc + { + get { return _ownedByGc == 1; } + } + + /// + /// Gets the current reference count of the object. + /// + /// + /// This information is for debugging purposes ONLY. DO NOT + /// base memory management logic upon this value. + /// + public int ReferenceCount + { + get { return Thread.VolatileRead(ref _refCount); } + } + +#if EXTENDED_FINALIZER + /// + /// Disables the finalizer if it is not already disabled. + /// + private void DisableFinalizer() + { + int oldFinalizerRegistered; + + oldFinalizerRegistered = Interlocked.CompareExchange(ref _finalizerRegistered, 0, 1); + + if (oldFinalizerRegistered == 1) + { + GC.SuppressFinalize(this); + } + } +#endif + + /// + /// Declares that the object should no longer be owned. + /// + protected void DisableOwnership(bool dispose) + { + if (dispose) + this.Dispose(); + +#if EXTENDED_FINALIZER + this.DisableFinalizer(); +#else + GC.SuppressFinalize(this); +#endif + _owned = false; + +#if ENABLE_STATISTICS + // If the object didn't get disposed, pretend the object + // never got created. + if (!dispose) + Interlocked.Decrement(ref _createdCount); +#endif + } + + /// + /// Decrements the reference count of the object. + /// + /// The old reference count. + /// + /// + /// DO NOT call Dereference if you have not called Reference. + /// Call Dispose instead. + /// + /// + /// If you are calling Dereference from a finalizer, call + /// Dereference(false). + /// + /// + public int Dereference() + { + return this.Dereference(true); + } + + /// + /// Decrements the reference count of the object. + /// + /// Whether to dispose managed resources. + /// The new reference count. + /// + /// If you are calling this method from a finalizer, set + /// to false. + /// + public int Dereference(bool managed) + { + return this.Dereference(1, managed); + } + + /// + /// Decreases the reference count of the object. + /// + /// The number of times to dereference the object. + /// The new reference count. + public int Dereference(int count) + { + return this.Dereference(count, true); + } + + /// + /// Decreases the reference count of the object. + /// + /// The number of times to dereference the object. + /// Whether to dispose managed resources. + /// The new reference count. + public int Dereference(int count, bool managed) + { + // Initial parameter validation. + if (count == 0) + return Interlocked.Add(ref _refCount, 0); + if (count < 0) + throw new ArgumentException("Cannot dereference a negative number of times."); + + // Critical, prevent thread abortion. + Thread.BeginCriticalRegion(); + + try + { + if (!_owned) + return 0; + +#if ENABLE_STATISTICS + // Statistics. + Interlocked.Add(ref _dereferencedCount, count); +#endif + + // Decrease the reference count. + int newRefCount = Interlocked.Add(ref _refCount, -count); + + // Should not ever happen. + if (newRefCount < 0) + throw new InvalidOperationException("Reference count cannot be negative."); + + // Dispose the object if the reference count is 0. + if (newRefCount == 0 && !_disposed) + { + // If the dispose object method throws an exception, nothing bad + // should happen if it does not invalidate any state. + this.DisposeObject(managed); + // Prevent the object from being disposed twice. + _disposed = true; + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _freedCount); +#endif + } + + return newRefCount; + } + finally + { + Thread.EndCriticalRegion(); + } + } + + /// + /// Queues the object for dereferencing in the current delayed release pool. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void DereferenceDelayed() + { + DelayedReleasePool.CurrentPool.AddDereference(this); + } + +#if EXTENDED_FINALIZER + /// + /// Enables the finalizer if it is not already enabled. + /// + private void EnableFinalizer() + { + int oldFinalizerRegistered; + + oldFinalizerRegistered = Interlocked.CompareExchange(ref _finalizerRegistered, 1, 0); + + if (oldFinalizerRegistered == 0) + { + GC.ReRegisterForFinalize(this); + } + } +#endif + + /// + /// Increments the reference count of the object. + /// + /// The new reference count. + /// + /// + /// You must call Dereference once (when you are finished with the + /// object) to match each call to Reference. Do not call Dispose. + /// + /// + public int Reference() + { + return this.Reference(1); + } + + /// + /// Increases the reference count of the object. + /// + /// The number of times to reference the object. + /// The new reference count. + public int Reference(int count) + { + // Don't do anything if the object isn't owned. + if (!_owned) + return 0; + // Parameter validation. + if (count == 0) + return Interlocked.Add(ref _refCount, 0); + if (count < 0) + throw new ArgumentException("Cannot reference a negative number of times."); + +#if ENABLE_STATISTICS + Interlocked.Add(ref _referencedCount, count); +#endif + + return Interlocked.Add(ref _refCount, count); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Objects/DelayedReleasePool.cs b/branches/ph-plugins/ProcessHacker.Common/Objects/DelayedReleasePool.cs new file mode 100644 index 000000000..b240d38c9 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Objects/DelayedReleasePool.cs @@ -0,0 +1,209 @@ +/* + * Process Hacker - + * delayed release pool + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ProcessHacker.Common.Objects +{ + /// + /// Indicates that an operation was performed out-of-order. + /// + public class OutOfOrderException : Exception + { + public OutOfOrderException(string message) + : base(message) + { } + } + + /// + /// Represents a pool of objects to be disposed or dereferenced at some point. + /// + public sealed class DelayedReleasePool : BaseObject + { + /// + /// Describes how an object should be disposed. + /// + [Flags] + private enum DelayedReleaseFlags + { + Dispose = 0x1, + Dereference = 0x2 + } + + /// + /// Describes an object that is to be disposed. + /// + private struct DelayedReleaseObject + { + private DelayedReleaseFlags _flags; + private BaseObject _object; + + public DelayedReleaseObject(DelayedReleaseFlags flags, BaseObject obj) + { + _flags = flags; + _object = obj; + } + + public DelayedReleaseFlags Flags + { + get { return _flags; } + } + + public BaseObject Object + { + get { return _object; } + } + } + + [ThreadStatic] + private static Stack _poolStack; + [ThreadStatic] + private static DelayedReleasePool _currentPool; + + /// + /// Gets the currently active delayed release pool. + /// + public static DelayedReleasePool CurrentPool + { + get + { + if (_currentPool == null) + _currentPool = new DelayedReleasePool(); + + return _currentPool; + } + private set { _currentPool = value; } + } + + /// + /// Gets the stack of delayed release pools. + /// + private static Stack PoolStack + { + get + { + // No locking needed because the stack is thread-local. + if (_poolStack == null) + _poolStack = new Stack(); + + return _poolStack; + } + } + + /// + /// Restores an older delayed release pool from the pool stack. + /// + /// The current delayed release pool. + private static void PopPool(DelayedReleasePool pool) + { + if (_currentPool != pool) + throw new OutOfOrderException( + "Attempted to pop a pool when it wasn't on top of the stack. " + + "This usually indicates that a pool was popped out-of-order." + ); + + _currentPool = PoolStack.Pop(); + } + + /// + /// Sets the specified delayed release pool as the currently active pool. + /// + /// The pool to set. + private static void PushPool(DelayedReleasePool pool) + { + PoolStack.Push(_currentPool); + _currentPool = pool; + } + + private int _creatorThreadId; + private List _objects = new List(); + + /// + /// Creates a delayed release pool and sets it as the currently active pool. + /// + public DelayedReleasePool() + { + _creatorThreadId = Thread.CurrentThread.ManagedThreadId; + PushPool(this); + } + + protected override void DisposeObject(bool disposing) + { + // Only pop the pool if we're on the same thread as the + // creator thread. This either means that the thread has + // died, or the user forgot to pop the pool by calling + // Dispose. If they forgot, it's not our problem... + if ( + disposing && + _creatorThreadId == Thread.CurrentThread.ManagedThreadId + ) + PopPool(this); + + this.Drain(disposing); + } + + /// + /// Adds the specified object for dereferencing. + /// + /// The object to dereference. + public void AddDereference(BaseObject obj) + { + _objects.Add(new DelayedReleaseObject(DelayedReleaseFlags.Dereference, obj)); + } + + /// + /// Adds the specified object for disposal. + /// + /// The object to dispose. + public void AddDispose(BaseObject obj) + { + _objects.Add(new DelayedReleaseObject(DelayedReleaseFlags.Dispose, obj)); + } + + /// + /// Releases all objects in the pool. + /// + public void Drain() + { + this.Drain(true); + } + + /// + /// Releases all objects in the pool. + /// + /// Whether to release managed resources. + public void Drain(bool managed) + { + foreach (var obj in _objects) + { + if ((obj.Flags & DelayedReleaseFlags.Dispose) == DelayedReleaseFlags.Dispose) + obj.Object.Dispose(); + if ((obj.Flags & DelayedReleaseFlags.Dereference) == DelayedReleaseFlags.Dereference) + obj.Object.Dereference(managed); + } + + _objects.Clear(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Objects/HandleTable.cs b/branches/ph-plugins/ProcessHacker.Common/Objects/HandleTable.cs new file mode 100644 index 000000000..680420697 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Objects/HandleTable.cs @@ -0,0 +1,301 @@ +/* + * Process Hacker - + * handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System.Collections.Generic; + +namespace ProcessHacker.Common.Objects +{ + public class HandleTableEntry + { + private int _handle; + private IRefCounted _object; + + public int Handle + { + get { return _handle; } + internal set { _handle = value; } + } + + public IRefCounted Object + { + get { return _object; } + internal set { _object = value; } + } + } + + /// + /// Provides methods for managing handles to objects. + /// + public class HandleTable : HandleTable + { } + + /// + /// Provides methods for managing handles to objects. + /// + /// The type of each handle table entry. + public class HandleTable : BaseObject + where TEntry : HandleTableEntry, new() + { + /// + /// Represents a callback function for handle table enumeration. + /// + /// The current handle. + /// The current object. + /// Return true to stop enumerating; otherwise return false. + public delegate bool EnumerateHandleTableDelegate(int handle, TEntry entry); + + private IdGenerator _handleGenerator = new IdGenerator(4, 4); + private Dictionary _handles = + new Dictionary(); + + protected override void DisposeObject(bool disposing) + { + lock (_handles) + { + foreach (var entry in _handles.Values) + entry.Object.Dereference(disposing); + } + } + + /// + /// Creates a handle to the specified object. + /// + /// The object to reference. + /// The new handle. + public int Allocate(IRefCounted obj) + { + TEntry entry = new TEntry(); + + return this.Allocate(obj, entry); + } + + /// + /// Creates a handle to the specified object. + /// + /// The object to reference. + /// The handle table entry to use. + /// The new handle. + public int Allocate(IRefCounted obj, TEntry entry) + { + int handle = _handleGenerator.Pop(); + + // Reference the object so it does not get freed while + // it is stored in the handle table. + obj.Reference(); + // GC should not own the object. + obj.Dispose(); + // Initialize the entry. + entry.Handle = handle; + entry.Object = obj; + + // Add the handle entry. + lock (_handles) + { + _handles.Add(handle, entry); + } + + return handle; + } + + /// + /// Enumerates the handles in the handle table. + /// + /// The callback for the enumeration. + /// Whether the enumeration was stopped by the callback. + public bool Enumerate(EnumerateHandleTableDelegate callback) + { + lock (_handles) + { + foreach (var entry in _handles.Values) + { + if (callback(entry.Handle, entry)) + return true; + } + + return false; + } + } + + /// + /// Closes a handle. + /// + /// The handle to close. + /// Whether the handle was closed. + public bool Free(int handle) + { + IRefCounted obj; + + lock (_handles) + { + if (!_handles.ContainsKey(handle)) + return false; + + // Store the object reference for dereferencing later. + obj = _handles[handle].Object; + // Remove the handle so it can no longer be used. + _handles.Remove(handle); + } + + // Make the handle value available. + _handleGenerator.Push(handle); + // Dereference the object (this doesn't need to be in the locking block). + obj.Dereference(); + + return true; + } + + /// + /// Gets the handle table entry for a handle. + /// + /// The handle to lookup. + /// A handle table entry. + public TEntry LookupEntry(int handle) + { + lock (_handles) + { + if (_handles.ContainsKey(handle)) + return _handles[handle]; + else + return null; + } + } + + /// + /// Gets the object referenced by a handle. + /// + /// The handle to lookup. + /// + /// An object. This object has not been referenced and is + /// not guaranteed to be valid. + /// + public IRefCounted LookupObject(int handle) + { + return this.LookupEntry(handle).Object; + } + + /// + /// Gets the object referenced by a handle. + /// + /// The type of the object to retrieve. + /// The handle to lookup. + /// + /// An object. This object has not been referenced and is + /// not guaranteed to be valid. + /// + public T LookupObject(int handle) + where T : class, IRefCounted + { + return this.LookupObject(handle) as T; + } + + /// + /// References an object using a handle. + /// + /// The handle to lookup. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public IRefCounted ReferenceByHandle(int handle) + { + TEntry entry; + return this.ReferenceByHandle(handle, out entry); + } + + /// + /// References an object using a handle. + /// + /// The handle to lookup. + /// The handle table entry. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public IRefCounted ReferenceByHandle(int handle, out TEntry entry) + { + lock (_handles) + { + if (_handles.ContainsKey(handle)) + { + IRefCounted obj = _handles[handle].Object; + + obj.Reference(); + entry = _handles[handle]; + + return obj; + } + else + { + entry = null; + return null; + } + } + } + + /// + /// References an object using a handle. + /// + /// The type of the object to reference. + /// The handle to lookup. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public T ReferenceByHandle(int handle) + where T : class, IRefCounted + { + TEntry entry; + return this.ReferenceByHandle(handle, out entry); + } + + /// + /// References an object using a handle. + /// + /// The type of the object to reference. + /// The handle to lookup. + /// The handle table entry. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public T ReferenceByHandle(int handle, out TEntry entry) + where T : class, IRefCounted + { + IRefCounted obj = this.ReferenceByHandle(handle, out entry); + + if (obj == null) + return null; + + // Check the type. + if (obj is T) + { + return (T)obj; + } + else + { + // Wrong type. Dereference and return. + obj.Dereference(); + return null; + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Objects/IRefCounted.cs b/branches/ph-plugins/ProcessHacker.Common/Objects/IRefCounted.cs new file mode 100644 index 000000000..cbb37b004 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Objects/IRefCounted.cs @@ -0,0 +1,55 @@ +using System; + +namespace ProcessHacker.Common.Objects +{ + public interface IRefCounted : IDisposable + { + /// + /// Decrements the reference count of the object. + /// + /// The new reference count. + int Dereference(); + + /// + /// Decrements the reference count of the object. + /// + /// Whether to dispose managed resources. + /// The new reference count. + int Dereference(bool managed); + + /// + /// Decreases the reference count of the object. + /// + /// The number of times to dereference the object. + /// The new reference count. + int Dereference(int count); + + /// + /// Decreases the reference count of the object. + /// + /// The number of times to dereference the object. + /// Whether to dispose managed resources. + /// The new reference count. + int Dereference(int count, bool managed); + + /// + /// Ensures that the reference counting system has exclusive control + /// over the lifetime of the object. + /// + /// Whether to dispose managed resources. + void Dispose(bool managed); + + /// + /// Increments the reference count of the object. + /// + /// The new reference count. + int Reference(); + + /// + /// Increases the reference count of the object. + /// + /// The number of times to reference the object. + /// The new reference count. + int Reference(int count); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Objects/SecuredHandleTable.cs b/branches/ph-plugins/ProcessHacker.Common/Objects/SecuredHandleTable.cs new file mode 100644 index 000000000..77dd24b2c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Objects/SecuredHandleTable.cs @@ -0,0 +1,202 @@ +/* + * Process Hacker - + * secured handle table + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; + +namespace ProcessHacker.Common.Objects +{ + public class SecuredHandleTableEntry : HandleTableEntry + { + private long _grantedAccess; + + public long GrantedAccess + { + get { return _grantedAccess; } + set { _grantedAccess = value; } + } + + public bool AreAllAccessesGranted(TAccess access) + where TAccess : struct + { + long accessLong = Convert.ToInt64(access); + + if ((_grantedAccess & accessLong) == accessLong) + return true; + else + return false; + } + + public bool AreAnyAccessesGranted(TAccess access) + where TAccess : struct + { + long accessLong = Convert.ToInt64(access); + + if ((_grantedAccess & accessLong) != 0) + return true; + else + return false; + } + } + + /// + /// Provides methods for managing handles to objects securely. + /// + public class SecuredHandleTable : SecuredHandleTable + { } + + /// + /// Provides methods for managing handles to objects securely. + /// + /// The type of each handle table entry. + public class SecuredHandleTable : HandleTable + where TEntry : SecuredHandleTableEntry, new() + { + /// + /// Creates a handle to an object with the specified granted access. + /// + /// The type of access mask. + /// The object to reference. + /// The granted access to the object. + /// The new handle. + public int Allocate(IRefCounted obj, TAccess grantedAccess) + where TAccess : struct + { + TEntry entry = new TEntry(); + + entry.GrantedAccess = Convert.ToInt64(grantedAccess); + + return base.Allocate(obj, entry); + } + + /// + /// References an object using a handle. + /// + /// The type of access mask. + /// The handle to lookup. + /// The desired access to the object. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public IRefCounted ReferenceByHandle(int handle, TAccess access) + where TAccess : struct + { + return this.ReferenceByHandle(handle, access, false); + } + + /// + /// References an object using a handle. + /// + /// The type of access mask. + /// The handle to lookup. + /// The desired access to the object. + /// + /// Whether an exception will be thrown if access to the object is denied. + /// + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public IRefCounted ReferenceByHandle(int handle, TAccess access, bool throwOnAccessDenied) + where TAccess : struct + { + TEntry entry; + IRefCounted obj; + + // Reference the object. + obj = this.ReferenceByHandle(handle, out entry); + + if (obj == null) + return null; + + // Check the access. + if (entry.AreAllAccessesGranted(access)) + { + // OK, return the object. + return obj; + } + else + { + // Access denied. Dereference the object and return. + obj.Dereference(); + + if (throwOnAccessDenied) + throw new UnauthorizedAccessException("Access denied."); + else + return null; + } + } + + /// + /// References an object using a handle. + /// + /// The type of the object to reference. + /// The type of access mask. + /// The handle to lookup. + /// The desired access to the object. + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public T ReferenceByHandle(int handle, TAccess access) + where T : class, IRefCounted + where TAccess : struct + { + return this.ReferenceByHandle(handle, access, false); + } + + /// + /// References an object using a handle. + /// + /// The type of the object to reference. + /// The type of access mask. + /// The handle to lookup. + /// The desired access to the object. + /// + /// Whether an exception will be thrown if access to the object is denied. + /// + /// + /// An object. This object has been referenced and must be + /// dereferenced once it is no longer needed. + /// + public T ReferenceByHandle(int handle, TAccess access, bool throwOnAccessDenied) + where T : class, IRefCounted + where TAccess : struct + { + IRefCounted obj = this.ReferenceByHandle(handle, access, throwOnAccessDenied); + + if (obj == null) + return null; + + // Check the type. + if (obj is T) + { + return (T)obj; + } + else + { + obj.Dereference(); + return null; + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/ProcessHacker.Common.csproj b/branches/ph-plugins/ProcessHacker.Common/ProcessHacker.Common.csproj new file mode 100644 index 000000000..73cc893c1 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/ProcessHacker.Common.csproj @@ -0,0 +1,129 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + Library + Properties + ProcessHacker.Common + ProcessHacker.Common + v2.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + true + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\ProcessHacker.Common.xml + 1591 + true + AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Common/Properties/AssemblyInfo.cs b/branches/ph-plugins/ProcessHacker.Common/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..b83edceee --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Process Hacker Common Library")] +[assembly: AssemblyDescription("Process Hacker Common Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("Process Hacker")] +[assembly: AssemblyCopyright("Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ab61b552-e6b9-43bb-baa0-73852d9e97ab")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/branches/ph-plugins/ProcessHacker.Common/Settings/SettingDefaultAttribute.cs b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingDefaultAttribute.cs new file mode 100644 index 000000000..8873cb387 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingDefaultAttribute.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Settings +{ + public class SettingDefaultAttribute + { + private string _value; + + public SettingDefaultAttribute(string value) + { + _value = value; + } + + public string Value + { + get { return _value; } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsBase.cs b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsBase.cs new file mode 100644 index 000000000..9dfe836f7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Settings +{ + public abstract class SettingsBase + { + private SettingsStore _store; + private Dictionary _settings = new Dictionary(); + + public SettingsBase(SettingsStore store) + { + _store = store; + } + + public object this[string name] + { + get { return this.GetValue(name); } + set { this.SetValue(name, value); } + } + + private object GetValue(string name) + { + return null; + } + + private void SetValue(string name, object value) + { + + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsManager.cs b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsManager.cs new file mode 100644 index 000000000..f75aabf75 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsManager.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Settings +{ + public sealed class SettingsManager + { + private SettingsStore _store; + + public SettingsManager(SettingsStore store) + { + _store = store; + } + + public T GetProperty(string name) + { + return (T)this.GetProperty(name); + } + + public object GetProperty(string name) + { + return null; + } + + public void SetProperty(string name, T value) + { + this.SetProperty(name, value); + } + + public void SetProperty(string name, object value) + { + + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsStore.cs b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsStore.cs new file mode 100644 index 000000000..ba2dbb69e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Settings/SettingsStore.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Common.Settings +{ + public abstract class SettingsStore + { + public abstract string GetValue(string name); + public abstract void SetValue(string name, string value); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/FastMutex.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/FastMutex.cs new file mode 100644 index 000000000..5a83cc3c3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/FastMutex.cs @@ -0,0 +1,92 @@ +using System; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + /// + /// Provides methods for synchronizing access to a shared resource. + /// + /// Just a wrapper around Monitor (minus the event methods + /// like Pulse and Wait). + public sealed class FastMutex + { + /// + /// Represents a context for mutex acquisition. + /// + public struct FastMutexContext : IDisposable + { + private bool _disposed; + private FastMutex _fastMutex; + + internal FastMutexContext(FastMutex fastMutex) + { + _fastMutex = fastMutex; + _disposed = false; + } + + /// + /// Releases the mutex. + /// + public void Dispose() + { + if (!_disposed) + { + _fastMutex.Release(); + _disposed = true; + } + } + } + + private object _lock = new object(); + + /// + /// Acquires the mutex and prevents others from acquiring it. + /// If the mutex is already acquired, the function will block + /// until it can acquire the mutex. + /// + public void Acquire() + { + Monitor.Enter(_lock); + } + + /// + /// Acquires the mutex and returns a context object which + /// must be disposed to release the mutex. + /// + /// The context object. + public FastMutexContext AcquireContext() + { + this.Acquire(); + return new FastMutexContext(this); + } + + /// + /// Releases the mutex and allows others to acquire the mutex. + /// + public void Release() + { + Monitor.Exit(_lock); + } + + /// + /// Attempts to acquire the mutex and returns immediately + /// regardless of whether the mutex was acquired. + /// + /// Whether or not the mutex was acquired. + public bool TryAcquire() + { + return Monitor.TryEnter(_lock); + } + + /// + /// Attempts to acquire the mutex and returns after a + /// timeout period if the mutex could not be acquired. + /// + /// The timeout, in milliseconds. + /// Whether or not the mutex was acquired. + public bool TryAcquire(int millisecondsTimeout) + { + return Monitor.TryEnter(_lock, millisecondsTimeout); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/FastQueue.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/FastQueue.cs new file mode 100644 index 000000000..5abe5a482 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/FastQueue.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ProcessHacker.Common.Threading +{ + public class FastQueue : IEnumerable + { + private class FastQueueNode + { + public U Value; + public FastQueueNode Next; + } + + private int _count = 0; + // The head node. The next pointer of the head node always points + // to the least recently added node - the node to dequeue first. + private FastQueueNode _head; + // The tail node. This is always the most recently added node. + private FastQueueNode _tail; + // Note: all next pointers point to less recently added nodes (i.e. + // the next node to dequeue). + + public FastQueue() + { + _head = new FastQueueNode(); + _tail = _head; + _tail.Next = null; + } + + public int Count + { + get { return _count; } + } + + public T Dequeue() + { + throw new NotImplementedException(); + } + + public void Enqueue(T value) + { + throw new NotImplementedException(); + + //FastQueueNode tail; + //FastQueueNode tailNext; + //FastQueueNode node; + + //// Create a new queue node. + //node = new FastQueueNode(); + //node.Value = value; + //node.Next = null; + + //// Add the node to the tail of the list, atomically. + //// We have to set the next pointer of the current tail node + //// and then replace the tail pointer with our new node. + //while (true) + //{ + // tailNext = _tail.Next; + + // while (true) + // { + // tail = _tail; + // } + //} + } + + public IEnumerator GetEnumerator() + { + return null; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/FastStack.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/FastStack.cs new file mode 100644 index 000000000..42fd55a71 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/FastStack.cs @@ -0,0 +1,109 @@ +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System; + +namespace ProcessHacker.Common.Threading +{ + public class FastStack : IEnumerable + { + private class FastStackNode + { + public U Value; + public FastStackNode Next; + } + + private int _count = 0; + private FastStackNode _bottom = null; + + public int Count + { + get { return _count; } + } + + public T Peek() + { + FastStackNode bottom; + + bottom = _bottom; + + if (bottom == null) + throw new InvalidOperationException("The stack is empty."); + + return bottom.Value; + } + + public T Pop() + { + FastStackNode bottom; + + // Atomically replace the bottom of the stack. + while (true) + { + bottom = _bottom; + + // If the bottom of the stack is null, the + // stack is empty. + if (bottom == null) + throw new InvalidOperationException("The stack is empty."); + + // Try to replace the pointer. + if (Interlocked.CompareExchange>( + ref _bottom, + bottom.Next, + bottom + ) == bottom) + { + // Success. + return bottom.Value; + } + } + } + + public void Push(T value) + { + FastStackNode bottom; + FastStackNode entry; + + entry = new FastStackNode(); + entry.Value = value; + + // Atomically replace the bottom of the stack. + while (true) + { + bottom = _bottom; + entry.Next = bottom; + + // Try to replace the pointer. + if (Interlocked.CompareExchange>( + ref _bottom, + entry, + bottom + ) == bottom) + { + // Success. + break; + } + } + } + + public IEnumerator GetEnumerator() + { + FastStackNode entry; + + entry = _bottom; + + // Start the enumeration. + while (entry != null) + { + yield return entry.Value; + entry = entry.Next; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/RundownProtection.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/RundownProtection.cs new file mode 100644 index 000000000..12a4f073c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/RundownProtection.cs @@ -0,0 +1,101 @@ +using System; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + /// + /// Provides methods for managing object/resource destruction. + /// + public sealed class RundownProtection + { + private object _rundownLock = new object(); + private volatile bool _rundownActive = false; + private int _refCount = 0; + + /// + /// Attempts to acquire rundown protection. + /// + /// Whether rundown protection was acquired. + public bool Acquire() + { + Thread.BeginCriticalRegion(); + + try + { + lock (_rundownLock) + { + if (_rundownActive) + return false; + + Interlocked.Increment(ref _refCount); + + return true; + } + } + finally + { + Thread.EndCriticalRegion(); + } + } + + /// + /// Releases rundown protection. + /// + public void Release() + { + Thread.BeginCriticalRegion(); + + try + { + lock (_rundownLock) + { + int newRefCount = Interlocked.Decrement(ref _refCount); + + if (newRefCount < 0) + throw new InvalidOperationException("Reference count cannot be negative."); + + if (_rundownActive) + { + // If we are the last out, release all waiters. + if (newRefCount == 0) + Monitor.PulseAll(_rundownLock); + } + } + } + finally + { + Thread.EndCriticalRegion(); + } + } + + /// + /// Waits for all references to be released while disallowing + /// attempts to acquire rundown protection. + /// + public void Wait() + { + this.Wait(-1); + } + + /// + /// Waits for all references to be released while disallowing + /// attempts to acquire rundown protection. + /// + /// The timeout, in milliseconds. + /// Whether all references were released. + public bool Wait(int timeout) + { + lock (_rundownLock) + { + _rundownActive = true; + + // If there are no references, we can exit. + if (Thread.VolatileRead(ref _refCount) == 0) + return true; + + // Otherwise, wait for the release signal. + return Monitor.Wait(_rundownLock, timeout); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/SemaphorePair.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/SemaphorePair.cs new file mode 100644 index 000000000..25013a681 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/SemaphorePair.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + public class SemaphorePair : IDisposable + { + private int _count; + private Semaphore _readSemaphore; + private Semaphore _writeSemaphore; + + public SemaphorePair(int count) + { + _count = count; + _readSemaphore = new Semaphore(0, count); + _writeSemaphore = new Semaphore(count, count); + } + + public int Count + { + get { return _count; } + } + + public void Dispose() + { + _readSemaphore.Close(); + _writeSemaphore.Close(); + } + + public void ReleaseRead() + { + _readSemaphore.Release(); + } + + public void ReleaseWrite() + { + _writeSemaphore.Release(); + } + + public void WaitRead() + { + _readSemaphore.WaitOne(); + } + + public bool WaitRead(int timeout) + { + return _readSemaphore.WaitOne(timeout, false); + } + + public void WaitWrite() + { + _writeSemaphore.WaitOne(); + } + + public bool WaitWrite(int timeout) + { + return _writeSemaphore.WaitOne(timeout, false); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/SpinLock.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/SpinLock.cs new file mode 100644 index 000000000..b75e2d836 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/SpinLock.cs @@ -0,0 +1,115 @@ +/* + * Process Hacker - + * spinlock + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + /// + /// Represents a spinlock, a high-performance mutual exclusion lock. + /// + public sealed class SpinLock + { + public struct SpinLockContext : IDisposable + { + private bool _disposed; + private SpinLock _spinLock; + + internal SpinLockContext(SpinLock spinLock) + { + _spinLock = spinLock; + _spinLock.Acquire(); + _disposed = false; + } + + public void Dispose() + { + if (!_disposed) + { + _spinLock.Release(); + _disposed = true; + } + } + } + + private int _value = 0; + private bool _spin; + private int _acquireCount = 0; + private int _spinCount = 0; + + /// + /// Creates a spinlock. + /// + public SpinLock() + { + // We don't want to spin on uniprocessor systems. + if (Environment.ProcessorCount == 1) + _spin = false; + else + _spin = true; + } + + /// + /// Acquires the spinlock. + /// + public void Acquire() + { + Thread.BeginCriticalRegion(); + + Interlocked.Increment(ref _acquireCount); + + if (_spin) + { + while (Interlocked.CompareExchange(ref _value, 1, 0) == 1) + Thread.SpinWait((_spinCount++ % Thread.VolatileRead(ref _acquireCount)) + 1); + } + else + { + while (Interlocked.CompareExchange(ref _value, 1, 0) == 1) + Thread.Sleep(0); + } + + Thread.EndCriticalRegion(); + } + + /// + /// Acquires the spinlock using a context object. + /// + /// A disposable context object. + public SpinLockContext AcquireContext() + { + return new SpinLockContext(this); + } + + /// + /// Releases the spinlock. + /// + public void Release() + { + Thread.BeginCriticalRegion(); + Interlocked.Exchange(ref _value, 0); + Interlocked.Decrement(ref _acquireCount); + Thread.EndCriticalRegion(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/ThreadTask.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/ThreadTask.cs new file mode 100644 index 000000000..7c06d6c3b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/ThreadTask.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + public delegate void ThreadTaskCompletedDelegate(object result); + public delegate void ThreadTaskRunTaskDelegate(object param, ref object result); + + public sealed class ThreadTask + { + public event ThreadTaskCompletedDelegate Completed; + public event ThreadTaskRunTaskDelegate RunTask; + + private Thread _thread = null; + private object _result; + private Exception _exception; + private bool _cancelled = false; + private bool _running = false; + + public bool Cancelled + { + get { return _cancelled; } + } + + public Exception Exception + { + get { return _exception; } + } + + public object Result + { + get { return _result; } + } + + public bool Running + { + get { return _running; } + } + + public void Cancel() + { + _cancelled = true; + } + + public void Start() + { + this.Start(null); + } + + public void Start(object param) + { + if (_thread != null) + throw new InvalidOperationException("The task has already been started."); + + _thread = new Thread(this.ThreadStart); + _thread.IsBackground = true; + _thread.Start(param); + } + + private void ThreadStart(object param) + { + _cancelled = false; + _running = true; + + try + { + if (this.RunTask != null) + this.RunTask(param, ref _result); + } + catch (Exception ex) + { + _exception = ex; + } + + if (!_cancelled && this.Completed != null) + this.Completed(_result); + + _running = false; + _thread = null; + } + + public void Wait() + { + _thread.Join(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Threading/WaitableQueue.cs b/branches/ph-plugins/ProcessHacker.Common/Threading/WaitableQueue.cs new file mode 100644 index 000000000..fef78eb7e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Threading/WaitableQueue.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace ProcessHacker.Common.Threading +{ + public class WaitableQueue : IEnumerable, IEnumerable + { + private Queue _queue = new Queue(); + private SemaphorePair _pair; + + public WaitableQueue() + : this(int.MaxValue) + { } + + public WaitableQueue(int maximumCount) + { + _pair = new SemaphorePair(maximumCount); + } + + public int Count + { + get { return _queue.Count; } + } + + public void Clear() + { + lock (_queue) + _queue.Clear(); + } + + public bool Contains(T item) + { + lock (_queue) + return _queue.Contains(item); + } + + public T Dequeue() + { + // Wait for an item to dequeue. + _pair.WaitRead(); + // Release a slot. + _pair.ReleaseWrite(); + + lock (_queue) + return _queue.Dequeue(); + } + + public bool Dequeue(int timeout, out T item) + { + bool waitResult = true; + + // Wait for an item to dequeue. + waitResult = _pair.WaitRead(timeout); + + // Dequeue an item if we waited successfully, + // otherwise pass the default value back. + if (waitResult) + { + lock (_queue) + item = _queue.Dequeue(); + + // We just dequeued an item, so we can + // release a slot. + _pair.ReleaseWrite(); + } + else + { + item = default(T); + } + + return waitResult; + } + + public void Enqueue(T item) + { + // Make sure we have an available slot. + _pair.WaitWrite(); + + // Enqueue the item. + lock (_queue) + _queue.Enqueue(item); + + // Unwait one dequeuer. + _pair.ReleaseRead(); + } + + public IEnumerator GetEnumerator() + { + return _queue.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _queue.GetEnumerator(); + } + + public T[] ToArray() + { + lock (_queue) + return _queue.ToArray(); + } + + public void TrimExcess() + { + lock (_queue) + _queue.TrimExcess(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Tokenizer.cs b/branches/ph-plugins/ProcessHacker.Common/Tokenizer.cs new file mode 100644 index 000000000..5847eafb6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Tokenizer.cs @@ -0,0 +1,237 @@ +using System; +using System.Text; + +namespace ProcessHacker.Common +{ + public class Tokenizer + { + private string _text; + private int _i = 0; + + public Tokenizer(string text) + { + _text = text; + } + + public int Index + { + get { return _i; } + set { _i = value; } + } + + public string EatId() + { + StringBuilder sb = new StringBuilder(); + + while (_i < _text.Length) + { + // identifiers can't start with a number- + if (sb.Length == 0) + { + if (!(char.IsLetter(_text[_i]) || _text[_i] == '_')) + break; + } + else + { + if (!(char.IsLetterOrDigit(_text[_i]) || _text[_i] == '_')) + break; + } + + sb.Append(_text[_i]); + _i++; + } + + return sb.ToString(); + } + + public string EatNumber() + { + StringBuilder sb = new StringBuilder(); + + while (_i < _text.Length) + { + // allow hex numbers and floating-point numbers + if (sb.Length == 1 && sb[0] == '0') + { + if (!char.IsDigit(_text[_i]) && char.ToLower(_text[_i]) != 'x' && _text[_i] != '.') + break; + } + else if (sb.Length >= 2 && sb[0] == '0' && char.ToLower(sb[1]) == 'x') + { + if (!(char.IsDigit(_text[_i]) || + char.ToLower(_text[_i]) == 'a' || + char.ToLower(_text[_i]) == 'b' || + char.ToLower(_text[_i]) == 'c' || + char.ToLower(_text[_i]) == 'd' || + char.ToLower(_text[_i]) == 'e' || + char.ToLower(_text[_i]) == 'f')) + break; + } + else + { + if (!char.IsDigit(_text[_i])) + break; + } + + sb.Append(_text[_i]); + _i++; + } + + return sb.ToString(); + } + + public string EatQuotedString() + { + StringBuilder sb = new StringBuilder(); + bool inEscape = false; + + if (_text[_i] == '"') + { + _i++; + } + else + return ""; + + while (_i < _text.Length) + { + if (_text[_i] == '\\') + { + inEscape = true; + _i++; + continue; + } + else if (inEscape) + { + if (_text[_i] == '\\') + sb.Append('\\'); + else if (_text[_i] == '"') + sb.Append('"'); + else if (_text[_i] == '\'') + sb.Append('\''); + else if (_text[_i] == 'r') + sb.Append('\r'); + else if (_text[_i] == 'n') + sb.Append('\n'); + else if (_text[_i] == 't') + sb.Append('\t'); + else + throw new Exception("Unrecognized escape sequence '\\" + _text[_i] + "'"); + + _i++; + inEscape = false; + continue; + } + else if (_text[_i] == '"') + { + _i++; + break; + } + + sb.Append(_text[_i]); + _i++; + } + + return sb.ToString(); + } + + public string EatSymbol() + { + StringBuilder sb = new StringBuilder(); + + while (_i < _text.Length && sb.Length < 1) // we need a proper parser to solve this + { + char c = _text[_i]; + + if (c < ' ' || c > '~') // check if its an ASCII character + break; + if (char.IsLetterOrDigit(c) || c == '_') // check if its eligible to be an identifier + break; + + sb.Append(c); + _i++; + } + + return sb.ToString(); + } + + public string EatUntil(char c) + { + StringBuilder sb = new StringBuilder(); + + while (_text[_i] != c && _i < _text.Length) + { + sb.Append(_text[_i]); + _i++; + } + + return sb.ToString(); + } + + public bool EatWhitespace() + { + return this.EatWhitespace(false); + } + + public bool EatWhitespace(bool comments) + { + bool ranOut = true; + bool preComment = false; // '/' + bool inComment = false; // '*' + bool prePostComment = false; // '*' + + while (_i < _text.Length) + { + if (comments && inComment && _text[_i] == '*') + { + prePostComment = true; + _i++; + continue; + } + else if (comments && prePostComment && _text[_i] == '/') + { + prePostComment = false; + inComment = false; + _i++; + continue; + } + else if (comments && !inComment && _text[_i] == '/') + { + preComment = true; + _i++; + continue; + } + else if (comments && preComment) + { + if (_text[_i] == '*') + { + preComment = false; + inComment = true; + _i++; + continue; + } + else + { + // it's a mistake, revert! + _i -= 1; + break; + } + } + else + { + preComment = false; + prePostComment = false; + } + + if (!(_text[_i] == '\r' || _text[_i] == '\n' || _text[_i] == ' ' || _text[_i] == '\t') && !inComment) + { + ranOut = false; + break; + } + + _i++; + } + + return ranOut; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Ui/ColumnHeaderExtensions.cs b/branches/ph-plugins/ProcessHacker.Common/Ui/ColumnHeaderExtensions.cs new file mode 100644 index 000000000..cef6c409d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Ui/ColumnHeaderExtensions.cs @@ -0,0 +1,75 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace ProcessHacker.Common.Ui +{ + // From http://stackoverflow.com/questions/254129/how-to-i-display-a-sort-arrow-in-the-header-of-a-list-view-column-using-c + [EditorBrowsable(EditorBrowsableState.Never)] + public static class ColumnHeaderExtensions + { + [StructLayout(LayoutKind.Sequential)] + private struct LVCOLUMN + { + public Int32 mask; + public Int32 cx; + [MarshalAs(UnmanagedType.LPTStr)] + public string pszText; + public IntPtr hbm; + public Int32 cchTextMax; + public Int32 fmt; + public Int32 iSubItem; + public Int32 iImage; + public Int32 iOrder; + } + + private const Int32 HDI_FORMAT = 0x4; + private const Int32 HDF_SORTUP = 0x400; + private const Int32 HDF_SORTDOWN = 0x200; + private const Int32 LVM_GETHEADER = 0x101f; + private const Int32 HDM_GETITEM = 0x120b; + private const Int32 HDM_SETITEM = 0x120c; + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", EntryPoint = "SendMessage")] + private static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, ref LVCOLUMN lPLVCOLUMN); + + public static void SetSortIcon(this ColumnHeader column, SortOrder order) + { + ListView listView = column.ListView; + IntPtr columnHeader = SendMessage(listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); + + for (int i = 0; i <= listView.Columns.Count - 1; i++) + { + IntPtr ColumnPtr = new IntPtr(i); + LVCOLUMN lvColumn = new LVCOLUMN(); + lvColumn.mask = HDI_FORMAT; + SendMessage(columnHeader, HDM_GETITEM, ColumnPtr, ref lvColumn); + + if (!(order == SortOrder.None) && i == column.Index) + { + switch (order) + { + case SortOrder.Ascending: + lvColumn.fmt &= ~HDF_SORTDOWN; + lvColumn.fmt |= HDF_SORTUP; + break; + case SortOrder.Descending: + lvColumn.fmt &= ~HDF_SORTUP; + lvColumn.fmt |= HDF_SORTDOWN; + break; + } + } + else + { + lvColumn.fmt &= ~HDF_SORTDOWN & ~HDF_SORTUP; + } + + SendMessage(columnHeader, HDM_SETITEM, ColumnPtr, ref lvColumn); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Ui/SortedListViewComparer.cs b/branches/ph-plugins/ProcessHacker.Common/Ui/SortedListViewComparer.cs new file mode 100644 index 000000000..588d1298c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Ui/SortedListViewComparer.cs @@ -0,0 +1,317 @@ +/* + * Process Hacker - + * sorted list comparer + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Windows.Forms; +using System.Globalization; + +namespace ProcessHacker.Common.Ui +{ + public interface ISortedListViewComparer + { + int Compare(ListViewItem x, ListViewItem y, int column); + } + + /// + /// Provides automatic sorting support for the ListView control. + /// + /// + /// myListView.ListViewItemSorter = new SortedListComparer(myListView); + /// + public class SortedListViewComparer : IComparer + { + private class DefaultComparer : ISortedListViewComparer + { + private SortedListViewComparer _sortedListComparer; + + public DefaultComparer(SortedListViewComparer sortedListComparer) + { + _sortedListComparer = sortedListComparer; + } + + public int Compare(ListViewItem x, ListViewItem y, int column) + { + string sx, sy; + long ix, iy; + IComparable cx, cy; + + sx = x.SubItems[column].Text.Replace(",", ""); + sy = y.SubItems[column].Text.Replace(",", ""); + + if (!long.TryParse(sx.StartsWith("0x") ? sx.Substring(2) : sx, + sx.StartsWith("0x") ? NumberStyles.AllowHexSpecifier : 0, + null, out ix) || + !long.TryParse(sy.StartsWith("0x") ? sy.Substring(2) : sy, + sy.StartsWith("0x") ? NumberStyles.AllowHexSpecifier : 0, + null, out iy)) + { + cx = x.SubItems[column].Text; + cy = y.SubItems[column].Text; + } + else + { + cx = ix; + cy = iy; + } + + return cx.CompareTo(cy); + } + } + + private ListView _list; + private bool _virtualMode = false; + private RetrieveVirtualItemEventHandler _retrieveVirtualItem; + private bool _triState = false; + private ISortedListViewComparer _comparer; + private ISortedListViewComparer _triStateComparer; + private int _sortColumn; + private SortOrder _sortOrder; + private Dictionary> _customSorters = + new Dictionary>(); + private List _columnSortOrder = new List(); + + /// + /// Creates a new sorted list manager. + /// + /// The ListView to manage. + public SortedListViewComparer(ListView list) + { + _list = list; + _list.ColumnClick += new ColumnClickEventHandler(list_ColumnClick); + _sortColumn = 0; + _sortOrder = SortOrder.Ascending; + _comparer = new DefaultComparer(this); + this.SetSortIcon(); + } + + /// + /// Specifies whether the ListView is using VirtualMode. If true, + /// the SortedListComparer will not automatically sort the ListView. + /// + public bool VirtualMode + { + get { return _virtualMode; } + set { _virtualMode = value; } + } + + public RetrieveVirtualItemEventHandler RetrieveVirtualItem + { + get { return _retrieveVirtualItem; } + set { _retrieveVirtualItem = value; } + } + + /// + /// Allows three states of sorting: Ascending, Descending and None. + /// You must specify the sorter used for the None state using + /// TriStateComparer. + /// + public bool TriState + { + get { return _triState; } + set { _triState = value; } + } + + /// + /// The comparer to use when sorting. This is optional because a + /// default comparer will be provided. + /// + public ISortedListViewComparer Comparer + { + get { return _comparer; } + set + { + if (value == null) + _comparer = new DefaultComparer(this); + else + _comparer = value; + } + } + + /// + /// Specifies the sorter used for the None sorting state. + /// + public ISortedListViewComparer TriStateComparer + { + get { return _triStateComparer; } + set { _triStateComparer = value; } + } + + public ListView ListView + { + get { return _list; } + } + + /// + /// Specifies the index of the column to sort. + /// + public int SortColumn + { + get { return _sortColumn; } + set + { + _sortColumn = value; + this.SetSortIcon(); + } + } + + /// + /// Specifies the sort order/state. + /// + public SortOrder SortOrder + { + get { return _sortOrder; } + set + { + _sortOrder = value; + this.SetSortIcon(); + } + } + + /// + /// Allows custom sorting for individual columns. + /// + public IDictionary> CustomSorters + { + get { return _customSorters; } + } + + public IList ColumnSortOrder + { + get { return _columnSortOrder; } + } + + private void list_ColumnClick(object sender, ColumnClickEventArgs e) + { + if (e.Column == _sortColumn) + { + if (_triState) + { + if (_sortOrder == SortOrder.Ascending) + _sortOrder = SortOrder.Descending; + else if (_sortOrder == SortOrder.Descending) + _sortOrder = SortOrder.None; + else + _sortOrder = SortOrder.Ascending; + } + else + { + _sortOrder = _sortOrder == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; + } + } + else + { + _sortColumn = e.Column; + _sortOrder = SortOrder.Ascending; + } + + this.SetSortIcon(); + + if (!_virtualMode) + _list.Sort(); + } + + private void SetSortIcon() + { + // Avoid forcing handle creation before all other initialization + // has finished. This is done by handling the Layout event and + // performing the icon setting there. + _list.DoDelayed((control) => _list.Columns[_sortColumn].SetSortIcon(_sortOrder)); + } + + private ListViewItem GetItem(int index) + { + if (_virtualMode) + { + var args = new RetrieveVirtualItemEventArgs(index); + _retrieveVirtualItem(this, args); + return args.Item; + } + else + { + return _list.Items[index]; + } + } + + private int ModifySort(int result, SortOrder order) + { + if (order == SortOrder.Ascending) + return result; + else if (order == SortOrder.Descending) + return -result; + else + return result; + } + + private int Compare(ListViewItem x, ListViewItem y, int column) + { + int result = 0; + + if (_triState && _sortOrder == SortOrder.None) + result = _triStateComparer.Compare(x, y, column); + + if (result != 0) + return result; + + if (_customSorters.ContainsKey(column)) + result = ModifySort(_customSorters[column](x, y), _sortOrder); + + if (result != 0) + return result; + + return ModifySort(_comparer.Compare(x, y, column), _sortOrder); + } + + public int Compare(ListViewItem x, ListViewItem y) + { + int result = this.Compare(x, y, _sortColumn); + + if (result != 0) + return result; + + foreach (int column in _columnSortOrder) + { + if (column == _sortColumn) + continue; + + result = this.Compare(x, y, column); + + if (result != 0) + return result; + } + + return 0; + } + + /// + /// Compares two ListView objects. + /// + /// The first ListView. + /// The second ListView. + /// A comparison result. + public int Compare(object x, object y) + { + return this.Compare(x as ListViewItem, y as ListViewItem); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/Utils.cs b/branches/ph-plugins/ProcessHacker.Common/Utils.cs new file mode 100644 index 000000000..8776faeee --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/Utils.cs @@ -0,0 +1,1439 @@ +/* + * Process Hacker - + * misc. functions + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Text; +using System.Windows.Forms; + +namespace ProcessHacker.Common +{ + /// + /// Provides methods for manipulating various types of data. + /// + public static class Utils + { + public enum Endianness + { + Little, Big + } + + #region Constants + + public static int[] Primes = + { + 3, 7, 11, 0x11, 0x17, 0x1d, 0x25, 0x2f, 0x3b, 0x47, 0x59, 0x6b, 0x83, 0xa3, 0xc5, 0xef, + 0x125, 0x161, 0x1af, 0x209, 0x277, 0x2f9, 0x397, 0x44f, 0x52f, 0x63d, 0x78b, 0x91d, 0xaf1, + 0xd2b, 0xfd1, 0x12fd, 0x16cf, 0x1b65, 0x20e3, 0x2777, 0x2f6f, 0x38ff, 0x446f, 0x521f, 0x628d, + 0x7655, 0x8e01, 0xaa6b, 0xcc89, 0xf583, 0x126a7, 0x1619b, 0x1a857, 0x1fd3b, 0x26315, 0x2dd67, + 0x3701b, 0x42023, 0x4f361, 0x5f0ed, 0x72125, 0x88e31, 0xa443b, 0xc51eb, 0xec8c1, 0x11bdbf, + 0x154a3f, 0x198c4f, 0x1ea867, 0x24ca19, 0x2c25c1, 0x34fa1b, 0x3f928f, 0x4c4987, 0x5b8b6f, 0x6dda89 + }; + + public static string[] SizeUnitNames = { "B", "kB", "MB", "GB", "TB", "PB", "EB" }; + + #endregion + + /// + /// The maximum unit specifier to use when formatting sizes. + /// + public static int UnitSpecifier = 4; + + /// + /// Flattens an array of arrays into a single array. + /// + /// The type of each element in the arrays. + /// + /// An array of arrays. If an array in the array is null, it will be ignored. + /// + /// An array containing elements from each array. + public static T[] Concat(params T[][] ap) + { + int tl = 0; + + foreach (var array in ap) + if (array != null) + tl += array.Length; + + T[] na = new T[tl]; + int i = 0; + + foreach (var array in ap) + { + if (array != null) + { + Array.Copy(array, 0, na, i, array.Length); + i += array.Length; + } + } + + return na; + } + + /// + /// Determines whether the specified value is contained + /// within an array. + /// + /// The type of the array. + /// The array to search. + /// The value to search for. + /// True if the array contains the value, otherwise false. + public static bool Contains(this T[] array, T value) + { + return Array.IndexOf(array, value) != -1; + } + + /// + /// Counts the number of bits in the specified number. + /// + /// The number to process. + /// The number of bits in the specified number. + public static int CountBits(this int value) + { + int count = 0; + + while (value != 0) + { + count++; + value &= value - 1; + } + + return count; + } + + /// + /// Counts the number of bits in the specified number. + /// + /// The number to process. + /// The number of bits in the specified number. + public static int CountBits(this long value) + { + int count = 0; + + while (value != 0) + { + count++; + value &= value - 1; + } + + return count; + } + + /// + /// Creates an array of bytes from the specified byte pointer. + /// + /// A pointer to an array of bytes. + /// The length of the array. + /// A new byte array. + public unsafe static byte[] Create(byte* ptr, int length) + { + byte[] array = new byte[length]; + + for (int i = 0; i < length; i++) + array[i] = ptr[i]; + + return array; + } + + /// + /// Adds an ellipsis to a string if it is longer than the specified length. + /// + /// The string. + /// The maximum length. + /// The modified string. + public static string CreateEllipsis(string s, int len) + { + if (s.Length <= len) + return s; + else + return s.Substring(0, len - 4) + " ..."; + } + + /// + /// Creates a string containing random uppercase characters. + /// + /// The number of characters to generate. + /// The generated string. + public static string CreateRandomString(int length) + { + Random r = new Random((int)(DateTime.Now.ToFileTime() & 0xffffffff)); + StringBuilder sb = new StringBuilder(length); + + for (int i = 0; i < length; i++) + sb.Append((char)('A' + r.Next(25))); + + return sb.ToString(); + } + + /// + /// Clears and cleans up resources held by the menu items. + /// + public static void DisposeAndClear(this Menu.MenuItemCollection items) + { + //foreach (MenuItem item in items) + //{ + // item.Dispose(); + //} + + items.Clear(); + } + + /// + /// Disables the menu items contained in the specified menu. + /// + /// The menu. + public static void DisableAllMenuItems(Menu menu) + { + foreach (MenuItem item in menu.MenuItems) + item.Enabled = false; + } + + /// + /// Disables all menu items. + /// + public static void DisableAll(this Menu menu) + { + DisableAllMenuItems(menu); + } + + /// + /// Performs a divide operation, rounding up. + /// + /// + /// The positive number to divide. The result is undefined if the dividend + /// is negative or zero. + /// + /// + /// The positive number to divide by. The result is undefined if the divisor + /// is negative or zero. + /// + /// A rounded-up quotient. + public static int DivideUp(int dividend, int divisor) + { + return (dividend - 1) / divisor + 1; + } + + /// + /// Performs an action on a control after its handle has been created. + /// If the control's handle has already been created, the action is + /// executed immediately. + /// + /// The control is execute the action on. + /// The action to execute. + public static void DoDelayed(this Control control, Action action) + { + if (control.IsHandleCreated) + { + action(control); + } + else + { + LayoutEventHandler handler = null; + + handler = (sender, e) => + { + if (control.IsHandleCreated) + { + control.Layout -= handler; + action(control); + } + }; + + control.Layout += handler; + } + } + + /// + /// Duplicates the specified array. + /// + /// The type of array to duplicate. + /// The array to duplicate. + /// A copy of the specified array. + public static T[] Duplicate(this T[] array) + { + T[] newArray = new T[array.Length]; + + array.CopyTo(newArray, 0); + + return newArray; + } + + /// + /// Enables the menu items contained in the specified menu. + /// + /// The menu. + public static void EnableAllMenuItems(Menu menu) + { + foreach (MenuItem item in menu.MenuItems) + item.Enabled = true; + } + + /// + /// Enables all menu items. + /// + public static void EnableAll(this Menu menu) + { + EnableAllMenuItems(menu); + } + + /// + /// Compares two arrays and determines whether they are equal. + /// + /// The type of each element in the arrays. + /// The first array. + /// The second array. + /// Whether the two arrays are considered to be equal. + public static bool Equals(this T[] array, T[] other) + { + return Equals(array, other, 0); + } + + /// + /// Compares two arrays and determines whether they are equal. + /// + /// The type of each element in the arrays. + /// The first array. + /// The second array. + /// The index from which to begin comparing. + /// Whether the two arrays are considered to be equal. + public static bool Equals(this T[] array, T[] other, int startIndex) + { + return Equals(array, other, startIndex, array.Length); + } + + /// + /// Compares two arrays and determines whether they are equal. + /// + /// The type of each element in the arrays. + /// The first array. + /// The second array. + /// The index from which to begin comparing. + /// The number of elements to compare. + /// Whether the two arrays are considered to be equal. + public static bool Equals(this T[] array, T[] other, int startIndex, int length) + { + for (int i = startIndex; i < startIndex + length; i++) + if (!array[i].Equals(other[i])) + return false; + + return true; + } + + /// + /// Escapes a string using C-style escaping. + /// + /// The string to escape. + /// The escaped string. + public static string Escape(this string str) + { + str = str.Replace("\\", "\\\\"); + str = str.Replace("\"", "\\\""); + + return str; + } + + public static void Fill(this T[] array, T value) + { + for (int i = 0; i < array.Length; i++) + array[i] = value; + } + + /// + /// Fills a combobox with enum value names. + /// + /// The combobox to modify. + /// The type of the enum. + public static void Fill(this ComboBox box, Type t) + { + foreach (string s in Enum.GetNames(t)) + box.Items.Add(s); + } + + /// + /// Moves the specified rectangle to fit inside the working area + /// of the display containing the specified control. + /// + /// The rectangle to process. + /// The control from which to get the display. + /// A new rectangle with its location modified. + public static Rectangle FitRectangle(Rectangle rect, Control c) + { + return FitRectangle(rect, Screen.GetWorkingArea(c)); + } + + /// + /// Moves the specified rectangle to fit inside the specified bounds. + /// + /// The rectangle to process. + /// The bounds in which the rectangle should be. + /// A new rectangle with its location modified. + public static Rectangle FitRectangle(Rectangle rect, Rectangle bounds) + { + if (rect.X < bounds.Left) + rect.X = bounds.Left; + if (rect.Y < bounds.Top) + rect.Y = bounds.Top; + if (rect.X + rect.Width > bounds.Width) + rect.X = bounds.Width - rect.Width; + if (rect.Y + rect.Height > bounds.Height) + rect.Y = bounds.Height - rect.Height; + + return rect; + } + + /// + /// Gets a string representation for an address. + /// + /// An address. + /// A string representation of the specified address. + public static string FormatAddress(int address) + { + return "0x" + address.ToString("x"); + } + + /// + /// Gets a string representation for an address. + /// + /// An address. + /// A string representation of the specified address. + public static string FormatAddress(uint address) + { + return "0x" + address.ToString("x"); + } + + /// + /// Gets a string representation for an address. + /// + /// An address. + /// A string representation of the specified address. + public static string FormatAddress(long address) + { + return "0x" + address.ToString("x"); + } + + /// + /// Gets a string representation for an address. + /// + /// An address. + /// A string representation of the specified address. + public static string FormatAddress(ulong address) + { + return "0x" + address.ToString("x"); + } + + /// + /// Gets a string representation for an address. + /// + /// An address. + /// A string representation of the specified address. + public static string FormatAddress(IntPtr address) + { + return "0x" + address.ToString("x"); + } + + public static string FormatFlags(Type e, long value) + { + string r = ""; + + for (int i = 0; i < 32; i++) + { + long fv = 1 << i; + + if ((value & fv) == fv) + { + r += Enum.GetName(e, fv) + ", "; + } + } + + if (r.EndsWith(", ")) + r = r.Remove(r.Length - 2, 2); + + return r; + } + + /// + /// Formats a object into a string representation. + /// + /// The to format. + /// + public static string FormatLongTimeSpan(TimeSpan time) + { + return String.Format( + "{0}{1:d2}:{2:d2}:{3:d2}", + time.Days != 0 ? (time.Days.ToString() + ".") : "", + time.Hours, + time.Minutes, + time.Seconds + ); + } + + /// + /// Gets the relative time in nice English. + /// + /// A DateTime. + /// A string. + public static string FormatRelativeDateTime(DateTime time) + { + // Get the time span from the time to now. + TimeSpan span = DateTime.Now.Subtract(time); + // The partial number of weeks. + double weeks = span.TotalDays / 7; + // The partial number of fortnights. + double fortnights = weeks / 2; + // ... + double months = span.TotalDays * 12 / 365; + double years = months / 12; + double centuries = years / 100; + string str = ""; + + // Start from the most general time unit and see if they can be used + // without any fractional component. + // x centur(y|ies) + if (centuries >= 1) + str = (int)centuries + " " + ((int)centuries == 1 ? "century" : "centuries"); + // x year(s) + else if (years >= 1) + str = (int)years + " " + ((int)years == 1 ? "year" : "years"); + // x month(s) + else if (months >= 1) + str = (int)months + " " + ((int)months == 1 ? "month" : "months"); + // x fortnight(s) + else if (fortnights >= 1) + str = (int)fortnights + " " + ((int)fortnights == 1 ? "fortnight" : "fortnights"); + // x week(s) + else if (weeks >= 1) + str = (int)weeks + " " + ((int)weeks == 1 ? "week" : "weeks"); + // x day(s) (and y hour(s)) + else if (span.TotalDays >= 1) + { + str = (int)span.TotalDays + " " + ((int)span.TotalDays == 1 ? "day" : "days"); + + if (span.Hours >= 1) + str += " and " + span.Hours + " " + + (span.Hours == 1 ? "hour" : "hours"); + } + // x hour(s) (and y minute(s)) + else if (span.Hours >= 1) + { + str = span.Hours + " " + (span.Hours == 1 ? "hour" : "hours"); + + if (span.Minutes >= 1) + str += " and " + span.Minutes + " " + + (span.Minutes == 1 ? "minute" : "minutes"); + } + // x minute(s) (and y second(s)) + else if (span.Minutes >= 1) + { + str = span.Minutes + " " + (span.Minutes == 1 ? "minute" : "minutes"); + + if (span.Seconds >= 1) + str += " and " + span.Seconds + " " + + (span.Seconds == 1 ? "second" : "seconds"); + } + // x second(s) + else if (span.Seconds >= 1) + str = span.Seconds + " " + (span.Seconds == 1 ? "second" : "seconds"); + // x millisecond(s) + else if (span.Milliseconds >= 1) + str = span.Milliseconds + " " + (span.Milliseconds == 1 ? "millisecond" : "milliseconds"); + else + str = "a very short time"; + + // Turn 1 into "a", e.g. 1 minute -> a minute + if (str.StartsWith("1 ")) + { + // Special vowel case: a hour -> an hour + if (str[2] != 'h') + str = "a " + str.Substring(2); + else + str = "an " + str.Substring(2); + } + + return str + " ago"; + } + + /// + /// Formats a size into a string representation, postfixing it with the correct unit. + /// + /// The size to format. + public static string FormatSize(int size) + { + return FormatSize((uint)size); + } + + /// + /// Formats a size into a string representation, postfixing it with the correct unit. + /// + /// The size to format. + public static string FormatSize(uint size) + { + int i = 0; + double s = (double)size; + + while (s > 1024 && i < SizeUnitNames.Length && i < UnitSpecifier) + { + s /= 1024; + i++; + } + + return (s == 0 ? "0" : s.ToString("#,#.##")) + " " + SizeUnitNames[i]; + } + + /// + /// Formats a size into a string representation, postfixing it with the correct unit. + /// + /// The size to format. + public static string FormatSize(IntPtr size) + { + unchecked + { + return FormatSize((ulong)size.ToInt64()); + } + } + + /// + /// Formats a size into a string representation, postfixing it with the correct unit. + /// + /// The size to format. + public static string FormatSize(long size) + { + return FormatSize((ulong)size); + } + + /// + /// Formats a size into a string representation, postfixing it with the correct unit. + /// + /// The size to format. + public static string FormatSize(ulong size) + { + int i = 0; + double s = (double)size; + + while (s > 1024 && i < SizeUnitNames.Length && i < UnitSpecifier) + { + s /= 1024; + i++; + } + + return (s == 0 ? "0" : s.ToString("#,#.##")) + " " + SizeUnitNames[i]; + } + + /// + /// Formats a object into a string representation. + /// + /// The to format. + /// + public static string FormatTimeSpan(TimeSpan time) + { + return String.Format("{0:d2}:{1:d2}:{2:d2}.{3:d3}", + time.Hours, + time.Minutes, + time.Seconds, + time.Milliseconds); + } + + // + // Gets a System.DateTime indicating the time the specified assembly was last built. + // This will attempt to calculate the time from the build number, if possible. + // Otherwise, the last write time of the assembly will be used. + // + // The assembly to get the build date for. + // True to always use the last write time of the assembly, otherwise false. + // The time this assembly was built. + public static DateTime GetAssemblyBuildDate(Assembly assembly, bool forceFileDate) + { + Version AssemblyVersion = assembly.GetName().Version; + DateTime dt; + + if (forceFileDate) + { + dt = GetAssemblyLastWriteTime(assembly); + } + else + { + dt = DateTime.Parse("01/01/2000").AddDays(AssemblyVersion.Build).AddSeconds(AssemblyVersion.Revision * 2); + if (TimeZone.IsDaylightSavingTime(dt, TimeZone.CurrentTimeZone.GetDaylightChanges(dt.Year))) + { + dt = dt.AddHours(1); + } + if (dt > DateTime.Now || AssemblyVersion.Build < 730 || AssemblyVersion.Revision == 0) + { + dt = GetAssemblyLastWriteTime(assembly); + } + } + + return dt; + } + + // + // Returns the last write time of the specified assembly. + // + // The last write time of the assembly, or DateTime.MaxValue if an exception occurred. + public static DateTime GetAssemblyLastWriteTime(Assembly assembly) + { + if (assembly.Location == null || assembly.Location == "") + return DateTime.MaxValue; + + try + { + return File.GetLastWriteTime(assembly.Location); + } + catch + { + return DateTime.MaxValue; + } + } + + public static byte[] GetBytes(this int n) + { + return n.GetBytes(Endianness.Little); + } + + public static byte[] GetBytes(this int n, Endianness type) + { + byte[] data = new byte[4]; + + if (type == Endianness.Little) + { + data[0] = (byte)(n & 0xff); + data[1] = (byte)((n >> 8) & 0xff); + data[2] = (byte)((n >> 16) & 0xff); + data[3] = (byte)((n >> 24) & 0xff); + } + else if (type == Endianness.Big) + { + data[0] = (byte)((n >> 24) & 0xff); + data[1] = (byte)((n >> 16) & 0xff); + data[2] = (byte)((n >> 8) & 0xff); + data[3] = (byte)(n & 0xff); + } + else + { + throw new ArgumentException(); + } + + return data; + } + + public static byte[] GetBytes(this uint n) + { + return n.GetBytes(Endianness.Little); + } + + public static byte[] GetBytes(this uint n, Endianness type) + { + byte[] data = new byte[4]; + + if (type == Endianness.Little) + { + data[0] = (byte)(n & 0xff); + data[1] = (byte)((n >> 8) & 0xff); + data[2] = (byte)((n >> 16) & 0xff); + data[3] = (byte)((n >> 24) & 0xff); + } + else if (type == Endianness.Big) + { + data[0] = (byte)((n >> 24) & 0xff); + data[1] = (byte)((n >> 16) & 0xff); + data[2] = (byte)((n >> 8) & 0xff); + data[3] = (byte)(n & 0xff); + } + else + { + throw new ArgumentException(); + } + + return data; + } + + public static byte[] GetBytes(this ushort n) + { + return n.GetBytes(Endianness.Little); + } + + public static byte[] GetBytes(this ushort n, Endianness type) + { + byte[] data = new byte[2]; + + if (type == Endianness.Little) + { + data[0] = (byte)(n & 0xff); + data[1] = (byte)((n >> 8) & 0xff); + } + else if (type == Endianness.Big) + { + data[0] = (byte)((n >> 8) & 0xff); + data[1] = (byte)(n & 0xff); + } + else + { + throw new ArgumentException(); + } + + return data; + } + + /// + /// Converts a 32-bit Unix time value into a DateTime object. + /// + /// The Unix time value. + public static DateTime GetDateTimeFromUnixTime(uint time) + { + return (new DateTime(1970, 1, 1, 0, 0, 0)).Add(new TimeSpan(0, 0, 0, (int)time)); + } + + public static int GetPrime(int minimum) + { + if (minimum < 0) + throw new ArgumentOutOfRangeException("minimum"); + + for (int i = 0; i < Primes.Length; i++) + { + if (Primes[i] >= minimum) + return Primes[i]; + } + + for (int i = minimum | 1; i < int.MaxValue; i += 2) + { + if (IsPrime(i)) + return i; + } + + return minimum; + } + + /// + /// Parses a string and produces a rectangle. + /// + /// + /// A string describing a rectangle in the following format: + /// x,y,width,height (with no spaces). + /// + /// A rectangle. + public static Rectangle GetRectangle(string s) + { + var split = s.Split(','); + + return new Rectangle(int.Parse(split[0]), int.Parse(split[1]), + int.Parse(split[2]), int.Parse(split[3])); + } + + /// + /// Returns a object of the specified thread ID. + /// + /// The process which the thread belongs to. + /// The ID of the thread. + /// + public static System.Diagnostics.ProcessThread GetThreadFromId(System.Diagnostics.Process p, int id) + { + foreach (System.Diagnostics.ProcessThread t in p.Threads) + if (t.Id == id) + return t; + + return null; + } + + /// + /// Determines whether the array is empty (all 0's). + /// + /// The array to search. + /// True if the array is empty; otherwise false. + public static bool IsEmpty(this byte[] array) + { + foreach (byte b in array) + { + if (b != 0) + return false; + } + + return true; + } + + public static bool IsPrime(this int number) + { + int x; + + // Is the number even? + if ((number & 1) == 0) + return number == 2; + + x = (int)Math.Sqrt(number); + + for (int i = 3; i <= x; i += 2) + { + if ((number % i) == 0) + return false; + } + + return true; + } + + /// + /// Makes a character printable by converting unprintable characters to a dot ('.'). + /// + /// The character to convert. + /// + public static char MakePrintable(char c) + { + if (c >= ' ' && c <= '~') + return c; + else + return '.'; + } + + /// + /// Makes a string printable by converting unprintable characters to a dot ('.'). + /// + /// The string to convert. + /// + public static string MakePrintable(string s) + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < s.Length; i++) + sb.Append(MakePrintable(s[i])); + + return sb.ToString(); + } + + /// + /// Determines whether a string matches according to a wildcard expression. + /// + /// The wildcard expression. + /// The string to match. + /// Whether the string matches. + public static bool MatchWildcards(string pattern, string text) + { + return MatchWildcards(pattern, 0, text, 0); + } + + private static bool MatchWildcards(string pattern, int patternStart, string text, int textStart) + { + // Note: this algorithm is currently recursive for easy understanding. + // It should be re-implemented without recursion... + + int patternIndex = patternStart; + int textIndex = textStart; + + // If we have a zero-length pattern, the string matches. + if (pattern.Length == 0 || patternIndex >= pattern.Length) + return true; + // If we have a zero-length string, the string doesn't match. + if (text.Length == 0 || textIndex >= text.Length) + return false; + + // Match up to the first asterisk (or maybe a number of them). + + while (true) + { + // Did we reach the end of the pattern? If so, check if we + // have also reached the end of the text. + if (patternIndex >= pattern.Length) + return textIndex >= text.Length; + + if (pattern[patternIndex] == '*') + { + patternIndex++; + + // Skip duplicate asterisks. + while (patternIndex < pattern.Length) + { + if (pattern[patternIndex] != '*') + break; + + patternIndex++; + } + + break; + } + + // Did we reach the end of the text? If so, the match fails. + if (textIndex >= text.Length) + return false; + + if (pattern[patternIndex] != text[textIndex] && pattern[patternIndex] != '?') + return false; + + patternIndex++; + textIndex++; + } + + // We reached an asterisk (otherwise we would have returned by now). + // Keep incrementing the text index until we get a match. + + // Shortcut: if we are at the end of the pattern, it means the pattern + // has trailing asterisk(s). The string matches. + if (patternIndex >= pattern.Length) + return true; + + while (textIndex < text.Length) + { + if (MatchWildcards(pattern, patternIndex, text, textIndex)) + return true; + + textIndex++; + } + + return false; + } + + public static Dictionary ParseCommandLine(string[] args) + { + Dictionary dict = new Dictionary(); + string argPending = null; + + foreach (string s in args) + { + if (s.StartsWith("-")) + { + if (dict.ContainsKey(s)) + throw new ArgumentException("Option already specified."); + + dict.Add(s, ""); + argPending = s; + } + else + { + if (argPending != null) + { + dict[argPending] = s; + argPending = null; + } + else + { + if (!dict.ContainsKey("")) + dict.Add("", s); + } + } + } + + return dict; + } + + public static int ReadInt32(Stream s, Endianness type) + { + byte[] buffer = new byte[4]; + + if (s.Read(buffer, 0, 4) == 0) + throw new EndOfStreamException(); + + return ToInt32(buffer, type); + } + + /// + /// Reads a null-terminated string from a stream. + /// + /// The stream to read from. + /// The read string. + public static string ReadString(Stream s) + { + StringBuilder str = new StringBuilder(); + + while (true) + { + int b = s.ReadByte(); + + if (b == 0 || b == -1) + break; + + str.Append((char)(byte)b); + } + + return str.ToString(); + } + + public static string ReadString(Stream s, int length) + { + byte[] buffer = new byte[length]; + + if (s.Read(buffer, 0, length) == 0) + throw new EndOfStreamException(); + + return System.Text.ASCIIEncoding.ASCII.GetString(buffer); + } + + public static uint ReadUInt32(Stream s, Endianness type) + { + byte[] buffer = new byte[4]; + + if (s.Read(buffer, 0, 4) == 0) + throw new EndOfStreamException(); + + return ToUInt32(buffer, type); + } + + /// + /// Reads a null-terminated Unicode string from a stream. + /// + /// The stream to read from. + /// The read string. + public static string ReadUnicodeString(Stream s) + { + StringBuilder str = new StringBuilder(); + + while (true) + { + int b = s.ReadByte(); + + if (b == -1) + break; + + int b2 = s.ReadByte(); + + if (b2 == -1) + break; + + if (b == 0 && b2 == 0) + break; + + str.Append(UnicodeEncoding.Unicode.GetChars(new byte[] { (byte)b, (byte)b2 })); + } + + return str.ToString(); + } + + /// + /// Reads a Unicode string from a stream. + /// + /// The stream to read from. + /// The length, in bytes, of the string. + /// The read string. + public static string ReadUnicodeString(Stream s, int length) + { + StringBuilder str = new StringBuilder(); + int i = 0; + + while (i < length) + { + int b = s.ReadByte(); + + if (b == -1) + break; + + int b2 = s.ReadByte(); + + if (b2 == -1) + break; + + str.Append(UnicodeEncoding.Unicode.GetChars(new byte[] { (byte)b, (byte)b2 })); + i += 2; + } + + return str.ToString(); + } + + /// + /// Swaps the order of the bytes. + /// + /// The number to change. + /// A number. + public static int Reverse(this int v) + { + byte b1 = (byte)v; + byte b2 = (byte)(v >> 8); + byte b3 = (byte)(v >> 16); + byte b4 = (byte)(v >> 24); + + return b4 | (b3 << 8) | (b2 << 16) | (b1 << 24); + } + + /// + /// Swaps the order of the bytes. + /// + /// The number to change. + /// A number. + public static uint Reverse(this uint v) + { + uint b0 = v & 0xff; + uint b1 = (v >> 8) & 0xff; + uint b2 = (v >> 16) & 0xff; + uint b3 = (v >> 24) & 0xff; + + b0 <<= 24; + b1 <<= 16; + b2 <<= 8; + + return b0 | b1 | b2 | b3; + } + + /// + /// Swaps the order of the bytes. + /// + /// The number to change. + /// A number. + public static ushort Reverse(this ushort v) + { + byte b1 = (byte)v; + byte b2 = (byte)(v >> 8); + + return (ushort)(b2 | (b1 << 8)); + } + + /// + /// Reverses an array. + /// + /// The array to reverse. + /// A new array. + public static T[] Reverse(this T[] data) + { + T[] newData = new T[data.Length]; + + for (int i = 0; i < data.Length; i++) + newData[i] = data[data.Length - i - 1]; + + return newData; + } + + /// + /// Selects all of the specified items. + /// + /// The items. + public static void SelectAll(this ListView.ListViewItemCollection items) + { + foreach (ListViewItem item in items) + item.Selected = true; + } + + /// + /// Selects all of the items in the specified ListView. + /// + /// The ListView to process. + public static void SelectAll(this ListView items) + { + for (int i = 0; i < items.VirtualListSize; i++) + if (!items.SelectedIndices.Contains(i)) + items.SelectedIndices.Add(i); + } + + /// + /// Enables or disables double buffering for a control. + /// + /// The control. + /// The type of the control. + /// The new setting. + public static void SetDoubleBuffered(this Control c, Type t, bool value) + { + PropertyInfo property = t.GetProperty("DoubleBuffered", + BindingFlags.NonPublic | BindingFlags.Instance); + + property.SetValue(c, value, null); + } + + /// + /// Enables or disables double buffering for a control. + /// + /// The control to set the property on. + /// The new value. + public static void SetDoubleBuffered(this Control c, bool value) + { + c.SetDoubleBuffered(c.GetType(), value); + } + + /// + /// Shows a file in Windows Explorer. + /// + /// The file to show. + public static void ShowFileInExplorer(string fileName) + { + System.Diagnostics.Process.Start("explorer.exe", "/select," + fileName); + } + + /// + /// Calculates the size of a structure. + /// + /// The structure type. + /// The size of the structure. + public static int SizeOf() + { + return System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); + } + + /// + /// Calculates the size of a structure. + /// + /// The structure type. + /// A power-of-two whole-structure alignment to apply. + /// The size of the structure. + public static int SizeOf(int alignment) + { + // HACK: This is wrong, but it works. + return SizeOf() + alignment; + } + + /// + /// Returns a sorted list of the names in a given enum type. + /// + /// The enum type to process. + /// A list of key-value pairs, sorted based on the number of bits in the value. + public static List> SortFlagNames(Type enumType) + { + List> nameList = new List>(); + + foreach (string name in Enum.GetNames(enumType)) + { + long nameLong = Convert.ToInt64(Enum.Parse(enumType, name)); + + nameList.Add(new KeyValuePair(name, nameLong)); + } + + nameList.Sort((kvp1, kvp2) => kvp2.Value.CountBits().CompareTo(kvp1.Value.CountBits())); + + return nameList; + } + + public static int ToInt32(this byte[] data) + { + return data.ToInt32(Endianness.Little); + } + + public static int ToInt32(this byte[] data, Endianness type) + { + if (type == Endianness.Little) + { + return (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + } + else if (type == Endianness.Big) + { + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); + } + else + { + throw new ArgumentException(); + } + } + + public static long ToInt64(this byte[] data) + { + return data.ToInt64(Endianness.Little); + } + + public static long ToInt64(this byte[] data, Endianness type) + { + if (type == Endianness.Little) + { + return (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24) | + (data[4] << 32) | (data[5] << 40) | (data[6] << 48) | (data[7] << 56); + } + else if (type == Endianness.Big) + { + return (data[0] << 56) | (data[1] << 48) | (data[2] << 40) | (data[3] << 32) | + (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | (data[7]); + } + else + { + throw new ArgumentException(); + } + } + + public static IntPtr ToIntPtr(this byte[] data) + { + if (IntPtr.Size != data.Length) + throw new ArgumentException("data"); + + if (IntPtr.Size == sizeof(int)) + return new IntPtr(data.ToInt32(Endianness.Little)); + else if (IntPtr.Size == sizeof(long)) + return new IntPtr(data.ToInt64(Endianness.Little)); + else + throw new ArgumentException("data"); + } + + public static ushort ToUInt16(this byte[] data, Endianness type) + { + return ToUInt16(data, 0, type); + } + + public static ushort ToUInt16(this byte[] data, int offset, Endianness type) + { + if (type == Endianness.Little) + { + return (ushort)(data[offset] | (data[offset + 1] << 8)); + } + else if (type == Endianness.Big) + { + return (ushort)((data[offset] << 8) | data[offset + 1]); + } + else + { + throw new ArgumentException(); + } + } + + public static uint ToUInt32(this byte[] data, Endianness type) + { + return ToUInt32(data, 0, type); + } + + public static uint ToUInt32(this byte[] data, int offset, Endianness type) + { + if (type == Endianness.Little) + { + return (uint)(data[offset]) | (uint)(data[offset + 1] << 8) | + (uint)(data[offset + 2] << 16) | (uint)(data[offset + 3] << 24); + } + else if (type == Endianness.Big) + { + return (uint)(data[offset] << 24) | (uint)(data[offset + 1] << 16) | + (uint)(data[offset + 2] << 8) | (uint)(data[offset + 3]); + } + else + { + throw new ArgumentException(); + } + } + + public static void ValidateBuffer(byte[] buffer, int offset, int length) + { + ValidateBuffer(buffer, offset, length, false); + } + + public static void ValidateBuffer(byte[] buffer, int offset, int length, bool canBeNull) + { + // Make sure the offset isn't negative. + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + + // Make sure the length isn't negative. + if (length < 0) + throw new ArgumentOutOfRangeException("length"); + + // Make sure we won't overrun the buffer. + if (buffer != null) + { + if (buffer.Length - offset < length) + throw new ArgumentOutOfRangeException("The buffer is too small for the specified offset and length."); + } + else + { + if (!canBeNull) + throw new ArgumentException("The buffer cannot be null."); + + // We don't have a buffer, so make sure the offset and length are zero. + if (offset != 0 || length != 0) + throw new ArgumentOutOfRangeException("The offset and length must be zero for a null buffer."); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/WeakReference.cs b/branches/ph-plugins/ProcessHacker.Common/WeakReference.cs new file mode 100644 index 000000000..a7d0bece0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/WeakReference.cs @@ -0,0 +1,39 @@ +using System; + +namespace ProcessHacker.Common +{ + public class WeakReference + where T : class + { + public static implicit operator T(WeakReference reference) + { + return reference.Target; + } + + private WeakReference _weakReference; + + public WeakReference(T obj) + : this(obj, false) + { } + + public WeakReference(T obj, bool trackResurrection) + { + _weakReference = new WeakReference(obj, trackResurrection); + } + + public bool Alive + { + get { return _weakReference.IsAlive; } + } + + public bool TrackResurrection + { + get { return _weakReference.TrackResurrection; } + } + + public T Target + { + get { return _weakReference.Target as T; } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/WorkQueue.cs b/branches/ph-plugins/ProcessHacker.Common/WorkQueue.cs new file mode 100644 index 000000000..7843a1071 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/WorkQueue.cs @@ -0,0 +1,598 @@ +/* + * Process Hacker - + * thread pool/work queue + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ProcessHacker.Common +{ + /// + /// Manages a work queue which is executed by worker threads. + /// + public sealed class WorkQueue + { + /// + /// Represents a work item to be executed on a worker thread. + /// + public sealed class WorkItem + { + private WorkQueue _owner; + private string _tag; + private Delegate _work; + private object[] _args; + private bool _enabled = true; + private bool _completed = false; + private object _completedEventLock = new object(); + private ManualResetEvent _completedEvent; + private object _result; + private Exception _exception; + + internal WorkItem(WorkQueue owner, Delegate work, object[] args) + : this(owner, work, args, null) + { } + + internal WorkItem(WorkQueue owner, Delegate work, object[] args, string tag) + { + _owner = owner; + _work = work; + _args = args; + _tag = tag; + } + + public Delegate Work + { + get { return _work; } + } + + public object[] Arguments + { + get { return _args; } + } + + /// + /// The tag associated with the work item. + /// + public string Tag + { + get { return _tag; } + } + + /// + /// Whether the work item is to be executed. + /// + internal bool Enabled + { + get { return _enabled; } + set { _enabled = value; } + } + + /// + /// Whether the work item has been completed. + /// + public bool Completed + { + get { return _completed; } + } + + /// + /// The value returned by the target method. + /// + public object Result + { + get { return _result; } + } + + /// + /// The exception thrown by the work item target, if any. + /// + public Exception Exception + { + get { return _exception; } + } + + /// + /// If the work item has not been executed yet, prevents the + /// work item from executing. Otherwise, takes no action. + /// + /// True if the work item has not been executed yet; otherwise false. + public bool Abort() + { + return _owner.RemoveQueuedWorkItem(this); + } + + /// + /// Waits for the work item to complete and returns the result. + /// + /// The value returned by the target method. + public object GetResult() + { + this.WaitOne(); + return _result; + } + + /// + /// Performs the work. + /// + internal void PerformWork() + { + if (!_enabled) + return; + + try + { + if (_args == null) + _result = _work.Method.Invoke(_work.Target, null); + else + _result = _work.Method.Invoke(_work.Target, _args.Length != 0 ? _args : null); + } + catch (Exception ex) + { + _exception = ex; + } + + _completed = true; + + lock (_completedEventLock) + { + if (_completedEvent != null) + _completedEvent.Set(); + } + } + + /// + /// Waits for the work item to be completed. + /// + /// Always returns true. + public bool WaitOne() + { + return this.WaitOne(-1); + } + + /// + /// Waits for the work item to be completed. + /// + /// The timeout for the wait operation. + /// + /// True if the work item was completed within the timeout + /// (or was already completed); otherwise false. + /// + public bool WaitOne(int timeout) + { + lock (_completedEventLock) + { + if (_completed) + return true; + + if (_completedEvent == null) + _completedEvent = new ManualResetEvent(false); + } + + return _completedEvent.WaitOne(timeout, false); + } + } + + private static WorkQueue _globalWorkQueue = new WorkQueue(); + + /// + /// Gets the global work queue instance. + /// + public static WorkQueue GlobalWorkQueue + { + get { return _globalWorkQueue; } + } + + /// + /// Queues work for the global work queue. + /// + /// The work to be executed. + public static WorkItem GlobalQueueWorkItem(Delegate work) + { + return _globalWorkQueue.QueueWorkItem(work); + } + + /// + /// Queues work for the global work queue. + /// + /// The work to be executed. + /// The arguments to pass to the delegate. + public static WorkItem GlobalQueueWorkItem(Delegate work, params object[] args) + { + return _globalWorkQueue.QueueWorkItemTag(work, null, true, args); + } + + /// + /// Queues work for the global work queue. + /// + /// The work to be executed. + /// A tag for the work item. + public static WorkItem GlobalQueueWorkItemTag(Delegate work, string tag) + { + return _globalWorkQueue.QueueWorkItemTag(work, tag, true, null); + } + + /// + /// Queues work for the global work queue. + /// + /// The work to be executed. + /// A tag for the work item. + /// The arguments to pass to the delegate. + public static WorkItem GlobalQueueWorkItemTag(Delegate work, string tag, params object[] args) + { + return _globalWorkQueue.QueueWorkItemTag(work, tag, true, args); + } + + /// + /// The work queue. This object is used as a lock. + /// + private Queue _workQueue = new Queue(); + /// + /// The maximum number of worker threads. If there are less worker threads + /// than this limit, they will be created as necessary. If there are more + /// worker threads than this limit, they will terminate once they have + /// finished processing their current work items. + /// + private int _maxWorkerThreads = 1; + /// + /// The minimum number of worker threads. Worker threads will be created + /// as necessary and the number of worker threads will never drop below + /// this number. + /// + private int _minWorkerThreads = 0; + /// + /// The pool of worker threads. This object is used as a lock. + /// + private Dictionary _workerThreads = new Dictionary(); + /// + /// The number of worker threads which are currently running work. + /// + private int _busyCount = 0; + /// + /// A worker will block on the work-arrived event for this amount of time + /// before terminating. + /// + private int _noWorkTimeout = 1000; + /// + /// If true, prevents new work items from being queued. + /// + private volatile bool _isJoining = false; + + /// + /// Creates a new work queue. + /// + public WorkQueue() + { } + + /// + /// Gets the number of worker threads that are currently busy. + /// + public int BusyCount + { + get { return _busyCount; } + } + + /// + /// Gets or sets the maximum number of worker threads. + /// + public int MaxWorkerThreads + { + get { return _maxWorkerThreads; } + set { _maxWorkerThreads = value; } + } + + /// + /// Gets or sets the minimum number of worker threads. + /// + public int MinWorkerThreads + { + get { return _minWorkerThreads; } + set { _minWorkerThreads = value; } + } + + /// + /// Gets or sets the time, in milliseconds, after which a + /// worker thread with no work will terminate. Specify 0 so that + /// worker threads will terminate immediately, or specify -1 so that + /// worker threads will wait indefinitely for work. + /// + public int NoWorkTimeout + { + get { return _noWorkTimeout; } + set { _noWorkTimeout = value; } + } + + /// + /// Gets the number of queued work items. + /// + public int QueuedCount + { + get { return _workQueue.Count; } + } + + /// + /// Gets the number of worker threads that are alive. + /// + public int WorkerCount + { + get { return _workerThreads.Count; } + } + + /// + /// Creates worker threads if necessary to satisfy the + /// worker thread minimum. + /// + public void CreateMinimumWorkerThreads() + { + if (_workerThreads.Count < _minWorkerThreads) + { + lock (_workerThreads) + { + // Create worker threads until we have enough. + while (_workerThreads.Count < _minWorkerThreads) + this.CreateWorkerThread(); + } + } + } + + /// + /// Creates a worker thread. + /// + private void CreateWorkerThread() + { + Thread workThread = new Thread(this.WorkerThreadStart); + workThread.IsBackground = true; + workThread.Priority = ThreadPriority.Lowest; + workThread.SetApartmentState(ApartmentState.STA); + _workerThreads.Add(workThread.ManagedThreadId, workThread); + workThread.Start(); + } + + /// + /// Destroys the current worker thread. + /// + private void DestroyWorkerThread() + { + _workerThreads.Remove(Thread.CurrentThread.ManagedThreadId); + } + + /// + /// Gets the work items in the queue. + /// + /// An array of WorkItem objects. + public WorkItem[] GetQueuedWorkItems() + { + lock (_workQueue) + return _workQueue.ToArray(); + } + + /// + /// Waits for all work items to complete and prevents new work items from being queued. + /// + public void JoinAll() + { + _isJoining = true; + + // Check for work items. + while (_workQueue.Count > 0) + { + WorkItem workItem = null; + + // Lock and re-check. + lock (_workQueue) + { + if (_workQueue.Count > 0) + workItem = _workQueue.Peek(); + else + continue; + } + + // Wait for this work item to finish. + workItem.WaitOne(); + } + } + + /// + /// Removes the work item from the work queue. + /// + /// The work item to remove + /// If the work item was in the work queue, true. Otherwise, false. + public bool RemoveQueuedWorkItem(WorkItem workItem) + { + // Lock the work queue to prevent data corruption. + lock (_workQueue) + { + // Check if the work queue (still) contains the work item. + if (_workQueue.Contains(workItem)) + { + // The work item is in the queue. Prevent it from executing. + workItem.Enabled = false; + return true; + } + else + { + // The work item is no longer in the queue. + return false; + } + } + } + + /// + /// Allows new work items to be queued. + /// + public void ResetJoin() + { + _isJoining = false; + } + + /// + /// Queues work for the worker thread(s). + /// + /// The work to be performed. + public WorkItem QueueWorkItem(Delegate work) + { + return this.QueueWorkItemTag(work, null, true, null); + } + + /// + /// Queues work for the worker thread(s). + /// + /// The work to be performed. + /// The arguments to pass to the delegate. + public WorkItem QueueWorkItem(Delegate work, params object[] args) + { + return this.QueueWorkItemTag(work, null, true, args); + } + + /// + /// Queues work for the worker thread(s). + /// + /// The work to be performed. + /// A tag for the work item. + public WorkItem QueueWorkItemTag(Delegate work, string tag) + { + return this.QueueWorkItemTag(work, tag, true, null); + } + + /// + /// Queues work for the worker thread(s). + /// + /// The work to be performed. + /// A tag for the work item. + /// The arguments to pass to the delegate. + public WorkItem QueueWorkItemTag(Delegate work, string tag, params object[] args) + { + return this.QueueWorkItemTag(work, tag, true, args); + } + + /// + /// Queues work for the worker thread(s). + /// + /// The work to be performed. + /// A tag for the work item. + /// Ignored. + /// The arguments to pass to the delegate. + public WorkItem QueueWorkItemTag(Delegate work, string tag, bool isArray, object[] args) + { + WorkItem workItem; + + // Can't queue any work items if joining. + if (_isJoining) + return null; + + lock (_workQueue) + { + _workQueue.Enqueue(workItem = new WorkItem(this, work, args, tag)); + Monitor.Pulse(_workQueue); + } + + // Check if all worker threads are currently busy. + if (Thread.VolatileRead(ref _busyCount) == _workerThreads.Count) + { + // Check if we still have available worker threads + if (_workerThreads.Count < _maxWorkerThreads) + { + // We do, so we must lock and re-check. + lock (_workerThreads) + { + if (_workerThreads.Count < _maxWorkerThreads) + { + this.CreateWorkerThread(); + } + } + } + } + + return workItem; + } + + /// + /// The entry point for all worker threads. + /// + private void WorkerThreadStart() + { + while (true) + { + // Check if we have more worker threads than the limit. + if (_workerThreads.Count > _maxWorkerThreads) + { + // Lock and re-check. + lock (_workerThreads) + { + // Check the minimum as well. + if (_workerThreads.Count > _maxWorkerThreads && + _workerThreads.Count > _minWorkerThreads) + { + // We have an excess amount of worker threads. + this.DestroyWorkerThread(); + return; + } + } + } + + // Check for work. + if (_workQueue.Count > 0) + { + WorkItem workItem = null; + + // There is work, but we must lock and re-check. + lock (_workQueue) + { + if (_workQueue.Count > 0) + workItem = _workQueue.Dequeue(); + else + continue; + } + + Interlocked.Increment(ref _busyCount); + workItem.PerformWork(); + Interlocked.Decrement(ref _busyCount); + } + else + { + // No work available. Wait for work. + bool workArrived = false; + + lock (_workQueue) + workArrived = Monitor.Wait(_workQueue, _noWorkTimeout); + + if (workArrived) + { + // Work arrived. Go back so we can perform it. + continue; + } + else + { + // No work arrived during the timeout period. Delete the thread. + lock (_workerThreads) + { + // Check the minimum. + if (_workerThreads.Count > _minWorkerThreads) + this.DestroyWorkerThread(); + } + + return; + } + } + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Common/app.config b/branches/ph-plugins/ProcessHacker.Common/app.config new file mode 100644 index 000000000..b7db28170 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Common/app.config @@ -0,0 +1,3 @@ + + + diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Enums.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Enums.cs new file mode 100644 index 000000000..8e1a6e484 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Enums.cs @@ -0,0 +1,1072 @@ +/* + * Process Hacker - + * windows API enums + * + * Copyright (C) 2009 Uday Shanbhag + * Copyright (C) 2009 Dean + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains enumeration declarations for the Win32 API. + * + * All enumerations which do not belong in any other category + * are placed in this file. + */ + +using System; + +namespace ProcessHacker.Native.Api +{ + public enum AddressMode : int + { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat + } + + public enum AiFamily : int + { + /// + /// The address family is unspecified. + /// + Unspecified = 0, + /// + /// The Internet Protocol version 4 (IPv4) address family. + /// + INet = 2, + /// + /// The NetBIOS address family. This address family is only supported + /// if a Windows Sockets provider for NetBIOS is installed. + /// + NetBios = 17, + /// + /// The Internet Protocol version 6 (IPv6) address family. + /// + INet6 = 23, + /// + /// The Infrared Data Association (IrDA) address family. This address + /// family is only supported if the computer has an infrared port and + /// driver installed. + /// + IrDA = 26, + /// + /// The Bluetooth address family. This address family is only supported + /// if a Bluetooth adapter is installed on Windows Server 2003 or later. + /// + Bth = 32 + } + + [Flags] + public enum AllocFlags : uint + { + LHnd = 0x42, + LMemFixed = 0x0, + LMemMoveable = 0x2, + LMemZeroInit = 0x40, + LPtr = 0x40, + NonZeroLHnd = LMemMoveable, + NonZeroLPtr = LMemFixed + } + + public enum DepFlags : uint + { + Disable = 0x00000000, + Enable = 0x00000001, + DisableAtlThunkEmulation = 0x00000002 + } + + public enum DepSystemPolicyType : int + { + AlwaysOff = 0, + AlwaysOn, + OptIn, + OptOut + } + + [Flags] + public enum ExitWindowsFlags : uint + { + Logoff = 0x0, + Poweroff = 0x8, + Reboot = 0x2, + RestartApps = 0x40, + Shutdown = 0x1, + Force = 0x4, + ForceIfHung = 0x10 + } + + public enum FileCreationDispositionWin32 : uint + { + /// + /// Creates a new file. The function fails if the specified file already exists. + /// + CreateNew = 1, + /// + /// Creates a new file. If the file exists, the function overwrites the file and clears the existing attributes. + /// + CreateAlways = 2, + /// + /// Opens the file. The function fails if the file does not exist. + /// + OpenExisting = 3, + /// + /// Opens the file, if it exists. If the file does not exist, the function creates the file. + /// + OpenAlways = 4, + /// + /// Opens the file. Once opened, the file is truncated so that its size is zero bytes. + /// The function fails if the file does not exist. + /// + TruncateExisting = 5 + } + + public enum GdiBlendMode : int + { + Black = 1, + NotMergePen, + MaskNotPen, + NotCopyPen, + MaskPenNot, + Not, + XorPen, + NotMaskPen, + MaskPen, + NotXorPen, + Nop, + MergeNotPen, + CopyPen, + MergePenNot, + MergePen, + White, + Last + } + + public enum GdiPenStyle : int + { + Solid = 0, + Dash, + Dot, + DashDot, + DashDotDot, + Null, + InsideFrame, + UserStyle, + Alternate + } + + public enum GdiStockObject : int + { + WhiteBrush = 0, + LightGrayBrush, + GrayBrush, + DarkGrayBrush, + BlackBrush, + NullBrush, + WhitePen, + BlackPen, + NullPen, + OemFixedFont, + AnsiFixedFont, + AnsiVarFont, + SystemFont, + DeviceDefaultFont, + DefaultPalette, + SystemFixedFont, + DefaultGuiFont, + DcBrush, + DcPen + } + + public enum GetWindowLongOffset : int + { + WndProc = -4, + HInstance = -6, + HwndParent = -8, + Id = -12, + Style = -16, + ExStyle = -20, + UserData = -21 + } + + [Flags] + public enum HeapEntry32Flags : int + { + Fixed = 0x00000001, + Free = 0x00000002, + Moveable = 0x00000004 + } + + public enum LogonFlags : uint + { + LogonWithProfile = 1, + NetCredentialsOnly = 2 + } + + public enum LogonType : uint + { + Interactive = 2, + Network = 3, + Batch = 4, + Service = 5, + Unlock = 7, + NetworkCleartext = 8, + NewCredentials = 9 + } + + public enum LogonProvider : uint + { + Default = 0, + WinNT35 = 1, + WinNT40 = 2, + WinNT50 = 3 + } + + [Flags] + public enum MemoryState : uint + { + Commit = 0x1000, + Reserve = 0x2000, + + /// + /// Decommits memory, putting it into the reserved state. + /// + Decommit = 0x4000, + + /// + /// Frees memory, putting it into the freed state. + /// + Release = 0x8000, + Free = 0x10000, + Reset = 0x80000, + Physical = 0x400000, + LargePages = 0x20000000 + } + + public enum MemoryType : int + { + Image = 0x1000000, + Mapped = 0x40000, + Private = 0x20000 + } + + public enum MibTcpState : int + { + Closed = 1, + Listening = 2, + SynSent = 3, + SynReceived = 4, + Established = 5, + FinWait1 = 6, + FinWait2 = 7, + CloseWait = 8, + Closing = 9, + LastAck = 10, + TimeWait = 11, + DeleteTcb = 12 + } + + public enum MinidumpType : uint + { + Normal = 0x00000000, + WithDataSegs = 0x00000001, + WithFullMemory = 0x00000002, + WithHandleData = 0x00000004, + FilterMemory = 0x00000008, + ScanMemory = 0x00000010, + WithUnloadedModules = 0x00000020, + WithIndirectlyReferencedMemory = 0x00000040, + FilterModulePaths = 0x00000080, + WithProcessThreadData = 0x00000100, + WithPrivateReadWriteMemory = 0x00000200, + WithoutOptionalData = 0x00000400, + WithFullMemoryInfo = 0x00000800, + WithThreadInfo = 0x00001000, + WithCodeSegs = 0x00002000, + WithoutAuxiliaryState = 0x00004000, + WithFullAuxiliaryState = 0x00008000 + } + + public enum PeekMessageFlags : int + { + NoRemove = 0, + Remove = 1, + NoYield = 2, + } + + [Flags] + public enum PipeAccessMode : uint + { + Inbound = 0x1, + Outbound = 0x2, + Duplex = 0x3, + FirstPipeInstance = 0x80000, + WriteThrough = 0x80000000, + Overlapped = 0x40000000, + WriteDac = 0x40000, + WriteOwner = 0x80000, + AccessSystemSecurity = 0x01000000 + } + + [Flags] + public enum PipeMode : uint + { + TypeByte = 0x0, + TypeMessage = 0x4, + ReadModeByte = 0x0, + ReadModeMessage = 0x2, + Wait = 0x0, + NoWait = 0x1, + AcceptRemoteClients = 0x0, + RejectRemoteClients = 0x8 + } + + public enum PoolType : uint + { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS + } + + public enum PrivateNamespaceFlags : int + { + Destroy = 0x1 + } + + [Flags] + public enum ProcessCreationFlags : uint + { + DebugProcess = 0x1, + DebugOnlyThisProcess = 0x2, + CreateSuspended = 0x4, + DetachedProcess = 0x8, + CreateNewConsole = 0x10, + NormalPriorityClass = 0x20, + IdlePriorityClass = 0x40, + HighPriorityClass = 0x80, + RealtimePriorityClass = 0x100, + CreateNewProcessGroup = 0x200, + CreateUnicodeEnvironment = 0x400, + CreateSeparateWowVdm = 0x800, + CreateSharedWowVdm = 0x1000, + CreateForceDos = 0x2000, + BelowNormalPriorityClass = 0x4000, + AboveNormalPriorityClass = 0x8000, + StackSizeParamIsAReservation = 0x10000, + InheritCallerPriority = 0x20000, + CreateProtectedProcess = 0x40000, + ExtendedStartupInfoPresent = 0x80000, + ProcessModeBackgroundBegin = 0x100000, + ProcessModeBackgroundEnd = 0x200000, + CreateBreakawayFromJob = 0x1000000, + CreatePreserveCodeAuthzLevel = 0x2000000, + CreateDefaultErrorMode = 0x4000000, + CreateNoWindow = 0x8000000, + ProfileUser = 0x10000000, + ProfileKernel = 0x20000000, + ProfileServer = 0x40000000, + CreateIgnoreSystemDefault = 0x80000000 + } + + public enum ProcessPriorityClassWin32 : int + { + Idle = 0x40, + Normal = 0x20, + High = 0x80, + RealTime = 0x100, + BelowNormal = 0x4000, + AboveNormal = 0x8000 + } + + [Flags] + public enum RedrawWindowFlags + { + Invalidate = 0x0001, + InternalPaint = 0x0002, + Erase = 0x0004, + + Validate = 0x0008, + NoInternalPaint = 0x0010, + NoErase = 0x0020, + + NoChildren = 0x0040, + AllChildren = 0x0080, + + UpdateNow = 0x0100, + EraseNow = 0x0200, + + Frame = 0x0400, + NoFrame = 0x0800 + } + + [Flags] + public enum RunFileDialogFlags : uint + { + /// + /// Don't use any of the flags (only works alone) + /// + None = 0x0000, + /// + /// Removes the browse button + /// + NoBrowse = 0x0001, + /// + /// No default item selected + /// + NoDefault = 0x0002, + /// + /// Calculates the working directory from the file name + /// + CalcDirectory = 0x0004, + /// + /// Removes the edit box label + /// + NoLabel = 0x0008, + /// + /// Removes the separate memory space checkbox (Windows NT only) + /// + NoSeparateMemory = 0x0020 + } + + public enum ScActionType : int + { + None = 0, + Reboot = 2, + Restart = 1, + RunCommand = 3 + } + + public enum SeObjectType : int + { + Unknown = 0, + FileObject, + Service, + Printer, + RegistryKey, + LmShare, + KernelObject, + WindowObject, + DsObject, + DsObjectAll, + ProviderDefinedObject, + WmiGuidObject, + RegistryWow6432Key + } + + [Flags] + public enum SePrivilegeAttributes : uint + { + Disabled = 0x00000000, + EnabledByDefault = 0x00000001, + Enabled = 0x00000002, + Removed = 0x00000004, + UsedForAccess = 0x80000000 + } + + public enum ShowWindowType : int + { + Hide = 0, + ShowNormal = 1, + Normal = 1, + ShowMinimized = 2, + ShowMaximized = 3, + Maximize = 3, + ShowNoActivate = 4, + Show = 5, + Minimize = 6, + ShowMinNoActive = 7, + ShowNa = 8, + Restore = 9, + ShowDefault = 10, + ForceMinimize = 11, + Max = 11 + } + + [Flags] + public enum SiAccessFlags : int + { + Specific = 0x00010000, + General = 0x00020000, + Container = 0x00040000, + Property = 0x00080000 + } + + public enum SiCallbackMessage : uint + { + Release = 1, + Create = 2, + InitDialog = WindowMessage.User + 1 + } + + [Flags] + public enum SiObjectInfoFlags : int + { + EditAll = EditPerms | EditOwner | EditAudits, + EditPerms = 0x00000000, + EditOwner = 0x00000001, + EditAudits = 0x00000002, + Container = 0x00000004, + ReadOnly = 0x00000008, + Advanced = 0x00000010, + Reset = 0x00000020, + OwnerReadOnly = 0x00000040, + EditProperties = 0x00000080, + Recurse = 0x00000100, + NoAclProtect = 0x00000200, + NoTreeApply = 0x00000400, + PageTitle = 0x00000800, + ServerIsDc = 0x00001000, + ResetDaclTree = 0x00004000, + ResetSaclTree = 0x00008000, + ObjectGuid = 0x00010000, + EditEffective = 0x00020000, + ResetDacl = 0x00040000, + ResetSacl = 0x00080000, + ResetOwner = 0x00100000, + NoAdditionalPermission = 0x00200000, + ViewOnly = 0x00400000, + PermsElevationRequired = 0x01000000, + AuditsElevationRequired = 0x02000000, + OwnerElevationRequested = 0x04000000, + MayWrite = 0x10000000 + } + + public enum SiPageType : int + { + Perm, + AdvPerm, + Audit, + Owner, + Effective, + TakeOwnership + } + + [Flags] + public enum SmtoFlags : int + { + Normal = 0x0, + Block = 0x1, + AbortIfHung = 0x2, + NoTimeoutIfNotHung = 0x8, + ErrorOnExit = 0x20 + } + + [Flags] + public enum SnapshotFlags : uint + { + HeapList = 0x00000001, + Process = 0x00000002, + Thread = 0x00000004, + Module = 0x00000008, + Module32 = 0x00000010, + Inherit = 0x80000000, + All = 0x0000001f + } + + [Flags] + public enum StartupFlags : uint + { + UseShowWindow = 0x1, + UseSize = 0x2, + UsePosition = 0x4, + UseCountChars = 0x8, + UseFillAttribute = 0x10, + RunFullScreen = 0x20, + ForceOnFeedback = 0x40, + ForceOffFeedback = 0x80, + UseStdHandles = 0x100, + UseHotkey = 0x200 + } + + [Flags] + public enum SymbolFlags : int + { + ClrToken = 0x00040000, + Constant = 0x00000100, + Export = 0x00000200, + Forwarder = 0x00000400, + FrameRel = 0x00000020, + Function = 0x00000800, + IlRel = 0x00010000, + Local = 0x00000080, + Metadata = 0x00020000, + Parameter = 0x00000040, + Register = 0x00000008, + RegRel = 0x00000010, + Slot = 0x00008000, + Thunk = 0x00002000, + TlsRel = 0x00004000, + ValuePresent = 0x00000001, + Virtual = 0x00001000 + } + + [Flags] + public enum SymbolOptions : uint + { + AllowAbsoluteSymbols = 0x00000800, + AllowZeroAddress = 0x01000000, + AutoPublics = 0x00010000, + CaseInsensitive = 0x00000001, + Debug = 0x80000000, + DeferredLoads = 0x00000004, + DisableSymSrvAutodetect = 0x02000000, + ExactSymbols = 0x00000400, + FailCriticalErrors = 0x00000200, + FavorCompressed = 0x00800000, + FlatDirectory = 0x00400000, + IgnoreCvRec = 0x00000080, + IgnoreImageDir = 0x00200000, + IgnoreNtSymPath = 0x00001000, + Include32BitModules = 0x00002000, + LoadAnything = 0x00000040, + LoadLines = 0x00000010, + NoCpp = 0x00000008, + NoImageSearch = 0x00020000, + NoPrompts = 0x00080000, + NoPublics = 0x00008000, + NoUnqualifiedLoads = 0x00000100, + Overwrite = 0x00100000, + PublicsOnly = 0x00004000, + Secure = 0x00040000, + UndName = 0x00000002 + } + + [Flags] + public enum SymbolServerOption + { + Callback = 0x01, + Unattended = 0x20, + ParentWin = 0x80, + } + + public enum TcpConnectionOffloadState + { + InHost = 0, + Offloading = 1, + Offloaded = 2, + Uploading = 3, + Max = 4 + } + + public enum TcpTableClass : int + { + BasicListener, + BasicConnections, + BasicAll, + OwnerPidListener, + OwnerPidConnections, + OwnerPidAll, + OwnerModuleListener, + OwnerModuleConnections, + OwnerModuleAll + } + + public enum UipiFilterFlag : uint + { + Add = 1, + Remove = 2 + } + + public enum UdpTableClass : int + { + Basic, + OwnerPid, + OwnerModule + } + + public enum WaitResult : uint + { + Object0 = 0x0, + Abandoned = 0x80, + Timeout = 0x102, + Failed = 0xffffffff + } + + [Flags] + public enum Win32HandleFlags : int + { + Inherit = 0x1, + ProtectFromClose = 0x2 + } + + public enum WindowMessage : uint + { + Null = 0x00, + Create = 0x01, + Destroy = 0x02, + Move = 0x03, + Size = 0x05, + Activate = 0x06, + SetFocus = 0x07, + KillFocus = 0x08, + Enable = 0x0a, + SetRedraw = 0x0b, + SetText = 0x0c, + GetText = 0x0d, + GetTextLength = 0x0e, + Paint = 0x0f, + Close = 0x10, + QueryEndSession = 0x11, + Quit = 0x12, + QueryOpen = 0x13, + EraseBkgnd = 0x14, + SysColorChange = 0x15, + EndSession = 0x16, + SystemError = 0x17, + ShowWindow = 0x18, + CtlColor = 0x19, + WinIniChange = 0x1a, + SettingChange = 0x1a, + DevModeChange = 0x1b, + ActivateApp = 0x1c, + FontChange = 0x1d, + TimeChange = 0x1e, + CancelMode = 0x1f, + SetCursor = 0x20, + MouseActivate = 0x21, + ChildActivate = 0x22, + QueueSync = 0x23, + GetMinMaxInfo = 0x24, + PaintIcon = 0x26, + IconEraseBkgnd = 0x27, + NextDlgCtl = 0x28, + SpoolerStatus = 0x2a, + DrawIcon = 0x2b, + MeasureItem = 0x2c, + DeleteItem = 0x2d, + VKeyToItem = 0x2e, + CharToItem = 0x2f, + + SetFont = 0x30, + GetFont = 0x31, + SetHotkey = 0x32, + GetHotkey = 0x33, + QueryDragIcon = 0x37, + CompareItem = 0x39, + Compacting = 0x41, + WindowPosChanging = 0x46, + WindowPosChanged = 0x47, + Power = 0x48, + CopyData = 0x4a, + CancelJournal = 0x4b, + Notify = 0x4e, + InputLangChangeRequest = 0x50, + InputLangChange = 0x51, + TCard = 0x52, + Help = 0x53, + UserChanged = 0x54, + NotifyFormat = 0x55, + ContextMenu = 0x7b, + StyleChanging = 0x7c, + StyleChanged = 0x7d, + DisplayChange = 0x7e, + GetIcon = 0x7f, + SetIcon = 0x80, + + NcCreate = 0x81, + NcDestroy = 0x82, + NcCalcSize = 0x83, + NcHitTest = 0x84, + NcPaint = 0x85, + NcActivate = 0x86, + GetDlgCode = 0x87, + NcMouseMove = 0xa0, + NcLButtonDown = 0xa1, + NcLButtonUp = 0xa2, + NcLButtonDblClk = 0xa3, + NcRButtonDown = 0xa4, + NcRButtonUp = 0xa5, + NcRButtonDblClk = 0xa6, + NcMButtonDown = 0xa7, + NcMButtonUp = 0xa8, + NcMButtonDblClk = 0xa9, + + KeyDown = 0x100, + KeyUp = 0x101, + Char = 0x102, + DeadChar = 0x103, + SysKeyDown = 0x104, + SysKeyUp = 0x105, + SysChar = 0x106, + SysDeadChar = 0x107, + + ImeStartComposition = 0x10d, + ImeEndComposition = 0x10e, + ImeComposition = 0x10f, + ImeKeyLast = 0x10f, + + InitDialog = 0x110, + Command = 0x111, + SysCommand = 0x112, + Timer = 0x113, + HScroll = 0x114, + VScroll = 0x115, + InitMenu = 0x116, + InitMenuPopup = 0x117, + MenuSelect = 0x11f, + MenuChar = 0x120, + EnterIdle = 0x121, + + CtlColorMsgBox = 0x132, + CtlColorEdit = 0x133, + CtlColorListBox = 0x134, + CtlColorBtn = 0x135, + CtlColorDlg = 0x136, + CtlColorScrollbar = 0x137, + CtlColorStatic = 0x138, + + MouseMove = 0x200, + LButtonDown = 0x201, + LButtonUp = 0x202, + LButtonDblClk = 0x203, + RButtonDown = 0x204, + RButtonUp = 0x205, + RButtonDblClk = 0x206, + MButtonDown = 0x207, + MButtonUp = 0x208, + MButtonDblClk = 0x209, + MouseWheel = 0x20a, + + ParentNotify = 0x210, + EnterMenuLoop = 0x211, + ExitMenuLoop = 0x212, + NextMenu = 0x213, + Sizing = 0x214, + CaptureChanged = 0x215, + Moving = 0x216, + PowerBroadcast = 0x218, + DeviceChange = 0x219, + + MdiCreate = 0x220, + MdiDestroy = 0x221, + MdiActivate = 0x222, + MdiRestore = 0x223, + MdiNext = 0x224, + MdiMaximize = 0x225, + MdiTile = 0x226, + MdiCascade = 0x227, + MdiIconArrange = 0x228, + MdiGetActive = 0x229, + MdiSetMenu = 0x230, + EnterSizeMove = 0x231, + ExitSizeMove = 0x232, + DropFiles = 0x233, + MdiRefreshMenu = 0x234, + + ImeSetContext = 0x281, + ImeNotify = 0x282, + ImeControl = 0x283, + ImeCompositionFull = 0x284, + ImeSelect = 0x285, + ImeChar = 0x286, + ImeKeyDown = 0x290, + ImeKeyUp = 0x291, + + NcMouseHover = 0x2a0, + MouseHover = 0x2a1, + NcMouseLeave = 0x2a2, + MouseLeave = 0x2a3, + + WtsSessionChange = 0x2b1, + + TabletFirst = 0x2c0, + TabletLast = 0x2df, + + Cut = 0x300, + Copy = 0x301, + Paste = 0x302, + Clear = 0x303, + Undo = 0x304, + + RenderFormat = 0x305, + RenderAllFormats = 0x306, + DestroyClipboard = 0x307, + DrawClipboard = 0x308, + PaintClipboard = 0x309, + VScrollClipboard = 0x30a, + SizeClipboard = 0x30b, + AskCbFormatName = 0x30c, + ChangeCbChain = 0x30d, + HScrollClipboard = 0x30e, + QueryNewPalette = 0x30f, + PaletteIsChanging = 0x310, + PaletteChanged = 0x311, + + Hotkey = 0x312, + Print = 0x317, + PrintClient = 0x318, + + DwmSendIconicThumbnail = 0x323, + DwmSendIconicLivePreviewBitmap = 0x326, + + HandheldFirst = 0x358, + HandheldLast = 0x35f, + PenWinFirst = 0x380, + PenWinLast = 0x38f, + CoalesceFirst = 0x390, + CoalesceLast = 0x39f, + DdeInitiate = 0x3e0, + DdeTerminate = 0x3e1, + DdeAdvise = 0x3e2, + DdeUnadvise = 0x3e3, + DdeAck = 0x3e4, + DdeData = 0x3e5, + DdeRequest = 0x3e6, + DdePoke = 0x3e7, + DdeExecute = 0x3e8, + + User = 0x400, + + BcmSetShield = 0x160c, + + App = 0x8000 + } + + [Flags] + public enum WindowPlacementFlags : int + { + SetMinPosition = 0x1, + RestoreToMaximized = 0x2, + AsyncWindowPlacement = 0x4 + } + + public enum WindowStyles : uint + { + Overlapped = 0x00000000, + Popup = 0x80000000, + Child = 0x40000000, + Minimize = 0x20000000, + Visible = 0x10000000, + Disabled = 0x08000000, + ClipSiblings = 0x04000000, + ClipChildren = 0x02000000, + Maximize = 0x01000000, + Caption = 0x00C00000, /* WindowStyles.Border | WindowStyles.DialogFrame */ + Border = 0x00800000, + DialogFrame = 0x00400000, + VerticalScroll = 0x00200000, + HorizontalScroll = 0x00100000, + SystemMenu = 0x00080000, + ThickFrame = 0x00040000, + Group = 0x00020000, + TabStop = 0x00010000, + MinimizeBox = 0x00020000, + MaximizeBox = 0x00010000 + } + + [Flags] + public enum WtProvFlags : int + { + RevocationCheckNone = 0x10, + RevocationCheckEndCert = 0x20, + RevocationCheckChain = 0x40, + RevocationCheckChainExcludeRoot = 0x80, + Safer = 0x100, + HashOnly = 0x200, + UseDefaultOsVerCheck = 0x800, + CacheOnlyUrlRetrieval = 0x1000 + } + + public enum WtRevocationChecks : int + { + None = 0, + WholeChain = 1 + } + + public enum WtStateAction + { + Ignore = 0, + Verify = 1, + Close = 2, + AutoCache = 3, + AutoCacheFlush = 4 + } + + public enum WtsConnectStateClass : int + { + Active, + Connected, + ConnectQuery, + Shadow, + Disconnected, + Idle, + Listen, + Reset, + Down, + Init + } + + public enum WtsInformationClass : int + { + InitialProgram, + ApplicationName, + WorkingDirectory, + OemId, + SessionId, + UserName, + WinStationName, + DomainName, + ConnectState, + ClientBuildNumber, + ClientName, + ClientDirectory, + ClientProductId, + ClientHardwareId, + ClientAddress, + ClientDisplay, + ClientProtocolType, + IdleTime, + LogonTime, + IncomingBytes, + OutgoingBytes, + IncomingFrames, + OutgoingFrames + } + + public enum WtsNotificationFlags : int + { + ThisSession = 0x0, + AllSessions = 0x1 + } + + public enum WtsSessionChangeEvent : int + { + ConsoleConnect = 1, + ConsoleDisconnect, + RemoteConnect, + RemoteDisconnect, + SessionLogon, + SessionLogoff, + SessionLock, + SessionUnlock, + RemoteControl + } + + public enum WtsShutdownFlags : int + { + Logoff = 0x1, + Shutdown = 0x2, + Reboot = 0x4, + Poweroff = 0x8, + FastReboot = 0x10 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Extensions.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Extensions.cs new file mode 100644 index 000000000..450c293ad --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Extensions.cs @@ -0,0 +1,504 @@ +/* + * Process Hacker - + * windows API structure extension functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + public static class NativeExtensions + { + public static ObjectBasicInformation GetBasicInfo(this SystemHandleEntry thisHandle) + { + using (ProcessHandle process = new ProcessHandle(thisHandle.ProcessId, ProcessAccess.DupHandle)) + { + return thisHandle.GetBasicInfo(process); + } + } + + public static ObjectBasicInformation GetBasicInfo(this SystemHandleEntry thisHandle, ProcessHandle process) + { + NtStatus status = NtStatus.Success; + IntPtr handle = new IntPtr(thisHandle.Handle); + IntPtr objectHandleI; + GenericHandle objectHandle = null; + int retLength; + int baseAddress; + + if (KProcessHacker.Instance == null) + { + if ((status = Win32.NtDuplicateObject( + process, handle, ProcessHandle.Current, out objectHandleI, 0, 0, 0)) >= NtStatus.Error) + Win32.ThrowLastError(); + + objectHandle = new GenericHandle(objectHandleI); + } + + try + { + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(ObjectBasicInformation)))) + { + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectBasicInformation, + data, data.Size, out retLength, out baseAddress); + } + else + { + status = Win32.NtQueryObject(objectHandle, ObjectInformationClass.ObjectBasicInformation, + data, data.Size, out retLength); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return data.ReadStruct(); + } + } + finally + { + if (objectHandle != null) + objectHandle.Dispose(); + } + } + + public static string GetName(this ClientId clientId) + { + return clientId.GetName(true); + } + + public static string GetName(this ClientId clientId, bool includeThread) + { + string processName = Windows.GetProcessName(clientId.ProcessId); + + if (includeThread) + { + if (processName != null) + return processName + " (" + clientId.ProcessId.ToString() + "): " + + clientId.ThreadId.ToString(); + else + return "Non-existent process (" + clientId.ProcessId.ToString() + "): " + + clientId.ThreadId.ToString(); + } + else + { + if (processName != null) + return processName + " (" + clientId.ProcessId.ToString() + ")"; + else + return "Non-existent process (" + clientId.ProcessId.ToString() + ")"; + } + } + + private static string GetObjectNameNt(ProcessHandle process, IntPtr handle, GenericHandle dupHandle) + { + int retLength; + int baseAddress = 0; + + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation, + IntPtr.Zero, 0, out retLength, out baseAddress); + } + else + { + Win32.NtQueryObject(dupHandle, ObjectInformationClass.ObjectNameInformation, + IntPtr.Zero, 0, out retLength); + } + + if (retLength > 0) + { + using (MemoryAlloc oniMem = new MemoryAlloc(retLength)) + { + if (KProcessHacker.Instance != null) + { + if (KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation, + oniMem, oniMem.Size, out retLength, out baseAddress) >= NtStatus.Error) + throw new Exception("ZwQueryObject failed."); + } + else + { + if (Win32.NtQueryObject(dupHandle, ObjectInformationClass.ObjectNameInformation, + oniMem, oniMem.Size, out retLength) >= NtStatus.Error) + throw new Exception("NtQueryObject failed."); + } + + var oni = oniMem.ReadStruct(); + var str = oni.Name; + + if (KProcessHacker.Instance != null) + str.Buffer = str.Buffer.Increment(oniMem.Memory.Decrement(baseAddress)); + + return str.Read(); + } + } + + throw new Exception("NtQueryObject failed."); + } + + public static ObjectInformation GetHandleInfo(this SystemHandleEntry thisHandle) + { + return thisHandle.GetHandleInfo(true); + } + + public static ObjectInformation GetHandleInfo(this SystemHandleEntry thisHandle, bool getName) + { + using (ProcessHandle process = new ProcessHandle(thisHandle.ProcessId, + KProcessHacker.Instance != null ? OSVersion.MinProcessQueryInfoAccess : ProcessAccess.DupHandle)) + { + return thisHandle.GetHandleInfo(process, getName); + } + } + + public static ObjectInformation GetHandleInfo(this SystemHandleEntry thisHandle, ProcessHandle process) + { + return thisHandle.GetHandleInfo(process, true); + } + + public static ObjectInformation GetHandleInfo(this SystemHandleEntry thisHandle, ProcessHandle process, bool getName) + { + IntPtr handle = new IntPtr(thisHandle.Handle); + IntPtr objectHandleI; + int retLength = 0; + GenericHandle objectHandle = null; + + if (thisHandle.Handle == 0 || thisHandle.Handle == -1 || thisHandle.Handle == -2) + throw new WindowsException(NtStatus.InvalidHandle); + + // Duplicate the handle if we're not using KPH + if (KProcessHacker.Instance == null) + { + NtStatus status; + + if ((status = Win32.NtDuplicateObject( + process, handle, ProcessHandle.Current, out objectHandleI, 0, 0, 0)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + objectHandle = new GenericHandle(objectHandleI); + } + + ObjectInformation info = new ObjectInformation(); + + // If the cache contains the object type's name, use it. Otherwise, query the type + // for its name. + lock (Windows.ObjectTypes) + { + if (Windows.ObjectTypes.ContainsKey(thisHandle.ObjectTypeNumber)) + { + info.TypeName = Windows.ObjectTypes[thisHandle.ObjectTypeNumber]; + } + else + { + int baseAddress = 0; + + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectTypeInformation, + IntPtr.Zero, 0, out retLength, out baseAddress); + } + else + { + Win32.NtQueryObject(objectHandle, ObjectInformationClass.ObjectTypeInformation, + IntPtr.Zero, 0, out retLength); + } + + if (retLength > 0) + { + using (MemoryAlloc otiMem = new MemoryAlloc(retLength)) + { + if (KProcessHacker.Instance != null) + { + if (KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectTypeInformation, + otiMem, otiMem.Size, out retLength, out baseAddress) >= NtStatus.Error) + throw new Exception("ZwQueryObject failed."); + } + else + { + if (Win32.NtQueryObject(objectHandle, ObjectInformationClass.ObjectTypeInformation, + otiMem, otiMem.Size, out retLength) >= NtStatus.Error) + throw new Exception("NtQueryObject failed."); + } + + var oti = otiMem.ReadStruct(); + var str = oti.Name; + + if (KProcessHacker.Instance != null) + str.Buffer = str.Buffer.Increment(otiMem.Memory.Decrement(baseAddress)); + + info.TypeName = str.Read(); + Windows.ObjectTypes.Add(thisHandle.ObjectTypeNumber, info.TypeName); + } + } + } + } + + if (!getName) + return info; + + // Get the object's name. If the object is a file we must take special + // precautions so that we don't hang. + if (info.TypeName == "File") + { + if (KProcessHacker.Instance != null) + { + // Use KProcessHacker for files to avoid hangs. + info.OrigName = KProcessHacker.Instance.GetHandleObjectName(process, handle); + } + else + { + // 0: No hack, query the thing normally. + // 1: No hack, use NProcessHacker. + // 2: Hack. + int hackLevel = 1; + + // Can't use NPH because XP had a bug where a thread hanging + // on NtQueryObject couldn't be terminated. + if (OSVersion.IsBelowOrEqual(WindowsVersion.XP)) + hackLevel = 2; + + // On Windows 7 and above the hanging bug appears to have + // been fixed. Query the object normally. + // UPDATE: Not so. It still happens. + //if (OSVersion.IsAboveOrEqual(WindowsVersion.Seven)) + // hackLevel = 0; + + if (hackLevel == 1) + { + try + { + // Use NProcessHacker. + using (MemoryAlloc oniMem = new MemoryAlloc(0x4000)) + { + if (NProcessHacker.PhQueryNameFileObject( + objectHandle, oniMem, oniMem.Size, out retLength) >= NtStatus.Error) + throw new Exception("PhQueryNameFileObject failed."); + + var oni = oniMem.ReadStruct(); + + info.OrigName = oni.Name.Read(); + } + } + catch (DllNotFoundException) + { + hackLevel = 2; + } + } + + if (hackLevel == 0) + { + info.OrigName = GetObjectNameNt(process, handle, objectHandle); + } + else if (hackLevel == 2) + { + // KProcessHacker and NProcessHacker not available. Fall back to using hack + // (i.e. not querying the name at all if the access is 0x0012019f). + if ((int)thisHandle.GrantedAccess != 0x0012019f) + info.OrigName = GetObjectNameNt(process, handle, objectHandle); + } + } + } + else + { + // Not a file. Query the object normally. + info.OrigName = GetObjectNameNt(process, handle, objectHandle); + } + + // Get a better name for the handle. + try + { + switch (info.TypeName) + { + case "File": + // Resolves \Device\Harddisk1 into C:, for example. + if (!string.IsNullOrEmpty(info.OrigName)) + info.BestName = FileUtils.GetFileName(info.OrigName); + + break; + + case "Key": + info.BestName = NativeUtils.FormatNativeKeyName(info.OrigName); + + break; + + case "Process": + { + int processId; + + if (KProcessHacker.Instance != null) + { + processId = KProcessHacker.Instance.KphGetProcessId(process, handle); + + if (processId == 0) + throw new Exception("Invalid PID"); + } + else + { + using (var processHandle = + new NativeHandle(process, handle, OSVersion.MinProcessQueryInfoAccess)) + { + if ((processId = Win32.GetProcessId(processHandle)) == 0) + Win32.ThrowLastError(); + } + } + + info.BestName = (new ClientId(processId, 0)).GetName(false); + } + + break; + + case "Thread": + { + int processId; + int threadId; + + if (KProcessHacker.Instance != null) + { + threadId = KProcessHacker.Instance.KphGetThreadId(process, handle, out processId); + + if (threadId == 0 || processId == 0) + throw new Exception("Invalid TID or PID"); + } + else + { + using (var threadHandle = + new NativeHandle(process, handle, OSVersion.MinThreadQueryInfoAccess)) + { + var basicInfo = ThreadHandle.FromHandle(threadHandle).GetBasicInformation(); + + threadId = basicInfo.ClientId.ThreadId; + processId = basicInfo.ClientId.ProcessId; + } + } + + info.BestName = (new ClientId(processId, threadId)).GetName(true); + } + + break; + + case "TmEn": + { + using (var enHandleDup = + new NativeHandle(process, handle, EnlistmentAccess.QueryInformation)) + { + var enHandle = EnlistmentHandle.FromHandle(enHandleDup); + + info.BestName = enHandle.GetBasicInformation().EnlistmentId.ToString("B"); + } + } + break; + + case "TmRm": + { + using (var rmHandleDup = + new NativeHandle(process, handle, ResourceManagerAccess.QueryInformation)) + { + var rmHandle = ResourceManagerHandle.FromHandle(rmHandleDup); + + info.BestName = rmHandle.GetDescription(); + + if (string.IsNullOrEmpty(info.BestName)) + info.BestName = rmHandle.GetGuid().ToString("B"); + } + } + break; + + case "TmTm": + { + using (var tmHandleDup = + new NativeHandle(process, handle, TmAccess.QueryInformation)) + { + var tmHandle = TmHandle.FromHandle(tmHandleDup); + + info.BestName = FileUtils.GetFileName(FileUtils.GetFileName(tmHandle.GetLogFileName())); + + if (string.IsNullOrEmpty(info.BestName)) + info.BestName = tmHandle.GetBasicInformation().TmIdentity.ToString("B"); + } + } + break; + + case "TmTx": + { + using (var transactionHandleDup = + new NativeHandle(process, handle, TransactionAccess.QueryInformation)) + { + var transactionHandle = TransactionHandle.FromHandle(transactionHandleDup); + + info.BestName = transactionHandle.GetDescription(); + + if (string.IsNullOrEmpty(info.BestName)) + info.BestName = transactionHandle.GetBasicInformation().TransactionId.ToString("B"); + } + } + break; + + case "Token": + { + using (var tokenHandleDup = + new NativeHandle(process, handle, TokenAccess.Query)) + { + var tokenHandle = TokenHandle.FromHandle(tokenHandleDup); + var sid = tokenHandle.GetUser(); + + using (sid) + info.BestName = sid.GetFullName(true) + ": 0x" + + tokenHandle.GetStatistics().AuthenticationId.ToString(); + } + } + + break; + + default: + if (info.OrigName != null && + info.OrigName != "") + { + info.BestName = info.OrigName; + } + else + { + info.BestName = null; + } + + break; + } + } + catch + { + if (info.OrigName != null && info.OrigName != "") + { + info.BestName = info.OrigName; + } + else + { + info.BestName = null; + } + } + + if (objectHandle != null) + objectHandle.Dispose(); + + return info; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Functions.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Functions.cs new file mode 100644 index 000000000..74caa4290 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Functions.cs @@ -0,0 +1,2816 @@ +/* + * Process Hacker - + * windows API functions + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2009 Uday Shanbhag + * Copyright (C) 2009 Dean + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains function declarations for the Win32 API. + * + * All functions which do not belong in any other category + * are placed in this file. + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Api +{ + public static partial class Win32 + { + #region Cryptography + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern bool CryptCATCatalogInfoFromContext( + [In] IntPtr CatInfoHandle, + [Out] out CatalogInfo CatInfo, + [In] int Flags + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern IntPtr CryptCATAdminEnumCatalogFromHash( + [In] IntPtr CatAdminHandle, + [In] byte[] Hash, + [In] int HashSize, + [In] int Flags, + [In] IntPtr PrevCatInfoHandle + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern bool CryptCATAdminAcquireContext( + [Out] out IntPtr CatAdminHandle, + [In] [MarshalAs(UnmanagedType.LPStruct)] Guid Subsystem, + [In] int Flags + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern bool CryptCATAdminCalcHashFromFileHandle( + [In] IntPtr FileHandle, + ref int HashSize, + [In] byte[] Hash, + [In] int Flags + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern bool CryptCATAdminReleaseContext( + [In] IntPtr CatAdminHandle, + [In] int Flags + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern bool CryptCATAdminReleaseCatalogContext( + [In] IntPtr CatAdminHandle, + [In] IntPtr CatInfoHandle, + [In] int Flags + ); + + [DllImport("wintrust.dll", SetLastError = true)] + public static extern uint WinVerifyTrust( + [In] IntPtr hWnd, + [In] [MarshalAs(UnmanagedType.LPStruct)] Guid ActionId, + [In] ref WintrustData WintrustData + ); + + #endregion + + #region Debugging + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DebugActiveProcess( + [In] int Pid + ); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DebugActiveProcessStop( + [In] int Pid + ); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DebugSetProcessKillOnExit( + [In] bool KillOnExit + ); + + #endregion + + #region Error Handling + + /// + /// Removes an Application from Windows Error Reporting on Windows XP + /// + /// The process.exe or the path\process.exe to be excluded + /// True if successfully excluded + [DllImport("faultrep.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern bool AddERExcludedApplication( + [In] string ExeName + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int FormatMessage( + [In] int Flags, + [In] [Optional] IntPtr Source, + [In] int MessageId, + [In] int LanguageId, + [Out] StringBuilder Buffer, + [In] int Size, + [In] [Optional] IntPtr Arguments + ); + + /// + /// Removes an Application from Windows Error Reporting on Windows Vista + /// + /// The process.exe or the path\process.exe to be excluded + /// true to exclude process from all users. Note: Administrator access is Required if set true + /// A HResult indicating the result + [DllImport("wer.dll", CharSet = CharSet.Unicode)] + public static extern HResult WerAddExcludedApplication( + [In] string ExeName, + [In] bool AllUsers + ); + + #endregion + + #region Files + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool GetFileInformationByHandleEx( + [In] IntPtr FileHandle, + [In] int FileInformationClass, + [In] IntPtr FileInformation, + [In] int FileInformationLength + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool GetFileSizeEx( + [In] IntPtr FileHandle, + [Out] out long FileSize + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int QueryDosDevice( + [In] [Optional] string DeviceName, + [In] IntPtr TargetPath, + [In] int MaxLength + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateFile( + [In] string FileName, + [In] FileAccess DesiredAccess, + [In] FileShareMode ShareMode, + [In] [Optional] int SecurityAttributes, + [In] FileCreationDispositionWin32 CreationDisposition, + [In] int FlagsAndAttributes, + [In] [Optional] IntPtr TemplateFile + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool ReadFile( + [In] IntPtr FileHandle, + [Out] byte[] Buffer, + [In] int Bytes, + [Out] [Optional] out int ReadBytes, + [Optional] IntPtr Overlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool WriteFile( + [In] IntPtr FileHandle, + [In] byte[] Buffer, + [In] int Bytes, + [Out] [Optional] out int WrittenBytes, + [Optional] IntPtr Overlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public unsafe static extern bool WriteFile( + [In] IntPtr FileHandle, + [In] void* Buffer, + [In] int Bytes, + [Out] [Optional] out int WrittenBytes, + [Optional] IntPtr Overlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool DeviceIoControl( + [In] IntPtr FileHandle, + [In] int IoControlCode, + [In] [Optional] byte[] InBuffer, + [In] int InBufferLength, + [Out] [Optional] byte[] OutBuffer, + [In] int OutBufferLength, + [Out] [Optional]out int BytesReturned, + [Optional] IntPtr Overlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public unsafe static extern bool DeviceIoControl( + [In] IntPtr FileHandle, + [In] int IoControlCode, + [In] [Optional] byte* InBuffer, + [In] int InBufferLength, + [Out] [Optional] byte* OutBuffer, + [In] int OutBufferLength, + [Out] [Optional] out int BytesReturned, + [Optional] IntPtr Overlapped + ); + + #endregion + + #region GDI + + [DllImport("gdi32.dll")] + public static extern bool DeleteObject( + [In] IntPtr Object + ); + + [DllImport("gdi32.dll")] + public static extern IntPtr GetStockObject( + [In] GdiStockObject Object + ); + + [DllImport("gdi32.dll")] + public static extern bool Rectangle( + [In] IntPtr hDC, + [In] int LeftRect, + [In] int TopRect, + [In] int RightRect, + [In] int BottomRect + ); + + [DllImport("gdi32.dll")] + public static extern IntPtr SelectObject( + [In] IntPtr hDC, + [In] IntPtr hGdiObject + ); + + [DllImport("gdi32.dll")] + public static extern IntPtr CreatePen( + [In] GdiPenStyle PenStyle, + [In] int Width, + [In] IntPtr Color + ); + + [DllImport("gdi32.dll")] + public static extern bool RestoreDC( + [In] IntPtr hDC, + [In] int SavedDC + ); + + [DllImport("gdi32.dll")] + public static extern int SaveDC( + [In] IntPtr hDC + ); + + [DllImport("gdi32.dll")] + public static extern GdiBlendMode SetROP2( + [In] IntPtr hDC, + [In] GdiBlendMode DrawMode + ); + + #endregion + + #region Images + + [DllImport("imagehlp.dll", SetLastError = true)] + public static extern IntPtr CheckSumMappedFile( + [In] IntPtr BaseAddress, + [In] int FileLength, + [Out] out int HeaderSum, + [Out] out int CheckSum + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + public static extern IntPtr ImageNtHeader( + [In] IntPtr ImageBase + ); + + [DllImport("imagehlp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + public static extern bool MapAndLoad( + [In] string ImageName, + [In] [Optional] string DllPath, + [Out] out LoadedImage LoadedImage, + [MarshalAs(UnmanagedType.Bool)] + [In] bool DotDll, + [In] bool ReadOnly + ); + + [DllImport("imagehlp.dll", SetLastError = true)] + public static extern bool UnMapAndLoad( + [In] ref LoadedImage LoadedImage + ); + + #endregion + + #region Jobs + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool TerminateJobObject( + [In] IntPtr JobHandle, + [In] int ExitCode + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AssignProcessToJobObject( + [In] IntPtr JobHandle, + [In] IntPtr ProcessHandle + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateJobObject( + [In] [Optional] IntPtr SecurityAttributes, + [In] [Optional] string Name + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr OpenJobObject( + [In] JobObjectAccess DesiredAccess, + [In] bool Inherit, + [In] string Name + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool QueryInformationJobObject( + [In] [Optional] IntPtr JobHandle, + [In] JobObjectInformationClass JobInformationClass, + [Out] IntPtr JobInformation, + [In] int JobInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool QueryInformationJobObject( + [In] [Optional] IntPtr JobHandle, + [In] JobObjectInformationClass JobInformationClass, + [Out] out JobObjectBasicUiRestrictions JobInformation, + [In] int JobInformationLength, + [Out] [Optional] out int ReturnLength + ); + + #endregion + + #region Kernel + + [DllImport("psapi.dll", SetLastError = true)] + public static extern bool EnumDeviceDrivers( + [Out] IntPtr[] ImageBases, + [In] int Size, + [Out] out int Needed + ); + + [DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int GetDeviceDriverBaseName( + [In] IntPtr ImageBase, + [Out] StringBuilder FileName, + [In] int Size + ); + + [DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int GetDeviceDriverFileName( + [In] IntPtr ImageBase, + [Out] StringBuilder FileName, + [In] int Size + ); + + #endregion + + #region Libraries + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr LoadLibrary( + [In] string FileName + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr LoadLibraryEx( + [In] string FileName, + IntPtr File, + [In] int Flags + ); + + [DllImport("kernel32.dll")] + public static extern bool FreeLibrary( + [In] IntPtr Handle + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr GetModuleHandle( + [In] [Optional] string ModuleName + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + public static extern IntPtr GetProcAddress( + [In] IntPtr Module, + [In] string ProcName + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + public static extern IntPtr GetProcAddress( + [In] IntPtr Module, + [In] ushort ProcOrdinal + ); + + #endregion + + #region Mailslots + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateMailslot( + [In] string Name, + [In] int MaxMessageSize, + [In] int ReadTimeout, + [In] IntPtr SecurityAttributes + ); + + #endregion + + #region Memory + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AllocateUserPhysicalPages( + [In] IntPtr ProcessHandle, + ref IntPtr NumberOfPages, + IntPtr[] UserPfnArray + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool FreeUserPhysicalPages( + [In] IntPtr ProcessHandle, + ref IntPtr NumberOfPages, + IntPtr[] UserPfnArray + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool MapUserPhysicalPages( + [In] IntPtr Address, + IntPtr NumberOfPages, + IntPtr[] UserPfnArray + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LocalAlloc( + [In] AllocFlags Flags, + [In] int Bytes + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LocalReAlloc( + [In] IntPtr Memory, + [In] AllocFlags Flags, + [In] int Bytes + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LocalFree( + [In] IntPtr Memory + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetProcessHeaps( + [In] int NumberOfHeaps, + [Out] IntPtr[] Heaps + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int HeapCompact( + [In] IntPtr Heap, + [In] bool NoSerialize + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr HeapCreate( + [In] HeapFlags Flags, + [In] IntPtr InitialSize, + [In] IntPtr MaximumSize + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool HeapDestroy( + [In] IntPtr Heap + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool HeapFree( + [In] IntPtr Heap, + [In] HeapFlags Flags, + [In] IntPtr Memory + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr HeapAlloc( + [In] IntPtr Heap, + [In] HeapFlags Flags, + [In] IntPtr Bytes + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr HeapReAlloc( + [In] IntPtr Heap, + [In] HeapFlags Flags, + [In] IntPtr Memory, + [In] IntPtr Bytes + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GetProcessHeap(); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int VirtualQueryEx( + [In] IntPtr Process, + [In] [Optional] IntPtr Address, + [Out] [MarshalAs(UnmanagedType.Struct)] out MemoryBasicInformation Buffer, + [In] int Size + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool VirtualProtectEx( + [In] IntPtr Process, + [In] IntPtr Address, + [In] int Size, + [In] MemoryProtection NewProtect, + [Out] out MemoryProtection OldProtect + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr VirtualAllocEx( + [In] IntPtr Process, + [In] [Optional] IntPtr Address, + [In] int Size, + [In] MemoryState Type, + [In] MemoryProtection Protect + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool VirtualFreeEx( + [In] IntPtr Process, + [In] IntPtr Address, + [In] int Size, + [In] MemoryState FreeType + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ReadProcessMemory( + [In] IntPtr Process, + [In] IntPtr BaseAddress, + [Out] byte[] Buffer, + [In] int Size, + [Out] out int BytesRead + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public unsafe static extern bool ReadProcessMemory( + [In] IntPtr Process, + [In] IntPtr BaseAddress, + [Out] void* Buffer, + [In] int Size, + [Out] out int BytesRead + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WriteProcessMemory( + [In] IntPtr Process, + [In] IntPtr BaseAddress, + [In] byte[] Buffer, + [In] int Size, + [Out] out int BytesWritten + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public unsafe static extern bool WriteProcessMemory( + [In] IntPtr Process, + [In] IntPtr BaseAddress, + [In] void* Buffer, + [In] int Size, + [Out] out int BytesWritten + ); + + #endregion + + #region Misc. + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetProcessShutdownParameters( + [In] int Level, + [In] int Flags + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ExitWindowsEx( + [In] ExitWindowsFlags flags, + [In] int reason + ); + + [DllImport("powrprof.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetSuspendState( + [In] bool hibernate, + [In] bool forceCritical, + [In] bool disableWakeEvent + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LockWorkStation(); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryPerformanceFrequency( + [Out] out long PerformanceFrequency + ); + + [DllImport("kernel32.dll")] + public static extern int GetTickCount(); + + #endregion + + #region Named Pipes + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ConnectNamedPipe( + [In] IntPtr NamedPipe, + [Optional] IntPtr Overlapped + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateNamedPipe( + [In] string Name, + [In] PipeAccessMode OpenMode, + [In] PipeMode PipeMode, + [In] int MaxInstances, + [In] int OutBufferSize, + [In] int InBufferSize, + [In] int DefaultTimeOut, + [In] [Optional] IntPtr SecurityAttributes + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DisconnectNamedPipe( + [In] IntPtr NamedPipe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetNamedPipeClientProcessId( + [In] IntPtr NamedPipeHandle, + [Out] out int ServerProcessId + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetNamedPipeHandleState( + [In] IntPtr NamedPipeHandle, + [Out] [Optional] out PipeState State, + [Out] [Optional] out int CurInstances, + [Out] [Optional] out int MaxCollectionCount, + [Out] [Optional] out int CollectDataTimeout, + [Out] [Optional] out int UserName, + [In] int MaxUserNameSize + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WaitNamedPipe( + [In] string Name, + [In] int Timeout + ); + + #endregion + + #region Network + + /// + /// Allows an application to check if a connection to the Internet can be established. + /// + /// A string that specifies the URL to use for checking the connection. + /// Forces a connection, must be 1. + /// This parameter is reserved and must be 0. + /// + [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public extern static bool InternetCheckConnection( + [In] string Url, + [In] int Flags, + [In] int Reserved + ); + + /// + /// The NdfCancelIncident function is used to cancel unneeded functions which have been previously called on an existing incident. + /// + /// Before using this API, an application must call an incident creation function such as NdfCreateWebIncident. + /// NdfCloseIncident should be used to close an incident once it has been resolved, as NdfCancelIncident does not actually close the incident itself. + /// + /// A handle to the Network Diagnostics Framework incident. + /// This handle should match the handle of an existing incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll")] + public static extern HResult NdfCancelIncident( + [In] IntPtr NdfHandle + ); + + /// + /// The NdfCloseIncident function is used to close an Network Diagnostics Framework (NDF) incident following its resolution. + /// + /// The handle to the NDF incident that is being closed. + /// A HResult value indicating the result + [DllImport("ndfapi.dll")] + public static extern HResult NdfCloseIncident( + [In] IntPtr NdfHandle + ); + + /// + /// The NdfCreateConnectivityIncident function diagnoses generic internet connectivity problems. + /// + /// The handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll")] + public static extern HResult NdfCreateConnectivityIncident( + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfCreateInboundIncident function creates a session to diagnose inbound connectivity for a specific application or service. + /// + /// The fully qualified path to the application receiving the inbound traffic. + /// The Windows service receiving the inbound traffic. + /// The SID for the application receiving the traffic. If NULL, the caller's SID is automatically used. + /// A SOCKADDR_STORAGE structure which limits the diagnosis to traffic to a specific IP address. If NULL, all traffic will be included in the diagnosis + /// The protocol which should be diagnosed. For example, IPPROTO_TCP would be used to indicate the TCP/IP protocol. + /// The Inbound flags for specifying the type of options to preform during diagnostics. + /// Pointer to a handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfCreateInboundIncident( + [In, Optional] string applicationID, + [In, Optional] string serviceID, + [In, Optional] int userID, //Incorrect + [In, Optional] int localTarget, //Incorrect + int protocol, //Incorrect + int dwFlags, //IsUnum: NDF_INBOUND_FLAG + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfCreateDNSIncident function diagnoses name resolution issues in resolving a specific host name. + /// + /// The host name with which there is a name resolution issue. + /// The numeric representation of the type of record that was queried when the issue occurred. + /// A handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfCreateDNSIncident( + [In] string hostname, + ushort querytype, //enum value: see the windns.h header file + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfCreateSharingIncident function diagnoses network problems in accessing a specific network share. + /// + /// The full UNC string (for example, "\\server\folder\file.ext")for the shared asset with which there is a connectivity issue. + /// A handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfCreateSharingIncident( + [In] string shareName, + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfCreateWebIncident function diagnoses web connectivity problems concerning a specific URL. + /// + /// The URL with which there is a connectivity issue. + /// A handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfCreateWebIncident( + [In] string url, + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfCreateWebIncidentEx function diagnoses web connectivity problems concerning a specific URL. This function allows for more control over the underlying diagnosis than the NdfCreateWebIncident function. + /// + /// The URL with which there is a connectivity issue. + /// If TRUE, diagnosis is performed using the WinHTTP APIs. Otherwise, the WinInet APIs are used. + /// The module name to use when checking against application-specific filtering rules (for example, "C:\Program Files\Internet Explorer\iexplorer.exe"). If NULL, the value is autodetected during the diagnosis. + /// A handle to the Network Diagnostics Framework incident. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfCreateWebIncidentEx( + [In] string url, + [MarshalAs(UnmanagedType.Bool)] + [In] bool useWinHTTP, + [In] string moduleName, + [In, Out] ref IntPtr NdfHandle + ); + + /// + /// The NdfExecuteDiagnosis function is used to diagnose the root cause of the incident that has occurred. + /// + /// A handle to the Network Diagnostics Framework incident. + /// The handle to the window that is intended to display the diagnostic information. If specified, the NDF UI is modal to the window. If NULL, the UI is non-modal. + /// A HResult value indicating the result + [DllImport("ndfapi.dll")] + public static extern HResult NdfExecuteDiagnosis( + [In] IntPtr NdfHandle, + [In] IntPtr hwnd + ); + + /// + /// The NdfGetTraceFile function is used to retrieve the path containing an Event Trace Log (ETL) file that contains Event Tracing for Windows (ETW) events from a diagnostic session. + /// + /// A handle to a Network Diagnostics Framework incident. This handle should match the handle of an existing incident. + /// Pointer to a string that contains the location of the trace file. + /// A HResult value indicating the result + [DllImport("ndfapi.dll", CharSet = CharSet.Unicode)] + public static extern HResult NdfGetTraceFile( + [In] IntPtr NdfHandle, + [Out] string TraceFileLocation + ); + + #endregion + + #region Private Namespaces + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AddSIDToBoundaryDescriptor( + ref IntPtr BoundaryDescriptor, + [In] IntPtr RequiredSid + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool ClosePrivateNamespace( + [In] IntPtr PrivateNamespaceHandle, + [In] PrivateNamespaceFlags Flags + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateBoundaryDescriptor( + [In] string Name, + [In] int Flags + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreatePrivateNamespace( + [In] IntPtr PrivateNamespaceAttributes, + [In] IntPtr BoundaryDescriptor, + [In] string AliasPrefix + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern void DeleteBoundaryDescriptor( + [In] IntPtr BoundaryDescriptor + ); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr OpenPrivateNamespace( + [In] IntPtr BoundaryDescriptor, + [In] string AliasPrefix + ); + + #endregion + + #region Processes + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern void ExitProcess( + [In] int ExitCode + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryProcessCycleTime( + [In] IntPtr ProcessHandle, + [Out] out ulong CycleTime + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetPriorityClass( + [In] IntPtr ProcessHandle, + [In] ProcessPriorityClassWin32 Priority + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern ProcessPriorityClassWin32 GetPriorityClass( + [In] IntPtr ProcessHandle + ); + + [DllImport("psapi.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EmptyWorkingSet( + [In] IntPtr ProcessHandle + ); + + [DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int GetMappedFileName( + [In] IntPtr ProcessHandle, + [In] IntPtr Address, + [Out] StringBuilder Buffer, + [In] int Size + ); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateProcessWithTokenW( + [In] IntPtr TokenHandle, + [In] LogonFlags Flags, + [In] [Optional] string ApplicationName, + [Optional] string CommandLine, + [In] ProcessCreationFlags CreationFlags, + [In] [Optional] int Environment, + [In] [Optional] string CurrentDirectory, + [In] ref StartupInfo StartupInfo, + [Out] out ProcessInformation ProcessInfo + ); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateProcessAsUser( + [In] [Optional] IntPtr TokenHandle, + [In] [Optional] string ApplicationName, + [Optional] string CommandLine, + [In] [Optional] IntPtr ProcessAttributes, + [In] [Optional] IntPtr ThreadAttributes, + [In] bool InheritHandles, + [In] ProcessCreationFlags CreationFlags, + [In] [Optional] IntPtr Environment, + [In] [Optional] string CurrentDirectory, + [In] ref StartupInfo StartupInfo, + [Out] out ProcessInformation ProcessInformation + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateProcess( + [In] [Optional] string ApplicationName, + [Optional] string CommandLine, + [In] [Optional] IntPtr ProcessAttributes, + [In] [Optional] IntPtr ThreadAttributes, + [In] bool InheritHandles, + [In] ProcessCreationFlags CreationFlags, + [In] [Optional] IntPtr Environment, + [In] [Optional] string CurrentDirectory, + [In] ref StartupInfo StartupInfo, + [Out] out ProcessInformation ProcessInformation + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetExitCodeProcess( + [In] IntPtr ProcessHandle, + [Out] out int ExitCode + ); + + // Vista and higher + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryFullProcessImageName( + [In] IntPtr ProcessHandle, + [In] [MarshalAs(UnmanagedType.Bool)] bool UseNativeName, + [Out] StringBuilder ExeName, + ref int Size + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsProcessInJob( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr JobHandle, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool Result + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetProcessAffinityMask( + [In] IntPtr ProcessHandle, + [In] IntPtr ProcessAffinityMask + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetProcessAffinityMask( + [In] IntPtr ProcessHandle, + [Out] out IntPtr ProcessAffinityMask, + [Out] out IntPtr SystemAffinityMask + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CheckRemoteDebuggerPresent( + [In] IntPtr ProcessHandle, + [MarshalAs(UnmanagedType.Bool)] ref bool DebuggerPresent + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetProcessId( + [In] IntPtr ProcessHandle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetCurrentProcessId(); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetProcessDEPPolicy( + [In] IntPtr ProcessHandle, + [Out] out DepFlags Flags, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool Permanent + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TerminateProcess( + [In] IntPtr ProcessHandle, + [In] int ExitCode + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess( + [In] ProcessAccess DesiredAccess, + [In] bool InheritHandle, + [In] int ProcessId + ); + + [DllImport("psapi.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumProcessModules( + [In] IntPtr ProcessHandle, + [Out] IntPtr[] ModuleHandles, + [In] int Size, + [Out] out int RequiredSize + ); + + [DllImport("psapi.dll", CharSet = CharSet.Unicode)] + public static extern int GetModuleBaseName( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr ModuleHandle, + [Out] StringBuilder BaseName, + [In] int Size + ); + + [DllImport("psapi.dll", CharSet = CharSet.Unicode)] + public static extern int GetModuleFileNameEx( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr ModuleHandle, + [Out] StringBuilder FileName, + [In] int Size + ); + + [DllImport("psapi.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetModuleInformation( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr ModuleHandle, + [Out] ModuleInfo ModInfo, + [In] int Size + ); + + #endregion + + #region Resources/Handles + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateMutex( + [In] [Optional] IntPtr attributes, + [In] bool initialOwner, + [In] [Optional] string name + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetHandleInformation( + [In] IntPtr handle, + [In] Win32HandleFlags mask, + [In] Win32HandleFlags flags + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetHandleInformation( + [In] IntPtr handle, + [Out] out Win32HandleFlags flags + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle( + [In] IntPtr Handle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern WaitResult WaitForSingleObject( + [In] IntPtr Object, + [In] uint Timeout + ); + + #endregion + + #region Security + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateWellKnownSid( + [In] WellKnownSidType WellKnownSidType, + [In] [Optional] IntPtr DomainSid, + [In] IntPtr Sid, + ref int SidSize + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EqualDomainSid( + [In] IntPtr Sid1, + [In] IntPtr Sid2, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool Equal + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ConvertStringSidToSid( + [In] string StringSid, + [Out] out IntPtr Sid + ); + + [DllImport("aclui.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EditSecurity( + [In] IntPtr hWnd, + [MarshalAs(UnmanagedType.Interface)] + [In] ISecurityInformation SecurityInformation + ); + + [DllImport("advapi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSecurityDescriptorDacl( + [In] IntPtr SecurityDescriptor, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool DaclPresent, + [Out] out IntPtr Dacl, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool DaclDefaulted + ); + + [DllImport("advapi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSecurityDescriptorGroup( + [In] IntPtr SecurityDescriptor, + [Out] out IntPtr Group, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool GroupDefaulted + ); + + [DllImport("advapi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSecurityDescriptorOwner( + [In] IntPtr SecurityDescriptor, + [Out] out IntPtr Owner, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool OwnerDefaulted + ); + + [DllImport("advapi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSecurityDescriptorSacl( + [In] IntPtr SecurityDescriptor, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool SaclPresent, + [Out] out IntPtr Sacl, + [MarshalAs(UnmanagedType.Bool)] + [Out] out bool SaclDefaulted + ); + + [DllImport("advapi32.dll")] + public static extern Win32Error GetSecurityInfo( + [In] IntPtr Handle, + [In] SeObjectType ObjectType, + [In] SecurityInformation SecurityInformation, + [Out] [Optional] out IntPtr OwnerSid, + [Out] [Optional] out IntPtr GroupSid, + [Out] [Optional] out IntPtr Dacl, + [Out] [Optional] out IntPtr Sacl, + [Out] [Optional] out IntPtr SecurityDescriptor + ); + + [DllImport("advapi32.dll")] + public static extern Win32Error SetSecurityInfo( + [In] IntPtr Handle, + [In] SeObjectType ObjectType, + [In] SecurityInformation SecurityInformation, + [In] [Optional] IntPtr OwnerSid, + [In] [Optional] IntPtr GroupSid, + [In] [Optional] IntPtr Dacl, + [In] [Optional] IntPtr Sacl + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ImpersonateLoggedOnUser( + [In] IntPtr TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool RevertToSelf(); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LogonUser( + [In] string Username, + [In] [Optional] string Domain, + [In] string Password, + [In] LogonType LogonType, + [In] LogonProvider LogonProvider, + [Out] out IntPtr TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken( + [In] IntPtr ProcessHandle, + [In] TokenAccess DesiredAccess, + [Out] out IntPtr TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenThreadToken( + [In] IntPtr ThreadHandle, + [In] TokenAccess DesiredAccess, + [In] bool OpenAsSelf, + [Out] out IntPtr TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DuplicateTokenEx( + [In] IntPtr ExistingToken, + [In] TokenAccess DesiredAccess, + [In] [Optional] IntPtr TokenAttributes, + [In] SecurityImpersonationLevel ImpersonationLevel, + [In] TokenType TokenType, + [Out] out IntPtr NewToken + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [In] ref int TokenInformation, + [In] int TokenInformationLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [Out] [Optional] IntPtr TokenInformation, + [In] int TokenInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [Out] out int TokenInformation, + [In] int TokenInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [Out] out IntPtr TokenInformation, + [In] int TokenInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [Optional] out TokenSource TokenInformation, + [In] int TokenInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTokenInformation( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [Optional] out TokenStatistics TokenInformation, + [In] int TokenInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupAccountName( + [In] [Optional] string SystemName, + [In] string AccountName, + [In] [Optional] IntPtr Sid, + ref int SidSize, + [Out] [Optional] StringBuilder ReferencedDomainName, + ref int ReferencedDomainNameSize, + [Out] out SidNameUse Use + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupAccountSid( + [In] [Optional] string SystemName, + [In] IntPtr Sid, + [Out] [Optional] StringBuilder Name, + ref int NameSize, + [Out] [Optional] StringBuilder ReferencedDomainName, + ref int ReferencedDomainNameSize, + [Out] out SidNameUse Use + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupPrivilegeDisplayName( + [In] [Optional] string SystemName, + [In] string Name, + [Out] [Optional] StringBuilder DisplayName, + ref int DisplayNameSize, + [Out] out int LanguageId + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupPrivilegeName( + [In] [Optional] string SystemName, + [In] ref Luid Luid, + [Out] [Optional] StringBuilder Name, + ref int RequiredSize + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LookupPrivilegeValue( + [In] [Optional] string SystemName, + [In] string PrivilegeName, + [Out] out Luid Luid + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AdjustTokenGroups( + [In] IntPtr TokenHandle, + [In] [MarshalAs(UnmanagedType.Bool)] bool ResetToDefault, + [In] [Optional] ref TokenGroups NewState, + [In] int BufferLength, + [Out] [Optional] IntPtr PreviousState, + [Out] [Optional] IntPtr ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AdjustTokenPrivileges( + [In] IntPtr TokenHandle, + [In] [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges, + [In] [Optional] ref TokenPrivileges NewState, + [In] int BufferLength, + [Out] [Optional] IntPtr PreviousState, + [Out] [Optional] IntPtr ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InitializeSecurityDescriptor( + IntPtr SecurityDescriptor, + [In] int Revision + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetSecurityDescriptorDacl( + IntPtr SecurityDescriptor, + [In] [MarshalAs(UnmanagedType.Bool)] bool DaclPresent, + [In] [Optional] IntPtr Dacl, + [In] [MarshalAs(UnmanagedType.Bool)] bool DaclDefaulted + ); + + #endregion + + #region Services + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseServiceHandle( + [In] IntPtr ServiceHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StartService( + [In] IntPtr Service, + [In] int NumServiceArgs, + [In] [Optional] string[] Args + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeServiceConfig( + [In] IntPtr Service, + [In] ServiceType ServiceType, + [In] ServiceStartType StartType, + [In] ServiceErrorControl ErrorControl, + [In] [Optional] string BinaryPath, + [In] [Optional] string LoadOrderGroup, + [Out] [Optional] IntPtr TagId, + [In] [Optional] string Dependencies, + [In] [Optional] string StartName, + [In] [Optional] string Password, + [In] [Optional] string DisplayName + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ControlService( + [In] IntPtr Service, + [In] ServiceControl Control, + [Out] out ServiceStatus ServiceStatus + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateService( + [In] IntPtr SCManager, + [In] string ServiceName, + [In] [Optional] string DisplayName, + [In] ServiceAccess DesiredAccess, + [In] ServiceType ServiceType, + [In] ServiceStartType StartType, + [In] ServiceErrorControl ErrorControl, + [In] [Optional] string BinaryPathName, + [In] [Optional] string LoadOrderGroup, + [Out] [Optional] IntPtr TagId, + [In] [Optional] IntPtr Dependencies, + [In] [Optional] string ServiceStartName, + [In] [Optional] string Password + ); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteService( + [In] IntPtr Service + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceStatus( + [In] IntPtr Service, + [Out] out ServiceStatus ServiceStatus + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceStatusEx( + [In] IntPtr Service, + [In] int InfoLevel, + [Out] [Optional] out ServiceStatusProcess ServiceStatus, + [In] int BufferSize, + [Out] out int BytesNeeded + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceConfig( + [In] IntPtr Service, + [Out] [Optional] IntPtr ServiceConfig, + [In] int BufferSize, + [Out] out int BytesNeeded + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceConfig2( + [In] IntPtr Service, + [In] ServiceInfoLevel InfoLevel, + [Out] [Optional] IntPtr Buffer, + [In] int BufferSize, + [Out] out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr OpenService( + [In] IntPtr SCManager, + [In] string ServiceName, + [In] ServiceAccess DesiredAccess + ); + + /// + /// Enumerates services in the specified service control manager database. + /// The name and status of each service are provided, along with additional + /// data based on the specified information level. + /// + /// A handle to the service control manager database. + /// Set this to 0. + /// The type of services to be enumerated. + /// The state of the services to be enumerated. + /// A pointer to the buffer that receives the status information. + /// The size of the buffer pointed to by the Services parameter, in bytes. + /// A pointer to a variable that receives the number of bytes needed to + /// return the remaining service entries, if the buffer is too small. + /// A pointer to a variable that receives the number of service + /// entries returned. + /// A pointer to a variable that, on input, specifies the + /// starting point of enumeration. You must set this value to zero the first time the + /// EnumServicesStatusEx function is called. + /// Must be 0 for this definition. + /// A non-zero value for success, zero for failure. + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumServicesStatusEx( + [In] IntPtr SCManager, + [In] IntPtr InfoLevel, + [In] ServiceQueryType ServiceType, + [In] ServiceQueryState ServiceState, + [Out] [Optional] IntPtr Services, + [In] int BufSize, + [Out] out int BytesNeeded, + [Out] out int ServicesReturned, + ref int ResumeHandle, + [In] [Optional] string GroupName + ); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern IntPtr OpenSCManager( + [In] [Optional] string MachineName, + [In] [Optional] string DatabaseName, + [In] ScManagerAccess DesiredAccess + ); + + #endregion + + #region Shell + + [DllImport("shell32.dll", CharSet = CharSet.Unicode)] + public static extern int ShellAbout( + [In] [Optional] IntPtr hWnd, + [In] string App, + [In] [Optional] string OtherStuff, + [In] [Optional] IntPtr IconHandle + ); + + [DllImport("shell32.dll", EntryPoint = "#61", CharSet = CharSet.Unicode)] + public static extern int RunFileDlg( + [In] IntPtr hWnd, + [In] IntPtr Icon, + [In] string Path, + [In] string Title, + [In] string Prompt, + [In] RunFileDialogFlags Flags + ); + + [DllImport("shell32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ShellExecuteEx( + [MarshalAs(UnmanagedType.Struct)] ref ShellExecuteInfo s + ); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetWindowsHookEx( + [In] int HookId, + [In] IntPtr HookFunction, + [In] IntPtr Module, + [In] int ThreadId + ); + + [DllImport("shell32.dll")] + public extern static int ExtractIconEx( + [In] string libName, + [In] int iconIndex, + [Out] IntPtr[] largeIcon, + [Out] IntPtr[] smallIcon, + [In] int nIcons + ); + + [DllImport("shell32.dll")] + public static extern int SHGetFileInfo( + [In] string pszPath, + [In] uint dwFileAttributes, + [Out] out ShFileInfo psfi, + [In] uint cbSizeFileInfo, + [In] uint uFlags); + + [DllImport("shell32.dll", EntryPoint = "#660")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FileIconInit([In] bool RestoreCache); + + #endregion + + #region Statistics + + [DllImport("psapi.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetPerformanceInfo( + [Out] out PerformanceInformation PerformanceInformation, + [In] int Size + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetProcessTimes( + [In] IntPtr ProcessHandle, + [Out] out LargeInteger CreationTime, + [Out] out LargeInteger ExitTime, + [Out] out LargeInteger KernelTime, + [Out] out LargeInteger UserTime + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetProcessIoCounters( + [In] IntPtr ProcessHandle, + [Out] out IoCounters IoCounters + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSystemTimes( + [Out] out LargeInteger IdleTime, + [Out] out LargeInteger KernelTime, + [Out] out LargeInteger UserTime + ); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetThreadTimes( + [In] IntPtr ThreadHandle, + [Out] out LargeInteger CreationTime, + [Out] out LargeInteger ExitTime, + [Out] out LargeInteger KernelTime, + [Out] out LargeInteger UserTime + ); + + #endregion + + #region Symbols/Stack Walking + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool MiniDumpWriteDump( + [In] IntPtr ProcessHandle, + [In] int ProcessId, + [In] IntPtr FileHandle, + [In] MinidumpType DumpType, + [In] IntPtr ExceptionParam, + [In] IntPtr UserStreamParam, + [In] IntPtr CallbackParam + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StackWalk64( + [In] MachineType MachineType, + [In] IntPtr ProcessHandle, + [In] IntPtr ThreadHandle, + ref StackFrame64 StackFrame, + [In] IntPtr ContextRecord, + [In] [Optional] ReadProcessMemoryProc64 ReadMemoryRoutine, + [In] [Optional] FunctionTableAccessProc64 FunctionTableAccessRoutine, + [In] [Optional] GetModuleBaseProc64 GetModuleBaseRoutine, + [In] [Optional] IntPtr TranslateAddress + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StackWalk64( + [In] MachineType MachineType, + [In] IntPtr ProcessHandle, + [In] IntPtr ThreadHandle, + ref StackFrame64 StackFrame, + ref Context ContextRecord, + [In] [Optional] ReadProcessMemoryProc64 ReadMemoryRoutine, + [In] [Optional] FunctionTableAccessProc64 FunctionTableAccessRoutine, + [In] [Optional] GetModuleBaseProc64 GetModuleBaseRoutine, + [In] [Optional] IntPtr TranslateAddress + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StackWalk64( + [In] MachineType MachineType, + [In] IntPtr ProcessHandle, + [In] IntPtr ThreadHandle, + ref StackFrame64 StackFrame, + ref ContextAmd64 ContextRecord, + [In] [Optional] ReadProcessMemoryProc64 ReadMemoryRoutine, + [In] [Optional] FunctionTableAccessProc64 FunctionTableAccessRoutine, + [In] [Optional] GetModuleBaseProc64 GetModuleBaseRoutine, + [In] [Optional] IntPtr TranslateAddress + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymCleanup( + [In] IntPtr ProcessHandle + ); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymEnumSymbols( + [In] IntPtr ProcessHandle, + [In] ulong BaseOfDll, + [In] [Optional] string Mask, + [In] SymEnumSymbolsProc EnumSymbolsCallback, + [In] [Optional] IntPtr UserContext + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymFromAddr( + [In] IntPtr ProcessHandle, + [In] ulong Address, + [Out] out ulong Displacement, + [In] IntPtr Symbol + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymFromIndex( + [In] IntPtr ProcessHandle, + [In] ulong BaseOfDll, + [In] int Index, + IntPtr Symbol + ); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymFromName( + [In] IntPtr ProcessHandle, + [In] string Name, + [In] IntPtr Symbol + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + public static extern IntPtr SymFunctionTableAccess64( + [In] IntPtr ProcessHandle, + [In] ulong AddrBase + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymGetLineFromAddr64( + [In] IntPtr ProcessHandle, + [In] ulong Address, + [Out] out int Displacement, + [Out] out ImagehlpLine64 Line + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + public static extern ulong SymGetModuleBase64( + [In] IntPtr ProcessHandle, + [In] ulong Address + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + public static extern SymbolOptions SymGetOptions(); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymGetSearchPath( + [In] IntPtr ProcessHandle, + [Out] StringBuilder SearchPath, + [In] int SearchPathLength + ); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymInitialize( + [In] IntPtr ProcessHandle, + [In] [Optional] string UserSearchPath, + [In] bool InvadeProcess + ); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + public static extern long SymLoadModule64( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr FileHandle, + [In] [Optional] string ImageName, + [In] [Optional] string ModuleName, + [In] ulong BaseOfDll, + [In] int SizeOfDll + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + public static extern SymbolOptions SymSetOptions( + [In] SymbolOptions SymOptions + ); + + [DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymSetSearchPath( + [In] IntPtr ProcessHandle, + [In] [Optional] string SearchPath + ); + + [DllImport("dbghelp.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymUnloadModule64( + [In] IntPtr ProcessHandle, + [In] ulong BaseOfDll + ); + + [DllImport("symsrv.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SymbolServerSetOptions( + [In] SymbolServerOption Options, + [In] ulong Data + ); + + #endregion + + #region TCP + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int SetTcpEntry( + [In] ref MibTcpRow TcpRow + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int GetExtendedTcpTable( + [Out] IntPtr Table, + ref int Size, + [In] bool Order, + [In] AiFamily IpVersion, + [In] TcpTableClass TableClass, + [In] int Reserved + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int GetTcpStatistics( + [Out] out MibTcpStats pStats + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public static extern int GetTcpTable( + [Out] byte[] tcpTable, + ref int pdwSize, + [In] bool bOrder + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public static extern int GetTcp6Table( + [Out] byte[] tcpTable, + ref int pdwSize, + [In] bool bOrder); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int AllocateAndGetTcpExTableFromStack( + [Out] out IntPtr pTable, + [In] bool bOrder, + [In] IntPtr heap, + [In] int flags, + [In] int family + ); + + #endregion + + #region Terminal Server + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ProcessIdToSessionId( + [In] int ProcessId, + [Out] out int SessionId + ); + + [DllImport("wtsapi32.dll")] + public static extern void WTSCloseServer( + [In] IntPtr ServerHandle + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSConnectSession( + [In] int LogonId, + [In] int TargetLogonId, + [In] string Password, + [In] bool Wait + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSDisconnectSession( + [In] IntPtr ServerHandle, + [In] int SessionId, + [In] bool Wait + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSEnumerateProcesses( + [In] IntPtr ServerHandle, + [In] int Reserved, + [In] int Version, + [Out] out IntPtr ProcessInfo, + [Out] out int Count + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSEnumerateSessions( + [In] IntPtr ServerHandle, + [In] int Reserved, + [In] int Version, + [Out] out IntPtr SessionInfo, + [Out] out int Count + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSFreeMemory( + [In] IntPtr Memory + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + public static extern int WTSGetActiveConsoleSessionId(); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSLogoffSession( + [In] IntPtr ServerHandle, + [In] int SessionId, + [In] bool Wait + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr WTSOpenServer( + [In] string ServerName + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSQuerySessionInformation( + [In] IntPtr ServerHandle, + [In] int SessionId, + [In] WtsInformationClass InfoClass, + [Out] out IntPtr Buffer, + [Out] out int BytesReturned + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSRegisterSessionNotification( + [In] IntPtr hWnd, + [In] WtsNotificationFlags Flags + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSRegisterSessionNotificationEx( + [In] IntPtr ServerHandle, + [In] IntPtr hWnd, + [In] WtsNotificationFlags Flags + ); + + [DllImport("wtsapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSSendMessage( + [In] IntPtr ServerHandle, + [In] int SessionId, + [In] string Title, + [In] int TitleLength, + [In] string Message, + [In] int MessageLength, + [In] int Style, + [In] int Timeout, + [Out] out DialogResult Response, + [In] bool Wait + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSShutdownSystem( + [In] IntPtr ServerHandle, + [In] WtsShutdownFlags ShutdownFlag + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSTerminateProcess( + [In] IntPtr ServerHandle, + [In] int ProcessId, + [In] int ExitCode + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSUnRegisterSessionNotification( + [In] IntPtr hWnd + ); + + [DllImport("wtsapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WTSUnRegisterSessionNotificationEx( + [In] IntPtr ServerHandle, + [In] IntPtr hWnd + ); + + #endregion + + #region Threads + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryThreadCycleTime( + [In] IntPtr ThreadHandle, + [Out] out ulong CycleTime + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueueUserAPC( + [In] IntPtr APC, + [In] IntPtr ThreadHandle, + [In] IntPtr Data + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueueUserAPC( + [MarshalAs(UnmanagedType.FunctionPtr)] + [In] ApcRoutine APC, + [In] IntPtr ThreadHandle, + [In] IntPtr Data + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetExitCodeThread( + [In] IntPtr ThreadHandle, + [Out] out int ExitCode + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetThreadPriority( + [In] IntPtr ThreadHandle, + [In] int Priority + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetThreadPriority([In] IntPtr ThreadHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateThread( + [In] [Optional] IntPtr ThreadAttributes, + [In] int StackSize, + [In] [MarshalAs(UnmanagedType.FunctionPtr)] ThreadStart StartAddress, + [In] IntPtr Parameter, + [In] int CreationFlags, + [Out] out int ThreadId + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetProcessIdOfThread( + [In] IntPtr ThreadHandle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetThreadId( + [In] IntPtr ThreadHandle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenThread( + [In] ThreadAccess DesiredAccess, + [In] bool InheritHandle, + [In] int ThreadId + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TerminateThread( + IntPtr ThreadHandle, + [In] int ExitCode + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int SuspendThread( + [In] IntPtr ThreadHandle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int ResumeThread( + [In] IntPtr ThreadHandle + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetThreadContext( + [In] IntPtr ThreadHandle, + [In] ref Context Context + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetThreadContext( + [In] IntPtr ThreadHandle, + ref Context Context + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateRemoteThread( + [In] IntPtr ProcessHandle, + [In] IntPtr ThreadAttributes, + [In] IntPtr StackSize, + [In] IntPtr StartAddress, + [In] IntPtr Parameter, + [In] ProcessCreationFlags CreationFlags, + [Out] out int ThreadId + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int GetCurrentThreadId(); + + #endregion + + #region Toolhelp + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateToolhelp32Snapshot( + [In] SnapshotFlags dwFlags, + [In] int th32ProcessID + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Process32First( + [In] IntPtr hSnapshot, + [MarshalAs(UnmanagedType.Struct)] ref ProcessEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Process32Next( + [In] IntPtr hSnapshot, + [Out] [MarshalAs(UnmanagedType.Struct)] out ProcessEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Thread32First( + [In] IntPtr hSnapshot, + [MarshalAs(UnmanagedType.Struct)] ref ThreadEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Thread32Next( + [In] IntPtr hSnapshot, + [Out] [MarshalAs(UnmanagedType.Struct)] out ThreadEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Module32First( + [In] IntPtr hSnapshot, + [MarshalAs(UnmanagedType.Struct)] ref ModuleEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Module32Next( + [In] IntPtr hSnapshot, + [Out] [MarshalAs(UnmanagedType.Struct)] out ModuleEntry32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Heap32ListFirst( + [In] IntPtr hSnapshot, + [MarshalAs(UnmanagedType.Struct)] ref HeapList32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Heap32ListNext( + [In] IntPtr hSnapshot, + [Out] [MarshalAs(UnmanagedType.Struct)] out HeapList32 lppe + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool Heap32First( + [MarshalAs(UnmanagedType.Struct)] ref HeapEntry32 lppe, + [In] int ProcessID, + [In] IntPtr HeapID + ); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int Heap32Next( + [Out] [MarshalAs(UnmanagedType.Struct)] out HeapEntry32 lppe + ); + + #endregion + + #region UDP + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int GetExtendedUdpTable( + [Out] IntPtr Table, + ref int Size, + [In] bool Order, + [In] AiFamily IpVersion, + [In] UdpTableClass TableClass, + [In] int Reserved + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public static extern int GetUdpStatistics( + [Out] out MibUdpStats pStats + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public static extern int GetUdpTable( + [Out] byte[] udpTable, + ref int pdwSize, + [In] bool bOrder + ); + + [DllImport("iphlpapi.dll", SetLastError = true)] + public extern static int AllocateAndGetUdpExTableFromStack( + [Out] out IntPtr pTable, + [In] bool bOrder, + [In] IntPtr heap, + [In] int flags, + [In] int family + ); + + #endregion + + #region User + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SwitchDesktop( + [In] IntPtr DesktopHandle + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetUserObjectSecurity( + [In] IntPtr Handle, + [In] ref SiRequested SiRequested, + [In] IntPtr Sid + ); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr OpenDesktop( + [In] string Desktop, + [In] int Flags, + [In] bool Inherit, + [In] DesktopAccess DesiredAccess + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseDesktop( + [In] IntPtr Handle + ); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr OpenWindowStation( + [In] string WinSta, + [In] bool Inherit, + [In] WindowStationAccess DesiredAccess + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseWindowStation( + [In] IntPtr Handle + ); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetThreadDesktop( + [In] int ThreadId + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetThreadDesktop( + [In] IntPtr DesktopHandle + ); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr GetProcessWindowStation(); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetProcessWindowStation( + [In] IntPtr WindowStationHandle + ); + + [DllImport("userenv.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateEnvironmentBlock( + [Out] out IntPtr Environment, + [In] IntPtr TokenHandle, + [In] bool Inherit + ); + + [DllImport("userenv.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool LoadUserProfile( + [In] IntPtr TokenHandle, + ref ProfileInformation ProfileInfo + ); + + [DllImport("userenv.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool UnloadUserProfile( + [In] IntPtr TokenHandle, + [In] IntPtr ProfileHandle + ); + + #endregion + + #region Windows + + [DllImport("user32.dll")] + public static extern bool EndTask( + [In] IntPtr hWnd, + [In] bool ShutDown, + [In] bool Force + ); + + [DllImport("user32.dll")] + public static extern bool UpdateWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll")] + public static extern bool MoveWindow( + [In] IntPtr hWnd, + [In] int X, + [In] int Y, + [In] int Width, + [In] int Height, + [In] bool Repaint + ); + + [DllImport("user32.dll")] + public static extern int GetSystemMetrics( + [In] int Index + ); + + [DllImport("user32.dll")] + public static extern bool InvalidateRect( + [In] IntPtr hWnd, + [In] IntPtr Rect, + [In] bool Erase + ); + + [DllImport("user32.dll")] + public static extern bool InvalidateRect( + [In] IntPtr hWnd, + [In] ref Rect Rect, + [In] bool Erase + ); + + [DllImport("user32.dll")] + public static extern bool RedrawWindow( + [In] IntPtr hWnd, + [In] IntPtr UpdateRect, + [In] IntPtr UpdateRgn, + [In] RedrawWindowFlags Flags + ); + + [DllImport("user32.dll")] + public static extern int ReleaseDC( + [In] IntPtr hWnd, + [In] IntPtr hDC + ); + + [DllImport("user32.dll")] + public static extern IntPtr GetWindowDC( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll")] + public static extern IntPtr WindowFromPoint( + [In] Point location + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetCursorPos( + [Out] out Point location + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeWindowMessageFilter( + [In] WindowMessage message, + [In] UipiFilterFlag flag + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage( + [In] IntPtr hWnd, + [In] WindowMessage msg, + [In] int w, + [In] int l + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessageTimeout( + [In] IntPtr hWnd, + [In] WindowMessage msg, + [In] int w, + [In] int l, + [In] SmtoFlags flags, + [In] int timeout, + [Out] out int result + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool PostMessage( + [In] IntPtr hWnd, + [In] WindowMessage msg, + [In] int w, + [In] int l + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AllowSetForegroundWindow( + [In] int processId + ); + + [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)] + public static extern HResult SetWindowTheme( + [In] IntPtr hWnd, + [In] string appName, + [In] string idList + ); + + [DllImport("user32.dll")] + public static extern int GetGuiResources( + [In] IntPtr ProcessHandle, + [In] int UserObjects + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyIcon( + [In] IntPtr Handle + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BringWindowToTop( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll")] + public static extern bool EnumWindows( + [In] [MarshalAs(UnmanagedType.FunctionPtr)] EnumWindowsProc Callback, + [In] int param + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumThreadWindows( + [In] int ThreadId, + [In] [MarshalAs(UnmanagedType.FunctionPtr)] EnumThreadWndProc callback, + [In] int Param + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumChildWindows( + [In] IntPtr hWnd, + [In] [MarshalAs(UnmanagedType.FunctionPtr)] EnumChildProc callback, + [In] int param + ); + + [DllImport("user32.dll")] + public static extern int GetWindowThreadProcessId( + [In] IntPtr hWnd, + [Out] out int processId + ); + + [DllImport("user32.dll")] + public static extern IntPtr SetActiveWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool PeekMessage( + [Out] out Message msg, + [In] IntPtr hWnd, + [In] uint messageFilterMin, + [In] uint messageFilterMax, + [In] PeekMessageFlags flags + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TranslateMessage( + [In] ref Message msg + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern IntPtr DispatchMessage( + [In] ref Message msg + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr DefWindowProc( + [In] IntPtr hWnd, + [In] WindowMessage msg, + [In] IntPtr wParam, + [In] IntPtr lParam + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern void PostQuitMessage( + [In] int exitCode + ); + +#if _WIN64 + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)] +#else + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)] +#endif + private static extern IntPtr SetWindowLongPtr( + [In] IntPtr hWnd, + [In] GetWindowLongOffset Index, + [In] [MarshalAs(UnmanagedType.FunctionPtr)] WndProcDelegate WndProc + ); + +#if _WIN64 + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)] +#else + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)] +#endif + public static extern IntPtr SetWindowLongPtr( + [In] IntPtr hWnd, + [In] GetWindowLongOffset Index, + [In] IntPtr NewLong + ); + +#if _WIN64 + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)] +#else + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)] +#endif + public static extern IntPtr GetWindowLongPtr( + [In] IntPtr hWnd, + [In] GetWindowLongOffset Index + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetClientRect( + [In] IntPtr hWnd, + [Out] out Rect rect + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowRect( + [In] IntPtr hWnd, + [Out] out Rect rect + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos( + [In] IntPtr hWnd, + [In] IntPtr hWndAfter, + [In] int x, + [In] int y, + [In] int w, + [In] int h, + [In] uint flags + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ScreenToClient( + [In] IntPtr hWnd, + ref Point point + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SetFocus( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr GetParent( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetMonitorInfo( + [In] IntPtr hWnd, + [Out] out MonitorInformation info + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr MonitorFromWindow( + [In] IntPtr hWnd, + [In] uint flags + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern short GetAsyncKeyState( + [In] uint Key + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SetCapture( + [In] IntPtr handle + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ReleaseCapture(); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ShowWindow( + [In] IntPtr hWnd, + [In] ShowWindowType flags + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetMenu( + [In] IntPtr hWnd, + [In] IntPtr menuHandle + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseWindow( + [In] IntPtr hWnd); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsIconic( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AdjustWindowRect( + ref Rect rect, + [In] WindowStyles style, + [In] [MarshalAs(UnmanagedType.Bool)]bool menu + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr RegisterClass( + [In] ref WindowClass wndClass + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UnregisterClass( + [In] [MarshalAs(UnmanagedType.LPTStr)] string className, + [In] IntPtr instanceHandle + ); + + [DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx", CharSet = CharSet.Auto)] + public static extern IntPtr CreateWindow( + [In] int ExStyle, + [In] [MarshalAs(UnmanagedType.LPTStr)] string ClassName, + [In] [MarshalAs(UnmanagedType.LPTStr)] string WindowName, + [In] WindowStyles Style, + [In] int X, + [In] int Y, + [In] int Width, + [In] int Height, + [In] IntPtr Parent, + [In] IntPtr MenuHandle, + [In] IntPtr InstanceHandle, + [In] IntPtr Zero + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int GetCaretBlinkTime(); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int InternalGetWindowText( + [In] IntPtr hWnd, + [In] IntPtr String, + [In] int MaxCount + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool IsHungAppWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool IsWindow( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool IsWindowVisible( + [In] IntPtr hWnd + ); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement( + [In] IntPtr hWnd, + ref WindowPlacement WindowPlacement + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr FindWindow( + [In] string ClassName, + [In] string WindowName + ); + + [DllImport("user32.dll")] + public static extern IntPtr GetDesktopWindow(); + + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + public static extern IntPtr GetShellWindow(); + + #endregion + + #region Window Stations + + [DllImport("winsta.dll", SetLastError = true)] + public static extern bool WinStationRevertFromServicesSession(); + + [DllImport("winsta.dll", SetLastError = true)] + public static extern bool WinStationSwitchToServicesSession(); + + [DllImport("winsta.dll", SetLastError = true)] + public static extern bool WinStationTerminateProcess( + [In] IntPtr ServerHandle, + [In] int ProcessId, + [In] int ExitCode + ); + + #endregion + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/HResult.cs b/branches/ph-plugins/ProcessHacker.Native/Api/HResult.cs new file mode 100644 index 000000000..b91d0cb78 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/HResult.cs @@ -0,0 +1,109 @@ +/* + * Process Hacker - + * HResult values + * + * Copyright (C) 2009 wj32 + * Copyright (C) 2009 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.Api +{ + /*////////////////////////// + // // + // COM Error Codes // + // // + //////////////////////////// + // + // The return value of COM functions and methods is an HRESULT. + // This is not a handle to anything, but is merely a 32-bit value + // with several fields encoded in the value. The parts of an + // HRESULT are shown below. + // + // Many of the macros and functions below were orginally defined to + // operate on SCODEs. SCODEs are no longer used. The macros are + // still present for compatibility and easy porting of Win16 code. + // Newly written code should use the HRESULT macros and functions. + // + // HRESULTs are 32 bit values layed out as follows: + // + // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + // +-+-+-+-+-+---------------------+-------------------------------+ + // | S | R | C | N | r | Facility | Code | + // +-+-+-+-+-+---------------------+-------------------------------+ + // + // where + // + // S - Severity - indicates success/fail + // + // 0 - Success + // 1 - Fail (COERROR) + // + // R - reserved portion of the facility code, corresponds to NT's + // second severity bit. + // + // C - reserved portion of the facility code, corresponds to NT's + // C field. + // + // N - reserved portion of the facility code. Used to indicate a + // mapped NT status value. + // + // r - reserved portion of the facility code. Reserved for internal + // use. Used to indicate HRESULT values that are not status + // values, but are instead message ids for display strings. + // + // Facility - is the facility code + // + // Code - is the facility's status code + */ + + public enum HResult : uint + { + False = 0x0001, + OK = 0x0000, + Cancelled = 1223, + + Error = 0x80000000, + NoInterface = 0x80004002, + Fail = 0x80004005, + TypeElementNotFound = 0x8002802b, + NoObject = 0x800401e5, + OutOfMemory = 0x8007000e, + InvalidArgument = 0x80070057, + ResourceInUse = 0x800700aa, + ElementNotFound = 0x80070490 + } + + public static class HResultExtensions + { + public static bool IsError(this HResult result) + { + //Return != OK because there are come errors with lower values than HResult.False + return result != HResult.OK; + } + + public static void ThrowIf(this HResult result) + { + if (result.IsError()) + throw Marshal.GetExceptionForHR((int)result); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/ISecurityInformation.cs b/branches/ph-plugins/ProcessHacker.Native/Api/ISecurityInformation.cs new file mode 100644 index 000000000..4938bca58 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/ISecurityInformation.cs @@ -0,0 +1,80 @@ +/* + * Process Hacker - + * ISecurityInformation definition + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.Api +{ + [ComImport, Guid("965fc360-16ff-11d0-91cb-00aa00bbb723"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISecurityInformation + { + [PreserveSig] + HResult GetObjectInformation( + [Out] out SiObjectInfo ObjectInfo + ); + + [PreserveSig] + HResult GetSecurity( + [In] SecurityInformation RequestedInformation, + [Out] out IntPtr SecurityDescriptor, + [In] bool Default + ); + + [PreserveSig] + HResult SetSecurity( + [In] SecurityInformation SecurityInformation, + [In] IntPtr SecurityDescriptor + ); + + [PreserveSig] + HResult GetAccessRights( + [In] ref Guid ObjectType, + [In] SiObjectInfoFlags Flags, + [Out] out IntPtr Access, + [Out] out int Accesses, + [Out] out int DefaultAccess + ); + + [PreserveSig] + HResult MapGeneric( + [In] ref Guid ObjectType, + [In] ref AceFlags AceFlags, + [In] ref int Mask + ); + + [PreserveSig] + HResult GetInheritTypes( + [Out] out IntPtr InheritTypes, + [Out] out int InheritTypesCount + ); + + [PreserveSig] + HResult PropertySheetPageCallback( + [In] IntPtr hWnd, + [In] SiCallbackMessage Msg, + [In] SiPageType Page + ); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/LsaEnums.cs b/branches/ph-plugins/ProcessHacker.Native/Api/LsaEnums.cs new file mode 100644 index 000000000..9dca920ef --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/LsaEnums.cs @@ -0,0 +1,120 @@ +/* + * Process Hacker - + * LSA enumerations + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace ProcessHacker.Native.Api +{ + [Flags] + public enum LsaOperationalMode + { + PasswordProtected = 0x1, + IndividualAccounts = 0x2, + MandatoryAccess = 0x4, + LogFull = 0x8 + } + + public enum PolicyDomainInformationClass + { + PolicyDomainEfsInformation = 2, + PolicyDomainKerberosTicketInformation + } + + public enum PolicyInformationClass + { + PolicyAuditLogInformation = 1, + PolicyAuditEventsInformation, + PolicyPrimaryDomainInformation, + PolicyPdAccountInformation, + PolicyAccountDomainInformation, + PolicyLsaServerRoleInformation, + PolicyReplicaSourceInformation, + PolicyDefaultQuotaInformation, + PolicyModificationInformation, + PolicyAuditFullSetInformation, + PolicyAuditFullQueryInformation, + PolicyDnsDomainInformation, + PolicyDnsDomainInformationInt + } + + public enum PolicyNotificationInformationClass + { + PolicyNotifyAuditEventsInformation = 1, + PolicyNotifyAccountDomainInformation, + PolicyNotifyServerRoleInformation, + PolicyNotifyDnsDomainInformation, + PolicyNotifyDomainEfsInformation, + PolicyNotifyDomainKerberosTicketInformation, + PolicyNotifyMachineAccountPasswordInformation + } + + public enum SecurityLogonType + { + Interactive = 2, + Network, + Batch, + Service, + Proxy, + Unlock, + NetworkCleartext, + NewCredentials, + RemoteInteractive, + CachedInteractive, + CachedRemoteInteractive, + CachedUnlock + } + + [Flags] + public enum SecuritySystemAccess : int + { + Interactive = 0x1, + Network = 0x2, + Batch = 0x4, + Service = 0x10, + Proxy = 0x20, + DenyInteractive = 0x40, + DenyNetwork = 0x80, + DenyBatch = 0x100, + DenyService = 0x200, + RemoteInteractive = 0x400, + DenyRemoteInteractive = 0x800 + } + + public enum TrustedInformationClass + { + TrustedDomainNameInformation = 1, + TrustedControllersInformation, + TrustedPosixOffsetInformation, + TrustedPasswordInformation, + TrustedDomainInformationBasic, + TrustedDomainInformationEx, + TrustedDomainAuthInformation, + TrustedDomainFullInformation, + TrustedDomainAuthInformationInternal, + TrustedDomainFullInformationInternal, + TrustedDomainInformationEx2Internal, + TrustedDomainFullInformation2Internal + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/LsaFunctions.cs b/branches/ph-plugins/ProcessHacker.Native/Api/LsaFunctions.cs new file mode 100644 index 000000000..d982add5a --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/LsaFunctions.cs @@ -0,0 +1,501 @@ +/* + * Process Hacker - + * LSA functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Api +{ + public static partial class Win32 + { + /* Note: Be very careful about where these functions are + * imported from. Some come from advapi32.dll, others are + * from secur32.dll. + * + * An important side-effect is that ALL buffers allocated + * by secur32 functions MUST be freed with + * LsaFreeReturnBuffer, while advapi32-allocated buffers + * must be freed with LsaFreeMemory. + */ + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaAddAccountRights( + [In] IntPtr PolicyHandle, + [In] IntPtr AccountSid, // Sid* + [In] UnicodeString[] UserRights, + [In] int CountOfRights + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaAddPrivilegesToAccount( + [In] IntPtr AccountHandle, + [In] IntPtr Privileges // PrivilegeSet* + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaClearAuditLog( + [In] IntPtr PolicyHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaClose( + [In] IntPtr ObjectHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaChangePassword( + [In] ref UnicodeString ServerName, + [In] ref UnicodeString DomainName, + [In] ref UnicodeString AccountName, + [In] ref UnicodeString OldPassword, + [In] ref UnicodeString NewPassword + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaConnectUntrusted( + [Out] out IntPtr LsaHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaCreateAccount( + [In] IntPtr PolicyHandle, + [In] IntPtr AccountSid, // Sid* + [In] LsaAccountAccess DesiredAccess, + [Out] out IntPtr AccountHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaCreateSecret( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString SecretName, + [In] LsaSecretAccess DesiredAccess, + [Out] out IntPtr SecretHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaCreateTrustedDomain( + [In] IntPtr PolicyHandle, + [In] ref LsaTrustInformation TrustedDomainInformation, + [In] LsaTrustedAccess DesiredAccess, + [Out] out IntPtr TrustedDomainHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaDelete( + [In] IntPtr ObjectHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaDeleteTrustedDomain( + [In] IntPtr PolicyHandle, + [In] IntPtr TrustedDomainSid // Sid* + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaDeregisterLogonProcess( + [In] IntPtr LsaHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumerateAccounts( + [In] IntPtr PolicyHandle, + ref int EnumerationContext, + [Out] out IntPtr Buffer, // Sid*** + [In] int PreferredMaximumLength, + [Out] out int CountReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumerateAccountsWithUserRight( + [In] IntPtr PolicyHandle, + [In] [Optional] ref UnicodeString UserRight, + [Out] out IntPtr Buffer, // Sid*** + [Out] out int CountReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumerateAccountRights( + [In] IntPtr PolicyHandle, + [In] IntPtr AccountSid, // Sid* + [Out] IntPtr UserRights, // UnicodeString** + [Out] out int CountOfRights + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaEnumerateLogonSessions( + [Out] out int LogonSessionCount, + [Out] out IntPtr LogonSessionList // Luid** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumeratePrivileges( + [In] IntPtr PolicyHandle, + ref int EnumerationContext, + [Out] out IntPtr Buffer, // PolicyPrivilegeDefinition** + [In] int PreferredMaximumLength, + [Out] out int CountReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumeratePrivilegesOfAccount( + [In] IntPtr AccountHandle, + [Out] out IntPtr Privileges // PrivilegeSet** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumerateTrustedDomains( + [In] IntPtr PolicyHandle, + ref int EnumerationContext, + [Out] out IntPtr Buffer, // LsaTrustInformation** + [In] int PreferredMaximumLength, + [Out] out int CountReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaEnumerateTrustedDomainsEx( + [In] IntPtr PolicyHandle, + ref int EnumerationContext, + [Out] out IntPtr Buffer, // TrustedDomainInformationEx** + [In] int PreferredMaximumLength, + [Out] out int CountReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaFreeMemory( + [In] IntPtr Buffer + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaFreeReturnBuffer( + [In] IntPtr Buffer + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaGetLogonSessionData( + [In] ref Luid LogonId, + [Out] out IntPtr LogonSessionData // SecurityLogonSessionData** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaGetQuotasForAccount( + [In] IntPtr AccountHandle, + [Out] out QuotaLimits QuotaLimits + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaGetRemoteUserName( + [In] [Optional] ref UnicodeString SystemName, + [Out] out IntPtr UserName, // UnicodeString** + [Out] [Optional] out IntPtr DomainName // UnicodeString** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaGetSystemAccessAccount( + [In] IntPtr AccountHandle, + [Out] out SecuritySystemAccess SystemAccess + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaGetUserName( + [Out] out IntPtr UserName, // UnicodeString** + [Out] [Optional] out IntPtr DomainName // UnicodeString** + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaLookupAuthenticationPackage( + [In] IntPtr LsaHandle, + [In] ref AnsiString PackageName, + [Out] out int AuthenticationPackage + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupNames( + [In] IntPtr PolicyHandle, + [In] int Count, + [In] UnicodeString[] Names, + [Out] out IntPtr ReferencedDomains, // LsaReferencedDomainList** + [Out] out IntPtr Sids // LsaTranslatedSid** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupNames2( + [In] IntPtr PolicyHandle, + [In] int Flags, + [In] int Count, + [In] UnicodeString[] Names, + [Out] out IntPtr ReferencedDomains, // LsaReferencedDomainList** + [Out] out IntPtr Sids // LsaTranslatedSid2** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupPrivilegeDisplayName( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString Name, + [Out] out IntPtr DisplayName, // UnicodeString** + [Out] out short LanguageReturned + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupPrivilegeName( + [In] IntPtr PolicyHandle, + [In] ref Luid Value, + [Out] out IntPtr Name // UnicodeString** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupPrivilegeValue( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString Name, + [Out] out Luid Value + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaLookupSids( + [In] IntPtr PolicyHandle, + [In] int Count, + [In] IntPtr[] Sids, // Sid** + [Out] out IntPtr ReferencedDomains, // LsaReferencedDomainList** + [Out] out IntPtr Names // LsaTranslatedName** + ); + + [DllImport("advapi32.dll")] + public static extern int LsaNtStatusToWinError( + [In] NtStatus Status + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenAccount( + [In] IntPtr PolicyHandle, + [In] IntPtr AccountSid, // Sid* + [In] LsaAccountAccess DesiredAccess, + [Out] out IntPtr AccountHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenPolicy( + [In] [Optional] ref UnicodeString SystemName, + [In] ref ObjectAttributes ObjectAttributes, + [In] LsaPolicyAccess DesiredAccess, + [Out] out IntPtr PolicyHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenPolicySce( + [In] [Optional] ref UnicodeString SystemName, + [In] ref ObjectAttributes ObjectAttributes, + [In] LsaPolicyAccess DesiredAccess, + [Out] out IntPtr PolicyHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenSecret( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString SecretName, + [In] LsaSecretAccess DesiredAccess, + [Out] out IntPtr SecretHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenTrustedDomain( + [In] IntPtr PolicyHandle, + [In] IntPtr TrustedDomainSid, // Sid* + [In] LsaTrustedAccess DesiredAccess, + [Out] out IntPtr TrustedDomainHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaOpenTrustedDomainByName( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString TrustedDomainName, + [In] LsaTrustedAccess DesiredAccess, + [Out] out IntPtr TrustedDomainHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQueryDomainInformationPolicy( + [In] IntPtr PolicyHandle, + [In] PolicyDomainInformationClass InformationClass, + [Out] out IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQueryInformationPolicy( + [In] IntPtr PolicyHandle, + [In] PolicyInformationClass InformationClass, + [Out] out IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQueryInfoTrustedDomain( + [In] IntPtr TrustedDomainHandle, + [In] TrustedInformationClass InformationClass, + [Out] out IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQuerySecret( + [In] IntPtr SecretHandle, + [Out] [Optional] out IntPtr CurrentValue, // UnicodeString** + [Out] [Optional] out long CurrentValueSetTime, + [Out] [Optional] out IntPtr OldValue, // UnicodeString** + [Out] [Optional] out long OldValueSetTime + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQuerySecurityObject( + [In] IntPtr ObjectHandle, + [In] SecurityInformation SecurityInformation, + [Out] out IntPtr SecurityDescriptor // SecurityDescriptor** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQueryTrustedDomainInfo( + [In] IntPtr PolicyHandle, + [In] IntPtr TrustedDomainSid, // Sid* + [In] TrustedInformationClass InformationClass, + [Out] out IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaQueryTrustedDomainInfoByName( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString TrustedDomainName, + [In] TrustedInformationClass InformationClass, + [Out] out IntPtr Buffer + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaRegisterLogonProcess( + [In] ref AnsiString LogonProcessName, + [Out] out IntPtr LsaHandle, + [Out] out LsaOperationalMode SecurityMode + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaRegisterPolicyChangeNotification( + [In] PolicyNotificationInformationClass InformationClass, + [In] IntPtr NotificationEventHandle + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaRemoveAccountRights( + [In] IntPtr PolicyHandle, + [In] IntPtr AccountSid, // Sid* + [In] bool AllRights, + [In] UnicodeString[] UserRights, + [In] int CountOfRights + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaRemovePrivilegesFromAccount( + [In] IntPtr AccountHandle, + [In] bool AllPrivileges, + [In] [Optional] IntPtr Privileges // PrivilegeSet* + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaRetrievePrivateData( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString KeyName, + [Out] out IntPtr PrivateData // UnicodeString** + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetDomainInformationPolicy( + [In] IntPtr PolicyHandle, + [In] PolicyDomainInformationClass InformationClass, + [In] [Optional] IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetInformationPolicy( + [In] IntPtr PolicyHandle, + [In] PolicyInformationClass InformationClass, + [In] IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetInformationTrustedDomain( + [In] IntPtr TrustedDomainHandle, + [In] TrustedInformationClass InformationClass, + [In] IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetQuotasForAccount( + [In] IntPtr AccountHandle, + [In] ref QuotaLimits QuotaLimits + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetSecret( + [In] IntPtr SecretHandle, + [In] [Optional] ref UnicodeString CurrentValue, + [In] [Optional] ref UnicodeString OldValue + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetSecurityObject( + [In] IntPtr ObjectHandle, + [In] SecurityInformation SecurityInformation, + [In] IntPtr SecurityDescriptor // SecurityDescriptor* + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetSystemAccessAccount( + [In] IntPtr AccountHandle, + [In] SecuritySystemAccess SystemAccess + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetTrustedDomainInformation( + [In] IntPtr PolicyHandle, + [In] IntPtr TrustedDomainSid, // Sid* + [In] TrustedInformationClass InformationClass, + [In] IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaSetTrustedDomainInfoByName( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString TrustedDomainName, + [In] TrustedInformationClass InformationClass, + [In] IntPtr Buffer + ); + + [DllImport("advapi32.dll")] + public static extern NtStatus LsaStorePrivateData( + [In] IntPtr PolicyHandle, + [In] ref UnicodeString KeyName, + [In] [Optional] ref UnicodeString PrivateData + ); + + [DllImport("secur32.dll")] + public static extern NtStatus LsaUnregisterPolicyChangeNotification( + [In] PolicyNotificationInformationClass InformationClass, + [In] IntPtr NotificationEventHandle + ); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/LsaStructs.cs b/branches/ph-plugins/ProcessHacker.Native/Api/LsaStructs.cs new file mode 100644 index 000000000..f18b4699e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/LsaStructs.cs @@ -0,0 +1,114 @@ +/* + * Process Hacker - + * LSA structures + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace ProcessHacker.Native.Api +{ + [StructLayout(LayoutKind.Sequential)] + public struct LsaReferencedDomainList + { + public int Entries; + public IntPtr Domains; // LsaTrustInformation* + } + + [StructLayout(LayoutKind.Sequential)] + public struct LsaTranslatedName + { + public SidNameUse Use; + public UnicodeString Name; + public int DomainIndex; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LsaTranslatedSid + { + public SidNameUse Use; + public int RelativeId; + public int DomainIndex; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LsaTranslatedSid2 + { + public SidNameUse Use; + public IntPtr Sid; // Sid* + public int DomainIndex; + public int Flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LsaTrustInformation + { + public UnicodeString Name; + public IntPtr Sid; // Sid* + } + + [StructLayout(LayoutKind.Sequential)] + public struct PolicyPrivilegeDefinition + { + public UnicodeString Name; + public Luid LocalValue; + } + + [StructLayout(LayoutKind.Sequential)] + public struct QuotaLimits + { + public IntPtr PagedPoolLimit; + public IntPtr NonPagedPoolLimit; + public IntPtr MinimumWorkingSetSize; + public IntPtr MaximumWorkingSetSize; + public IntPtr PagefileLimit; + public long TimeLimit; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecurityLogonSessionData + { + public int Size; + public Luid LogonId; + public UnicodeString UserName; + public UnicodeString LogonDomain; + public UnicodeString AuthenticationPackage; + public LogonType LogonType; + public int Session; + public IntPtr Sid; // Sid* + public long LogonTime; + public UnicodeString LogonServer; + public UnicodeString DnsDomainName; + public UnicodeString Upn; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TrustedDomainInformationEx + { + public UnicodeString Name; + public UnicodeString FlatName; + public IntPtr Sid; // Sid* + public int TrustDirection; + public int TrustType; + public int TrustAttributes; + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/NativeDefinitions.cs b/branches/ph-plugins/ProcessHacker.Native/Api/NativeDefinitions.cs new file mode 100644 index 000000000..f72d1a20b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/NativeDefinitions.cs @@ -0,0 +1,105 @@ +/* + * Process Hacker - + * native API consts and delegates + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.Api +{ + public delegate void ApcCallbackDelegate(NtStatus ioStatus, IntPtr apcContext, IntPtr context); + public delegate void ApcRoutine(IntPtr parameter); + public delegate void IoApcRoutine(IntPtr apcContext, ref IoStatusBlock ioStatusBlock, int reserved); + public delegate void TimerApcRoutine(IntPtr context, int lowValue, int highValue); + public delegate void WaitOrTimerCallbackDelegate(IntPtr context, bool timeout); + public delegate void WorkerCallbackDelegate(IntPtr context); + + public static partial class Win32 + { + public const int AclRevision = 2; + public const int AclRevisionDs = 4; + public const int CsrSrvServerDllIndex = 0; + public const int CsrSrvFirstApiNumber = 0; + public const int BaseSrvServerDllIndex = 1; + public const int BaseSrvFirstApiNumber = 0; + public const int ConSrvServerDllIndex = 2; + public const int ConSrvFirstApiNumber = 512; + public const int UserSrvServerDllIndex = 3; + public const int UserSrvFirstApiNumber = 1024; + public const int ExceptionMaximumParameters = 15; + public const uint FileWriteToEndOfFile = 0xffffffff; + public const uint FileUseFilePointerPosition = 0xfffffffe; + public const int FlsMaximumAvailable = 128; +#if _WIN64 + public const int GdiHandleBufferSize = 60; +#else + public const int GdiHandleBufferSize = 34; +#endif + public const int MaximumSupportedExtension = 512; + public const int MaximumWaitObjects = 64; + public const int MaxKeyNameLength = 512; + public const int MaxKeyValueNameLength = 32767; + public const int MaxStackDepth = 32; + public const int MaxWow64SharedEntries = 16; + public const short Pe32Magic = 0x10b; + public const short Pe32PlusMagic = 0x20b; + public const short RomMagic = 0x107; + public const int PortMessageMaxDataLength = 0x130; + public const int PortMessageMaxLength = 0x148; + public const int ProcessHandleTracingMaxStacks = 16; + public const int ProcessorFeatureMax = 64; + public const int SecurityDescriptorRevision = 1; + public const int SidMaxSubAuthorities = 15; + public const int SidRecommendedSubAuthorities = 1; + public const int SidRevision = 1; + public const int SizeOf80387Registers = 80; + public const int TimeMsTo100Ns = 10000; + + // Known object paths + public const string BeepDeviceName = @"\Device\Beep"; + public const string EnlistmentPath = @"\Enlistment"; + public const string MailslotPath = @"\Device\Mailslot"; + public const string MountMgrDeviceName = @"\Device\MountPointManager"; + public const string NamedPipePath = @"\Device\NamedPipe"; + public const string ResourceManagerPath = @"\ResourceManager"; + public const string TransactionPath = @"\Transaction"; + public const string TransactionManagerPath = @"\TransactionManager"; + + public static readonly IntPtr KnownAceSidStartOffset = Marshal.OffsetOf(typeof(KnownAceStruct), "SidStart"); + public static readonly int SecurityDescriptorMinLength = Marshal.SizeOf(typeof(SecurityDescriptorStruct)); + public static readonly int SecurityMaxSidSize = + Marshal.SizeOf(typeof(SidStruct)) - sizeof(int) + (SidMaxSubAuthorities * sizeof(int)); + public static readonly IntPtr UserSharedData = new IntPtr(0x7ffe0000); + + public static int CsrMakeApiNumber(int dllIndex, int apiIndex) + { + return (dllIndex << 16) | apiIndex; + } + + public static int CtlCode(DeviceType type, int function, DeviceControlMethod method, DeviceControlAccess access) + { + return ((int)type << 16) | + ((int)access << 14) | + (function << 2) | + (int)method; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/NativeEnums.cs b/branches/ph-plugins/ProcessHacker.Native/Api/NativeEnums.cs new file mode 100644 index 000000000..c8dda5eab --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/NativeEnums.cs @@ -0,0 +1,2146 @@ +/* + * Process Hacker - + * native API enumerations + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains enumeration declarations for the Native API. + * Enumerations shared between the Native API and Win32 are placed + * in this file. + */ + +using System; +using System.Collections.Generic; + +namespace ProcessHacker.Native.Api +{ + [Flags] + public enum AceFlags : byte + { + ObjectInherit = 0x1, + ContainerInherit = 0x2, + NoPropagateInherit = 0x4, + InheritOnly = 0x8, + Inherited = 0x10, + Valid = 0x1f, + + // For SystemAudit and SystemAlarm ACEs. + SuccessfulAccess = 0x40, + FailedAccess = 0x80 + } + + public enum AceType : byte + { + //Mininum = 0x0, + AccessAllowed = 0x0, + AccessDenied = 0x1, + SystemAudit = 0x2, + SystemAlarm = 0x3, + //MaximumV2 = 0x3, + + AccessAllowedCompound = 0x4, + //MaximumV3 = 0x4, + + //MinimumObject = 0x5, + AccessAllowedObject = 0x5, + AccessDeniedObject = 0x6, + SystemAuditObject = 0x7, + SystemAlarmObject = 0x8, + //MaximumObject = 0x8, + //MaximumV4 = 0x8, + //Maximum = 0x8, + + AccessAllowedCallback = 0x9, + AccessDeniedCallback = 0xa, + AccessAllowedCallbackObject = 0xb, + AccessDeniedCallbackObject = 0xc, + SystemAuditCallback = 0xd, + SystemAlarmCallback = 0xe, + SystemAuditCallbackObject = 0xf, + SystemAlarmCallbackObject = 0x10, + //MaximumV5 = 0x10 + } + + public enum AclInformationClass : int + { + AclRevisionInformation = 1, + AclSizeInformation + } + + public enum AlternativeArchitectureType : int + { + StandardDesign, + Nec98x86, + EndAlternatives + } + + public enum BaseSrvApiNumber : int + { + BasepCreateProcess = Win32.BaseSrvFirstApiNumber, + BasepCreateThread, + BasepGetTempFile, + BasepExitProcess, + BasepDebugProcess, + BasepCheckVDM, + BasepUpdateVDMEntry, + BasepGetNextVDMCommand, + BasepExitVDM, + BasepIsFirstVDM, + BasepGetVDMExitCode, + BasepSetReenterCount, + BasepSetProcessShutdownParam, + BasepGetProcessShutdownParam, + BasepNlsSetUserInfo, + BasepNlsSetMultipleUserInfo, + BasepNlsCreateSortSection, + BasepNlsPreserveSection, + BasepSetVDMCurDirs, + BasepGetVDMCurDirs, + BasepBatNotification, + BasepRegisterWowExec, + BasepSoundSentryNotification, + BasepRefreshIniFileMapping, + BasepDefineDosDevice, + BasepMaxApiNumber + } + + public enum CompoundAceType : ushort + { + Impersonation = 1 + } + + /// + /// Generic context-related flags. + /// + [Flags] + public enum ContextFlagsGeneric : uint + { + // Context architecture + I386 = 0x00010000, + I486 = 0x00010000, + Amd64 = 0x00100000, + + // Context flags + Control = 0x00000001, + Integer = 0x00000002, + Segments = 0x00000004, + FloatingPoint = 0x00000008, + DebugRegisters = 0x00000010, + ExtendedRegisters = 0x00000020, + } + + /// + /// x86 context. + /// + [Flags] + public enum ContextFlags : uint + { + I386 = ContextFlagsGeneric.I386, + I486 = ContextFlagsGeneric.I486, + + Control = I386 | ContextFlagsGeneric.Control, + Integer = I386 | ContextFlagsGeneric.Integer, + Segments = I386 | ContextFlagsGeneric.Segments, + FloatingPoint = I386 | ContextFlagsGeneric.FloatingPoint, + DebugRegisters = I386 | ContextFlagsGeneric.DebugRegisters, + ExtendedRegisters = I386 | ContextFlagsGeneric.ExtendedRegisters, + + Full = Control | Integer | Segments, + All = Control | Integer | Segments | FloatingPoint | DebugRegisters | ExtendedRegisters + } + + /// + /// AMD64 context. + /// + [Flags] + public enum ContextFlagsAmd64 : uint + { + Amd64 = ContextFlagsGeneric.Amd64, + + Control = Amd64 | ContextFlagsGeneric.Control, + Integer = Amd64 | ContextFlagsGeneric.Integer, + Segments = Amd64 | ContextFlagsGeneric.Segments, + FloatingPoint = Amd64 | ContextFlagsGeneric.FloatingPoint, + DebugRegisters = Amd64 | ContextFlagsGeneric.DebugRegisters, + + Full = Control | Integer | FloatingPoint, + All = Control | Integer | Segments | FloatingPoint | DebugRegisters, + + ExceptionActive = 0x08000000, + ServiceActive = 0x10000000, + ExceptionRequest = 0x40000000, + ExceptionReporting = 0x80000000 + } + + [Flags] + public enum CrmProtocolOptions : int + { + ExplicitMarshalOnly = 0x1, + DynamicMarshalInfo = 0x2, + MaximumOption = 0x3 + } + + [Flags] + public enum DebugObjectFlags : uint + { + KillOnClose = 0x1 + } + + [Flags] + public enum DebugObjectInformationClass : int + { + DebugObjectFlags, + MaxDebugObjectInfoClass + } + + public enum DeviceControlAccess : int + { + Any = 0, + Special = Any, + Read = 1, + Write = 2 + } + + public enum DeviceControlMethod : int + { + Buffered = 0, + InDirect = 1, + OutDirect = 2, + Neither = 3 + } + + public enum DeviceType : int + { + Beep = 0x1, + CdRom = 0x2, + CdRomFileSystem = 0x3, + Controller = 0x4, + DataLink = 0x5, + Dfs = 0x6, + Disk = 0x7, + DiskFileSystem = 0x8, + FileSystem = 0x9, + InportPort = 0xa, + Keyboard = 0xb, + Mailslot = 0xc, + MidiIn = 0xd, + MidiOut = 0xe, + Mouse = 0xf, + MultiUncProvider = 0x10, + NamedPipe = 0x11, + Network = 0x12, + NetworkBrowser = 0x13, + NetworkFileSystem = 0x14, + Null = 0x15, + ParallelPort = 0x16, + PhysicalNetCard = 0x17, + Printer = 0x18, + Scanner = 0x19, + SerialMousePort = 0x1a, + SerialPort = 0x1b, + Screen = 0x1c, + Sound = 0x1d, + Streams = 0x1e, + Tape = 0x1f, + TapeFileSystem = 0x20, + Transport = 0x21, + Unknown = 0x22, + Video = 0x23, + VirtualDisk = 0x24, + WaveIn = 0x25, + WaveOut = 0x26, + EightZeroFourTwoPort = 0x27, + NetworkRedirector = 0x28, + Battery = 0x29, + BusExtender = 0x2a, + Modem = 0x2b, + Vdm = 0x2c, + MassStorage = 0x2d, + Smb = 0x2e, + Ks = 0x2f, + Changer = 0x30, + SmartCard = 0x31, + Acpi = 0x32, + Dvd = 0x33, + FullscreenVideo = 0x34, + DfsFileSystem = 0x35, + DfsVolume = 0x36, + Serenum = 0x37, + TermSrv = 0x38, + KSec = 0x39, + Fips = 0x3a, + Infiniband = 0x3b, + + MountMgr = 'm', + MountMgrDevice = 'M' + } + + [Flags] + public enum DbgState : int + { + DbgIdle, + DbgReplyPending, + DbgCreateThreadStateChange, + DbgCreateProcessStateChange, + DbgExitThreadStateChange, + DbgExitProcessStateChange, + DbgExceptionStateChange, + DbgBreakpointStateChange, + DbgSingleStepStateChange, + DbgLoadDllStateChange, + DbgUnloadDllStateChange + } + + [Flags] + public enum DuplicateOptions : int + { + CloseSource = 0x1, + SameAccess = 0x2, + SameAttributes = 0x4 + } + + public enum EnlistmentInformationClass : int + { + EnlistmentBasicInformation, + EnlistmentRecoveryInformation, + EnlistmentFullInformation + } + + [Flags] + public enum EnlistmentOptions : int + { + Superior = 0x1, + MaximumOption = 0x1 + } + + public enum EventInformationClass : int + { + EventBasicInformation + } + + public enum EventType : int + { + NotificationEvent, + SynchronizationEvent + } + + public enum FileAlignment : int + { + Byte = 0x0, + Word = 0x1, + Long = 0x3, + Quad = 0x7, + Octa = 0xf, + ThirtyTwoByte = 0x1f, + SixtyFourByte = 0x3f, + OneHundredAndTwentyEightByte = 0x7f, + TwoHundredAndFiftySixByte = 0xff, + FiveHundredAndTwelveByte = 0x1ff + } + + [Flags] + public enum FileAttributes : uint + { + ReadOnly = 0x1, + Hidden = 0x2, + System = 0x4, + + Directory = 0x10, + Archive = 0x20, + Device = 0x40, + Normal = 0x80, + + Temporary = 0x100, + SparseFile = 0x200, + ReparsePoint = 0x400, + Compressed = 0x800, + + Offline = 0x1000, + NotContextIndexed = 0x2000, + Encrypted = 0x4000 + } + + [Flags] + public enum FileCreateOptions : uint + { + DirectoryFile = 0x1, + WriteThrough = 0x2, + SequentialOnly = 0x4, + NoIntermediateBuffering = 0x8, + + SynchronousIoAlert = 0x10, + SynchronousIoNonAlert = 0x20, + NonDirectoryFile = 0x40, + CreateTreeConnection = 0x80, + + CompleteIfOpLocked = 0x100, + NoEaKnowledge = 0x200, + OpenForRecovery = 0x400, + RandomAccess = 0x800, + + DeleteOnClose = 0x1000, + OpenByFileId = 0x2000, + OpenForBackupIntent = 0x4000, + NoCompression = 0x8000, + + ReserveOpFilter = 0x100000, + OpenReparsePoint = 0x200000, + OpenNoRecall = 0x400000, + OpenForFreeSpaceQuery = 0x800000, + + CopyStructuredStorage = 0x41, + StructuredStorage = 0x441, + + ValidOptionFlags = 0xffffff, + ValidPipeOptionFlags = 0x32, + ValidMailslotOptionFlags = 0x32, + ValidSetFlags = 0x36 + } + + public enum FileCreationDisposition : int + { + Supersede = 0x0, + Open = 0x1, + Create = 0x2, + OpenIf = 0x3, + Overwrite = 0x4, + OverwriteIf = 0x5 + } + + public enum FileInformationClass : int + { + FileDirectoryInformation = 1, // dir + FileFullDirectoryInformation, // dir + FileBothDirectoryInformation, // dir + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, // 10 + FileLinkInformation, + FileNamesInformation, // dir + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, // 20 + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, // dir + FileCompletionInformation, // 30 + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, // dir + FileIdFullDirectoryInformation, // dir + FileValidDataLengthInformation, + FileShortNameInformation, // 40 + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, // 50 + FileMaximumInformation + } + + public enum FileIoStatus : int + { + Superseded = 0, + Opened = 1, + Created = 2, + Overwritten = 3, + Exists = 4, + DoesNotExist = 5 + } + + public enum FileNotifyAction : int + { + Added = 0x1, + Removed = 0x2, + Modified = 0x3, + RenamedOldName = 0x4, + RenamedNewName = 0x5, + AddedStream = 0x6, + RemovedStream = 0x7, + ModifiedStream = 0x8, + RemovedByDelete = 0x9, + IdNotTunnelled = 0xa, + TunnelledIdCollision = 0xb + } + + [Flags] + public enum FileNotifyFlags : int + { + FileName = 0x1, + DirName = 0x2, + Name = 0x3, + Attributes = 0x4, + Size = 0x8, + LastWrite = 0x10, + LastAccess = 0x20, + Creation = 0x40, + Ea = 0x80, + Security = 0x100, + StreamName = 0x200, + StreamSize = 0x400, + StreamWrite = 0x800, + Valid = 0xfff + } + + [Flags] + public enum FileObjectFlags : int + { + FileOpen = 0x00000001, + SynchronousIo = 0x00000002, + AlertableIo = 0x00000004, + NoIntermediateBuffering = 0x00000008, + WriteThrough = 0x00000010, + SequentialOnly = 0x00000020, + CacheSupported = 0x00000040, + NamedPipe = 0x00000080, + StreamFile = 0x00000100, + MailSlot = 0x00000200, + GenerateAuditOnClose = 0x00000400, + QueueIrpToThread = GenerateAuditOnClose, + DirectDeviceOpen = 0x00000800, + FileModified = 0x00001000, + FileSizeChanged = 0x00002000, + CleanupComplete = 0x00004000, + TemporaryFile = 0x00008000, + DeleteOnClose = 0x00010000, + OpenedCaseSensitivity = 0x00020000, + HandleCreated = 0x00040000, + FileFastIoRead = 0x00080000, + RandomAccess = 0x00100000, + FileOpenCancelled = 0x00200000, + VolumeOpen = 0x00400000, + RemoteOrigin = 0x01000000, + SkipCompletionPort = 0x02000000, + SkipSetEvent = 0x04000000, + SkipSetFastIo = 0x08000000 + } + + [Flags] + public enum FileShareMode : uint + { + Exclusive = 0x0, + Read = 0x1, + Write = 0x2, + Delete = 0x4, + + ReadWrite = Read | Write, + ReadWriteDelete = Read | Write | Delete + } + + public enum FsInformationClass : int + { + FileFsVolumeInformation = 1, + FileFsLabelInformation, + FileFsSizeInformation, + FileFsDeviceInformation, + FileFsAttributeInformation, + FileFsControlInformation, + FileFsFullSizeInformation, + FileFsObjectIdInformation, + FileFsDriverPathInformation, + FileFsVolumeFlagsInformation, // 10 + FileFsMaximumInformation + } + + [Flags] + public enum HandleFlags : byte + { + ProtectFromClose = 0x1, + Inherit = 0x2, + AuditObjectClose = 0x4 + } + + public enum HandleTraceType : int + { + Open = 1, + Close = 2, + BadRef = 3 + } + + [Flags] + public enum HashStringAlgorithm : int + { + Default = 0, + X65599 = 1, + Invalid = -1 + } + + [Flags] + public enum HeapFlags : uint + { + NoSerialize = 0x00000001, + Growable = 0x00000002, + GenerateExceptions = 0x00000004, + ZeroMemory = 0x00000008, + ReallocInPlaceOnly = 0x00000010, + TailCheckingEnabled = 0x00000020, + FreeCheckingEnabled = 0x00000040, + DisableCoalesceOnFree = 0x00000080, + + CreateAlign16 = 0x00010000, + CreateEnableTracing = 0x00020000, + CreateEnableExecute = 0x00040000, + + SettableUserValue = 0x00000100, + SettableUserFlag1 = 0x00000200, + SettableUserFlag2 = 0x00000400, + SettableUserFlag3 = 0x00000800, + SettableUserFlags = 0x00000e00, + + Class0 = 0x00000000, // Process heap + Class1 = 0x00001000, // Private heap + Class2 = 0x00002000, // Kernel heap + Class3 = 0x00003000, // GDI heap + Class4 = 0x00004000, // User heap + Class5 = 0x00005000, // Console heap + Class6 = 0x00006000, // User desktop heap + Class7 = 0x00007000, // CSRSS shared heap + Class8 = 0x00008000, // CSR port heap + ClassMask = 0x0000f000 + } + + public enum ImageBaseRelocationType : short + { + /// + /// The base relocation is skipped. This type can be used to pad a block. + /// + Absolute = 0, + + /// + /// The base relocation adds the high 16 bits of the difference to the 16-bit + /// field at offset. The 16-bit field represents the high value of a 32-bit word. + /// + High = 1, + + /// + /// The base relocation adds the low 16 bits of the difference to the 16-bit + /// field at offset. The 16-bit field represents the low half of a 32-bit word. + /// + Low = 2, + + /// + /// The base relocation applies all 32 bits of the difference to the 32-bit + /// field at offset. + /// + HighLow = 3, + + /// + /// The base relocation adds the high 16 bits of the difference to the 16-bit + /// field at offset. The 16-bit field represents the high value of a 32-bit word. + /// The low 16 bits of the 32-bit value are stored in the 16-bit word that follows + /// this base relocation. This means that this base relocation occupies two slots. + /// + HighAdj = 4, + + /// + /// The base relocation applies to a MIPS jump instruction. + /// + MipsJmpAddr = 5, + + /// + /// The base relocation applies to a MIPS16 jump instruction. + /// + MipsJmpAddr16 = 9, + Ia64Imm64 = 9, + + /// + /// The base relocation applies the difference to the 64-bit field at offset. + /// + Dir16 = 10 + } + + [Flags] + public enum ImageCharacteristics : ushort + { + /// + /// Image only, Windows CE, and Windows NT® and later. This indicates that the file does + /// not contain base relocations and must therefore be loaded at its preferred base address. + /// If the base address is not available, the loader reports an error. The default behavior + /// of the linker is to strip base relocations from executable (EXE) files. + /// + RelocsStripped = 0x0001, + + /// + /// Image only. This indicates that the image file is valid and can be run. If this flag + /// is not set, it indicates a linker error. + /// + ExecutableImage = 0x0002, + + /// + /// COFF line numbers have been removed. This flag is deprecated and should be zero. + /// + LineNumsStripped = 0x0004, + + /// + /// COFF symbol table entries for local symbols have been removed. This flag is deprecated + /// and should be zero. + /// + LocalSymsStripped = 0x0008, + + /// + /// Obsolete. Aggressively trim working set. This flag is deprecated for Windows 2000 and later + /// and must be zero. + /// + AggressiveWsTrim = 0x0010, + + /// + /// Application can handle > 2 GB addresses. + /// + LargeAddressAware = 0x0020, + + /// + /// This flag is reserved for future use. + /// + Reserved = 0x0040, + + /// + /// Little endian: the least significant bit (LSB) precedes the most significant bit (MSB) in + /// memory. This flag is deprecated and should be zero. + /// + BytesReversedLo = 0x0080, + + /// + /// Machine is based on a 32-bit-word architecture. + /// + ThirtyTwoBitMachine = 0x0100, + + /// + /// Debugging information is removed from the image file. + /// + DebugStripped = 0x0200, + + /// + /// If the image is on removable media, fully load it and copy it to the swap file. + /// + RemovableRunFromSwap = 0x0400, + + /// + /// If the image is on network media, fully load it and copy it to the swap file. + /// + NetRunFromSwap = 0x0800, + + /// + /// The image file is a system file, not a user program. + /// + System = 0x1000, + + /// + /// The image file is a dynamic-link library (DLL). Such files are considered + /// executable files for almost all purposes, although they cannot be directly run. + /// + DLL = 0x2000, + + /// + /// The file should be run only on a uniprocessor machine. + /// + UPSystemOnly = 0x4000, + + /// + /// Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero. + /// + BytesReversedHi = 0x8000 + } + + [Flags] + public enum ImageDllCharacteristics : ushort + { + DynamicBase = 0x0040, + ForceIntegrity = 0x0080, + NxCompat = 0x0100, + NoIsolation = 0x0200, + NoSeh = 0x0400, + NoBind = 0x0800, + WdmDriver = 0x2000, + TerminalServerAware = 0x8000 + } + + public enum ImageI386RelocationType : short + { + Absolute = 0x0, + Dir16 = 0x1, + Rel16 = 0x2, + Dir32 = 0x6, + Dir32Nb = 0x7, + Seg12 = 0x9, + Section = 0xa, + SecRel = 0xb, + Token = 0xc, + SecRel7 = 0xd, + Rel32 = 0x14 + } + + [Flags] + public enum ImageSectionFlags : uint + { + /// + /// Reserved for future use. + /// + Reserved1 = 0x00000000, + + /// + /// Reserved for future use. + /// + Reserved2 = 0x00000001, + + /// + /// Reserved for future use. + /// + Reserved3 = 0x00000002, + + /// + /// Reserved, must be zero. + /// + Reserved4 = 0x00000004, + + /// + /// The section should not be padded to the next boundary. + /// This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. + /// This is valid only for object files. + /// + NoPad = 0x00000008, + + /// + /// The section contains executable code. + /// + Code = 0x00000020, + + /// + /// The section contains initialized data. + /// + InitializedData = 0x00000040, + + /// + /// The section contains uninitialized data. + /// + UninitializedData = 0x00000080, + + /// + /// Reserved for future use. + /// + Other = 0x00000100, + + /// + /// The section contains comments or other information. The + /// .drectve section has this type. This is valid for object + /// files only. + /// + Info = 0x00000200, + + /// + /// Reserved for future use. + /// + Reserved5 = 0x00000400, + + /// + /// The section will not become part of the image. This is valid + /// only for object files. + /// + Remove = 0x00000800, + + /// + /// The section contains COMDAT data. + /// + COMDAT = 0x00001000, + + /// + /// The section contains data referenced through the global pointer (GP). + /// + GPRel = 0x00008000, + + /// + /// Reserved for future use. + /// + MemoryPurgeable = 0x00010000, + + /// + /// Reserved for future use. + /// + Memory16Bit = 0x00020000, + + /// + /// Reserved for future use. + /// + MemoryLocked = 0x00040000, + + /// + /// Reserved for future use. + /// + MemoryPeload = 0x00080000, + + Align1Bytes = 0x00100000, + Align2Bytes = 0x00200000, + Align4Bytes = 0x00300000, + Align8Bytes = 0x00400000, + Align16Bytes = 0x00500000, + Align32Bytes = 0x00600000, + Align64Bytes = 0x00700000, + Align128Bytes = 0x00800000, + Align256Bytes = 0x00900000, + Align512Bytes = 0x00a00000, + Align1024Bytes = 0x00b00000, + Align2048Bytes = 0x00c00000, + Align4096Bytes = 0x00d00000, + Align8192Bytes = 0x00e00000, + + /// + /// The section contains extended relocations. + /// + NRelocOvfl = 0x01000000, + + /// + /// The section can be discarded as needed. + /// + MemoryDiscardable = 0x02000000, + + /// + /// The section cannot be cached. + /// + MemoryNotCached = 0x04000000, + + /// + /// The section is not pageable. + /// + MemoryNotPaged = 0x08000000, + + /// + /// The section can be shared in memory. + /// + MemoryShared = 0x10000000, + + /// + /// The section can be executed as code. + /// + MemoryExecute = 0x20000000, + + /// + /// The section can be read. + /// + MemoryRead = 0x40000000, + + /// + /// The section can be written to. + /// + MemoryWrite = 0x80000000 + } + + public enum ImageSubsystem : short + { + Unknown = 0, + Native = 1, + WindowsGui = 2, + WindowsCui = 3, + OS2Cui = 5, + PosixCui = 7, + NativeWindows = 8, + WindowsCeGui = 9, + EfiApplication = 10, + EfiBootServiceDriver = 11, + EfiRuntimeDriver = 12, + EfiRom = 13, + Xbox = 14, + WindowsBootApplication = 16 + } + + public enum IoCompletionInformationClass : int + { + IoCompletionBasicInformation + } + + [Flags] + public enum JobObjectBasicUiRestrictions : uint + { + Handles = 0x1, + ReadClipboard = 0x2, + WriteClipboard = 0x4, + SystemParameters = 0x8, + DisplaySettings = 0x10, + GlobalAtoms = 0x20, + Desktop = 0x40, + ExitWindows = 0x80 + } + + public enum JobObjectInformationClass : int + { + JobObjectBasicAccountingInformation = 1, + JobObjectBasicLimitInformation, + JobObjectBasicProcessIdList, + JobObjectBasicUIRestrictions, + JobObjectSecurityLimitInformation, + JobObjectEndOfJobTimeInformation, + JobObjectAssociateCompletionPortInformation, + JobObjectBasicAndIoAccountingInformation, + JobObjectExtendedLimitInformation, + JobObjectJobSetInformation + } + + [Flags] + public enum JobObjectLimitFlags : uint + { + WorkingSet = 0x1, + ProcessTime = 0x2, + JobTime = 0x4, + ActiveProcess = 0x8, + Affinity = 0x10, + PriorityClass = 0x20, + PreserveJobTime = 0x40, + SchedulingClass = 0x80, + ProcessMemory = 0x100, + JobMemory = 0x200, + DieOnUnhandledException = 0x400, + BreakawayOk = 0x800, + SilentBreakawayOk = 0x1000, + KillOnJobClose = 0x2000, + } + + [Flags] + public enum KeyCreationDisposition : int + { + CreatedNewKey = 0x1, + OpenedExistingKey = 0x2 + } + + public enum KeyInformationClass : int + { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + MaxKeyInfoClass + } + + public enum KeySetInformationClass : int + { + KeyWriteTimeInformation, + KeyUserFlagsInformation, + MaxKeySetInfoClass + } + + public enum KProcessorMode : byte + { + KernelMode = 0, + UserMode = 1 + } + + public enum KProfileSource : int + { + ProfileTime, + ProfileAlignmentFixup, + ProfileTotalIssues, + ProfilePipelineDry, + ProfileLoadInstructions, + ProfilePipelineFrozen, + ProfileBranchInstructions, + ProfileTotalNonissues, + ProfileDcacheMisses, + ProfileIcacheMisses, + ProfileCacheMisses, + ProfileBranchMispredictions, + ProfileStoreInstructions, + ProfileFpInstructions, + ProfileIntegerInstructions, + Profile2Issue, + Profile3Issue, + Profile4Issue, + ProfileSpecialInstructions, + ProfileTotalCycles, + ProfileIcacheIssues, + ProfileDcacheAccesses, + ProfileMemoryBarrierCycles, + ProfileLoadLinkedIssues, + ProfileMaximum + } + + public enum KtmObjectType : int + { + Transaction, + TransactionManager, + ResourceManager, + Enlistment, + Invalid + } + + public enum KWaitReason : int + { + Executive = 0, + FreePage = 1, + PageIn = 2, + PoolAllocation = 3, + DelayExecution = 4, + Suspended = 5, + UserRequest = 6, + WrExecutive = 7, + WrFreePage = 8, + WrPageIn = 9, + WrPoolAllocation = 10, + WrDelayExecution = 11, + WrSuspended = 12, + WrUserRequest = 13, + WrEventPair = 14, + WrQueue = 15, + WrLpcReceive = 16, + WrLpcReply = 17, + WrVirtualMemory = 18, + WrPageOut = 19, + WrRendezvous = 20, + Spare2 = 21, + Spare3 = 22, + Spare4 = 23, + Spare5 = 24, + WrCalloutStack = 25, + WrKernel = 26, + WrResource = 27, + WrPushLock = 28, + WrMutex = 29, + WrQuantumEnd = 30, + WrDispatchInt = 31, + WrPreempted = 32, + WrYieldExecution = 33, + WrFastMutex = 34, + WrGuardedMutex = 35, + WrRundown = 36, + MaximumWaitReason = 37 + } + + [Flags] + public enum LdrpDataTableEntryFlags : uint + { + StaticLink = 0x00000002, + ImageDll = 0x00000004, + Flag0x8 = 0x00000008, + Flag0x10 = 0x00000010, + LoadInProgress = 0x00001000, + UnloadInProgress = 0x00002000, + EntryProcessed = 0x00004000, + EntryInserted = 0x00008000, + CurrentLoad = 0x00010000, + FailedBuiltInLoad = 0x00020000, + DontCallForThreads = 0x00040000, + ProcessAttachCalled = 0x00080000, + DebugSymbolsLoaded = 0x00100000, + ImageNotAtBase = 0x00200000, + CorImage = 0x00400000, + CorOwnsUnmap = 0x00800000, + SystemMapped = 0x01000000, + ImageVerifying = 0x02000000, + DriverDependentDll = 0x04000000, + EntryNative = 0x08000000, + Redirected = 0x10000000, + NonPagedDebugInfo = 0x20000000, + MmLoaded = 0x40000000, + CompatDatabaseProcessed = 0x80000000 + } + + /// + /// Specifies an executable's target CPU type. + /// + public enum MachineType : ushort + { + /// + /// Assumed to be applicable to any machine type. + /// + Unknown = 0x0, + + /// + /// Matsushita AM33. + /// + Am33 = 0x1d3, + + /// + /// x64. + /// + Amd64 = 0x8664, + + /// + /// ARM little-endian. + /// + Arm = 0x1c0, + + /// + /// EFI byte code. + /// + Ebc = 0xebc, + + /// + /// Intel 386 or later processors and compatible processors. + /// + I386 = 0x14c, + + /// + /// Intel Itanium processor family. + /// + Ia64 = 0x200, + + /// + /// Mitsubishi M32R little endian. + /// + M32R = 0x9041, + + /// + /// MIPS16. + /// + Mips16 = 0x266, + + /// + /// MIPS with FPU. + /// + MipsFpu = 0x366, + + /// + /// MIPS16 with FPU. + /// + MipsFpu16 = 0x466, + + /// + /// PowerPC little-endian. + /// + PowerPc = 0x1f0, + + /// + /// PowerPC with floating point support. + /// + PowerPcFp = 0x1f1, + + /// + /// MIPS little-endian. + /// + R4000 = 0x166, + + /// + /// Hitachi SH3. + /// + Sh3 = 0x1a2, + + /// + /// Hitachi SH3 DSP. + /// + Sh3Dsp = 0x1a3, + + /// + /// Hitachi SH4. + /// + Sh4 = 0x1a6, + + /// + /// Hitachi SH5. + /// + Sh5 = 0x1a8, + + /// + /// Thumb. + /// + Thumb = 0x1c2, + + /// + /// MIPS little-endian WCE v2. + /// + WceMipsv2 = 0x169 + } + + [Flags] + public enum MemExecuteOptions : int + { + ExecuteDisable = 0x1, + ExecuteEnable = 0x2, + DisableThunkEmulation = 0x4, + Permanent = 0x8 + } + + [Flags] + public enum MemoryFlags : uint + { + Commit = 0x1000, + Reserve = 0x2000, + Decommit = 0x4000, + Release = 0x8000, + Free = 0x10000, + Private = 0x20000, + Mapped = 0x40000, + Reset = 0x80000, + TopDown = 0x100000, + WriteWatch = 0x200000, + Physical = 0x400000, + LargePages = 0x20000000, + DosLimit = 0x40000000, + FourMbPages = 0x80000000 + } + + public enum MemoryInformationClass : int + { + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation + } + + public enum MemoryMapType : int + { + Process = 1, + System = 2 + } + + [Flags] + public enum MemoryProtection : uint + { + AccessDenied = 0x0, + Execute = 0x10, + ExecuteRead = 0x20, + ExecuteReadWrite = 0x40, + ExecuteWriteCopy = 0x80, + Guard = 0x100, + NoCache = 0x200, + WriteCombine = 0x400, + NoAccess = 0x01, + ReadOnly = 0x02, + ReadWrite = 0x04, + WriteCopy = 0x08 + } + + [Flags] + public enum MessageResourceFlags : ushort + { + Unicode = 0x1 + } + + public enum MutantInformationClass : int + { + MutantBasicInformation, + MutantOwnerInformation + } + + [Flags] + public enum NotificationMask : uint + { + Mask = 0x3fffffff, + PrePrepare = 0x00000001, + Prepare = 0x00000002, + Commit = 0x00000004, + Rollback = 0x00000008, + PrePrepareComplete = 0x00000010, + PrepareComplete = 0x00000020, + CommitComplete = 0x00000040, + RollbackComplete = 0x00000080, + Recover = 0x00000100, + SinglePhaseComplete = 0x00000200, + DelegateCommit = 0x00000400, + RecoverQuery = 0x00000800, + EnlistPrePrepare = 0x00001000, + LastRecover = 0x00002000, + Indoubt = 0x00004000, + PropagatePull = 0x00008000, + PropagatePush = 0x00010000, + Marshal = 0x00020000, + EnlistMask = 0x00040000, + RmDisconnected = 0x01000000, + TmOnline = 0x02000000, + CommitRequest = 0x04000000, + Promote = 0x08000000, + PromoteNew = 0x10000000, + RequestOutcome = 0x20000000, + + // For filter manager use only. DO NOT SPECIFY. + CommitFinalize = 0x40000000 + } + + [Flags] + public enum ObjectAceFlags : uint + { + ObjectTypePresent = 0x1, + InheritedObjectTypePresent = 0x2 + } + + [Flags] + public enum ObjectFlags : uint + { + Inherit = 0x2, + Permanent = 0x10, + Exclusive = 0x20, + CaseInsensitive = 0x40, + OpenIf = 0x80, + OpenLink = 0x100, + KernelHandle = 0x200, + ForceAccessCheck = 0x400, + ValidAttributes = 0x7f2 + } + + public enum ObjectInformationClass : int + { + ObjectBasicInformation = 0, + ObjectNameInformation = 1, + ObjectTypeInformation = 2, + ObjectTypesInformation = 3, + ObjectHandleFlagInformation = 4, + ObjectSessionInformation = 5 + } + + public enum PipeEnd : int + { + Client = 0, + Server = 1 + } + + public enum PipeCompletionMode : int + { + Queue = 0, + Complete = 1 + } + + public enum PipeConfiguration : int + { + Inbound = 0, + Outbound = 1, + FullDuplex = 2 + } + + public enum PipeState : uint + { + Disconnected = 1, + Listening = 2, + Connected = 3, + Closing = 4 + } + + public enum PipeType : int + { + ByteStream = 0, + Message = 1 + } + + public enum PortMessageType : short + { + Request = 1, + Reply = 2, + Datagram = 3, + LostReply = 4, + PortClosed = 5, + ClientDied = 6, + Exception = 7, + DebugEvent = 8, + ErrorEvent = 9, + ConnectionRequest = 10 + } + + [Flags] + public enum PrivilegeSetFlags : int + { + AnyNecessary = 0, + AllNecessary = 0x1 + } + + public enum ProcessInformationClass : int + { + ProcessBasicInformation, // 0 + ProcessQuotaLimits, + ProcessIoCounters, + ProcessVmCounters, + ProcessTimes, + ProcessBasePriority, + ProcessRaisePriority, + ProcessDebugPort, + ProcessExceptionPort, + ProcessAccessToken, + ProcessLdtInformation, // 10 + ProcessLdtSize, + ProcessDefaultHardErrorMode, + ProcessIoPortHandlers, + ProcessPooledUsageAndLimits, + ProcessWorkingSetWatch, + ProcessUserModeIOPL, + ProcessEnableAlignmentFaultFixup, + ProcessPriorityClass, + ProcessWx86Information, + ProcessHandleCount, // 20 + ProcessAffinityMask, + ProcessPriorityBoost, + ProcessDeviceMap, + ProcessSessionInformation, + ProcessForegroundInformation, + ProcessWow64Information, + ProcessImageFileName, + ProcessLUIDDeviceMapsEnabled, + ProcessBreakOnTermination, + ProcessDebugObjectHandle, // 30 + ProcessDebugFlags, + ProcessHandleTracing, + ProcessIoPriority, + ProcessExecuteFlags, + ProcessResourceManagement, + ProcessCookie, + ProcessImageInformation, + ProcessCycleTime, + ProcessPagePriority, + ProcessInstrumentationCallback, // 40 + ProcessThreadStackAllocation, + ProcessWorkingSetWatchEx, + ProcessImageFileNameWin32, + ProcessImageFileMapping, + ProcessAffinityUpdateMode, + ProcessMemoryAllocationMode, + MaxProcessInfoClass + } + + public enum ProcessPriorityClass : byte + { + Unknown = 0, + Idle = 1, + Normal = 2, + High = 3, + RealTime = 4, + BelowNormal = 5, + AboveNormal = 6 + } + + [Flags] + public enum RegHiveFormat : int + { + Standard = 0x1, + Latest = 0x2, + NoCompression = 0x4 + } + + [Flags] + public enum RegNotifyFilter : int + { + Name = 0x1, + Attributes = 0x2, + LastSet = 0x4, + Security = 0x8, + Legal = Name | Attributes | LastSet | Security + } + + [Flags] + public enum RegOptions : int + { + Reserved = 0x0, + NonVolatile = 0x0, + Volatile = 0x1, + CreateLink = 0x2, + BackupRestore = 0x4, + OpenLink = 0x8, + Legal = Reserved | NonVolatile | Volatile | CreateLink | BackupRestore | OpenLink + } + + [Flags] + public enum RegRestoreFlags : int + { + WholeHiveVolatile = 0x1, + RefreshHive = 0x2, + NoLazyFlush = 0x4, + ForceRestore = 0x8 + } + + [Flags] + public enum RegUnloadFlags : int + { + ForceUnload = 0x1 + } + + public enum ResourceManagerInformationClass : int + { + ResourceManagerBasicInformation, + ResourceManagerCompletionInformation, + ResourceManagerFullInformation + } + + [Flags] + public enum ResourceManagerOptions : int + { + Volatile = 0x1, + Communication = 0x2, + MaximumOption = 0x3 + } + + [Flags] + public enum RtlAcquirePrivilegeFlags : int + { + Revert = 0x1, + Process = 0x2 + } + + [Flags] + public enum RtlDuplicateUnicodeStringFlags : int + { + NullTerminate = 0x1, + AllocateNullString = 0x2 + } + + public enum RtlLockType : ushort + { + CriticalSection = 0, + Resource = 1 + } + + [Flags] + public enum RtlQueryProcessDebugFlags : uint + { + Modules = 0x00000001, + BackTraces = 0x00000002, + HeapSummary = 0x00000004, + HeapTags = 0x00000008, + HeapEntries = 0x00000010, + Locks = 0x00000020, + Modules32 = 0x00000040, + + NonInvasive = 0x80000000 + } + + [Flags] + public enum RtlUserProcessFlags : uint + { + ParamsNormalized = 0x00000001, + ProfileUser = 0x00000002, + ProfileKernel = 0x00000004, + ProfileServer = 0x00000008, + Reserve1Mb = 0x00000020, + Reserve16Mb = 0x00000040, + CaseSensitive = 0x00000080, + DisableHeapDecommit = 0x00000100, + DllRedirectionLocal = 0x00001000, + AppManifestPresent = 0x00002000, + ImageKeyMissing = 0x00004000, + OptInProcess = 0x00020000 + } + + [Flags] + public enum SectionAttributes : uint + { + Based = 0x200000, + NoChange = 0x400000, + File = 0x800000, + Image = 0x1000000, + Reserve = 0x4000000, + Commit = 0x8000000, + NoCache = 0x10000000, + Global = 0x20000000, + LargePages = 0x80000000 + } + + [Flags] + public enum SectionInformationClass : int + { + SectionBasicInformation, + SectionImageInformation + } + + public enum SectionInherit : int + { + ViewShare = 1, + ViewUnmap = 2 + } + + [Flags] + public enum SecurityDescriptorControlFlags : ushort + { + OwnerDefaulted = 0x0001, + GroupDefaulted = 0x0002, + DaclPresent = 0x0004, + DaclDefaulted = 0x0008, + SaclPresent = 0x0010, + SaclDefaulted = 0x0020, + DaclUntrusted = 0x0040, + ServerSecurity = 0x0080, + DaclAutoInheritReq = 0x0100, + SaclAutoInheritReq = 0x0200, + DaclAutoInherited = 0x0400, + SaclAutoInherited = 0x0800, + DaclProtected = 0x1000, + SaclProtected = 0x2000, + RmControlValid = 0x4000, + SelfRelative = 0x8000 + } + + public enum SecurityImpersonationLevel : int + { + SecurityAnonymous, + SecurityIdentification, + SecurityImpersonation, + SecurityDelegation + } + + [Flags] + public enum SecurityInformation : uint + { + Owner = 0x00000001, + Group = 0x00000002, + Dacl = 0x00000004, + Sacl = 0x00000008, + Label = 0x00000010, + + ProtectedDacl = 0x80000000, + ProtectedSacl = 0x40000000, + UnprotectedDacl = 0x20000000, + UnprotectedSacl = 0x10000000 + } + + public enum SemaphoreInformationClass : int + { + SemaphoreBasicInformation + } + + [Flags] + public enum SidAttributes : uint + { + Mandatory = 0x00000001, + EnabledByDefault = 0x00000002, + Enabled = 0x00000004, + Owner = 0x00000008, + UseForDenyOnly = 0x00000010, + Integrity = 0x00000020, + IntegrityEnabled = 0x00000040, + LogonId = 0xc0000000, + Resource = 0x20000000 + } + + public enum SidNameUse : int + { + User = 1, + Group, + Domain, + Alias, + WellKnownGroup, + DeletedAccount, + Invalid, + Unknown, + Computer, + Label + } + + [Flags] + public enum SiRequested : uint + { + OwnerSecurityInformation = 0x1, + GroupSecurityInformation = 0x2, + DaclSecurityInformation = 0x4, + SaclSecurityInformation = 0x8, + LabelSecurityInformation = 0x10 + } + + [Flags] + public enum SuiteType : uint + { + SmallBusiness = 0x00000001, + Enterprise = 0x00000002, + BackOffice = 0x00000004, + Communications = 0x00000008, + Terminal = 0x00000010, + SmallBusinessRestricted = 0x00000020, + EmbeddedNt = 0x00000040, + DataCenter = 0x00000080, + SingleUserTs = 0x00000100, + Personal = 0x00000200, + Blade = 0x00000400, + EmbeddedRestricted = 0x00000800, + SecurityAppliance = 0x00001000, + StorageServer = 0x00002000, + ComputeServer = 0x00004000, + + WorkstationNt = 0x40000000, + ServerNt = 0x80000000 + } + + public enum SystemInformationClass : int + { + SystemBasicInformation, + SystemProcessorInformation, + SystemPerformanceInformation, + SystemTimeOfDayInformation, + SystemPathInformation, + SystemProcessInformation, + SystemCallCountInformation, + SystemDeviceInformation, + SystemProcessorPerformanceInformation, + SystemFlagsInformation, + SystemCallTimeInformation, // 10 + SystemModuleInformation, + SystemLocksInformation, + SystemStackTraceInformation, + SystemPagedPoolInformation, + SystemNonPagedPoolInformation, + SystemHandleInformation, + SystemObjectInformation, + SystemPageFileInformation, + SystemVdmInstemulInformation, + SystemVdmBopInformation, // 20 + SystemFileCacheInformation, + SystemPoolTagInformation, + SystemInterruptInformation, + SystemDpcBehaviorInformation, + SystemFullMemoryInformation, + SystemLoadGdiDriverInformation, + SystemUnloadGdiDriverInformation, + SystemTimeAdjustmentInformation, + SystemSummaryMemoryInformation, + SystemMirrorMemoryInformation, // 30 + SystemPerformanceTraceInformation, + SystemCrashDumpInformation, + SystemExceptionInformation, + SystemCrashDumpStateInformation, + SystemKernelDebuggerInformation, + SystemContextSwitchInformation, + SystemRegistryQuotaInformation, + SystemExtendServiceTableInformation, // used to be SystemLoadAndCallImage + SystemPrioritySeparation, + SystemVerifierAddDriverInformation, // 40 + SystemVerifierRemoveDriverInformation, + SystemProcessorIdleInformation, + SystemLegacyDriverInformation, + SystemCurrentTimeZoneInformation, + SystemLookasideInformation, + SystemTimeSlipNotification, + SystemSessionCreate, + SystemSessionDetach, + SystemSessionInformation, + SystemRangeStartInformation, // 50 + SystemVerifierInformation, + SystemVerifierThunkExtend, + SystemSessionProcessInformation, + SystemLoadGdiDriverInSystemSpace, + SystemNumaProcessorMap, + SystemPrefetcherInformation, + SystemExtendedProcessInformation, + SystemRecommendedSharedDataAlignment, + SystemComPlusPackage, + SystemNumaAvailableMemory, // 60 + SystemProcessorPowerInformation, + SystemEmulationBasicInformation, + SystemEmulationProcessorInformation, + SystemExtendedHandleInformation, + SystemLostDelayedWriteInformation, + SystemBigPoolInformation, + SystemSessionPoolTagInformation, + SystemSessionMappedViewInformation, + SystemHotpatchInformation, + SystemObjectSecurityMode, // 70 + SystemWatchdogTimerHandler, // doesn't seem to be implemented + SystemWatchdogTimerInformation, + SystemLogicalProcessorInformation, + SystemWow64SharedInformation, + SystemRegisterFirmwareTableInformationHandler, + SystemFirmwareTableInformation, + SystemModuleInformationEx, + SystemVerifierTriageInformation, + SystemSuperfetchInformation, + SystemMemoryListInformation, // 80 + SystemFileCacheInformationEx, + SystemNotImplemented19, + SystemProcessorDebugInformation, + SystemVerifierInformation2, + SystemNotImplemented20, + SystemRefTraceInformation, + SystemSpecialPoolTag, // MmSpecialPoolTag, then MmSpecialPoolCatchOverruns != 0 + SystemProcessImageName, + SystemNotImplemented21, + SystemBootEnvironmentInformation, // 90 + SystemEnlightenmentInformation, + SystemVerifierInformationEx, + SystemNotImplemented22, + SystemNotImplemented23, + SystemCovInformation, + SystemNotImplemented24, + SystemNotImplemented25, + SystemPartitionInformation, + SystemSystemDiskInformation, // this and SystemPartitionInformation both call IoQuerySystemDeviceName + SystemPerformanceDistributionInformation, // 100 + SystemNumaProximityNodeInformation, + SystemTimeZoneInformation2, + SystemCodeIntegrityInformation, + SystemNotImplemented26, + SystemUnknownInformation, // No symbols for this case, very strange... + SystemVaInformation // 106, calls MmQuerySystemVaInformation + } + + public enum ThreadInformationClass : uint + { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, // 10 + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + ThreadHideFromDebugger, + ThreadBreakOnTermination, + ThreadSwitchLegacyState, + ThreadIsTerminated, // 20 + ThreadLastSystemCall, + ThreadIoPriority, + ThreadCycleTime, + ThreadPagePriority, + ThreadActualBasePriority, + ThreadTebInformation, + ThreadCSwitchMon, + MaxThreadInfoClass + } + + public enum TimerInformationClass : int + { + TimerBasicInformation + } + + public enum TimerType : int + { + NotificationTimer, + SynchronizationTimer + } + + public enum TmInformationClass : int + { + TransactionManagerBasicInformation, + TransactionManagerLogInformation, + TransactionManagerLogPathInformation, + TransactionManagerOnlineProbeInformation, + TransactionManagerRecoveryInformation + } + + [Flags] + public enum TmOptions : int + { + Volatile = 0x1, + CommitDefault = 0x0, + CommitSystemVolume = 0x2, + CommitSystemHives = 0x4, + CommitLowest = 0x8, + CorruptForRecovery = 0x10, + CorruptForProgress = 0x20, + MaximumOption = 0x3f + } + + public enum TokenElevationType : int + { + Default = 1, + Full, + Limited + } + + public enum TokenInformationClass + { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, // 10 + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin, + TokenElevationType, + TokenLinkedToken, + TokenElevation, // 20 + TokenHasRestrictions, + TokenAccessInformation, + TokenVirtualizationAllowed, + TokenVirtualizationEnabled, + TokenIntegrityLevel, + TokenUIAccess, + TokenMandatoryPolicy, + TokenLogonSid, + MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum + } + + public enum TokenType : int + { + Primary = 1, + Impersonation + } + + public enum TransactionInformationClass : int + { + TransactionBasicInformation, + TransactionPropertiesInformation, + TransactionEnlistmentInformation, + TransactionFullInformation + } + + [Flags] + public enum TransactionOptions : int + { + DoNotPromote = 0x1, + MaximumOption = 0x1 + } + + public enum TransactionOutcome : int + { + Undetermined = 1, + Committed, + Aborted + } + + public enum TransactionState : int + { + Normal = 1, + Indoubt, + CommittedNotify + } + + public enum WaitType : int + { + WaitAll, + WaitAny + } + + public enum WellKnownSidType : int + { + WinNullSid = 0, + WinWorldSid = 1, + WinLocalSid = 2, + WinCreatorOwnerSid = 3, + WinCreatorGroupSid = 4, + WinCreatorOwnerServerSid = 5, + WinCreatorGroupServerSid = 6, + WinNtAuthoritySid = 7, + WinDialupSid = 8, + WinNetworkSid = 9, + WinBatchSid = 10, + WinInteractiveSid = 11, + WinServiceSid = 12, + WinAnonymousSid = 13, + WinProxySid = 14, + WinEnterpriseControllersSid = 15, + WinSelfSid = 16, + WinAuthenticatedUserSid = 17, + WinRestrictedCodeSid = 18, + WinTerminalServerSid = 19, + WinRemoteLogonIdSid = 20, + WinLogonIdsSid = 21, + WinLocalSystemSid = 22, + WinLocalServiceSid = 23, + WinNetworkServiceSid = 24, + WinBuiltinDomainSid = 25, + WinBuiltinAdministratorsSid = 26, + WinBuiltinUsersSid = 27, + WinBuiltinGuestsSid = 28, + WinBuiltinPowerUsersSid = 29, + WinBuiltinAccountOperatorsSid = 30, + WinBuiltinSystemOperatorsSid = 31, + WinBuiltinPrintOperatorsSid = 32, + WinBuiltinBackupOperatorsSid = 33, + WinBuiltinReplicatorSid = 34, + WinBuiltinPreWindows2000CompatibleAccessSid = 35, + WinBuiltinRemoteDesktopUsersSid = 36, + WinBuiltinNetworkConfigurationOperatorsSid = 37, + WinAccountAdministratorSid = 38, + WinAccountGuestSid = 39, + WinAccountKrbtgtSid = 40, + WinAccountDomainAdminsSid = 41, + WinAccountDomainUsersSid = 42, + WinAccountDomainGuestsSid = 43, + WinAccountComputersSid = 44, + WinAccountControllersSid = 45, + WinAccountCertAdminsSid = 46, + WinAccountSchemaAdminsSid = 47, + WinAccountEnterpriseAdminsSid = 48, + WinAccountPolicyAdminsSid = 49, + WinAccountRasAndIasServersSid = 50, + WinNTLMAuthenticationSid = 51, + WinDigestAuthenticationSid = 52, + WinSChannelAuthenticationSid = 53, + WinThisOrganizationSid = 54, + WinOtherOrganizationSid = 55, + WinBuiltinIncomingForestTrustBuildersSid = 56, + WinBuiltinPerfMonitoringUsersSid = 57, + WinBuiltinPerfLoggingUsersSid = 58, + WinBuiltinAuthorizationAccessSid = 59, + WinBuiltinTerminalServerLicenseServersSid = 60, + WinBuiltinDCOMUsersSid = 61, + WinBuiltinIUsersSid = 62, + WinIUserSid = 63, + WinBuiltinCryptoOperatorsSid = 64, + WinUntrustedLabelSid = 65, + WinLowLabelSid = 66, + WinMediumLabelSid = 67, + WinHighLabelSid = 68, + WinSystemLabelSid = 69, + WinWriteRestrictedCodeSid = 70, + WinCreatorOwnerRightsSid = 71, + WinCacheablePrincipalsGroupSid = 72, + WinNonCacheablePrincipalsGroupSid = 73, + WinEnterpriseReadonlyControllersSid = 74, + WinAccountReadonlyControllersSid = 75, + WinBuiltinEventLogReadersGroup = 76, + WinNewEnterpriseReadonlyControllersSid = 77, + WinBuiltinCertSvcDComAccessGroup = 78 + } + + public enum WinNtProductType : int + { + WinNt = 1, + LanManNt, + Server + } + + [Flags] + public enum WtFlags : uint + { + ExecuteDefault = 0x0, + ExecuteInIoThread = 0x1, + ExecuteInUiThread = 0x2, + ExecuteInWaitThread = 0x4, + ExecuteOnlyOnce = 0x8, + ExecuteLongFunction = 0x10, + ExecuteInTimerThread = 0x20, + ExecuteInPersistentIoThread = 0x40, + ExecuteInPersistentThread = 0x80, + TransferImpersonation = 0x100 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/NativeFunctions.cs b/branches/ph-plugins/ProcessHacker.Native/Api/NativeFunctions.cs new file mode 100644 index 000000000..a94d8d23a --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/NativeFunctions.cs @@ -0,0 +1,3771 @@ +/* + * Process Hacker - + * native API functions + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains function declarations for the Native API. + * + * Only functions from ntdll.dll are considered to be part of the + * Native API. + */ + +// Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) +#pragma warning disable 1573 + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Api +{ + public static partial class Win32 + { + // IMPORTANT: All timeouts, etc. are in 100ns units except when stated otherwise. + + // These definitions were gathered from these sources: + // + // * The NT API headers - almost everything + // * Alex Ionescu's NDK + // * ReactOS source code + // * The Windows DDK - Kernel Transaction Manager (KTM) types + + #region System Calls + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAcceptConnectPort( + [Out] out IntPtr PortHandle, + [In] [Optional] IntPtr PortContext, + [In] IntPtr ConnectionRequest, + [In] bool AcceptConnection, + [Optional] ref PortView ServerView, + [Optional] ref RemotePortView ClientView + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAcceptConnectPort( + [Out] out IntPtr PortHandle, + [In] [Optional] IntPtr PortContext, + [In] IntPtr ConnectionRequest, + [In] bool AcceptConnection, + [Optional] IntPtr ServerView, + [Optional] IntPtr ClientView + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAccessCheck( + [In] IntPtr SecurityDescriptor, + [In] IntPtr ClientToken, + [In] int DesiredAccess, + [In] ref GenericMapping GenericMapping, + [In] [Optional] IntPtr PrivilegeSet, // out PrivilegeSet* + ref int PrivilegeSetLength, + [Out] out int GrantedAccess, + [Out] out NtStatus AccessStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAdjustGroupsToken( + [In] IntPtr TokenHandle, + [In] bool ResetToDefault, + [In] ref TokenGroups NewState, + [In] [Optional] int BufferLength, + [In] [Optional] IntPtr PreviousState, // out TokenGroups* + [In] [Optional] IntPtr ReturnLength // out int* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAdjustPrivilegesToken( + [In] IntPtr TokenHandle, + [In] bool DisableAllPrivileges, + [In] [Optional] ref TokenPrivileges NewState, + [In] [Optional] int BufferLength, + [In] [Optional] IntPtr PreviousState, // out TokenPrivileges* + [In] [Optional] IntPtr ReturnLength // out int* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAlertThread( + [In] IntPtr ThreadHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAlertResumeThread( + [In] IntPtr ThreadHandle, + [Out] [Optional] out int PreviousSuspendCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAllocateLocallyUniqueId( + [Out] out Luid Luid + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAllocateVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + [In] IntPtr ZeroBits, + ref IntPtr RegionSize, + [In] MemoryFlags AllocationType, + [In] MemoryProtection Protect + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAreMappedFilesTheSame( + [In] IntPtr File1MappedAsAnImage, + [In] IntPtr File2MappedAsFile + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtAssignProcessToJobObject( + [In] IntPtr JobHandle, + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCancelIoFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCancelIoFile( + [In] IntPtr FileHandle, + [In] IntPtr IoStatusBlock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCancelTimer( + [In] IntPtr TimerHandle, + [Out] [Optional] out bool CurrentState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtClearEvent( + [In] IntPtr EventHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtClose( + [In] IntPtr Handle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCommitComplete( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCommitEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCommitTransaction( + [In] IntPtr TransactionHandle, + [In] bool Wait + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCompareTokens( + [In] IntPtr FirstTokenHandle, + [In] IntPtr SecondTokenHandle, + [MarshalAs(UnmanagedType.I1)] + [Out] out bool Equal + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCompleteConnectPort( + [In] IntPtr PortHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtConnectPort( + [Out] out IntPtr PortHandle, + [In] ref UnicodeString PortName, + [In] ref SecurityQualityOfService SecurityQos, + [Optional] ref PortView ClientView, + [Optional] ref RemotePortView ServerView, + [Out] [Optional] out int MaxMessageLength, + [Optional] IntPtr ConnectionInformation, + [Optional] ref int ConnectionInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtConnectPort( + [Out] out IntPtr PortHandle, + [In] ref UnicodeString PortName, + [In] ref SecurityQualityOfService SecurityQos, + [Optional] IntPtr ClientView, + [Optional] IntPtr ServerView, + [Out] [Optional] out int MaxMessageLength, + [Optional] IntPtr ConnectionInformation, + [Optional] ref int ConnectionInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtConnectPort( + [Out] out IntPtr PortHandle, + [In] ref UnicodeString PortName, + [In] ref SecurityQualityOfService SecurityQos, + [Optional] ref PortView ClientView, + [Optional] ref RemotePortView ServerView, + [Out] [Optional] IntPtr MaxMessageLength, + [Optional] IntPtr ConnectionInformation, + [Optional] IntPtr ConnectionInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtConnectPort( + [Out] out IntPtr PortHandle, + [In] ref UnicodeString PortName, + [In] ref SecurityQualityOfService SecurityQos, + [Optional] IntPtr ClientView, + [Optional] IntPtr ServerView, + [Out] [Optional] IntPtr MaxMessageLength, + [Optional] IntPtr ConnectionInformation, + [Optional] IntPtr ConnectionInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateDebugObject( + [Out] out IntPtr DebugObjectHandle, + [In] DebugObjectAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] DebugObjectFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateDebugObject( + [Out] out IntPtr DebugObjectHandle, + [In] DebugObjectAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] DebugObjectFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateDirectoryObject( + [Out] out IntPtr DirectoryHandle, + [In] DirectoryAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateEnlistment( + [Out] out IntPtr EnlistmentHandle, + [In] EnlistmentAccess DesiredAccess, + [In] IntPtr ResourceManagerHandle, + [In] IntPtr TransactionHandle, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] EnlistmentOptions CreateOptions, + [In] NotificationMask NotificationMask, + [In] [Optional] IntPtr EnlistmentKey + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateEvent( + [Out] out IntPtr EventHandle, + [In] EventAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] EventType EventType, + [In] bool InitialState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateEvent( + [Out] out IntPtr EventHandle, + [In] EventAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] EventType EventType, + [In] bool InitialState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateEventPair( + [Out] out IntPtr EventPairHandle, + [In] EventPairAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateEventPair( + [Out] out IntPtr EventPairHandle, + [In] EventPairAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateFile( + [Out] out IntPtr FileHandle, + [In] FileAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [Out] out IoStatusBlock IoStatusBlock, + [In] [Optional] ref long allocationSize, + [In] FileAttributes fileAttributes, + [In] FileShareMode shareAccess, + [In] FileCreationDisposition createDisposition, + [In] FileCreateOptions createOptions, + [In] [Optional] IntPtr EaBuffer, + [In] int EaLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateIoCompletion( + [Out] out IntPtr IoCompletionHandle, + [In] IoCompletionAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] int Count + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateIoCompletion( + [Out] out IntPtr IoCompletionHandle, + [In] IoCompletionAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] [Optional] int Count + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateJobObject( + [Out] out IntPtr JobHandle, + [In] JobObjectAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateJobObject( + [Out] out IntPtr JobHandle, + [In] JobObjectAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateJobSet( + [In] int NumJob, + JobSetArray[] UserJobSet, + [In] int Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateKey( + [Out] out IntPtr KeyHandle, + [In] KeyAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] int TitleIndex, + [In] [Optional] ref UnicodeString Class, + [In] RegOptions CreateOptions, + [Out] [Optional] out KeyCreationDisposition Disposition + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateKey( + [Out] out IntPtr KeyHandle, + [In] KeyAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] int TitleIndex, + [In] [Optional] IntPtr Class, + [In] RegOptions CreateOptions, + [Out] [Optional] out KeyCreationDisposition Disposition + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateKeyedEvent( + [Out] out IntPtr KeyedEventHandle, + [In] KeyedEventAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] int Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateKeyedEvent( + [Out] out IntPtr KeyedEventHandle, + [In] KeyedEventAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] int Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateMailslotFile( + [Out] out IntPtr FileHandle, + [In] FileAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [Out] out IoStatusBlock IoStatusBlock, + [In] FileCreateOptions CreateOptions, + [In] int MailslotQuota, + [In] int MaximumMessageSize, + [In] ref long ReadTimeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateMutant( + [Out] out IntPtr MutantHandle, + [In] MutantAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] bool InitialOwner + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateMutant( + [Out] out IntPtr MutantHandle, + [In] MutantAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] bool InitialOwner + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateNamedPipeFile( + [Out] out IntPtr FileHandle, + [In] FileAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [Out] out IoStatusBlock IoStatusBlock, + [In] FileShareMode ShareAccess, + [In] FileCreationDisposition CreateDisposition, + [In] FileCreateOptions CreateOptions, + [In] PipeType NamedPipeType, + [In] PipeType ReadMode, + [In] PipeCompletionMode CompletionMode, + [In] int MaximumInstances, + [In] int InboundQuota, + [In] int OutboundQuota, + [In] [Optional] ref long DefaultTimeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreatePort( + [Out] out IntPtr PortHandle, + [In] ref ObjectAttributes ObjectAttributes, + [In] int MaxConnectionInfoLength, + [In] int MaxMessageLength, + [In] [Optional] int MaxPoolUsage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateProcess( + [Out] out IntPtr ProcessHandle, + [In] ProcessAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] IntPtr ParentProcess, + [In] bool InheritHandleTable, + [In] [Optional] IntPtr SectionHandle, + [In] [Optional] IntPtr DebugPort, + [In] [Optional] IntPtr ExceptionPort + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateProcess( + [Out] out IntPtr ProcessHandle, + [In] ProcessAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] IntPtr ParentProcess, + [In] bool InheritHandleTable, + [In] [Optional] IntPtr SectionHandle, + [In] [Optional] IntPtr DebugPort, + [In] [Optional] IntPtr ExceptionPort + ); + + /// + /// Creates a profile object. + /// + /// A handle to the profile object. + /// + /// A handle to the process to profile. If NULL, all address spaces are profiled. + /// + /// + /// The first address at which to collect profiling information. + /// + /// + /// The size of the range to profile. ProfileBase <= address < + /// ProfileBase + ProfileSize will generate a hit. + /// + /// + /// A log2 value of each address bucket. Acceptable values are from 2 to 30. + /// + /// An array of int hit counters. + /// The size of the buffer, in bytes. + /// The profiling source. + /// The processors to profile. + /// A NTSTATUS value. + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateProfile( + [Out] out IntPtr ProfileHandle, + [In] [Optional] IntPtr ProcessHandle, + [In] IntPtr ProfileBase, + [In] IntPtr ProfileSize, + [In] int BucketSize, + [In] IntPtr Buffer, + [In] int BufferSize, + [In] KProfileSource ProfileSource, + [In] IntPtr Affinity + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateResourceManager( + [Out] out IntPtr ResourceManagerHandle, + [In] ResourceManagerAccess DesiredAccess, + [In] IntPtr TmHandle, + [In] [Optional] ref Guid ResourceManagerGuid, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ResourceManagerOptions CreateOptions, + [In] [Optional] ref UnicodeString Description // should be null-terminated + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSection( + [Out] out IntPtr SectionHandle, + [In] SectionAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref long MaximumSize, + [In] MemoryProtection PageAttributes, + [In] SectionAttributes SectionAttributes, + [In] [Optional] IntPtr FileHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSection( + [Out] out IntPtr SectionHandle, + [In] SectionAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] IntPtr MaximumSize, + [In] MemoryProtection PageAttributes, + [In] SectionAttributes SectionAttributes, + [In] [Optional] IntPtr FileHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSection( + [Out] out IntPtr SectionHandle, + [In] SectionAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] [Optional] ref long MaximumSize, + [In] int PageAttributes, + [In] int SectionAttributes, + [In] [Optional] IntPtr FileHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSemaphore( + [Out] out IntPtr SemaphoreHandle, + [In] SemaphoreAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] int InitialCount, + [In] int MaximumCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSemaphore( + [Out] out IntPtr SemaphoreHandle, + [In] SemaphoreAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] int InitialCount, + [In] int MaximumCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateSymbolicLinkObject( + [Out] out IntPtr LinkHandle, + [In] SymbolicLinkAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] ref UnicodeString LinkTarget + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateThread( + [Out] out IntPtr ThreadHandle, + [In] ThreadAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] IntPtr ProcessHandle, + [Out] out ClientId ClientId, + [In] ref Context ThreadContext, + [In] ref InitialTeb InitialTeb, + [In] bool CreateSuspended + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateThreadEx( + [Out] out IntPtr ThreadHandle, + [In] ThreadAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] IntPtr ProcessHandle, + [In] IntPtr StartAddress, + [In] IntPtr Parameter, + [In] int Flags, + [In] [Optional] int Reserved, + [In] [Optional] int StackCommit, + [In] [Optional] int StackReserve, + [In] [Optional] IntPtr Unknown + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateTimer( + [Out] out IntPtr TimerHandle, + [In] TimerAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] TimerType TimerType + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateTimer( + [Out] out IntPtr TimerHandle, + [In] TimerAccess DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] TimerType TimerType + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateToken( + [Out] out IntPtr TokenHandle, + [In] TokenAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] TokenType TokenType, + [In] ref Luid AuthenticationId, + [In] ref long ExpirationTime, + [In] ref TokenUser User, + [In] ref TokenGroups Groups, + [In] ref TokenPrivileges Privileges, + [In] [Optional] ref TokenOwner Owner, + [In] ref TokenPrimaryGroup PrimaryGroup, + [In] [Optional] ref TokenDefaultDacl DefaultDacl, + [In] ref TokenSource TokenSource + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateTransaction( + [Out] out IntPtr TransactionHandle, + [In] TransactionAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref Guid Uow, // Unit of work identifier + [In] [Optional] IntPtr TmHandle, + [In] [Optional] TransactionOptions CreateOptions, + [In] [Optional] int IsolationLevel, // Reserved + [In] [Optional] int IsolationFlags, // Reserved + [In] [Optional] ref long Timeout, + [In] [Optional] ref UnicodeString Description + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateTransactionManager( + [Out] out IntPtr TmHandle, + [In] TmAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref UnicodeString LogFileName, + [In] [Optional] TmOptions CreateOptions, + [In] [Optional] int CreateStrength // Reserved + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtCreateWaitablePort( + [Out] out IntPtr PortHandle, + [In] ref ObjectAttributes ObjectAttributes, + [In] int MaxConnectionInfoLength, + [In] int MaxMessageLength, + [In] [Optional] int MaxPoolUsage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDebugActiveProcess( + [In] IntPtr ProcessHandle, + [In] IntPtr DebugObjectHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDebugContinue( + [In] IntPtr DebugObjectHandle, + [In] ref ClientId ClientId, + [In] NtStatus ContinueStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDelayExecution( + [In] bool Alertable, + [In] ref long DelayInterval + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDeleteFile( + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDeleteKey( + [In] IntPtr KeyHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDeleteValueKey( + [In] IntPtr KeyHandle, + [In] ref UnicodeString ValueName + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDeviceIoControlFile( + [In] IntPtr FileHandle, + [In] IntPtr Event, + [In] IoApcRoutine ApcRoutine, + [In] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] int IoControlCode, + [In] IntPtr InputBuffer, + [In] int InputBufferLength, + [In] IntPtr OutputBuffer, + [In] int OutputBufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDeviceIoControlFile( + [In] IntPtr FileHandle, + [In] IntPtr Event, + [In] IoApcRoutine ApcRoutine, + [In] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] int IoControlCode, + [In] IntPtr InputBuffer, + [In] int InputBufferLength, + [In] IntPtr OutputBuffer, + [In] int OutputBufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDuplicateObject( + [In] IntPtr SourceProcessHandle, + [In] IntPtr SourceHandle, + [In] IntPtr TargetProcessHandle, + [Out] out IntPtr TargetHandle, + [In] int DesiredAccess, + [In] HandleFlags Attributes, + [In] DuplicateOptions Options + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtDuplicateToken( + [In] IntPtr ExistingTokenHandle, + [In] TokenAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] bool EffectiveOnly, + [In] TokenType TokenType, + [Out] out IntPtr NewTokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtEnumerateTransactionObject( + [In] [Optional] IntPtr RootObjectHandle, + [In] KtmObjectType QueryType, + ref KtmObjectCursor ObjectCursor, + [In] int ObjectCursorLength, + [Out] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtExtendSection( + [In] IntPtr SectionHandle, + ref long NewSectionSize + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFilterToken( + [In] IntPtr ExistingTokenHandle, + [In] int Flags, + [In] [Optional] ref TokenGroups SidsToDisable, + [In] [Optional] ref TokenPrivileges PrivilegesToDelete, + [In] [Optional] ref TokenGroups RestrictedSids, + [Out] out IntPtr NewTokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFlushBuffersFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFlushKey( + [In] IntPtr KeyHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFlushVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + ref IntPtr RegionSize, + [Out] out IoStatusBlock IoStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFreeVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + ref IntPtr RegionSize, + [In] MemoryFlags FreeType + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFsControlFile( + [In] IntPtr FileHandle, + [In] IntPtr Event, + [In] IoApcRoutine ApcRoutine, + [In] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] int FsControlCode, + [In] IntPtr InputBuffer, + [In] int InputBufferLength, + [In] IntPtr OutputBuffer, + [In] int OutputBufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtFsControlFile( + [In] IntPtr FileHandle, + [In] IntPtr Event, + [In] IoApcRoutine ApcRoutine, + [In] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] int FsControlCode, + [In] IntPtr InputBuffer, + [In] int InputBufferLength, + [In] IntPtr OutputBuffer, + [In] int OutputBufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetContextThread( + [In] IntPtr ThreadHandle, + [In] IntPtr ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetContextThread( + [In] IntPtr ThreadHandle, + ref Context ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetContextThread( + [In] IntPtr ThreadHandle, + ref ContextAmd64 ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetCurrentProcessorNumber(); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetNextProcess( + [In] [Optional] IntPtr ProcessHandle, + [In] ProcessAccess DesiredAccess, + [In] HandleFlags HandleAttributes, + [In] int Flags, + [Out] out IntPtr NewProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetNextThread( + [In] [Optional] IntPtr ProcessHandle, + [In] [Optional] IntPtr ThreadHandle, + [In] ThreadAccess DesiredAccess, + [In] HandleFlags HandleAttributes, + [In] int Flags, + [Out] out IntPtr NewThreadHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtGetNotificationResourceManager( + [In] IntPtr ResourceManagerHandle, + [In] IntPtr TransactionNotification, // TransactionNotification* + [In] int NotificationLength, + [In] ref long Timeout, + [Out] [Optional] out int ReturnLength, + [In] int Asynchronous, // Must be zero. + [In] [Optional] IntPtr AsynchronousContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtImpersonateAnonymousToken( + [In] IntPtr ThreadHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtImpersonateClientOfPort( + [In] IntPtr PortHandle, + [In] IntPtr Message + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtImpersonateThread( + [In] IntPtr ServerThreadHandle, + [In] IntPtr ClientThreadHandle, + [In] ref SecurityQualityOfService SecurityQos + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtIsProcessInJob( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr JobHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtListenPort( + [In] IntPtr PortHandle, + [In] IntPtr ConnectionRequest + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtLoadDriver( + [In] ref UnicodeString DriverServiceName + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtLockFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] ref long ByteOffset, + [In] ref long Length, + [In] int Key, + [In] bool FailImmediately, + [In] bool ExclusiveLock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtLockFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] ref long ByteOffset, + [In] ref long Length, + [In] int Key, + [In] bool FailImmediately, + [In] bool ExclusiveLock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtLockVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + ref IntPtr RegionSize, + [In] MemoryFlags MapType + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtMakePermanentObject( + [In] IntPtr Handle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtMakeTemporaryObject( + [In] IntPtr Handle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtMapViewOfSection( + [In] IntPtr SectionHandle, + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + [In] IntPtr ZeroBits, + [In] IntPtr CommitSize, + [Optional] ref long SectionOffset, + ref IntPtr ViewSize, + [In] SectionInherit InheritDisposition, + [In] MemoryFlags AllocationType, + [In] MemoryProtection Win32Protect + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtNotifyChangeDirectoryFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] FileNotifyFlags CompletionFilter, + [In] bool WatchTree + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtNotifyChangeDirectoryFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] FileNotifyFlags CompletionFilter, + [In] bool WatchTree + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenDirectoryObject( + [Out] out IntPtr DirectoryHandle, + [In] DirectoryAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenEnlistment( + [Out] out IntPtr EnlistmentHandle, + [In] EnlistmentAccess DesiredAccess, + [In] IntPtr RmHandle, + [In] ref Guid EnlistmentGuid, + [In] [Optional] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenEvent( + [Out] out IntPtr EventHandle, + [In] EventAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenEventPair( + [Out] out IntPtr EventPairHandle, + [In] EventPairAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenFile( + [Out] out IntPtr FileHandle, + [In] FileAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [Out] out IoStatusBlock IoStatusBlock, + [In] FileShareMode ShareAccess, + [In] FileCreateOptions OpenOptions + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenIoCompletion( + [Out] out IntPtr IoCompletionHandle, + [In] IoCompletionAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenJobObject( + [Out] out IntPtr JobHandle, + [In] JobObjectAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenKey( + [Out] out IntPtr KeyHandle, + [In] KeyAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenKeyedEvent( + [Out] out IntPtr KeyedEventHandle, + [In] KeyedEventAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenMutant( + [Out] out IntPtr MutantHandle, + [In] MutantAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenProcess( + [Out] out IntPtr ProcessHandle, + [In] ProcessAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref ClientId ClientId + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenProcess( + [Out] out IntPtr ProcessHandle, + [In] ProcessAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] [Optional] IntPtr ClientId + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenProcessToken( + [In] IntPtr ProcessHandle, + [In] TokenAccess DesiredAccess, + [Out] out IntPtr TokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenProcessTokenEx( + [In] IntPtr ProcessHandle, + [In] TokenAccess DesiredAccess, + [In] int HandleAttributes, + [Out] out IntPtr TokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenResourceManager( + [Out] out IntPtr ResourceManagerHandle, + [In] ResourceManagerAccess DesiredAccess, + [In] IntPtr TmHandle, + [In] ref Guid ResourceManagerGuid, + [In] [Optional] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenSection( + [Out] out IntPtr SectionHandle, + [In] SectionAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenSemaphore( + [Out] out IntPtr SemaphoreHandle, + [In] SemaphoreAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenSymbolicLinkObject( + [Out] out IntPtr LinkHandle, + [In] SymbolicLinkAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenThread( + [Out] out IntPtr ThreadHandle, + [In] ThreadAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref ClientId ClientId + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenThread( + [Out] out IntPtr ThreadHandle, + [In] ThreadAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes, + [In] [Optional] IntPtr ClientId + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenThreadToken( + [In] IntPtr ThreadHandle, + [In] TokenAccess DesiredAccess, + [In] bool OpenAsSelf, + [Out] out IntPtr TokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenThreadTokenEx( + [In] IntPtr ThreadHandle, + [In] TokenAccess DesiredAccess, + [In] bool OpenAsSelf, + [In] int HandleAttributes, + [Out] out IntPtr TokenHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenTimer( + [Out] out IntPtr TimerHandle, + [In] TimerAccess DesiredAccess, + [In] ref ObjectAttributes ObjectAttributes + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenTransaction( + [Out] out IntPtr TransactionHandle, + [In] TransactionAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] ref Guid Uow, + [In] [Optional] IntPtr TmHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenTransactionManager( + [Out] out IntPtr TmHandle, + [In] TmAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] ref UnicodeString LogFileName, + [In] [Optional] ref Guid TmIdentity, + [In] [Optional] int OpenOptions // Must be zero. + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtOpenTransactionManager( + [Out] out IntPtr TmHandle, + [In] TmAccess DesiredAccess, + [In] [Optional] ref ObjectAttributes ObjectAttributes, + [In] [Optional] IntPtr LogFileName, + [In] [Optional] IntPtr TmIdentity, + [In] [Optional] int OpenOptions // Must be zero. + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPrepareComplete( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPrepareEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPrePrepareComplete( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPrePrepareEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPrivilegeCheck( + [In] IntPtr ClientToken, + [In] IntPtr RequiredPrivileges, // PrivilegeSet* + [MarshalAs(UnmanagedType.U1)] + [Out] out bool Result + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtProtectVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + ref IntPtr RegionSize, + [In] MemoryProtection NewProtect, + [Out] out MemoryProtection OldProtect + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtPulseEvent( + [In] IntPtr EventHandle, + [Out] [Optional] out int PreviousState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryDebugFilterState( + [In] int ComponentId, + [In] int Level + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryDirectoryFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr FileInformation, + [In] int Length, + [In] FileInformationClass FileInformationClass, + [In] bool ReturnSingleEntry, + [In] [Optional] IntPtr FileName, + [In] bool RestartScan + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryDirectoryObject( + [In] IntPtr DirectoryHandle, + [In] IntPtr Buffer, + [In] int Length, + [In] bool ReturnSingleEntry, + [In] bool RestartScan, + ref int Context, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryEvent( + [In] IntPtr EventHandle, + [In] EventInformationClass EventInformationClass, + [Out] out EventBasicInformation EventInformation, + [In] int EventInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationEnlistment( + [In] IntPtr EnlistmentHandle, + [In] EnlistmentInformationClass EnlistmentInformationClass, + [In] IntPtr EnlistmentInformation, + [In] int EnlistmentInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationEnlistment( + [In] IntPtr EnlistmentHandle, + [In] EnlistmentInformationClass EnlistmentInformationClass, + [Out] out EnlistmentBasicInformation EnlistmentInformation, + [In] int EnlistmentInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr FileInformation, + [In] int FileInformationLength, + [In] FileInformationClass FileInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationJobObject( + [In] [Optional] IntPtr JobHandle, + [In] JobObjectInformationClass JobObjectInformationClass, + [In] IntPtr JobObjectInformation, + [In] int JobObjectInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + IntPtr ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out int ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out IntPtr ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out IoCounters ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out PooledUsageAndLimits ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out ProcessPriorityClassStruct ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out QuotaLimits ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out VmCounters ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out MemExecuteOptions ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out ProcessBasicInformation ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [Out] out UnicodeString ProcessInformation, + [In] int ProcessInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationResourceManager( + [In] IntPtr ResourceManagerHandle, + [In] ResourceManagerInformationClass ResourceManagerInformationClass, + [In] IntPtr ResourceManagerInformation, + [In] int ResourceManagerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + ref ThreadBasicInformation ThreadInformation, + [In] int ThreadInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + [Out] out int ThreadInformation, + [In] int ThreadInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + [Out] out IntPtr ThreadInformation, + [In] int ThreadInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + IntPtr ThreadInformation, + [In] int ThreadInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public unsafe static extern NtStatus NtQueryInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + void* ThreadInformation, + [In] int ThreadInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationToken( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [In] IntPtr TokenInformation, + [In] int TokenInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransaction( + [In] IntPtr TransactionHandle, + [In] TransactionInformationClass TransactionInformationClass, + [In] IntPtr TransactionInformation, + [In] int TransactionInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransaction( + [In] IntPtr TransactionHandle, + [In] TransactionInformationClass TransactionInformationClass, + [Out] out TransactionBasicInformation TransactionInformation, + [In] int TransactionInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransactionManager( + [In] IntPtr TransactionManagerHandle, + [In] TmInformationClass TransactionManagerInformationClass, + [In] IntPtr TransactionManagerInformation, + [In] int TransactionManagerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransactionManager( + [In] IntPtr TransactionManagerHandle, + [In] TmInformationClass TransactionManagerInformationClass, + [Out] out TmBasicInformation TransactionManagerInformation, + [In] int TransactionManagerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransactionManager( + [In] IntPtr TransactionManagerHandle, + [In] TmInformationClass TransactionManagerInformationClass, + [Out] out TmLogInformation TransactionManagerInformation, + [In] int TransactionManagerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryInformationTransactionManager( + [In] IntPtr TransactionManagerHandle, + [In] TmInformationClass TransactionManagerInformationClass, + [Out] out TmRecoveryInformation TransactionManagerInformation, + [In] int TransactionManagerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryIntervalProfile( + [In] KProfileSource Source, + [Out] out int Interval + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryIoCompletion( + [In] IntPtr IoCompletionHandle, + [In] IoCompletionInformationClass IoCompletionInformationClass, + [Out] out IoCompletionBasicInformation IoCompletionInformation, + [In] int IoCompletionInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryMutant( + [In] IntPtr MutantHandle, + [In] MutantInformationClass MutantInformationClass, + [Out] out MutantBasicInformation MutantInformation, + [In] int MutantInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryMutant( + [In] IntPtr MutantHandle, + [In] MutantInformationClass MutantInformationClass, + [Out] out MutantOwnerInformation MutantInformation, + [In] int MutantInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryObject( + [In] IntPtr Handle, + [In] ObjectInformationClass ObjectInformationClass, + [Out] IntPtr ObjectInformation, + [In] int ObjectInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryPortInformationProcess(); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySection( + [In] IntPtr SectionHandle, + [In] SectionInformationClass SectionInformationClass, + [Out] out SectionBasicInformation SectionInformation, + [In] IntPtr SectionInformationLength, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySection( + [In] IntPtr SectionHandle, + [In] SectionInformationClass SectionInformationClass, + [Out] out SectionImageInformation SectionInformation, + [In] IntPtr SectionInformationLength, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySecurityObject( + [In] IntPtr Handle, + [In] SecurityInformation SecurityInformation, + [In] IntPtr SecurityDescriptor, + [In] int SecurityDescriptorLength, + [Out] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySemaphore( + [In] IntPtr SemaphoreHandle, + [In] SemaphoreInformationClass SemaphoreInformationClass, + [Out] out SemaphoreBasicInformation SemaphoreInformation, + [In] int SemaphoreInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySymbolicLinkObject( + [In] IntPtr LinkHandle, + ref UnicodeString LinkName, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + [Out] out SystemBasicInformation SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + IntPtr SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + [MarshalAs(UnmanagedType.LPArray)] SystemProcessorPerformanceInformation[] SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + [Out] out SystemPerformanceInformation SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + [Out] out SystemTimeOfDayInformation SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQuerySystemInformation( + [In] SystemInformationClass SystemInformationClass, + [Out] out SystemCacheInformation SystemInformation, + [In] int SystemInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryTimer( + [In] IntPtr TimerHandle, + [In] TimerInformationClass TimerInformationClass, + [Out] out TimerBasicInformation TimerInformation, + [In] int TimerInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryVirtualMemory( + [In] IntPtr ProcessHandle, + [In] IntPtr BaseAddress, + [In] MemoryInformationClass MemoryInformationClass, + [In] IntPtr Buffer, + [In] IntPtr MemoryInformationLength, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryVirtualMemory( + [In] IntPtr ProcessHandle, + [In] IntPtr BaseAddress, + [In] MemoryInformationClass MemoryInformationClass, + [Out] out MemoryBasicInformation Buffer, + [In] IntPtr MemoryInformationLength, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueryVolumeInformationFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr FsInformation, + [In] int FsInformationLength, + [In] FsInformationClass FsInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtQueueApcThread( + [In] IntPtr ThreadHandle, + [In] IntPtr ApcRoutine, + [In] [Optional] IntPtr ApcArgument1, + [In] [Optional] IntPtr ApcArgument2, + [In] [Optional] IntPtr ApcArgument3 + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] ref long ByteOffset, + [In] [Optional] ref int Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] IntPtr ByteOffset, + [In] [Optional] IntPtr Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] IntPtr ByteOffset, + [In] [Optional] IntPtr Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadOnlyEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadRequestData( + [In] IntPtr PortHandle, + [In] IntPtr Message, + [In] int DataEntryIndex, + [In] IntPtr Buffer, + [In] IntPtr BufferSize, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReadVirtualMemory( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr BaseAddress, + [In] IntPtr Buffer, + [In] IntPtr BufferSize, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRecoverEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] IntPtr EnlistmentKey + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRecoverResourceManager( + [In] IntPtr ResourceManagerHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRecoverTransactionManager( + [In] IntPtr TransactionManagerHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRegisterThreadTerminatePort( + [In] IntPtr PortHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReleaseKeyedEvent( + [In] IntPtr KeyedEventHandle, + [In] IntPtr KeyValue, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReleaseMutant( + [In] IntPtr MutantHandle, + [Out] [Optional] out int PreviousCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReleaseSemaphore( + [In] IntPtr SemaphoreHandle, + [In] int ReleaseCount, + [Out] [Optional] out int PreviousCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRemoveIoCompletion( + [In] IntPtr IoCompletionHandle, + [Out] out IntPtr KeyContext, + [Out] out IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRemoveProcessDebug( + [In] IntPtr ProcessHandle, + [In] IntPtr DebugObjectHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReplyPort( + [In] IntPtr PortHandle, + [In] IntPtr ReplyMessage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReplyWaitReceivePort( + [In] IntPtr PortHandle, + [Out] [Optional] out IntPtr PortContext, + [In] [Optional] IntPtr ReplyMessage, + [In] IntPtr ReceiveMessage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReplyWaitReceivePortEx( + [In] IntPtr PortHandle, + [Out] [Optional] out IntPtr PortContext, + [In] [Optional] IntPtr ReplyMessage, + [In] IntPtr ReceiveMessage, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtReplyWaitReplyPort( + [In] IntPtr PortHandle, + [In] IntPtr ReplyMessage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRequestPort( + [In] IntPtr PortHandle, + [In] IntPtr RequestMessage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRequestWaitReplyPort( + [In] IntPtr PortHandle, + [In] IntPtr RequestMessage, + [In] IntPtr ReplyMessage + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtResetEvent( + [In] IntPtr EventHandle, + [Out] [Optional] out int PreviousState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtResumeProcess( + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtResumeThread( + [In] IntPtr ThreadHandle, + [Out] [Optional] out int PreviousSuspendCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRollbackComplete( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRollbackEnlistment( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRollbackTransaction( + [In] IntPtr TransactionHandle, + [In] bool Wait + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtRollforwardTransactionManager( + [In] IntPtr TransactionManagerHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetContextThread( + [In] IntPtr ThreadHandle, + [In] IntPtr ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetContextThread( + [In] IntPtr ThreadHandle, + [In] ref Context ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetContextThread( + [In] IntPtr ThreadHandle, + [In] ref ContextAmd64 ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetDebugFilterState( + [In] int ComponentId, + [In] int Level, + [In] bool State + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetEvent( + [In] IntPtr EventHandle, + [Out] [Optional] out int PreviousState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetEventBoostPriority( + [In] IntPtr EventHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetHighEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetHighWaitLowEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationDebugObject( + [In] IntPtr DebugObjectHandle, + [In] DebugObjectInformationClass DebugObjectInformationClass, + [In] IntPtr DebugObjectInformation, + [In] int DebugObjectInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr FileInformation, + [In] int Length, + [In] FileInformationClass FileInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationJobObject( + [In] IntPtr JobHandle, + [In] JobObjectInformationClass JobObjectInformationClass, + [In] IntPtr JobObjectInformation, + [In] int JobObjectInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationObject( + [In] IntPtr Handle, + [In] ObjectInformationClass ObjectInformationClass, + [In] IntPtr ObjectInformation, + [In] int ObjectInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [In] IntPtr ProcessInformation, + [In] int ProcessInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [In] ref int ProcessInformation, + [In] int ProcessInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [In] ref ProcessHandleTracingEnable ProcessInformation, + [In] int ProcessInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationProcess( + [In] IntPtr ProcessHandle, + [In] ProcessInformationClass ProcessInformationClass, + [In] ref ProcessPriorityClassStruct ProcessInformation, + [In] int ProcessInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + [In] IntPtr ThreadInformation, + [In] int ThreadInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + [In] ref int ThreadInformation, + [In] int ThreadInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationThread( + [In] IntPtr ThreadHandle, + [In] ThreadInformationClass ThreadInformationClass, + [In] ref IntPtr ThreadInformation, + [In] int ThreadInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetInformationToken( + [In] IntPtr TokenHandle, + [In] TokenInformationClass TokenInformationClass, + [In] IntPtr TokenInformation, + [In] int TokenInformationLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetIntervalProfile( + [In] int Interval, + [In] KProfileSource Source + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetIoCompletion( + [In] IntPtr IoCompletionHandle, + [In] IntPtr KeyContext, + [In] [Optional] IntPtr ApcContext, + [In] NtStatus IoStatus, + [In] IntPtr IoStatusInformation + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetLowEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetLowWaitHighEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetSecurityObject( + [In] IntPtr Handle, + [In] SecurityInformation SecurityInformation, + [In] IntPtr SecurityDescriptor + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetSystemInformation( + [In] SystemInformationClass SystemInformationClass, + [In] ref SystemLoadAndCallImage SystemInformation, + [In] int SystemInformationLength + ); + + /// Period, in milliseconds. + [DllImport("ntdll.dll")] + public static extern NtStatus NtSetTimer( + [In] IntPtr TimerHandle, + [In] ref long DueTime, + [In] [Optional] TimerApcRoutine TimerApcRoutine, + [In] [Optional] IntPtr TimerContext, + [In] bool ResumeTimer, + [In] [Optional] int Period, + [Out] [Optional] out bool PreviousState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSignalAndWaitForSingleObject( + [In] IntPtr SignalHandle, + [In] IntPtr WaitHandle, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSinglePhaseReject( + [In] IntPtr EnlistmentHandle, + [In] [Optional] ref long TmVirtualClock + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtStartProfile( + [In] IntPtr ProfileHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtStopProfile( + [In] IntPtr ProfileHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSuspendProcess( + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtSuspendThread( + [In] IntPtr ThreadHandle, + [Out] [Optional] out int PreviousSuspendCount + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtTerminateJobObject( + [In] IntPtr JobHandle, + [In] NtStatus ExitStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtTerminateProcess( + [In] [Optional] IntPtr ProcessHandle, + [In] NtStatus ExitStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtTerminateThread( + [In] [Optional] IntPtr ThreadHandle, + [In] NtStatus ExitStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtTestAlert(); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtUnloadDriver( + [In] ref UnicodeString DriverServiceName + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtUnlockFile( + [In] IntPtr FileHandle, + [Out] out IoStatusBlock IoStatusBlock, + [In] ref long ByteOffset, + [In] ref long Length, + [In] int Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtUnlockVirtualMemory( + [In] IntPtr ProcessHandle, + ref IntPtr BaseAddress, + ref IntPtr RegionSize, + [In] MemoryFlags MapType + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtUnmapViewOfSection( + [In] IntPtr ProcessHandle, + [In] IntPtr BaseAddress + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitForDebugEvent( + [In] IntPtr DebugObjectHandle, + [In] bool Alertable, + [In] [Optional] ref long Timeout, + [In] IntPtr WaitStateChange + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitForKeyedEvent( + [In] IntPtr KeyedEventHandle, + [In] IntPtr KeyValue, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitForMultipleObjects( + [In] int Count, + [In] IntPtr[] Handles, + [In] WaitType WaitType, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitForMultipleObjects32( + [In] int Count, + [In] int[] Handles, + [In] WaitType WaitType, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitForSingleObject( + [In] IntPtr Handle, + [In] bool Alertable, + [In] [Optional] ref long Timeout + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitHighEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWaitLowEventPair( + [In] IntPtr EventPairHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWriteFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] ref long ByteOffset, + [In] [Optional] ref int Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWriteFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [Out] out IoStatusBlock IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] IntPtr ByteOffset, + [In] [Optional] IntPtr Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWriteFile( + [In] IntPtr FileHandle, + [In] [Optional] IntPtr Event, + [In] [Optional] IoApcRoutine ApcRoutine, + [In] [Optional] IntPtr ApcContext, + [In] IntPtr IoStatusBlock, + [In] IntPtr Buffer, + [In] int Length, + [In] [Optional] IntPtr ByteOffset, + [In] [Optional] IntPtr Key + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWriteRequestData( + [In] IntPtr PortHandle, + [In] IntPtr Message, + [In] int DataEntryIndex, + [In] IntPtr Buffer, + [In] IntPtr BufferSize, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtWriteVirtualMemory( + [In] IntPtr ProcessHandle, + [In] [Optional] IntPtr BaseAddress, + [In] IntPtr Buffer, + [In] IntPtr BufferSize, + [Out] [Optional] out IntPtr ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus NtYieldExecution(); + + #endregion + + #region CSR + + [DllImport("ntdll.dll")] + // return: CsrCaptureHeader* + public static extern IntPtr CsrAllocateCaptureBuffer( + [In] int CountMessagePointers, + [In] int Size + ); + + [DllImport("ntdll.dll")] + public static extern int CsrAllocateMessagePointer( + [In] IntPtr CaptureBuffer, // CsrCaptureHeader* + [In] int Length, + [Out] out IntPtr Pointer + ); + + [DllImport("ntdll.dll")] + public static extern void CsrCaptureMessageBuffer( + [In] IntPtr CaptureBuffer, // CsrCaptureHeader* + [In] [Optional] IntPtr Buffer, + [In] int Length, + [Out] out IntPtr CapturedBuffer + ); + + [DllImport("ntdll.dll")] + public static extern void CsrCaptureMessageString( + [In] IntPtr CaptureBuffer, // CsrCaptureHeader* + [MarshalAs(UnmanagedType.LPStr)] + [In] string String, + [In] int Length, + [In] int MaximumLength, + [Out] out AnsiString CapturedString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus CsrClientCallServer( + [In] IntPtr Message, // CsrApiMsg* + [In] [Optional] IntPtr CaptureBuffer, // CsrCaptureHeader* + [In] int ApiNumber, + [In] int ArgLength + ); + + [DllImport("ntdll.dll")] + public static extern void CsrFreeCaptureBuffer( + [In] IntPtr CaptureBuffer // CsrCaptureHeader* + ); + + #endregion + + #region Debugging + + [DllImport("ntdll.dll")] + public static extern void DbgBreakPoint(); + + [DllImport("ntdll.dll")] + public static extern void DbgBreakPointWithStatus( + [In] int Status + ); + + [DllImport("ntdll.dll")] + public static extern int DbgPrompt( + [MarshalAs(UnmanagedType.LPStr)] + [In] string Prompt, + [In] IntPtr Response, + [In] int MaximumResponseLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgQueryDebugFilterState( + [In] int ComponentId, + [In] int Level + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgSetDebugFilterState( + [In] int ComponentId, + [In] int Level, + [In] bool State + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiConnectToDbg(); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiContinue( + [In] ref ClientId ClientId, + [In] NtStatus ContinueStatus + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiConvertStateChangeStructure( + [In] IntPtr WaitStateChange, // DbgUiWaitStateChange* + [In] IntPtr Win32DebugEvent // DebugEvent* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiDebugActiveProcess( + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr DbgUiGetThreadDebugObject(); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiIssueRemoteBreakin( + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern void DbgUiRemoteBreakin( + [In] IntPtr Parameter + ); + + [DllImport("ntdll.dll")] + public static extern void DbgUiSetThreadDebugObject( + [In] IntPtr DebugObjectHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiStopDebugging( + [In] IntPtr ProcessHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus DbgUiWaitStateChange( + [In] IntPtr WaitStateChange, // DbgUiWaitStateChange* + [In] [Optional] ref long Timeout + ); + + #endregion + + #region Loader + + [DllImport("ntdll.dll", CharSet = CharSet.Unicode)] + public static extern NtStatus LdrGetDllHandle( + [In] [Optional] string DllPath, + [In] [Optional] ref int DllCharacteristics, + [In] ref UnicodeString DllName, + [Out] out IntPtr DllHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus LdrGetProcedureAddress( + [In] IntPtr DllHandle, + [In] [Optional] ref AnsiString ProcedureName, + [In] [Optional] int ProcedureNumber, + [Out] out IntPtr ProcedureAddress + ); + + [DllImport("ntdll.dll", CharSet = CharSet.Unicode)] + public static extern NtStatus LdrLoadDll( + [In] [Optional] string DllPath, + [In] [Optional] ref int DllCharacteristics, + [In] ref UnicodeString DllName, + [Out] out IntPtr DllHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus LdrQueryProcessModuleInformation( + [In] IntPtr ModuleInformation, // RtlProcessModules* + [In] int ModuleInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus LdrUnloadDll( + [In] IntPtr DllHandle + ); + + #endregion + + #region Misc. + + [DllImport("ntdll.dll")] + public static extern IntPtr NtCurrentTeb(); + + #endregion + + #region Run-Time Library + + #region Access Control + + #region Access Control Entries + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAccessAllowedAce( + [In] IntPtr Acl, + [In] int AceRevision, + [In] int AccessMask, + [In] IntPtr Sid // Sid* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAccessAllowedAceEx( + [In] IntPtr Acl, + [In] int AceRevision, + [In] AceFlags AceFlags, + [In] int AccessMask, + [In] IntPtr Sid // Sid* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAccessDeniedAce( + [In] IntPtr Acl, + [In] int AceRevision, + [In] int AccessMask, + [In] IntPtr Sid // Sid* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAccessDeniedAceEx( + [In] IntPtr Acl, + [In] int AceRevision, + [In] AceFlags AceFlags, + [In] int AccessMask, + [In] IntPtr Sid // Sid* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAce( + [In] IntPtr Acl, + [In] int AceRevision, + [In] int StartingAceIndex, + [In] IntPtr AceList, // Ace** + [In] int AceListLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAuditAccessAce( + [In] IntPtr Acl, + [In] int AceRevision, + [In] int AccessMask, + [In] IntPtr Sid, // Sid* + [In] bool AuditSuccess, + [In] bool AuditFailure + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddAuditAccessAceEx( + [In] IntPtr Acl, + [In] int AceRevision, + [In] AceFlags AceFlags, + [In] int AccessMask, + [In] IntPtr Sid, // Sid* + [In] bool AuditSuccess, + [In] bool AuditFailure + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAddCompoundAce( + [In] IntPtr Acl, + [In] int AceRevision, + [In] AceType AceType, + [In] int AccessMask, + [In] IntPtr ServerSid, // Sid* + [In] IntPtr ClientSid + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeleteAce( + [In] IntPtr Acl, + [In] int AceIndex + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlFirstFreeAce( + [In] IntPtr Acl, + [Out] out IntPtr FirstFree + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetAce( + [In] IntPtr Acl, + [In] int AceIndex, + [Out] out IntPtr Ace // Ace** + ); + + #endregion + + #region Access Control Lists + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateAcl( + [In] IntPtr Acl, // Acl* + [In] int AclLength, + [In] int AclRevision + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryInformationAcl( + [In] IntPtr Acl, + [In] IntPtr AclInformation, + [In] int AclInformationLength, + [In] AclInformationClass AclInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryInformationAcl( + [In] IntPtr Acl, + [Out] out AclRevisionInformation AclInformation, + [In] int AclInformationLength, + [In] AclInformationClass AclInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryInformationAcl( + [In] IntPtr Acl, + [Out] out AclSizeInformation AclInformation, + [In] int AclInformationLength, + [In] AclInformationClass AclInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetInformationAcl( + [In] IntPtr Acl, + [In] IntPtr AclInformation, + [In] int AclInformationLength, + [In] AclInformationClass AclInformationClass + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlValidAcl( + [In] IntPtr Acl + ); + + #endregion + + #region Access Masks + + [DllImport("ntdll.dll")] + public static extern bool RtlAreAllAccessesGranted( + [In] int GrantedAccess, + [In] int DesiredAccess + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlAreAnyAccessesGranted( + [In] int GrantedAccess, + [In] int DesiredAccess + ); + + [DllImport("ntdll.dll")] + public static extern void RtlMapGenericMask( + ref int AccessMask, + [In] ref GenericMapping GenericMapping + ); + + #endregion + + #region Security Descriptors + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAbsoluteToSelfRelativeSD( + [In] IntPtr AbsoluteSecurityDescriptor, + [In] IntPtr SelfRelativeSecurityDescriptor, + ref int BufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateSecurityDescriptor( + [In] IntPtr SecurityDescriptor, // SecurityDescriptor* + [In] int Revision + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateSecurityDescriptorRelative( + [In] IntPtr SecurityDescriptor, // SecurityDescriptorRelative* + [In] int Revision + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetControlSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [Out] out SecurityDescriptorControlFlags Control, + [Out] out int Revision + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetDaclSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [MarshalAs(UnmanagedType.U1)] + [Out] out bool DaclPresent, + [Out] out IntPtr Dacl, // Acl** + [MarshalAs(UnmanagedType.U1)] + [Out] out bool DaclDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetGroupSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [Out] out IntPtr Group, // Sid** + [MarshalAs(UnmanagedType.U1)] + [Out] out bool GroupDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetOwnerSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [Out] out IntPtr Owner, // Sid** + [MarshalAs(UnmanagedType.U1)] + [Out] out bool OwnerDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlGetSaclSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [MarshalAs(UnmanagedType.U1)] + [Out] out bool SaclPresent, + [Out] out IntPtr Sacl, // Acl** + [MarshalAs(UnmanagedType.U1)] + [Out] out bool SaclDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlGetSecurityDescriptorRMControl( + [In] IntPtr SecurityDescriptor, + [Out] out byte RMControl + ); + + [DllImport("ntdll.dll")] + public static extern int RtlLengthSecurityDescriptor( + [In] IntPtr SecurityDescriptor + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlMakeSelfRelativeSD( + [In] IntPtr AbsoluteSecurityDescriptor, + [In] IntPtr SelfRelativeSecurityDescriptor, + ref int BufferLength + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSelfRelativeToAbsoluteSD( + [In] IntPtr SelfRelativeSecurityDescriptor, + [In] IntPtr AbsoluteSecurityDescriptor, + ref int AbsoluteSecurityDescriptorSize, + [In] IntPtr Dacl, // Acl* + ref int DaclSize, + [In] IntPtr Sacl, // Acl* + ref int SaclSize, + [In] IntPtr Owner, // Sid* + ref int OwnerSize, + [In] IntPtr PrimaryGroup, // Sid* + ref int PrimaryGroupSize + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSelfRelativeToAbsoluteSD2( + [In] IntPtr SelfRelativeSecurityDescriptor, + ref int BufferSize + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetAttributesSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] SecurityDescriptorControlFlags Control, + ref int Revision + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetControlSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] SecurityDescriptorControlFlags ControlBitsOfInterest, + [In] SecurityDescriptorControlFlags ControlBitsToSet + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetDaclSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] bool DaclPresent, + [In] IntPtr Dacl, // Acl* + [In] bool DaclDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetGroupSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] IntPtr Group, // Sid* + [In] bool GroupDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetOwnerSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] IntPtr Owner, // Sid* + [In] bool OwnerDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetSaclSecurityDescriptor( + [In] IntPtr SecurityDescriptor, + [In] bool SaclPresent, + [In] IntPtr Sacl, // Acl* + [In] bool SaclDefaulted + ); + + [DllImport("ntdll.dll")] + public static extern void RtlSetSecurityDescriptorRMControl( + [In] IntPtr SecurityDescriptor, + [In] [Optional] ref byte RMControl + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlValidRelativeSecurityDescriptor( + [In] IntPtr SecurityDescriptorInput, + [In] int SecurityDescriptorLength, + [In] SecurityInformation RequiredInformation + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlValidSecurityDescriptor( + [In] IntPtr SecurityDescriptor + ); + + #endregion + + #region Security Objects + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCopySecurityDescriptor( + [In] IntPtr InputSecurityDescriptor, + [Out] out IntPtr OutputSecurityDescriptor + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateUserSecurityObject( + [In] RtlAceData[] AceData, + [In] int AceCount, + [In] IntPtr OwnerSid, // Sid* + [In] IntPtr GroupSid, // Sid* + [In] bool IsDirectoryObject, + [In] ref GenericMapping GenericMapping, + [Out] out IntPtr NewDescriptor // SecurityDescriptor** + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeleteSecurityObject( + ref IntPtr ObjectDescriptor // SecurityDescriptor** + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlNewSecurityObject( + [In] IntPtr ParentDescriptor, + [In] IntPtr CreatorDescriptor, + [Out] out IntPtr NewDescriptor, // SecurityDescriptor** + [In] bool IsDirectoryObject, + [In] IntPtr Token, + [In] ref GenericMapping GenericMapping + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetSecurityObject( + [In] SecurityInformation SecurityInformation, + [In] IntPtr ModificationDescriptor, + [Out] out IntPtr ObjectsSecurityDescriptor, // SecurityDescriptor** + [In] ref GenericMapping GenericMapping, + [In] IntPtr Token + ); + + #endregion + + #endregion + + #region Bitmaps + + [DllImport("ntdll.dll")] + public static extern bool RtlAreBitsClear( + [In] ref RtlBitmap BitMapHeader, + [In] int StartingIndex, + [In] int Length + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlAreBitsSet( + [In] ref RtlBitmap BitMapHeader, + [In] int StartingIndex, + [In] int Length + ); + + public static int RtlCheckBit( + ref RtlBitmap BitMapHeader, + int BitPosition + ) + { + unsafe + { + int* buffer = (int*)BitMapHeader.Buffer; + + return (buffer[BitPosition / 32] >> (BitPosition % 32)) & 0x1; + } + } + + [DllImport("ntdll.dll")] + public static extern void RtlClearAllBits( + [In] ref RtlBitmap BitMapHeader + ); + + [DllImport("ntdll.dll")] + public static extern void RtlClearBit( + [In] ref RtlBitmap BitMapHeader, + [In] int BitNumber + ); + + [DllImport("ntdll.dll")] + public static extern void RtlClearBits( + [In] ref RtlBitmap BitMapHeader, + [In] int StartingIndex, + [In] int NumberToClear + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindClearBits( + [In] ref RtlBitmap BitMapHeader, + [In] int NumberToFind, + [In] int HintIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindClearBitsAndSet( + [In] ref RtlBitmap BitMapHeader, + [In] int NumberToFind, + [In] int HintIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindClearRuns( + [In] ref RtlBitmap BitMapHeader, + RtlBitmapRun[] RunArray, + [In] int SizeOfRunArray, + [In] bool LocateLongestRuns + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindLastBackwardRunClear( + [In] ref RtlBitmap BitMapHeader, + [In] int FromIndex, + [Out] out int StartingRunIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindNextForwardRunClear( + [In] ref RtlBitmap BitMapHeader, + [In] int FromIndex, + [Out] out int StartingRunIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindSetBits( + [In] ref RtlBitmap BitMapHeader, + [In] int NumberToFind, + [In] int HintIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindSetBitsAndClear( + [In] ref RtlBitmap BitMapHeader, + [In] int NumberToFind, + [In] int HintIndex + ); + + [DllImport("ntdll.dll")] + public static extern void RtlInitializeBitMap( + [Out] out RtlBitmap BitMapHeader, + [In] IntPtr BitMapBuffer, // int* + [In] int SizeOfBitMap + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindFirstRunClear( + [In] ref RtlBitmap BitMapHeader, + [Out] out int StartingIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlFindLongestRunClear( + [In] ref RtlBitmap BitMapHeader, + [Out] out int StartingIndex + ); + + [DllImport("ntdll.dll")] + public static extern int RtlNumberOfClearBits( + [In] ref RtlBitmap BitMapHeader + ); + + [DllImport("ntdll.dll")] + public static extern int RtlNumberOfSetBits( + [In] ref RtlBitmap BitMapHeader + ); + + [DllImport("ntdll.dll")] + public static extern void RtlSetBit( + [In] ref RtlBitmap BitMapHeader, + [In] int BitNumber + ); + + [DllImport("ntdll.dll")] + public static extern void RtlSetBits( + [In] ref RtlBitmap BitMapHeader, + [In] int StartingIndex, + [In] int NumberToSet + ); + + [DllImport("ntdll.dll")] + public static extern void RtlSetAllBits( + [In] ref RtlBitmap BitMapHeader + ); + + [DllImport("ntdll.dll")] + public static extern bool RtlTestBit( + [In] ref RtlBitmap BitMapHeader, + [In] int BitNumber + ); + + #endregion + + #region Bits + + [DllImport("ntdll.dll")] + public static extern sbyte RtlFindLeastSignificantBit( + [In] long Set + ); + + [DllImport("ntdll.dll")] + public static extern sbyte RtlFindMostSignificantBit( + [In] long Set + ); + + #endregion + + #region Debugging + + [DllImport("ntdll.dll")] + // return: RtlDebugInformation* + public static extern IntPtr RtlCreateQueryDebugBuffer( + [In] [Optional] int MaximumCommit, + [In] bool UseEventPair + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDestroyQueryDebugBuffer( + [In] IntPtr Buffer // RtlDebugInformation* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryProcessBackTraceInformation( + [In] IntPtr Buffer // RtlDebugInformation* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryProcessDebugInformation( + [In] IntPtr UniqueProcessId, + [In] RtlQueryProcessDebugFlags Flags, + [In] IntPtr Buffer // RtlDebugInformation* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryProcessHeapInformation( + [In] IntPtr Buffer // RtlDebugInformation* + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryProcessLockInformation( + [In] IntPtr Buffer // RtlDebugInformation* + ); + + // Not exported. + //[DllImport("ntdll.dll")] + //public static extern NtStatus RtlQueryProcessModuleInformation( + // [In] [Optional] IntPtr ProcessHandle, + // [In] RtlQueryProcessDebugFlags Flags, + // [In] IntPtr Buffer // RtlDebugInformation* + // ); + + #endregion + + #region Handle Tables + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlAllocateHandle( + [In] ref RtlHandleTable HandleTable, + [Out] [Optional] out int HandleIndex + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDestroyHandleTable( + ref RtlHandleTable HandleTable + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlFreeHandle( + [In] ref RtlHandleTable HandleTable, + [In] IntPtr Handle + ); + + [DllImport("ntdll.dll")] + public static extern void RtlInitializeHandleTable( + [In] int MaximumNumberOfHandles, + [In] int SizeOfHandleTableEntry, + [Out] out RtlHandleTable HandleTable + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlIsValidHandle( + [In] ref RtlHandleTable HandleTable, + [In] IntPtr Handle + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlIsValidIndexHandle( + [In] ref RtlHandleTable HandleTable, + [In] int HandleIndex, + [Out] out IntPtr Handle + ); + + #endregion + + #region Heaps + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlAllocateHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags, + [In] IntPtr Size + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlCompactHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlCreateHeap( + [In] HeapFlags Flags, + [In] [Optional] IntPtr HeapBase, + [In] [Optional] IntPtr ReserveSize, + [In] [Optional] IntPtr CommitSize, + [In] [Optional] IntPtr Lock, + [In] [Optional] IntPtr Parameters + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlDestroyHeap( + [In] IntPtr HeapHandle + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool RtlFreeHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags, + [In] IntPtr BaseAddress + ); + + [DllImport("ntdll.dll")] + public static extern int RtlGetProcessHeaps( + [In] int NumberOfHeaps, + IntPtr[] ProcessHeaps + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool RtlLockHeap( + [In] IntPtr HeapHandle + ); + + [DllImport("ntdll.dll")] + public static extern void RtlProtectHeap( + [In] IntPtr HeapHandle, + [In] bool MakeReadOnly + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlReAllocateHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags, + [In] IntPtr BaseAddress, + [In] IntPtr Size + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlSizeHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags, + [In] IntPtr BaseAddress + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool RtlUnlockHeap( + [In] IntPtr HeapHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlZeroHeap( + [In] IntPtr HeapHandle, + [In] HeapFlags Flags + ); + + #endregion + + #region Memory + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlCompareMemory( + [In] IntPtr Source1, + [In] IntPtr Source2, + [In] IntPtr Length + ); + + [DllImport("ntdll.dll")] + public static extern void RtlFillMemory( + [In] IntPtr Destination, + [In] IntPtr Length, + [In] byte Fill + ); + + [DllImport("ntdll.dll")] + public static extern void RtlMoveMemory( + [In] IntPtr Destination, + [In] IntPtr Source, + [In] IntPtr Length + ); + + [DllImport("ntdll.dll")] + public static extern void RtlZeroMemory( + [In] IntPtr Destination, + [In] IntPtr Length + ); + + #endregion + + #region Message Resources + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlFindMessage( + [In] IntPtr DllHandle, + [In] int MessageTableId, + [In] int MessageLanguageId, + [In] int MessageId, + [Out] out IntPtr MessageEntry // MessageResourceEntry* + ); + + #endregion + + #region Privileges + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAcquirePrivilege( + [In] uint[] Privilege, + [In] int NumPriv, + [In] RtlAcquirePrivilegeFlags Flags, + [Out] out IntPtr ReturnedState + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAdjustPrivilege( + [In] uint Privilege, + [In] bool Enable, + [In] bool Client, + [MarshalAs(UnmanagedType.I1)] + [Out] bool WasEnabled + ); + + [DllImport("ntdll.dll")] + public static extern void RtlReleasePrivilege( + [In] IntPtr StatePointer + ); + + #endregion + + #region Processes and Threads + + [DllImport("ntdll.dll")] + public static extern void RtlAcquirePebLock(); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAllocateFromPeb( + [In] int Size, + [Out] out IntPtr Block + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateEnvironment( + [In] bool CloneCurrentEnvironment, + [Out] out IntPtr Environment + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateProcessParameters( + [Out] out IntPtr ProcessParameters, + [In] ref UnicodeString ImagePathName, + [In] ref UnicodeString DllPath, + [In] ref UnicodeString CurrentDirectory, + [In] ref UnicodeString CommandLine, + [In] IntPtr Environment, + [In] ref UnicodeString WindowTitle, + [In] ref UnicodeString DesktopInfo, + [In] ref UnicodeString ShellInfo, + [In] ref UnicodeString RuntimeData + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateUserProcess( + [In] ref UnicodeString NtImagePathName, + [In] ObjectFlags Attributes, + [In] ref RtlUserProcessParameters ProcessParameters, + [In] IntPtr ProcessSecurityDescriptor, + [In] IntPtr ThreadSecurityDescriptor, + [In] IntPtr ParentProcess, + [In] bool InheritHandles, + [In] IntPtr DebugPort, + [In] IntPtr ExceptionPort, + [Out] out RtlUserProcessInformation ProcessInformation + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateUserThread( + [In] IntPtr Process, + [In] IntPtr ThreadSecurityDescriptor, + [In] bool CreateSuspended, + [In] int StackZeroBits, + [In] [Optional] IntPtr MaximumStackSize, + [In] [Optional] IntPtr InitialStackSize, + [In] IntPtr StartAddress, + [In] IntPtr Parameter, + [Out] out IntPtr Thread, + [Out] out ClientId ClientId + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlDeNormalizeProcessParameters( + [In] IntPtr ProcessParameters + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDestroyEnvironment( + [In] IntPtr Environment + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDestroyProcessParameters( + [In] IntPtr ProcessParameters + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlExitUserProcess( + [In] NtStatus ExitStatus + ); + + [DllImport("ntdll.dll")] + public static extern void RtlExitUserThread( + [In] NtStatus ExitStatus + ); + + [DllImport("ntdll.dll")] + public static extern void RtlFreeUserThreadStack( + [In] IntPtr Process, + [In] IntPtr Thread + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlFreeToPeb( + [In] IntPtr Block, + [In] int Size + ); + + [DllImport("ntdll.dll")] + public static extern void RtlInitializeContext( + [In] IntPtr Process, + ref Context Context, + [In] IntPtr Parameter, + [In] IntPtr InitialPc, + [In] IntPtr InitialSp + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlNormalizeProcessParameters( + [In] IntPtr ProcessParameters + ); + + [DllImport("ntdll.dll")] + public static extern Win32Error RtlNtStatusToDosError( + [In] NtStatus Status + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueryEnvironmentVariable_U( + [In] [Optional] IntPtr Environment, + [In] ref UnicodeString Name, + ref UnicodeString Value + ); + + [DllImport("ntdll.dll")] + public static extern void RtlReleasePebLock(); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlRemoteCall( + [In] IntPtr Process, + [In] IntPtr Thread, + [In] IntPtr CallSite, + [In] int ArgumentCount, + [In] IntPtr[] Arguments, + [In] bool PassContext, + [In] bool AlreadySuspended + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetCurrentEnvironment( + [In] IntPtr Environment, + [Out] out IntPtr PreviousEnvironment + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetEnvironmentVariable( + ref IntPtr Environment, + [In] ref UnicodeString Name, + [In] [Optional] ref UnicodeString Value + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetEnvironmentVariable( + [In] [Optional] IntPtr Environment, + [In] ref UnicodeString Name, + [In] [Optional] ref UnicodeString Value + ); + + #endregion + + #region Security IDs + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAllocateAndInitializeSid( + [In] ref SidIdentifierAuthority IdentifierAuthority, + [In] int SubAuthorityCount, + [In] int SubAuthority0, + [In] int SubAuthority1, + [In] int SubAuthority2, + [In] int SubAuthority3, + [In] int SubAuthority4, + [In] int SubAuthority5, + [In] int SubAuthority6, + [In] int SubAuthority7, + [Out] out IntPtr Sid + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlConvertSidToUnicodeString( + ref UnicodeString UnicodeString, + [In] IntPtr Sid, + [In] bool AllocateDestinationString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCopySid( + [In] int DestinationSidLength, + [In] IntPtr DestinationSid, + [In] IntPtr SourceSid + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlEqualSid( + [In] IntPtr Sid1, + [In] IntPtr Sid2 + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlEqualPrefixSid( + [In] IntPtr Sid1, + [In] IntPtr Sid2 + ); + + [DllImport("ntdll.dll")] + public static extern IntPtr RtlFreeSid( + [In] IntPtr Sid + ); + + [DllImport("ntdll.dll")] + public unsafe static extern SidIdentifierAuthority* RtlIdentifierAuthoritySid( + [In] IntPtr Sid + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlInitializeSid( + [In] IntPtr Sid, + [In] ref SidIdentifierAuthority IdentifierAuthority, + [In] int SubAuthorityCount + ); + + [DllImport("ntdll.dll")] + public static extern int RtlLengthRequiredSid( + [In] int SubAuthorityCount + ); + + [DllImport("ntdll.dll")] + public static extern int RtlLengthSid( + [In] IntPtr Sid + ); + + [DllImport("ntdll.dll")] + public unsafe static extern int* RtlSubAuthoritySid( + [In] IntPtr Sid, + [In] int SubAuthority + ); + + [DllImport("ntdll.dll")] + public unsafe static extern byte* RtlSubAuthorityCountSid( + [In] IntPtr Sid + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlValidSid( + [In] IntPtr Sid + ); + + #endregion + + #region Strings + + #region ANSI + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlAnsiStringToUnicodeString( + ref UnicodeString DestinationString, + [In] ref AnsiString SourceString, + [In] bool AllocateDestinationString + ); + + [DllImport("ntdll.dll")] + public static extern void RtlFreeAnsiString( + [In] ref AnsiString AnsiString + ); + + #endregion + + #region Unicode + + [DllImport("ntdll.dll")] + public static extern int RtlCompareUnicodeString( + [In] ref UnicodeString String1, + [In] ref UnicodeString String2, + [In] bool CaseInSensitive + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlCreateUnicodeString( + [Out] out UnicodeString DestinationString, + [MarshalAs(UnmanagedType.LPWStr)] + [In] string SourceString + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlCreateUnicodeStringFromAsciiz( + [Out] out UnicodeString DestinationString, + [MarshalAs(UnmanagedType.LPStr)] + [In] string SourceString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDuplicateUnicodeString( + [In] RtlDuplicateUnicodeStringFlags Flags, + [In] ref UnicodeString StringIn, + [Out] out UnicodeString StringOut + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlEqualUnicodeString( + [In] ref UnicodeString String1, + [In] ref UnicodeString String2, + [In] bool CaseInSensitive + ); + + [DllImport("ntdll.dll")] + public static extern void RtlFreeUnicodeString( + [In] ref UnicodeString UnicodeString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlHashUnicodeString( + [In] ref UnicodeString String, + [In] bool CaseInSensitive, + [In] HashStringAlgorithm HashAlgorithm, + [Out] out int HashValue + ); + + [DllImport("ntdll.dll")] + [return: MarshalAs(UnmanagedType.I1)] + public static extern bool RtlPrefixUnicodeString( + [In] ref UnicodeString String1, + [In] ref UnicodeString String2, + [In] bool CaseInSensitive + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlUnicodeStringToAnsiString( + ref AnsiString DestinationString, + [In] ref UnicodeString SourceString, + [In] bool AllocateDestinationString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlUpcaseUnicodeStringToAnsiString( + ref AnsiString DestinationString, + [In] ref UnicodeString SourceString, + [In] bool AllocateDestinationString + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlValidateUnicodeString( + [In] int Flags, + [In] ref UnicodeString String + ); + + #endregion + + #endregion + + #region Threading + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateTimer( + [In] IntPtr TimerQueueHandle, + [Out] out IntPtr Handle, + [In] WaitOrTimerCallbackDelegate Function, + [In] IntPtr Context, + [In] int DueTime, + [In] int Period, + [In] WtFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlCreateTimerQueue( + [Out] out IntPtr TimerQueueHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeleteTimer( + [In] IntPtr TimerQueueHandle, + [In] IntPtr TimerToCancel, + [In] IntPtr Event + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeleteTimerQueue( + [In] IntPtr TimerQueueHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeleteTimerQueueEx( + [In] IntPtr TimerQueueHandle, + [In] IntPtr Event + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeregisterWait( + [In] IntPtr WaitHandle + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlDeregisterWaitEx( + [In] IntPtr WaitHandle, + [In] IntPtr Event + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlQueueWorkItem( + [MarshalAs(UnmanagedType.FunctionPtr)] + [In] WorkerCallbackDelegate Function, + [In] IntPtr Context, + [In] WtFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlRegisterWait( + [Out] out IntPtr WaitHandle, + [In] IntPtr Handle, + [MarshalAs(UnmanagedType.FunctionPtr)] + [In] WaitOrTimerCallbackDelegate Function, + [In] IntPtr Context, + [In] int Milliseconds, + [In] WtFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlSetIoCompletionCallback( + [In] IntPtr FileHandle, + [In] ApcCallbackDelegate CompletionProc, + [In] WtFlags Flags + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlUpdateTimer( + [In] IntPtr TimerQueueHandle, + [In] IntPtr TimerHandle, + [In] int DueTime, + [In] int Period + ); + + #endregion + + #region WOW64 + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlWow64GetThreadContext( + [In] IntPtr ThreadHandle, + ref Context ThreadContext + ); + + [DllImport("ntdll.dll")] + public static extern NtStatus RtlWow64SetThreadContext( + [In] IntPtr ThreadHandle, + [In] ref Context ThreadContext + ); + + #endregion + + #endregion + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/NativeStructs.cs b/branches/ph-plugins/ProcessHacker.Native/Api/NativeStructs.cs new file mode 100644 index 000000000..0765aaa1c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/NativeStructs.cs @@ -0,0 +1,3206 @@ +/* + * Process Hacker - + * native API structs + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains structure declarations for the Native API. + * Structures shared between the Native API and Win32 are placed + * in this file. + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Api +{ + [StructLayout(LayoutKind.Sequential)] + public struct AccessAllowedAceStruct + { + public AceHeader Header; + public int Mask; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AccessAllowedObjectAceStruct + { + public AceHeader Header; + public int Mask; + public ObjectAceFlags Flags; + public Guid ObjectType; + public Guid InheritedObjectType; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AccessDeniedAceStruct + { + public AceHeader Header; + public int Mask; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AccessDeniedObjectAceStruct + { + public AceHeader Header; + public int Mask; + public ObjectAceFlags Flags; + public Guid ObjectType; + public Guid InheritedObjectType; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AceData + { + public AceType AceType; + public byte InheritFlags; + public AceFlags AceFlags; + public int Mask; + public IntPtr Sid; // Sid** + } + + [StructLayout(LayoutKind.Sequential)] + public struct AceHeader + { + public AceType AceType; + public AceFlags AceFlags; + public ushort AceSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AclRevisionInformation + { + public int AclRevision; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AclSizeInformation + { + public int AceCount; + public int AclBytesInUse; + public int AclBytesFree; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AclStruct + { + public byte AclRevision; + public byte Sbz1; + public ushort AclSize; + public ushort AceCount; + public ushort Sbz2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AnsiString : IDisposable + { + public AnsiString(string str) + { + UnicodeString unicodeStr; + + unicodeStr = new UnicodeString(str); + this = unicodeStr.ToAnsiString(); + unicodeStr.Dispose(); + } + + public ushort Length; + public ushort MaximumLength; + public IntPtr Buffer; + + public void Dispose() + { + if (this.Buffer == IntPtr.Zero) + return; + + Win32.RtlFreeAnsiString(ref this); + this.Buffer = IntPtr.Zero; + } + + public UnicodeString ToUnicodeString() + { + NtStatus status; + UnicodeString unicodeStr = new UnicodeString(); + + if ((status = Win32.RtlAnsiStringToUnicodeString(ref unicodeStr, ref this, true)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return unicodeStr; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct BaseCreateProcessMsg + { + public IntPtr ProcessHandle; + public IntPtr ThreadHandle; + public ClientId ClientId; + public ClientId DebuggerClientId; + public ProcessCreationFlags CreationFlags; + public int IsVdm; + public IntPtr VdmHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct BaseCreateThreadMsg + { + public IntPtr ThreadHandle; + public ClientId ClientId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ClientId + { + public ClientId(int processId, int threadId) + { + this.UniqueProcess = new IntPtr(processId); + this.UniqueThread = new IntPtr(threadId); + } + + public IntPtr UniqueProcess; + public IntPtr UniqueThread; + + public int ProcessId { get { return this.UniqueProcess.ToInt32(); } } + public int ThreadId { get { return this.UniqueThread.ToInt32(); } } + } + + [StructLayout(LayoutKind.Sequential)] + public struct CompoundAccessAllowedAceStruct + { + public AceHeader Header; + public int Mask; + public CompoundAceType CompoundAceType; + public ushort Reserved; + public int SidStart; + } + + /// + /// x86 context + /// + [StructLayout(LayoutKind.Sequential)] + public struct Context + { + public ContextFlags ContextFlags; + + public int Dr0; + public int Dr1; + public int Dr2; + public int Dr3; + public int Dr6; + public int Dr7; + + [MarshalAs(UnmanagedType.Struct)] + public FloatingSaveArea FloatSave; + + public int SegGs; + public int SegFs; + public int SegEs; + public int SegDs; + + public int Edi; + public int Esi; + public int Ebx; + public int Edx; + public int Ecx; + public int Eax; + + public int Ebp; + public int Eip; + public int SegCs; + public int EFlags; + public int Esp; + public int SegSs; + + public unsafe fixed byte ExtendedRegisters[Win32.MaximumSupportedExtension]; + } + + /// + /// AMD64 context. + /// + [StructLayout(LayoutKind.Sequential)] + public struct ContextAmd64 + { + public long P1Home; + public long P2Home; + public long P3Home; + public long P4Home; + public long P5Home; + public long P6Home; + + public ContextFlagsAmd64 ContextFlags; + public int MxCsr; + + public ushort SegCs; + public ushort SegDs; + public ushort SegEs; + public ushort SegFs; + public ushort SegGs; + public ushort SegSs; + public int EFlags; + + public long Dr0; + public long Dr1; + public long Dr2; + public long Dr3; + public long Dr6; + public long Dr7; + + public long Rax; + public long Rcx; + public long Rdx; + public long Rbx; + public long Rsp; + public long Rbp; + public long Rsi; + public long Rdi; + public long R8; + public long R9; + public long R10; + public long R11; + public long R12; + public long R13; + public long R14; + public long R15; + + public long Rip; + + public XmmSaveArea32 FltSave; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)] + public M128A[] VectorRegister; + public long VectorControl; + + public long DebugControl; + public long LastBranchToRip; + public long LastBranchFromRip; + public long LastExceptionToRip; + public long LastExceptionFromRip; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CsrApiMsg + { + public static readonly int ApiMessageDataOffset = + Marshal.OffsetOf(typeof(CsrApiMsg), "ApiMessageData").ToInt32(); + + public PortMessageStruct Header; + public IntPtr CaptureBuffer; // CsrCaptureHeader* + public int ApiNumber; + public int ReturnValue; + public int Reserved; + public int ApiMessageData; + // API message data follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct CsrCaptureHeader + { + public static readonly int MessagePointerOffsetsOffset = + Marshal.OffsetOf(typeof(CsrCaptureHeader), "MessagePointerOffsets").ToInt32(); + + public int Length; + public IntPtr RelatedCaptureBuffer; // CsrCaptureBuffer* + public int CountMessagePointers; + public IntPtr FreeSpace; + public IntPtr MessagePointerOffsets; + // Array of ULONG_PTRs follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmCreateProcess + { + public int SubSystemKey; + public IntPtr FileHandle; + public IntPtr BaseOfImage; + public int DebugInfoFileOffset; + public int DebugInfoSize; + public DbgKmCreateThread InitialThread; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmCreateThread + { + public int SubSystemKey; + public IntPtr StartAddress; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmException + { + public ExceptionRecord ExceptionRecord; + public int FirstChance; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmExitProcess + { + public NtStatus ExitStatus; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmExitThread + { + public NtStatus ExitStatus; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmLoadDll + { + public IntPtr FileHandle; + public IntPtr BaseOfDll; + public int DebugInfoFileOffset; + public int DebugInfoSize; + public IntPtr NamePointer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgKmUnloadDll + { + public IntPtr BaseAddress; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgUiCreateProcess + { + public IntPtr HandleToProcess; + public IntPtr HandleToThread; + public DbgKmCreateProcess NewProcess; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgUiCreateThread + { + public IntPtr HandleToThread; + public DbgKmCreateThread NewThread; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DbgUiWaitStateChange + { + // Overlapping objects and non-objects. Must manually marshal. + //[StructLayout(LayoutKind.Explicit, Pack = 1)] + //public struct StateInfoUnion + //{ + // [FieldOffset(0)] + // public DbgKmException Exception; + // [FieldOffset(0)] + // public DbgUiCreateThread CreateThread; + // [FieldOffset(0)] + // public DbgUiCreateProcess CreateProcess; + // [FieldOffset(0)] + // public DbgKmExitThread ExitThread; + // [FieldOffset(0)] + // public DbgKmExitProcess ExitProcess; + // [FieldOffset(0)] + // public DbgKmLoadDll LoadDll; + // [FieldOffset(0)] + // public DbgKmUnloadDll UnloadDll; + //} + + public DbgState NewState; + public ClientId AppClientId; + //public StateInfoUnion StateInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct EnlistmentBasicInformation + { + public Guid EnlistmentId; + public Guid TransactionId; + public Guid ResourceManagerId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct EventBasicInformation + { + public EventType EventType; + public int EventState; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ExceptionRecord + { + public NtStatus ExceptionCode; + public int ExceptionFlags; + public IntPtr ExceptionRecordPtr; + public IntPtr ExceptionAddress; + public int NumberParameters; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.ExceptionMaximumParameters)] + public IntPtr[] ExceptionInformation; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileAccessInformation + { + public FileAccess AccessFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileAlignmentInformation + { + public FileAlignment AlignmentRequirement; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileAllInformation + { + public FileBasicInformation BasicInformation; + public FileStandardInformation StandardInformation; + public FileInternalInformation InternalInformation; + public FileEaInformation EaInformation; + public FileAccessInformation AccessInformation; + public FilePositionInformation PositionInformation; + public FileModeInformation ModeInformation; + public FileAlignmentInformation AlignmentInformation; + public FileNameInformation NameInformation; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileBasicInformation + { + public long CreationTime; + public long LastAccessTime; + public long LastWriteTime; + public long ChangeTime; + public FileAttributes FileAttributes; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileCompletionInformation + { + public IntPtr Port; + public IntPtr Key; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileDirectoryInformation + { + public static int FileNameOffset = + Marshal.OffsetOf(typeof(FileDirectoryInformation), "FileName").ToInt32(); + + public int NextEntryOffset; + public int FileIndex; + public long CreationTime; + public long LastAccessTime; + public long LastWriteTime; + public long ChangeTime; + public long EndOfFile; + public long AllocationSize; + public FileAttributes FileAttributes; + public int FileNameLength; + public short FileName; + // File name string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileDispositionInformation + { + [MarshalAs(UnmanagedType.I1)] + public bool DeleteFile; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileEaInformation + { + public int EaSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileEndOfFileInformation + { + public long EndOfFile; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileFsAttributeInformation + { + public int FileSystemAttributes; + public int MaximumComponentNameLength; + public int FileSystemNameLength; + public short FileSystemName; + // File system name string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileFsLabelInformation + { + public int VolumeLabelLength; + public short VolumeLabel; + // Volume label string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileFsVolumeInformation + { + public long VolumeCreationTime; + public int VolumeSerialNumber; + public int VolumeLabelLength; + [MarshalAs(UnmanagedType.I1)] + public bool SupportsObjects; + public short VolumeLabel; + // Volume label string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileInternalInformation + { + public long IndexNumber; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileMailslotQueryInformation + { + public int MaximumMessageSize; + public int MailslotQuota; + public int NextMessageSize; + public int MessagesAvailable; + public long ReadTimeout; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileMailslotSetInformation + { + public long ReadTimeout; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileModeInformation + { + public FileObjectFlags Mode; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileNameInformation + { + public static int FileNameOffset = + Marshal.OffsetOf(typeof(FileNameInformation), "FileName").ToInt32(); + + public int FileNameLength; + public short FileName; + // File name string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileNamesInformation + { + public int NextEntryOffset; + public int FileIndex; + public int FileNameLength; + public short FileName; + // File name string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileNotifyInformation + { + public static int FileNameOffset = + Marshal.OffsetOf(typeof(FileNotifyInformation), "FileName").ToInt32(); + + public int NextEntryOffset; + public FileNotifyAction Action; + public int FileNameLength; + public short FileName; + // Unicode file name string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct FilePipeInformation + { + public PipeType ReadMode; + public PipeCompletionMode CompletionMode; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FilePipeLocalInformation + { + public PipeType NamedPipeType; + public PipeConfiguration NamedPipeConfiguration; + public int MaximumInstances; + public int CurrentInstances; + public int InboundQuota; + public int ReadDataAvailable; + public int OutboundQuota; + public int WriteQuotaAvailable; + public PipeState NamedPipeState; + public PipeEnd NamedPipeEnd; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FilePipePeekBuffer + { + public static readonly int DataOffset = + Marshal.OffsetOf(typeof(FilePipePeekBuffer), "Data").ToInt32(); + + public PipeState NamedPipeState; + public int ReadDataAvailable; + public int NumberOfMessages; + public int MessageLength; + public byte Data; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FilePipeWaitForBuffer + { + public static readonly int NameOffset = + Marshal.OffsetOf(typeof(FilePipeWaitForBuffer), "Name").ToInt32(); + + public long Timeout; + public int NameLength; + [MarshalAs(UnmanagedType.I1)] + public bool TimeoutSpecified; + public short Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FilePositionInformation + { + public long CurrentByteOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileStandardInformation + { + public long AllocationSize; + public long EndOfFile; + public int NumberOfLinks; + [MarshalAs(UnmanagedType.I1)] + public bool DeletePending; + [MarshalAs(UnmanagedType.I1)] + public bool Directory; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileStreamInformation + { + public static int StreamNameOffset = + Marshal.OffsetOf(typeof(FileStreamInformation), "StreamName").ToInt32(); + + public int NextEntryOffset; + public int StreamNameLength; + public long StreamSize; + public long StreamAllocationSize; + public short StreamName; + // Stream name string follows (WCHAR). + } + + [StructLayout(LayoutKind.Sequential)] + public struct FloatingSaveArea + { + public int ControlWord; + public int StatusWord; + public int TagWord; + public int ErrorOffset; + public int ErrorSelector; + public int DataOffset; + public int DataSelector; + + public unsafe fixed byte RegisterArea[Win32.SizeOf80387Registers]; + + public int Cr0NpxState; + } + + [StructLayout(LayoutKind.Sequential)] + public struct GenericMapping + { + public int GenericRead; + public int GenericWrite; + public int GenericExecute; + public int GenericAll; + } + + [StructLayout(LayoutKind.Sequential)] + public struct GenericMapping + where T : struct + { + public GenericMapping(T read, T write, T execute, T all) + { + this.GenericRead = read; + this.GenericWrite = write; + this.GenericExecute = execute; + this.GenericAll = all; + } + + public T GenericRead; + public T GenericWrite; + public T GenericExecute; + public T GenericAll; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageBaseRelocation + { + public int VirtualAddress; + public int SizeOfBlock; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageBoundForwarderRef + { + public int TimeDateStamp; + public short OffsetModuleName; + public short Reserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageBoundImportDescriptor + { + public int TimeDateStamp; + public short OffsetModuleName; + public short NumberOfModuleForwarderRefs; + public ImageBoundForwarderRef ForwarderRefs; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageDataDirectory + { + public int VirtualAddress; + public int Size; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageExportDirectory + { + public int Characteristics; + public int TimeDateStamp; + public short MajorVersion; + public short MinorVersion; + public int Name; + public int Base; + public int NumberOfFunctions; + public int NumberOfNames; + public int AddressOfFunctions; + public int AddressOfNames; + public int AddressOfNameOrdinals; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageFileHeader + { + public MachineType Machine; + public short NumberOfSections; + public int TimeDateStamp; + public int PointerToSymbolTable; + public int NumberOfSymbols; + public short SizeOfOptionalHeader; + public ImageCharacteristics Characteristics; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageImportByName + { + public short Hint; + public byte Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageImportDescriptor + { + public int OriginalFirstThunk; // also Characteristics + public int TimeDateStamp; + public int ForwarderChain; + public int Name; + public int FirstThunk; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageLoadConfigDirectory + { + public int Size; + public int TimeDateStamp; + public short MajorVersion; + public short MinorVersion; + public int GlobalFlagsClear; + public int GlobalFlagsSet; + public int CriticalSectionDefaultTimeout; + public int DeCommitFreeBlockThreshold; + public int DeCommitTotalFreeThreshold; + public int LockPrefixTable; + public int MaximumAllocationSize; + public int VirtualMemoryThreshold; + public int ProcessHeapFlags; + public int ProcessAffinityMask; + public short CsdVersion; + public short Reserved1; + public int EditList; + public int SecurityCookie; + public int SEHandlerTable; + public int SEHandlerCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageLoadConfigDirectory64 + { + public int Size; + public int TimeDateStamp; + public short MajorVersion; + public short MinorVersion; + public int GlobalFlagsClear; + public int GlobalFlagsSet; + public int CriticalSectionDefaultTimeout; + public long DeCommitFreeBlockThreshold; + public long DeCommitTotalFreeThreshold; + public long LockPrefixTable; + public long MaximumAllocationSize; + public long VirtualMemoryThreshold; + public long ProcessAffinityMask; + public int ProcessHeapFlags; + public short CsdVersion; + public short Reserved1; + public long EditList; + public long SecurityCookie; + public long SEHandlerTable; + public long SEHandlerCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageNtHeaders + { + public int Signature; + public ImageFileHeader FileHeader; + public ImageOptionalHeader OptionalHeader; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageOptionalHeader + { + public short Magic; + public byte MajorLinkerVersion; + public byte MinorLinkerVersion; + public int SizeOfCode; + public int SizeOfInitializedData; + public int SizeOfUninitializedData; + public int AddressOfEntryPoint; + public int BaseOfCode; + public int BaseOfData; + public int ImageBase; + public int SectionAlignment; + public int FileAlignment; + public short MajorOperatingSystemVersion; + public short MinorOperatingSystemVersion; + public short MajorImageVersion; + public short MinorImageVersion; + public short MajorSubsystemVersion; + public short MinorSubsystemVersion; + public int Win32VersionValue; + public int SizeOfImage; + public int SizeOfHeaders; + public int CheckSum; + public ImageSubsystem Subsystem; + public ImageDllCharacteristics DllCharacteristics; + public int SizeOfStackReserve; + public int SizeOfStackCommit; + public int SizeOfHeapReserve; + public int SizeOfHeapCommit; + public int LoaderFlags; + public int NumberOfRvaAndSizes; + public ImageDataDirectory DataDirectory; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageOptionalHeader64 + { + public short Magic; + public byte MajorLinkerVersion; + public byte MinorLinkerVersion; + public int SizeOfCode; + public int SizeOfInitializedData; + public int SizeOfUninitializedData; + public int AddressOfEntryPoint; + public int BaseOfCode; + public long ImageBase; + public int SectionAlignment; + public int FileAlignment; + public short MajorOperatingSystemVersion; + public short MinorOperatingSystemVersion; + public short MajorImageVersion; + public short MinorImageVersion; + public short MajorSubsystemVersion; + public short MinorSubsystemVersion; + public int Win32VersionValue; + public int SizeOfImage; + public int SizeOfHeaders; + public int CheckSum; + public ImageSubsystem Subsystem; + public ImageDllCharacteristics DllCharacteristics; + public long SizeOfStackReserve; + public long SizeOfStackCommit; + public long SizeOfHeapReserve; + public long SizeOfHeapCommit; + public int LoaderFlags; + public int NumberOfRvaAndSizes; + public ImageDataDirectory DataDirectory; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageRelocation + { + public int VirtualAddress; + public int SymbolTableIndex; + public short Type; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct ImageSectionHeader + { + public fixed byte Name[8]; + public int Misc; // PhysicalAddress, VirtualSize + public int VirtualAddress; + public int SizeOfRawData; + public int PointerToRawData; + public int PointerToRelocations; + public int PointerToLinenumbers; + public short NumberOfRelocations; + public short NumberOfLinenumbers; + public ImageSectionFlags Characteristics; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageThunkData + { + public int ForwarderString; // byte* + public int Function; // int* + public int Ordinal; + public int AddressOfData; // ImageImportByName* + } + + [StructLayout(LayoutKind.Sequential)] + public struct ImageThunkData64 + { + public long ForwarderString; // byte* + public long Function; // int* + public long Ordinal; + public long AddressOfData; // ImageImportByName* + } + + [StructLayout(LayoutKind.Sequential)] + public struct InitialTeb + { + public struct OldInitialTebStruct + { + public IntPtr OldStackBase; + public IntPtr OldStackLimit; + } + + public OldInitialTebStruct OldInitialTeb; + public IntPtr StackBase; + public IntPtr StackLimit; + public IntPtr StackAllocationBase; + } + + [StructLayout(LayoutKind.Sequential)] + public struct IoCompletionBasicInformation + { + public int Depth; + } + + [StructLayout(LayoutKind.Sequential)] + public struct IoCounters + { + public ulong ReadOperationCount; + public ulong WriteOperationCount; + public ulong OtherOperationCount; + public ulong ReadTransferCount; + public ulong WriteTransferCount; + public ulong OtherTransferCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct IoStatusBlock + { + public IoStatusBlock(NtStatus status) + : this(status, IntPtr.Zero) + { } + + public IoStatusBlock(NtStatus status, IntPtr information) + { + this.Pointer = IntPtr.Zero; + this.Information = information; + this.Status = status; + } + + public IoStatusBlock(IntPtr pointer) + : this(pointer, IntPtr.Zero) + { } + + public IoStatusBlock(IntPtr pointer, IntPtr information) + { + this.Pointer = pointer; + this.Information = information; + } + + public IntPtr Pointer; + public IntPtr Information; + + public unsafe NtStatus Status + { + get + { + fixed (IoStatusBlock* thisPtr = &this) + return *(NtStatus*)&thisPtr->Pointer; + } + set + { + fixed (IoStatusBlock* thisPtr = &this) + *(NtStatus*)&thisPtr->Pointer = value; + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectBasicAccountingInformation + { + public long TotalUserTime; + public long TotalKernelTime; + public long ThisPeriodTotalUserTime; + public long ThisPeriodTotalKernelTime; + public int TotalPageFaultCount; + public int TotalProcesses; + public int ActiveProcesses; + public int TotalTerminatedProcesses; + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectBasicAndIoAccountingInformation + { + public JobObjectBasicAccountingInformation BasicInfo; + public IoCounters IoInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectBasicLimitInformation + { + public long PerProcessUserTimeLimit; + public long PerJobUserTimeLimit; + public JobObjectLimitFlags LimitFlags; + public int MinimumWorkingSetSize; + public int MaximumWorkingSetSize; + public int ActiveProcessLimit; + public int Affinity; + public int PriorityClass; + public int SchedulingClass; + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectBasicProcessIdList + { + public int NumberOfAssignedProcesses; + public int NumberOfProcessIdsInList; + /* an array follows */ + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectEndOfJobTimeInformation + { + public int EndOfJobTimeAction; // 0: Terminate, 1: Post + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobObjectExtendedLimitInformation + { + public JobObjectBasicLimitInformation BasicLimitInformation; + public IoCounters IoInfo; + public int ProcessMemoryLimit; + public int JobMemoryLimit; + public int PeakProcessMemoryUsed; + public int PeakJobMemoryUsed; + } + + [StructLayout(LayoutKind.Sequential)] + public struct JobSetArray + { + public IntPtr JobHandle; + public uint MemberLevel; + public int Flags; // Unused + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyBasicInformation + { + public LargeInteger LastWriteTime; + public int TitleIndex; + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyCachedInformation + { + public LargeInteger LastWriteTime; + public int TitleIndex; + public int SubKeys; + public int MaxNameLen; + public int Values; + public int MaxValueNameLen; + public int MaxValueDataLen; + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyFlagsInformation + { + public int UserFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyFullInformation + { + public LargeInteger LastWriteTime; + public int TitleIndex; + public int ClassOffset; + public int ClassLength; + public int SubKeys; + public int MaxNameLen; + public int MaxClassLen; + public int Values; + public int MaxValueNameLen; + public int MaxValueDataLen; + public short Class; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyNameInformation + { + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyNodeInformation + { + public LargeInteger LastWriteTime; + public int TitleIndex; + public int ClassOffset; + public int ClassLength; + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyUserFlagsInformation + { + public int UserFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyValueBasicInformation + { + public int TitleIndex; + public int Type; + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyValueEntry + { + public IntPtr ValueName; // pointer to UNICODE_STRING + public int DataLength; + public int DataOffset; + public int Type; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyValueFullInformation + { + public int TitleIndex; + public int Type; + public int DataOffset; + public int DataLength; + public int NameLength; + public short Name; + // Variable length string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyValuePartialInformation + { + public int TitleIndex; + public int Type; + public int DataLength; + public byte Data; + // Variable length data follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeyWriteTimeInformation + { + public LargeInteger LastWriteTime; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KnownAceStruct + { + public AceHeader Header; + public int Mask; + public int SidStart; + } + + [StructLayout(LayoutKind.Explicit, Size = 12)] + public struct KSystemTime + { + [FieldOffset(0)] + public uint LowPart; + [FieldOffset(4)] + public int High1Time; + [FieldOffset(8)] + public int High2Time; + + [FieldOffset(0)] + public long QuadPart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KtmObjectCursor + { + public Guid LastQuery; + public int ObjectIdCount; + public byte ObjectIds; + // Array of Guids follows. + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] + public struct KUserSharedData + { + public static readonly int TickCountOffset = + Marshal.OffsetOf(typeof(KUserSharedData), "TickCount").ToInt32(); + public static readonly int TickCountMultiplierOffset = + Marshal.OffsetOf(typeof(KUserSharedData), "TickCountMultiplier").ToInt32(); + + public int TickCountLowDeprecated; + public int TickCountMultiplier; + public KSystemTime InterruptTime; + public KSystemTime SystemTime; + public KSystemTime TimeZoneBias; + public ushort ImageNumberLow; + public ushort ImageNumberHigh; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string NtSystemRoot; + + public int MaxStackTraceDepth; + public int CryptoExponent; + public int TimeZoneId; + public int LargePageMinimum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public int[] Reserved2; + + public WinNtProductType NtProductType; + [MarshalAs(UnmanagedType.U1)] + public bool ProductTypeIsValid; + + public int NtMajorVersion; + public int NtMinorVersion; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.ProcessorFeatureMax)] + public byte[] ProcessorFeatures; + + public int Reserved1; + public int Reserved3; + public int TimeSlip; + public AlternativeArchitectureType AlternativeArchitecture; + public int Padding1; + public long SystemExpirationDate; + public SuiteType SuiteMask; + [MarshalAs(UnmanagedType.U1)] + public bool KdDebuggerEnabled; + public byte NXSupportPolicy; + public int ActiveConsoleId; + public int DismountCount; + public int ComPlusPackage; + public int LastSystemRITEventTickCount; + public int NumberOfPhysicalPages; + [MarshalAs(UnmanagedType.U1)] + public bool SafeBootMode; + public int TraceLogging; + public int Padding3; + + public long TestRetInstruction; + public int SystemCall; + public int SystemCallReturn; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public long[] SystemCallPad; + + public KSystemTime TickCount; + + public int Cookie; + } + + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct LargeInteger + { + public static implicit operator long(LargeInteger li) + { + return li.QuadPart; + } + + public LargeInteger(long quadPart) + { + this.LowPart = 0; + this.HighPart = 0; + this.QuadPart = quadPart; + } + + [FieldOffset(0)] + public long QuadPart; + [FieldOffset(0)] + public uint LowPart; + [FieldOffset(4)] + public int HighPart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LdrDataTableEntry + { + public static readonly int LoadCountOffset = + Marshal.OffsetOf(typeof(LdrDataTableEntry), "LoadCount").ToInt32(); + + public ListEntry InLoadOrderLinks; + public ListEntry InMemoryOrderLinks; + public ListEntry InInitializationOrderLinks; + public IntPtr DllBase; + public IntPtr EntryPoint; + public int SizeOfImage; + public UnicodeString FullDllName; + public UnicodeString BaseDllName; + public LdrpDataTableEntryFlags Flags; + public short LoadCount; + public short TlsIndex; + public ListEntry HashTableEntry; + public int TimeDateStamp; + public IntPtr EntryPointActivationContext; + public IntPtr PatchInformation; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ListEntry + { + public IntPtr Flink; + public IntPtr Blink; + } + + /// + /// Represents a locally unique identifier (LUID), a value which + /// is unique on the currently running system. + /// + [StructLayout(LayoutKind.Explicit, Pack = 4)] + public struct Luid : IEquatable, IEquatable + { + public static readonly Luid Empty = new Luid(); + public static readonly Luid System = new Luid(0x3e7, 0); + public static readonly Luid AnonymousLogon = new Luid(0x3e6, 0); + public static readonly Luid LocalService = new Luid(0x3e5, 0); + public static readonly Luid NetworkService = new Luid(0x3e4, 0); + + /// + /// Creates a LUID from a single 64-bit value. + /// + /// The value. + public Luid(long quadPart) + { + this.LowPart = 0; + this.HighPart = 0; + this.QuadPart = quadPart; + } + + /// + /// Creates a LUID from two 32-bit values. + /// + /// The low 32 bits of the LUID. + /// The high 32 bits of the LUID. + public Luid(uint lowPart, int highPart) + { + this.QuadPart = 0; + this.LowPart = lowPart; + this.HighPart = highPart; + } + + /// + /// The 64-bit value of the LUID. + /// + [FieldOffset(0)] + public long QuadPart; + /// + /// The low 32 bits of the LUID. + /// + [FieldOffset(0)] + public uint LowPart; + /// + /// The high 32 bits of the LUID. + /// + [FieldOffset(4)] + public int HighPart; + + /// + /// Allocates a locally unique identifier (LUID) from + /// the kernel. + /// + /// A new LUID. + public static Luid Allocate() + { + NtStatus status; + Luid luid; + + if ((status = Win32.NtAllocateLocallyUniqueId(out luid)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return luid; + } + + public bool Equals(Luid other) + { + return this.QuadPart == other.QuadPart; + } + + public bool Equals(long other) + { + return this.QuadPart == other; + } + + public long ToLong() + { + return this.QuadPart; + } + + public override string ToString() + { + return this.QuadPart.ToString("x"); + } + + public uint ToUInt32() + { + return this.LowPart; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct M128A + { + public ulong Low; + public long High; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MessageResourceEntry + { + public static readonly int TextOffset = Marshal.OffsetOf(typeof(MessageResourceEntry), "Text").ToInt32(); + + public ushort Length; + public MessageResourceFlags Flags; + public byte Text; + // ANSI/Unicode string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct MutantBasicInformation + { + public int CurrentCount; + [MarshalAs(UnmanagedType.U1)] + public bool OwnedByCaller; + [MarshalAs(UnmanagedType.U1)] + public bool AbandonedState; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MutantOwnerInformation + { + public ClientId ClientId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NtTib + { + public IntPtr ExceptionList; // ExceptionRegistrationRecord* + public IntPtr StackBase; + public IntPtr StackLimit; + public IntPtr SubSystemTib; + public IntPtr FiberData; + public IntPtr ArbitraryUserPointer; + public IntPtr Self; // NtTib* + } + + [StructLayout(LayoutKind.Sequential)] + public struct ObjectAttributes : IDisposable + { + public ObjectAttributes( + string objectName, + ObjectFlags attributes, + NativeHandle rootDirectory) + : this(objectName, attributes, rootDirectory, null, null) + { } + + public ObjectAttributes( + string objectName, + ObjectFlags attributes, + NativeHandle rootDirectory, + SecurityDescriptor securityDescriptor, + SecurityQualityOfService? securityQos + ) + { + this.Length = Marshal.SizeOf(typeof(ObjectAttributes)); + this.RootDirectory = IntPtr.Zero; + this.ObjectName = IntPtr.Zero; + this.SecurityDescriptor = IntPtr.Zero; + this.SecurityQualityOfService = IntPtr.Zero; + + // Object name + if (objectName != null) + { + UnicodeString unicodeString = new UnicodeString(objectName); + IntPtr unicodeStringMemory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(UnicodeString))); + + Marshal.StructureToPtr(unicodeString, unicodeStringMemory, false); + this.ObjectName = unicodeStringMemory; + } + + // Object flags + this.Attributes = attributes; + + // Root directory + if (rootDirectory != null) + this.RootDirectory = rootDirectory; + + // Security descriptor + this.SecurityDescriptor = securityDescriptor ?? IntPtr.Zero; + + // Security QOS + if (securityQos.HasValue) + { + this.SecurityQualityOfService = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecurityQualityOfService))); + Marshal.StructureToPtr(securityQos.Value, this.SecurityQualityOfService, false); + } + } + + public int Length; + public IntPtr RootDirectory; + public IntPtr ObjectName; + public ObjectFlags Attributes; + public IntPtr SecurityDescriptor; + public IntPtr SecurityQualityOfService; + + public void Dispose() + { + // Object name + if (this.ObjectName != IntPtr.Zero) + { + UnicodeString unicodeString = + (UnicodeString)Marshal.PtrToStructure(this.ObjectName, typeof(UnicodeString)); + + unicodeString.Dispose(); + Marshal.FreeHGlobal(this.ObjectName); + + this.ObjectName = IntPtr.Zero; + } + + // Security QOS + if (this.SecurityQualityOfService != null) + { + Marshal.FreeHGlobal(this.SecurityQualityOfService); + this.SecurityQualityOfService = IntPtr.Zero; + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct ObjectBasicInformation + { + public uint Attributes; + public int GrantedAccess; + public uint HandleCount; + public uint PointerCount; + public uint PagedPoolUsage; + public uint NonPagedPoolUsage; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] Reserved; + + public uint NameInformationLength; + public uint TypeInformationLength; + public uint SecurityDescriptorLength; + public ulong CreateTime; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ObjectDirectoryInformation + { + public UnicodeString Name; + public UnicodeString TypeName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ObjectNameInformation + { + public UnicodeString Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ObjectTypeInformation + { + public UnicodeString Name; + public int TotalNumberOfObjects; + public int TotalNumberOfHandles; + public int TotalPagedPoolUsage; + public int TotalNonPagedPoolUsage; + public int TotalNamePoolUsage; + public int TotalHandleTableUsage; + public int HighWaterNumberOfObjects; + public int HighWaterNumberOfHandles; + public int HighWaterPagedPoolUsage; + public int HighWaterNonPagedPoolUsage; + public int HighWaterNamePoolUsage; + public int HighWaterHandleTableUsage; + public int InvalidAttributes; + public GenericMapping GenericMapping; + public int ValidAccess; + public byte SecurityRequired; + public byte MaintainHandleCount; + public ushort MaintainTypeList; + public PoolType PoolType; + public int PagedPoolUsage; + public int NonPagedPoolUsage; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Peb + { + public static readonly int ImageSubsystemOffset = + Marshal.OffsetOf(typeof(Peb), "ImageSubsystem").ToInt32(); + public static readonly int LdrOffset = + Marshal.OffsetOf(typeof(Peb), "Ldr").ToInt32(); + public static readonly int ProcessHeapOffset = + Marshal.OffsetOf(typeof(Peb), "ProcessHeap").ToInt32(); + public static readonly int ProcessParametersOffset = + Marshal.OffsetOf(typeof(Peb), "ProcessParameters").ToInt32(); + + [MarshalAs(UnmanagedType.I1)] + public bool InheritedAddressSpace; + [MarshalAs(UnmanagedType.I1)] + public bool ReadImageFileExecOptions; + [MarshalAs(UnmanagedType.I1)] + public bool BeingDebugged; + [MarshalAs(UnmanagedType.I1)] + public bool BitField; + public IntPtr Mutant; + + public IntPtr ImageBaseAddress; + public IntPtr Ldr; // PebLdrData* + public IntPtr ProcessParameters; // RtlUserProcessParameters* + public IntPtr SubSystemData; + public IntPtr ProcessHeap; + public IntPtr FastPebLock; + public IntPtr AtlThunkSListPtr; + public IntPtr SparePrt2; + public int EnvironmentUpdateCount; + public IntPtr KernelCallbackTable; + public int SystemReserved; + public int SpareUlong; + public IntPtr FreeList; + public int TlsExpansionCounter; + public IntPtr TlsBitmap; + public unsafe fixed int TlsBitmapBits[2]; + public IntPtr ReadOnlySharedMemoryBase; + public IntPtr ReadOnlySharedMemoryHeap; + public IntPtr ReadOnlyStaticServerData; + public IntPtr AnsiCodePageData; + public IntPtr OemCodePageData; + public IntPtr UnicodeCaseTableData; + + public int NumberOfProcessors; + public int NtGlobalFlag; + + public long CriticalSectionTimeout; + public IntPtr HeapSegmentReserve; + public IntPtr HeapSegmentCommit; + public IntPtr HeapDeCommitTotalFreeThreshold; + public IntPtr HeapDeCommitFreeBlockThreshold; + + public int NumberOfHeaps; + public int MaximumNumberOfHeaps; + public IntPtr ProcessHeaps; + + public IntPtr GdiSharedHandleTable; + public IntPtr ProcessStarterHelper; + public int GdiDCAttributeList; + public IntPtr LoaderLock; + + public int OSMajorVersion; + public int OSMinorVersion; + public short OSBuildNumber; + public short OSCSDVersion; + public int OSPlatformId; + public int ImageSubsystem; + public int ImageSubsystemMajorVersion; + public int ImageSubsystemMinorVersion; + public IntPtr ImageProcessAffinityMask; + public unsafe fixed byte GdiHandleBuffer[Win32.GdiHandleBufferSize]; + public IntPtr PostProcessInitRoutine; + + public IntPtr TlsExpansionBitmap; + public unsafe fixed int TlsExpansionBitmapBits[32]; + + public int SessionId; + + public long AppCompatFlags; + public long AppCompatFlagsUser; + public IntPtr pShimData; + public IntPtr AppCompatInfo; + + public UnicodeString CSDVersion; + + public IntPtr ActivationContextData; + public IntPtr ProcessAssemblyStorageMap; + public IntPtr SystemDefaultActivationContextData; + public IntPtr SystemAssemblyStorageMap; + + public IntPtr MinimumStackCommit; + + public IntPtr FlsCallback; + public ListEntry FlsListHead; + public IntPtr FlsBitmap; + public unsafe fixed int FlsBitmapBits[Win32.FlsMaximumAvailable / (sizeof(int) * 8)]; + public int FlsHighIndex; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PebLdrData + { + public int Length; + [MarshalAs(UnmanagedType.I1)] + public bool Initialized; + public IntPtr SsHandle; + public ListEntry InLoadOrderModuleList; + public ListEntry InMemoryOrderModuleList; + public ListEntry InInitializationOrderModuleList; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PooledUsageAndLimits + { + public int PeakPagedPoolUsage; + public int PagedPoolUsage; + public int PagedPoolLimit; + public int PeakNonPagedPoolUsage; + public int NonPagedPoolUsage; + public int NonPagedPoolLimit; + public int PeakPagefileUsage; + public int PagefileUsage; + public int PagefileLimit; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PortMessageStruct + { + public short DataLength; + public short TotalLength; + public PortMessageType Type; + public short DataInfoOffset; + public ClientId ClientId; + public int MessageId; + public IntPtr ClientViewSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PortView + { + public int Length; + public IntPtr SectionHandle; + public int SectionOffset; + public IntPtr ViewSize; + public IntPtr ViewBase; + public IntPtr ViewRemoteBase; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PrivilegeSetStruct + { + public static int PrivilegesOffset = + Marshal.OffsetOf(typeof(PrivilegeSetStruct), "Privileges").ToInt32(); + + public int Count; + public PrivilegeSetFlags Flags; + public LuidAndAttributes Privileges; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessBasicInformation + { + public NtStatus ExitStatus; + public IntPtr PebBaseAddress; + public IntPtr AffinityMask; + public int BasePriority; + public IntPtr UniqueProcessId; + public IntPtr InheritedFromUniqueProcessId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessForegroundBackground + { + [MarshalAs(UnmanagedType.I1)] + public bool Foreground; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessHandleTracingEnable + { + public int Flags; // No flags. Set to 0. + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessHandleTracingEnableEx + { + public int Flags; // No flags. Set to 0. + public int TotalSlots; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessHandleTracingEntry + { + public IntPtr Handle; + public ClientId ClientId; + public HandleTraceType Type; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.ProcessHandleTracingMaxStacks)] + public IntPtr[] Stacks; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessHandleTracingQuery + { + public static readonly int HandleTraceOffset = + Marshal.OffsetOf(typeof(ProcessHandleTracingQuery), "HandleTrace").ToInt32(); + + public IntPtr Handle; + public int TotalTraces; + public ProcessHandleTracingEntry HandleTrace; + // An array of ProcessHandleTracingEntry structures follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessPriorityClassStruct + { + [MarshalAs(UnmanagedType.I1)] + public bool Foreground; + public ProcessPriorityClass PriorityClass; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RemotePortView + { + public int Length; + public IntPtr ViewSize; + public IntPtr ViewBase; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ResourceManagerBasicInformation + { + public static readonly int DescriptionOffset = + Marshal.OffsetOf(typeof(ResourceManagerBasicInformation), "Description").ToInt32(); + + /// + /// The GUID assigned to the resource manager. + /// + public Guid ResourceManagerId; + + /// + /// The length, in bytes, of the resource manager description string. + /// + public int DescriptionLength; + + /// + /// The first byte of the description string. + /// + public byte Description; // wchar[] + // Description string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlAceData + { + public AceType AceType; + public AceFlags InheritFlags; + public AceFlags AceFlags; + public int Mask; + public IntPtr Sid; // Sid** + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlBitmap + { + public int SizeOfBitMap; + public IntPtr Buffer; // int* + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlBitmapRun + { + public int StartingIndex; + public int NumberOfBits; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlDebugInformation + { + public IntPtr SectionHandleClient; + public IntPtr ViewBaseClient; + public IntPtr ViewBaseTarget; + public IntPtr ViewBaseDelta; + public IntPtr EventPairClient; + public IntPtr EventPairTarget; + public IntPtr TargetProcessId; + public IntPtr TargetThreadHandle; + public int Flags; + public IntPtr OffsetFree; + public IntPtr CommitSize; + public IntPtr ViewSize; + public IntPtr Modules; // RtlProcessModules* + public IntPtr BackTraces; // RtlProcessBackTraces* + public IntPtr Heaps; // RtlProcessHeaps* + public IntPtr Locks; // RtlProcessLocks* + public IntPtr SpecificHeap; + public IntPtr TargetProcessHandle; +#if _X64 + public unsafe fixed long Reserved[6]; +#else + public unsafe fixed int Reserved[6]; +#endif + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlHandleTable + { + public int MaximumNumberOfHandles; + public int SizeOfHandleTableEntry; + public int Reserved1; + public int Reserved2; + public IntPtr FreeHandles; + public IntPtr CommittedHandles; + public IntPtr UnCommittedHandles; + public IntPtr MaxReservedHandles; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlHeapInformation + { + public IntPtr BaseAddress; + public int Flags; + public ushort EntryOverhead; + public ushort CreatorBackTraceIndex; + public IntPtr BytesAllocated; + public IntPtr BytesCommitted; + public int NumberOfTags; + public int NumberOfEntries; + public int NumberOfPseudoTags; + public int PseudoTagGranularity; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public int[] Reserved; + public IntPtr Tags; + public IntPtr Entries; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessBackTraceInformation + { + public IntPtr SymbolicBackTrace; // PCHAR, always NULL. + public int TraceCount; + public ushort Index; + public ushort Depth; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.MaxStackDepth)] + public IntPtr[] BackTrace; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessBackTraces + { + public int CommittedMemory; + public int ReservedMemory; + public int NumberOfBackTraceLookups; + public int NumberOfBackTraces; + public byte BackTraces; // RtlProcessBackTraceInformation[] BackTraces + // Array of RtlProcessBackTraceInformation structures follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessHeaps + { + public static readonly int HeapsOffset = + Marshal.OffsetOf(typeof(RtlProcessHeaps), "Heaps").ToInt32(); + + public int NumberOfHeaps; + public RtlHeapInformation Heaps; + // Array of RtlHeapInformation structures follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessLockInformation + { + public IntPtr Address; + public RtlLockType Type; + public ushort CreatorBackTraceInformation; + + public IntPtr OwningThread; // TID + public int LockCount; + public int ContentionCount; + public int EntryCount; + + // Valid for critical sections + public int RecursionCount; + + // Valid for resources + public int NumberOfWaitingShared; + public int NumberOfWaitingExclusive; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessLocks + { + public int NumberOfLocks; + // RtlProcessLockInformation[] Locks + // Array of RtlProcessLockInformation structures follows. + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct RtlProcessModuleInformation + { + public IntPtr Section; // empty + public IntPtr MappedBase; + public IntPtr ImageBase; + public int ImageSize; + public LdrpDataTableEntryFlags Flags; + public ushort LoadOrderIndex; + public ushort InitOrderIndex; + public ushort LoadCount; + public ushort OffsetToFileName; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public char[] FullPathName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlProcessModules + { + public static readonly int ModulesOffset = + Marshal.OffsetOf(typeof(RtlProcessModules), "Modules").ToInt32(); + + public int NumberOfModules; + public RtlProcessModuleInformation Modules; + // Array of RtlProcessModuleInformation structures follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlUserProcessInformation + { + public int Length; + public IntPtr Process; + public IntPtr Thread; + public ClientId ClientId; + public SectionImageInformation ImageInformation; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RtlUserProcessParameters + { + public static readonly int CurrentDirectoryOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "CurrentDirectory").ToInt32(); + public static readonly int DllPathOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "DllPath").ToInt32(); + public static readonly int ImagePathNameOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "ImagePathName").ToInt32(); + public static readonly int CommandLineOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "CommandLine").ToInt32(); + public static readonly int EnvironmentOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "Environment").ToInt32(); + public static readonly int WindowTitleOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "WindowTitle").ToInt32(); + public static readonly int DesktopInfoOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "DesktopInfo").ToInt32(); + public static readonly int ShellInfoOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "ShellInfo").ToInt32(); + public static readonly int RuntimeDataOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "RuntimeData").ToInt32(); + public static readonly int CurrentDirectoriesOffset = + Marshal.OffsetOf(typeof(RtlUserProcessParameters), "CurrentDirectories").ToInt32(); + + public struct CurDir + { + public UnicodeString DosPath; + public IntPtr Handle; + } + + public struct RtlDriveLetterCurDir + { + public ushort Flags; + public ushort Length; + public uint TimeStamp; + public IntPtr DosPath; + } + + public int MaximumLength; + public int Length; + + public RtlUserProcessFlags Flags; + public int DebugFlags; + + public IntPtr ConsoleHandle; + public int ConsoleFlags; + public IntPtr StandardInput; + public IntPtr StandardOutput; + public IntPtr StandardError; + + public CurDir CurrentDirectory; + public UnicodeString DllPath; + public UnicodeString ImagePathName; + public UnicodeString CommandLine; + public IntPtr Environment; + + public int StartingX; + public int StartingY; + public int CountX; + public int CountY; + public int CountCharsX; + public int CountCharsY; + public int FillAttribute; + + public StartupFlags WindowFlags; + public int ShowWindowFlags; + public UnicodeString WindowTitle; + public UnicodeString DesktopInfo; + public UnicodeString ShellInfo; + public UnicodeString RuntimeData; + + //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + //public RtlDriveLetterCurDir[] CurrentDirectories; + public RtlDriveLetterCurDir CurrentDirectories; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SectionBasicInformation + { + public int Unknown; + public SectionAttributes SectionAttributes; + public long SectionSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SectionImageInformation + { + public IntPtr TransferAddress; + public int StackZeroBits; + public IntPtr StackReserved; + public IntPtr StackCommit; + public int ImageSubsystem; + public short SubSystemVersionLow; + public short SubSystemVersionHigh; + public int GpValue; + public short ImageCharacteristics; + public short DllCharacteristics; + public int ImageMachineType; + [MarshalAs(UnmanagedType.I1)] + public bool ImageContainsCode; + [MarshalAs(UnmanagedType.I1)] + public bool Spare1; + public int LoaderFlags; + public int ImageFileSize; + public int Reserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecurityDescriptorStruct + { + public byte Revision; + public byte Sbz1; + public SecurityDescriptorControlFlags Control; + public IntPtr Owner; // Sid* + public IntPtr Group; // Sid* + public IntPtr Sacl; // Acl* + public IntPtr Dacl; // Acl* + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecurityDescriptorRelativeStruct + { + public byte Revision; + public byte Sbz1; + public SecurityDescriptorControlFlags Control; + public int Owner; + public int Group; + public int Sacl; + public int Dacl; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecurityQualityOfService + { + public SecurityQualityOfService( + SecurityImpersonationLevel impersonationLevel, + bool dynamicTracking, + bool effectiveOnly + ) + { + this.Length = Marshal.SizeOf(typeof(SecurityQualityOfService)); + this.ImpersonationLevel = impersonationLevel; + this.ContextTrackingMode = dynamicTracking; + this.EffectiveOnly = effectiveOnly; + } + + public int Length; + public SecurityImpersonationLevel ImpersonationLevel; + [MarshalAs(UnmanagedType.I1)] + public bool ContextTrackingMode; // True for dynamic tracking, false for static tracking + [MarshalAs(UnmanagedType.I1)] + public bool EffectiveOnly; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SemaphoreBasicInformation + { + public int CurrentCount; + public int MaximumCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SidStruct + { + public byte Revision; + public byte SubAuthorityCount; + public SidIdentifierAuthority IdentifierAuthority; + + // Array of ULONG follows + } + + [StructLayout(LayoutKind.Sequential)] + public struct SidAndAttributes + { + public IntPtr Sid; // ptr to a SID object + public SidAttributes Attributes; + + public Sid ToSid() + { + return new Sid(this); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SidIdentifierAuthority + { + public unsafe fixed byte Value[6]; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemAlarmAceStruct + { + public AceHeader Header; + public int Mask; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemAuditAceStruct + { + public AceHeader Header; + public int Mask; + public int SidStart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemBasicInformation + { + public int Reserved; + public int TimerResolution; + public int PageSize; + public int NumberOfPhysicalPages; + public int LowestPhysicalPageNumber; + public int HighestPhysicalPageNumber; + public int AllocationGranularity; + public IntPtr MinimumUserModeAddress; + public IntPtr MaximumUserModeAddress; + public IntPtr ActiveProcessorsAffinityMask; + public byte NumberOfProcessors; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemCacheInformation + { + /// + /// The size of the system working set, in bytes. + /// + public IntPtr SystemCacheWsSize; + public IntPtr SystemCacheWsPeakSize; + public int SystemCacheWsFaults; + + /// + /// Measured in pages. + /// + public IntPtr SystemCacheWsMinimum; + + /// + /// Measured in pages. + /// + public IntPtr SystemCacheWsMaximum; + public IntPtr TransitionSharedPages; + public IntPtr TransitionSharedPagesPeak; + public int TransitionRePurposeCount; + public int Flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemExtendedThreadInformation + { + public SystemThreadInformation ThreadInfo; + public IntPtr StackBase; // 16 + public IntPtr StackLimit; + public IntPtr Win32StartAddress; + public IntPtr TebAddress; // Vista+ + public IntPtr Unused1; + public IntPtr Unused2; + public IntPtr Unused3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemHandleEntry + { + public int ProcessId; + public byte ObjectTypeNumber; + public HandleFlags Flags; + public short Handle; + public IntPtr Object; + public int GrantedAccess; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemHandleInformation + { + public static readonly int HandlesOffset = + Marshal.OffsetOf(typeof(SystemHandleInformation), "Handles").ToInt32(); + + public int NumberOfHandles; + public SystemHandleEntry Handles; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemLoadAndCallImage + { + public UnicodeString ModuleName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemObjectInformation + { + public int NextEntryOffset; + public IntPtr Object; + public IntPtr CreatorUniqueProcess; + public ushort CreatorBackTraceIndex; + public ushort Flags; + public int PointerCount; + public int HandleCount; + public uint PagedPoolCharge; + public uint NonPagedPoolCharge; + public IntPtr ExclusiveProcessId; + public IntPtr SecurityDescriptor; + public ObjectNameInformation NameInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemObjectTypeInformation + { + public int NextEntryOffset; + public UnicodeString Name; + public int ObjectCount; + public int HandleCount; + public int TypeNumber; + public int InvalidAttributes; + public GenericMapping GenericMapping; + public int ValidAccessMask; + public PoolType PoolType; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemPagefileInformation + { + public int NextEntryOffset; + public int TotalSize; + public int TotalInUse; + public int PeakUsage; + public UnicodeString PageFileName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemPerformanceInformation + { + /// + /// The total idle time of all processors in units of 100-nanoseconds. + /// + public long IdleProcessTime; + /// + /// Total bytes read by calls to NtReadFile. + /// + public long IoReadTransferCount; + /// + /// Total bytes written by calls to NtWriteFile. + /// + public long IoWriteTransferCount; + /// + /// Total bytes transferred by other I/O operations. + /// + public long IoOtherTransferCount; + /// + /// Number of calls to NtReadFile. + /// + public int IoReadOperationCount; + /// + /// Number of calls to NtWriteFile. + /// + public int IoWriteOperationCount; + /// + /// Number of calls to other I/O functions. + /// + public int IoOtherOperationCount; + /// + /// The number of pages of physical memory available. + /// + public int AvailablePages; + /// + /// The number of pages of committed virtual memory. + /// + public int CommittedPages; + /// + /// The number of pages of virtual memory that could be committed + /// without extending the system's pagefiles. + /// + public int CommitLimit; + /// + /// The peak number of pages of committed virtual memory. + /// + public int PeakCommitment; + /// + /// The total number of soft and hard page faults. + /// + public int PageFaultCount; + /// + /// The number of copy-on-write page faults. + /// + public int CopyOnWriteCount; + /// + /// The number of soft page faults. + /// + public int TransitionCount; + /// + /// Something that the Native API reference book doesn't have. + /// + public int CacheTransitionCount; + /// + /// The number of demand zero faults. + /// + public int DemandZeroCount; + /// + /// The number of pages read from disk to resolve page faults. + /// + public int PageReadCount; + /// + /// The number of read operations initiated to resolve page faults. + /// + public int PageReadIoCount; + public int CacheReadCount; + public int CacheIoCount; + /// + /// The number of pages written to the system's pagefiles. + /// + public int DirtyPagesWriteCount; + /// + /// The number of write operations performed on the system's pagefiles. + /// + public int DirtyWriteIoCount; + /// + /// The number of pages written to mapped files. + /// + public int MappedPagesWriteCount; + /// + /// The number of write operations performed on mapped files. + /// + public int MappedWriteIoCount; + /// + /// The number of pages used by the paged pool. + /// + public int PagedPoolPages; + /// + /// The number of pages used by the non-paged pool. + /// + public int NonPagedPoolPages; + /// + /// The number of allocations made from the paged pool. + /// + public int PagedPoolAllocs; + /// + /// The number of allocations returned to the paged pool. + /// + public int PagedPoolFrees; + /// + /// The number of allocations made from the non-paged pool. + /// + public int NonPagedPoolAllocs; + /// + /// The number of allocations returned to the non-paged pool. + /// + public int NonPagedPoolFrees; + /// + /// The number of available System Page Table Entries. + /// + public int FreeSystemPtes; + /// + /// The number of pages of pageable OS code and data in physical + /// memory. + /// + public int ResidentSystemCodePage; + /// + /// The number of pages of pageable driver code and data. + /// + public int TotalSystemDriverPages; + /// + /// The number of pages of OS driver code and data. + /// + public int TotalSystemCodePages; + /// + /// The number of times an allocation could be statisfied by one of the + /// small non-paged lookaside lists. + /// + public int NonPagedPoolLookasideHits; + /// + /// The number of times an allocation could be statisfied by one of the + /// small paged lookaside lists. + /// + public int PagedPoolLookasideHits; + /// + /// The number of pages available for use by the paged pool. + /// + public int AvailablePagedPoolPages; + /// + /// The number of pages of the system cache in physical memory. + /// + public int ResidentSystemCachePage; + /// + /// The number of pages of the paged pool in physical memory. + /// + public int ResidentPagedPoolPage; + /// + /// The number of pages of pageable driver code and data in physical memory. + /// + public int ResidentSystemDriverPage; + /// + /// The number of asynchronous fast read operations. + /// + public int CcFastReadNoWait; + /// + /// The number of synchronous fast read operations. + /// + public int CcFastReadWait; + /// + /// The number of fast read operations not possible because of resource + /// conflicts. + /// + public int CcFastReadResourceMiss; + public int CcFastReadNotPossible; + public int CcFastMdlReadNoWait; + public int CcFastMdlReadWait; + public int CcFastMdlReadResourceMiss; + public int CcFastMdlReadNotPossible; + public int CcMapDataNoWait; + public int CcMapDataWait; + public int CcMapDataNoWaitMiss; + public int CcMapDataWaitMiss; + public int CcPinMappedDataCount; + public int CcPinReadNoWait; + public int CcPinReadWait; + public int CcPinReadNoWaitMiss; + public int CcPinReadWaitMiss; + public int CcCopyReadNoWait; + public int CcCopyReadWait; + public int CcCopyReadNoWaitMiss; + public int CcCopyReadWaitMiss; + public int CcMdlReadNoWait; + public int CcMdlReadWait; + public int CcMdlReadNoWaitMiss; + public int CcMdlReadWaitMiss; + public int CcReadAheadIos; + public int CcLazyWriteIos; + public int CcLazyWritePages; + public int CcDataFlushes; + public int CcDataPages; + public int ContextSwitches; + public int FirstLevelTbFills; + public int SecondLevelTbFills; + public int SystemCalls; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemProcessInformation + { + public int NextEntryOffset; + public int NumberOfThreads; + public long SpareLi1; + public long SpareLi2; + public long SpareLi3; + public long CreateTime; // 8 + public long UserTime; + public long KernelTime; + public UnicodeString ImageName; + public int BasePriority; + private IntPtr _processId; + private IntPtr _inheritedFromProcessId; + public int HandleCount; + public int SessionId; + public IntPtr PageDirectoryBase; + public VmCountersEx VirtualMemoryCounters; + public IoCounters IoCounters; + + public int ProcessId + { + get { return _processId.ToInt32(); } + set { _processId = value.ToIntPtr(); } + } + + public int InheritedFromProcessId + { + get { return _inheritedFromProcessId.ToInt32(); } + set { _inheritedFromProcessId = value.ToIntPtr(); } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemProcessorPerformanceInformation + { + public long IdleTime; + public long KernelTime; + public long UserTime; + public long DpcTime; + public long InterruptTime; + public int InterruptCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemSessionProcessInformation + { + public int SessionId; + public int BufferLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemThreadInformation + { + public long KernelTime; + public long UserTime; + public long CreateTime; + public int WaitTime; + public IntPtr StartAddress; + public ClientId ClientId; + public int Priority; + public int BasePriority; + public int ContextSwitchCount; // 12 + public int State; // 13 + public KWaitReason WaitReason; // 14 + } + + [StructLayout(LayoutKind.Sequential)] + public struct SystemTimeOfDayInformation + { + public long BootTime; + public long CurrentTime; + public long TimeZoneBias; + public int TimeZoneId; + public int Reserved; + public long BootTimeBias; + public long SleepTimeBias; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct Teb + { + public NtTib NtTib; + public IntPtr EnvironmentPointer; + public ClientId ClientId; + public IntPtr ActiveRpcHandle; + public IntPtr ThreadLocalStoragePointer; + public IntPtr ProcessEnvironmentBlock; // Peb* + public Win32Error LastErrorValue; + public int CountOfOwnedCriticalSections; + public IntPtr CsrClientThread; + public IntPtr Win32ThreadInfo; + public fixed int User32Reserved[26]; + public fixed int UserReserved[5]; + public IntPtr Wow32Reserved; + public int CurrentLocale; + public int FpSoftwareStatusRegister; + // Variable size part follows + } + + [StructLayout(LayoutKind.Sequential)] + public struct ThreadBasicInformation + { + public NtStatus ExitStatus; + public IntPtr TebBaseAddress; + public ClientId ClientId; + public IntPtr AffinityMask; + public int Priority; + public int BasePriority; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TimerBasicInformation + { + public LargeInteger RemainingTime; + [MarshalAs(UnmanagedType.I1)] + public bool TimerState; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TmBasicInformation + { + public Guid TmIdentity; + public long VirtualClock; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TmLogInformation + { + public Guid LogIdentity; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TmLogPathInformation + { + public static readonly int LogPathOffset = Marshal.OffsetOf(typeof(TmLogPathInformation), "LogPath").ToInt32(); + + /// + /// The length, in characters, of the log path string. + /// + public int LogPathLength; + + /// + /// The first byte of the log path string. + /// + public short LogPath; // wchar[] + // Log path follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct TmRecoveryInformation + { + public long LastRecoveredLsn; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenDefaultDacl + { + public TokenDefaultDacl(Acl defaultDacl) + { + this.DefaultDacl = defaultDacl ?? IntPtr.Zero; + } + + public IntPtr DefaultDacl; // Acl* + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenGroups + { + public static readonly int GroupsOffset = + Marshal.OffsetOf(typeof(TokenGroups), "Groups").ToInt32(); + + public TokenGroups(Sid[] sids) + { + this.GroupCount = sids.Length; + this.Groups = new SidAndAttributes[sids.Length]; + + for (int i = 0; i < sids.Length; i++) + this.Groups[i] = sids[i].ToSidAndAttributes(); + } + + public int GroupCount; + + [MarshalAs(UnmanagedType.ByValArray)] + public SidAndAttributes[] Groups; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenOwner + { + public TokenOwner(Sid owner) + { + this.Owner = owner ?? IntPtr.Zero; + } + + public IntPtr Owner; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenPrimaryGroup + { + public TokenPrimaryGroup(Sid primaryGroup) + { + this.PrimaryGroup = primaryGroup ?? IntPtr.Zero; + } + + public IntPtr PrimaryGroup; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenPrivileges + { + public TokenPrivileges(PrivilegeSet privileges) + { + this = privileges.ToTokenPrivileges(); + } + + public int PrivilegeCount; + + [MarshalAs(UnmanagedType.ByValArray)] + public LuidAndAttributes[] Privileges; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct TokenSource + { + public TokenSource(string sourceName, Luid sourceIdentifier) + { + if (sourceName.Length > 8) + throw new ArgumentException("Source name must be equal to or less than 8 characters long."); + + this.SourceName = sourceName; + this.SourceIdentifier = sourceIdentifier; + } + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string SourceName; + + public Luid SourceIdentifier; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenStatistics + { + public Luid TokenId; + public Luid AuthenticationId; + public long ExpirationTime; + public TokenType TokenType; + public SecurityImpersonationLevel ImpersonationLevel; + public int DynamicCharged; + public int DynamicAvailable; + public int GroupCount; + public int PrivilegeCount; + public Luid ModifiedId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TokenUser + { + public TokenUser(Sid user) + { + this.User = user.ToSidAndAttributes(); + } + + public SidAndAttributes User; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransactionBasicInformation + { + public Guid TransactionId; + public TransactionState State; + public TransactionOutcome Outcome; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransactionEnlistmentPair + { + public Guid EnlistmentId; + public Guid ResourceManagerId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransactionEnlistmentsInformation + { + public int NumberOfEnlistments; + public byte EnlistmentPair; // TransactionEnlistmentPair[] + // Array of TransactionEnlistmentPair structures follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransactionNotification + { + public IntPtr TransactionKey; + public NotificationMask Notification; // Original name: TransactionNotification + public long TmVirtualClock; + public int ArgumentLength; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransactionPropertiesInformation + { + public static readonly int DescriptionOffset = + Marshal.OffsetOf(typeof(TransactionPropertiesInformation), "Description").ToInt32(); + + public int IsolationLevel; + public int IsolationFlags; + public long Timeout; + public TransactionOutcome Outcome; + + /// + /// The length, in bytes, of the description string. + /// + public int DescriptionLength; + + /// + /// The first byte of the description string. + /// + public byte Description; // wchar[] + // Description string follows. + } + + [StructLayout(LayoutKind.Sequential)] + public struct UnicodeString : IComparable, IEquatable, IDisposable + { + public UnicodeString(string str) + { + if (str != null) + { + UnicodeString newString; + + if (!Win32.RtlCreateUnicodeString(out newString, str)) + throw new OutOfMemoryException(); + + this = newString; + } + else + { + this.Length = 0; + this.MaximumLength = 0; + this.Buffer = IntPtr.Zero; + } + } + + public ushort Length; + public ushort MaximumLength; + public IntPtr Buffer; + + public int CompareTo(UnicodeString unicodeString, bool caseInsensitive) + { + return Win32.RtlCompareUnicodeString(ref this, ref unicodeString, caseInsensitive); + } + + public int CompareTo(UnicodeString unicodeString) + { + return this.CompareTo(unicodeString, false); + } + + public void Dispose() + { + if (this.Buffer == IntPtr.Zero) + return; + + Win32.RtlFreeUnicodeString(ref this); + this.Buffer = IntPtr.Zero; + } + + /// + /// Copies the string to a newly allocated string. + /// + public UnicodeString Duplicate() + { + NtStatus status; + UnicodeString newString; + + if ((status = Win32.RtlDuplicateUnicodeString( + RtlDuplicateUnicodeStringFlags.AllocateNullString | + RtlDuplicateUnicodeStringFlags.NullTerminate, + ref this, out newString)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return newString; + } + + public bool Equals(UnicodeString unicodeString, bool caseInsensitive) + { + return Win32.RtlEqualUnicodeString(ref this, ref unicodeString, caseInsensitive); + } + + public bool Equals(UnicodeString unicodeString) + { + return this.Equals(unicodeString, false); + } + + public override int GetHashCode() + { + return this.Hash(); + } + + public int Hash(HashStringAlgorithm algorithm, bool caseInsensitive) + { + NtStatus status; + int hash; + + if ((status = Win32.RtlHashUnicodeString(ref this, + caseInsensitive, algorithm, out hash)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return hash; + } + + public int Hash(HashStringAlgorithm algorithm) + { + return this.Hash(algorithm, false); + } + + public int Hash() + { + return this.Hash(HashStringAlgorithm.Default); + } + + public string Read() + { + if (this.Length == 0) + return ""; + + return Marshal.PtrToStringUni(this.Buffer, this.Length / 2); + } + + public string Read(ProcessHandle processHandle) + { + if (this.Length == 0) + return ""; + + byte[] strData = processHandle.ReadMemory(this.Buffer, this.Length); + GCHandle strDataHandle = GCHandle.Alloc(strData, GCHandleType.Pinned); + + try + { + return Marshal.PtrToStringUni(strDataHandle.AddrOfPinnedObject(), this.Length / 2); + } + finally + { + strDataHandle.Free(); + } + } + + public bool StartsWith(UnicodeString unicodeString, bool caseInsensitive) + { + return Win32.RtlPrefixUnicodeString(ref this, ref unicodeString, caseInsensitive); + } + + public bool StartsWith(UnicodeString unicodeString) + { + return this.StartsWith(unicodeString, false); + } + + public AnsiString ToAnsiString() + { + NtStatus status; + AnsiString ansiStr = new AnsiString(); + + if ((status = Win32.RtlUnicodeStringToAnsiString(ref ansiStr, ref this, true)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return ansiStr; + } + + public override string ToString() + { + return this.Read(); + } + + public AnsiString ToUpperAnsiString() + { + NtStatus status; + AnsiString ansiStr = new AnsiString(); + + if ((status = Win32.RtlUpcaseUnicodeStringToAnsiString(ref ansiStr, ref this, true)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return ansiStr; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct VmCounters + { + public IntPtr PeakVirtualSize; + public IntPtr VirtualSize; + public int PageFaultCount; + public IntPtr PeakWorkingSetSize; + public IntPtr WorkingSetSize; + public IntPtr QuotaPeakPagedPoolUsage; + public IntPtr QuotaPagedPoolUsage; + public IntPtr QuotaPeakNonPagedPoolUsage; + public IntPtr QuotaNonPagedPoolUsage; + public IntPtr PagefileUsage; + public IntPtr PeakPagefileUsage; + } + + [StructLayout(LayoutKind.Sequential)] + public struct VmCountersEx + { + public IntPtr PeakVirtualSize; + public IntPtr VirtualSize; + public int PageFaultCount; + public IntPtr PeakWorkingSetSize; + public IntPtr WorkingSetSize; + public IntPtr QuotaPeakPagedPoolUsage; + public IntPtr QuotaPagedPoolUsage; + public IntPtr QuotaPeakNonPagedPoolUsage; + public IntPtr QuotaNonPagedPoolUsage; + public IntPtr PagefileUsage; + public IntPtr PeakPagefileUsage; + public IntPtr PrivatePageCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct XmmSaveArea32 + { + public ushort ControlWord; + public ushort StatusWord; + public byte TagWord; + public byte Reserved1; + public ushort ErrorOpcode; + public int ErrorOffset; + public ushort ErrorSelector; + public ushort Reserved2; + public int DataOffset; + public ushort DataSelector; + public ushort Reserved3; + public int MxCsr; + public int MxCsrMask; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public M128A[] FloatRegisters; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public M128A[] XmmRegisters; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] + public byte[] Reserved4; + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/NtStatus.cs b/branches/ph-plugins/ProcessHacker.Native/Api/NtStatus.cs new file mode 100644 index 000000000..1e3aa3de3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/NtStatus.cs @@ -0,0 +1,379 @@ +/* + * Process Hacker - + * NT status values + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +namespace ProcessHacker.Native.Api +{ + /// + /// A NT status value. + /// + public enum NtStatus : uint + { + // Success + Success = 0x00000000, + Wait0 = 0x00000000, + Wait1 = 0x00000001, + Wait2 = 0x00000002, + Wait3 = 0x00000003, + Wait63 = 0x0000003f, + Abandoned = 0x00000080, + AbandonedWait0 = 0x00000080, + AbandonedWait1 = 0x00000081, + AbandonedWait2 = 0x00000082, + AbandonedWait3 = 0x00000083, + AbandonedWait63 = 0x000000bf, + UserApc = 0x000000c0, + KernelApc = 0x00000100, + Alerted = 0x00000101, + Timeout = 0x00000102, + Pending = 0x00000103, + Reparse = 0x00000104, + MoreEntries = 0x00000105, + NotAllAssigned = 0x00000106, + SomeNotMapped = 0x00000107, + OpLockBreakInProgress = 0x00000108, + VolumeMounted = 0x00000109, + RxActCommitted = 0x0000010a, + NotifyCleanup = 0x0000010b, + NotifyEnumDir = 0x0000010c, + NoQuotasForAccount = 0x0000010d, + PrimaryTransportConnectFailed = 0x0000010e, + PageFaultTransition = 0x00000110, + PageFaultDemandZero = 0x00000111, + PageFaultCopyOnWrite = 0x00000112, + PageFaultGuardPage = 0x00000113, + PageFaultPagingFile = 0x00000114, + CrashDump = 0x00000116, + ReparseObject = 0x00000118, + NothingToTerminate = 0x00000122, + ProcessNotInJob = 0x00000123, + ProcessInJob = 0x00000124, + ProcessCloned = 0x00000129, + FileLockedWithOnlyReaders = 0x0000012a, + FileLockedWithWriters = 0x0000012b, + + // Informational + Informational = 0x40000000, + ObjectNameExists = 0x40000000, + ThreadWasSuspended = 0x40000001, + WorkingSetLimitRange = 0x40000002, + ImageNotAtBase = 0x40000003, + RegistryRecovered = 0x40000009, + + // Warning + Warning = 0x80000000, + GuardPageViolation = 0x80000001, + DatatypeMisalignment = 0x80000002, + Breakpoint = 0x80000003, + SingleStep = 0x80000004, + BufferOverflow = 0x80000005, + NoMoreFiles = 0x80000006, + HandlesClosed = 0x8000000a, + PartialCopy = 0x8000000d, + DeviceBusy = 0x80000011, + InvalidEaName = 0x80000013, + EaListInconsistent = 0x80000014, + NoMoreEntries = 0x8000001a, + LongJump = 0x80000026, + DllMightBeInsecure = 0x8000002b, + + // Error + Error = 0xc0000000, + Unsuccessful = 0xc0000001, + NotImplemented = 0xc0000002, + InvalidInfoClass = 0xc0000003, + InfoLengthMismatch = 0xc0000004, + AccessViolation = 0xc0000005, + InPageError = 0xc0000006, + PagefileQuota = 0xc0000007, + InvalidHandle = 0xc0000008, + BadInitialStack = 0xc0000009, + BadInitialPc = 0xc000000a, + InvalidCid = 0xc000000b, + TimerNotCanceled = 0xc000000c, + InvalidParameter = 0xc000000d, + NoSuchDevice = 0xc000000e, + NoSuchFile = 0xc000000f, + InvalidDeviceRequest = 0xc0000010, + EndOfFile = 0xc0000011, + WrongVolume = 0xc0000012, + NoMediaInDevice = 0xc0000013, + NoMemory = 0xc0000017, + NotMappedView = 0xc0000019, + UnableToFreeVm = 0xc000001a, + UnableToDeleteSection = 0xc000001b, + IllegalInstruction = 0xc000001d, + AlreadyCommitted = 0xc0000021, + AccessDenied = 0xc0000022, + BufferTooSmall = 0xc0000023, + ObjectTypeMismatch = 0xc0000024, + NonContinuableException = 0xc0000025, + BadStack = 0xc0000028, + NotLocked = 0xc000002a, + NotCommitted = 0xc000002d, + InvalidParameterMix = 0xc0000030, + ObjectNameInvalid = 0xc0000033, + ObjectNameNotFound = 0xc0000034, + ObjectNameCollision = 0xc0000035, + ObjectPathInvalid = 0xc0000039, + ObjectPathNotFound = 0xc000003a, + ObjectPathSyntaxBad = 0xc000003b, + DataOverrun = 0xc000003c, + DataLate = 0xc000003d, + DataError = 0xc000003e, + CrcError = 0xc000003f, + SectionTooBig = 0xc0000040, + PortConnectionRefused = 0xc0000041, + InvalidPortHandle = 0xc0000042, + SharingViolation = 0xc0000043, + QuotaExceeded = 0xc0000044, + InvalidPageProtection = 0xc0000045, + MutantNotOwned = 0xc0000046, + SemaphoreLimitExceeded = 0xc0000047, + PortAlreadySet = 0xc0000048, + SectionNotImage = 0xc0000049, + SuspendCountExceeded = 0xc000004a, + ThreadIsTerminating = 0xc000004b, + BadWorkingSetLimit = 0xc000004c, + IncompatibleFileMap = 0xc000004d, + SectionProtection = 0xc000004e, + EasNotSupported = 0xc000004f, + EaTooLarge = 0xc0000050, + NonExistentEaEntry = 0xc0000051, + NoEasOnFile = 0xc0000052, + EaCorruptError = 0xc0000053, + FileLockConflict = 0xc0000054, + LockNotGranted = 0xc0000055, + DeletePending = 0xc0000056, + CtlFileNotSupported = 0xc0000057, + UnknownRevision = 0xc0000058, + RevisionMismatch = 0xc0000059, + InvalidOwner = 0xc000005a, + InvalidPrimaryGroup = 0xc000005b, + NoImpersonationToken = 0xc000005c, + CantDisableMandatory = 0xc000005d, + NoLogonServers = 0xc000005e, + NoSuchLogonSession = 0xc000005f, + NoSuchPrivilege = 0xc0000060, + PrivilegeNotHeld = 0xc0000061, + InvalidAccountName = 0xc0000062, + UserExists = 0xc0000063, + NoSuchUser = 0xc0000064, + GroupExists = 0xc0000065, + NoSuchGroup = 0xc0000066, + MemberInGroup = 0xc0000067, + MemberNotInGroup = 0xc0000068, + LastAdmin = 0xc0000069, + WrongPassword = 0xc000006a, + IllFormedPassword = 0xc000006b, + PasswordRestriction = 0xc000006c, + LogonFailure = 0xc000006d, + AccountRestriction = 0xc000006e, + InvalidLogonHours = 0xc000006f, + InvalidWorkstation = 0xc0000070, + PasswordExpired = 0xc0000071, + AccountDisabled = 0xc0000072, + FileInvalid = 0xc0000098, + InstanceNotAvailable = 0xc00000ab, + PipeNotAvailable = 0xc00000ac, + InvalidPipeState = 0xc00000ad, + PipeBusy = 0xc00000ae, + IllegalFunction = 0xc00000af, + PipeDisconnected = 0xc00000b0, + PipeClosing = 0xc00000b1, + PipeConnected = 0xc00000b2, + PipeListening = 0xc00000b3, + InvalidReadMode = 0xc00000b4, + IoTimeout = 0xc00000b5, + FileForcedClosed = 0xc00000b6, + ProfilingNotStarted = 0xc00000b7, + ProfilingNotStopped = 0xc00000b8, + NotSameDevice = 0xc00000d4, + FileRenamed = 0xc00000d5, + CantWait = 0xc00000d8, + PipeEmpty = 0xc00000d9, + CantTerminateSelf = 0xc00000db, + InternalError = 0xc00000e5, + InvalidParameter1 = 0xc00000ef, + InvalidParameter2 = 0xc00000f0, + InvalidParameter3 = 0xc00000f1, + InvalidParameter4 = 0xc00000f2, + InvalidParameter5 = 0xc00000f3, + InvalidParameter6 = 0xc00000f4, + InvalidParameter7 = 0xc00000f5, + InvalidParameter8 = 0xc00000f6, + InvalidParameter9 = 0xc00000f7, + InvalidParameter10 = 0xc00000f8, + InvalidParameter11 = 0xc00000f9, + InvalidParameter12 = 0xc00000fa, + MappedFileSizeZero = 0xc000011e, + TooManyOpenedFiles = 0xc000011f, + Cancelled = 0xc0000120, + CannotDelete = 0xc0000121, + InvalidComputerName = 0xc0000122, + FileDeleted = 0xc0000123, + SpecialAccount = 0xc0000124, + SpecialGroup = 0xc0000125, + SpecialUser = 0xc0000126, + MembersPrimaryGroup = 0xc0000127, + FileClosed = 0xc0000128, + TooManyThreads = 0xc0000129, + ThreadNotInProcess = 0xc000012a, + TokenAlreadyInUse = 0xc000012b, + PagefileQuotaExceeded = 0xc000012c, + CommitmentLimit = 0xc000012d, + InvalidImageLeFormat = 0xc000012e, + InvalidImageNotMz = 0xc000012f, + InvalidImageProtect = 0xc0000130, + InvalidImageWin16 = 0xc0000131, + LogonServer = 0xc0000132, + DifferenceAtDc = 0xc0000133, + SynchronizationRequired = 0xc0000134, + DllNotFound = 0xc0000135, + IoPrivilegeFailed = 0xc0000137, + OrdinalNotFound = 0xc0000138, + EntryPointNotFound = 0xc0000139, + ControlCExit = 0xc000013a, + PortNotSet = 0xc0000353, + DebuggerInactive = 0xc0000354, + CallbackBypass = 0xc0000503, + PortClosed = 0xc0000700, + MessageLost = 0xc0000701, + InvalidMessage = 0xc0000702, + RequestCanceled = 0xc0000703, + RecursiveDispatch = 0xc0000704, + LpcReceiveBufferExpected = 0xc0000705, + LpcInvalidConnectionUsage = 0xc0000706, + LpcRequestsNotAllowed = 0xc0000707, + ResourceInUse = 0xc0000708, + ProcessIsProtected = 0xc0000712, + VolumeDirty = 0xc0000806, + FileCheckedOut = 0xc0000901, + CheckOutRequired = 0xc0000902, + BadFileType = 0xc0000903, + FileTooLarge = 0xc0000904, + FormsAuthRequired = 0xc0000905, + VirusInfected = 0xc0000906, + VirusDeleted = 0xc0000907, + + MaximumNtStatus = 0xffffffff + } + + public static class NtStatusExtensions + { + /// + /// Gets a string which describes the NT status value. + /// + /// The NT status value. + /// A message, or null if the message could not be retrieved. + public static string GetMessage(this NtStatus status) + { + string message; + + message = NativeUtils.GetMessage( + Loader.GetDllHandle("ntdll.dll"), + 0xb, + System.Threading.Thread.CurrentThread.CurrentUICulture.LCID, + (int)status + ); + + if (message != null) + { + // Fix those messages which are formatted like: + // {Asdf}\r\nAsdf asdf asdf... + if (message.StartsWith("{")) + { + string[] split = message.Split('\n'); + + if (split.Length > 1) + message = split[1]; + } + } + + return message; + } + + /// + /// Gets whether the NT status value indicates an error. + /// + /// The NT status value. + public static bool IsError(this NtStatus status) + { + return status >= NtStatus.Error && status <= NtStatus.MaximumNtStatus; + } + + /// + /// Gets whether the NT status value indicates information. + /// + /// The NT status value. + public static bool IsInformational(this NtStatus status) + { + return status >= NtStatus.Informational && status < NtStatus.Warning; + } + + /// + /// Gets whether the NT status value indicates success. + /// + /// The NT status value. + public static bool IsSuccess(this NtStatus status) + { + return status >= NtStatus.Success && status < NtStatus.Informational; + } + + /// + /// Gets whether the NT status value indicates a warning. + /// + /// The NT status value. + public static bool IsWarning(this NtStatus status) + { + return status >= NtStatus.Warning && status < NtStatus.Error; + } + + /// + /// Throws the NT status value as an exception. + /// + /// The NT status value. + public static void Throw(this NtStatus status) + { + throw new WindowsException(status); + } + + /// + /// Throws the NT status value as an exception if it is an error or warning. + /// + /// The NT status value. + public static void ThrowIf(this NtStatus status) + { + if (status.IsError() || status.IsWarning()) + status.Throw(); + } + + /// + /// Converts the NT status value to a DOS/Windows error code. + /// + /// The NT status value. + /// A DOS/Windows error code. + public static Win32Error ToDosError(this NtStatus status) + { + return Win32.RtlNtStatusToDosError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Structs.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Structs.cs new file mode 100644 index 000000000..c644a958e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Structs.cs @@ -0,0 +1,834 @@ +/* + * Process Hacker - + * windows API structs + * + * Copyright (C) 2009 Uday Shanbhag + * Copyright (C) 2009 Dean + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* This file contains structure declarations for the Win32 API. + * + * All structures which do not belong in any other category + * are placed in this file. + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Api +{ + [StructLayout(LayoutKind.Sequential)] + public struct Address64 + { + public ulong Offset; + public ushort Segment; + public AddressMode Mode; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct CatalogInfo + { + public int Size; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string CatalogFile; + } + + [StructLayout(LayoutKind.Sequential)] + public struct EnumServiceStatus + { + [MarshalAs(UnmanagedType.LPTStr)] + public string ServiceName; + + [MarshalAs(UnmanagedType.LPTStr)] + public string DisplayName; + + [MarshalAs(UnmanagedType.Struct)] + public ServiceStatus ServiceStatus; + } + + [StructLayout(LayoutKind.Sequential)] + public struct EnumServiceStatusProcess + { + [MarshalAs(UnmanagedType.LPTStr)] + public string ServiceName; + + [MarshalAs(UnmanagedType.LPTStr)] + public string DisplayName; + + [MarshalAs(UnmanagedType.Struct)] + public ServiceStatusProcess ServiceStatusProcess; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FpoData + { + public int ulOffStart; + public int cbProcSize; + public int cdwLocals; + public short cdwParams; + + public long Part1; + public long Part2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HeapEntry32 + { + public int dwSize; + public IntPtr hHandle; + public IntPtr dwAddress; + public int dwBlockSize; + public HeapEntry32Flags dwFlags; + public int dwLockCount; + public int dwResvd; + public int th32ProcessID; + public int th32HeapID; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HeapList32 + { + public int dwSize; + public int th32ProcessID; + public IntPtr th32HeapID; + public int dwFlags; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ImagehlpLine64 + { + public int SizeOfStruct; + public int Key; + public int LineNumber; + public string FileName; + public long Address; + } + + [StructLayout(LayoutKind.Explicit)] + public struct INet6Address + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + [FieldOffset(0)] + public byte[] Bytes; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + [FieldOffset(0)] + public ushort[] Words; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KdHelp64 + { + public long Thread; + public int ThCallbackStack; + public int ThCallbackBSTore; + public int NextCallback; + public int FramePointer; + public long KiCallUserMode; + public long KeUserCallbackDispatcher; + public long SystemRangeStart; + public long KiUserExceptionDispatcher; + public long StackBase; + public long StackLimit; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public long[] Reserved; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct LoadedImage + { + public IntPtr ModuleName; + public IntPtr FileHandle; + public IntPtr MappedAddress; + public IntPtr FileHeader; // ImageNtHeaders32* + public IntPtr LastRvaSection; // ImageSectionHeader* + public int NumberOfSections; + public IntPtr Sections; // ImageSectionHeader* + public int Characteristics; + [MarshalAs(UnmanagedType.U1)] + public bool SystemImage; + [MarshalAs(UnmanagedType.U1)] + public bool DosImage; + [MarshalAs(UnmanagedType.U1)] + public bool ReadOnly; + public byte Version; + public ListEntry Links; + public int SizeOfImage; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LuidAndAttributes + { + public Luid Luid; + public SePrivilegeAttributes Attributes; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemoryBasicInformation + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public MemoryProtection AllocationProtect; + public IntPtr RegionSize; + public MemoryState State; + public MemoryProtection Protect; + public MemoryType Type; + } + + [StructLayout(LayoutKind.Sequential, Pack = 16)] + public struct MemoryBasicInformation64 + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public MemoryProtection AllocationProtect; + private int _alignment1; + public ulong RegionSize; + public MemoryState State; + public MemoryProtection Protect; + public MemoryType Type; + private int _alignment2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcpRow + { + public MibTcpState State; + public uint LocalAddress; + public int LocalPort; + public uint RemoteAddress; + public int RemotePort; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcp6Row + { + public MibTcpState State; + public uint LocalAddress; + public uint LocalScopeId; + public int LocalPort; + public uint RemoteAddr; + public int RemoteScopeId; + public int RemotePort; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcp6Row2 + { + public INet6Address LocalAddr; + public uint LocalScopeId; + public int LocalPort; + public INet6Address RemoteAddr; + public uint RemoteScopeId; + public int RemotePort; + public MibTcpState State; + public int OwningPid; + public TcpConnectionOffloadState OffloadState; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcpRowOwnerPid + { + public MibTcpState State; + public uint LocalAddress; + public int LocalPort; + public uint RemoteAddress; + public int RemotePort; + public int OwningProcessId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct MibTcp6RowOwnerPid + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] LocalAddress; + public uint LocalScopeId; + public int LocalPort; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] RemoteAddress; + public uint RemoteScopeId; + public int RemotePort; + public MibTcpState State; + public int OwningProcessId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcpStats + { + public uint RtoAlgorithm; + public uint RtoMin; + public uint RtoMax; + public uint MaxConn; + public uint ActiveOpens; + public uint PassiveOpens; + public uint AttemptFails; + public uint EstabResets; + public uint CurrEstab; + public uint InSegs; + public uint OutSegs; + public uint RetransSegs; + public uint InErrs; + public uint OutRsts; + public uint NumConns; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcpTable + { + public int NumEntries; + public MibTcpRow[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcp6Table + { + public int NumEntries; + public MibTcp6Row[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcpTableOwnerPid + { + public int NumEntries; + public MibTcpRowOwnerPid[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibTcp6TableOwnerPid + { + public int NumEntries; + public MibTcp6RowOwnerPid[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdpRow + { + public uint LocalAddress; + public int LocalPort; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdp6Row + { + public INet6Address LocalAddress; + public uint LocalScopeId; + public int LocalPort; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdpTable + { + public uint NumEntries; + public MibUdpRow[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdp6Table + { + public int NumEntries; + public MibUdp6Row[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdpRowOwnerPid + { + public uint LocalAddress; + public int LocalPort; + public int OwningProcessId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct MibUdp6RowOwnerPid + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] LocalAddress; + public uint LocalScopeId; + public int LocalPort; + public int OwningProcessId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct MibUdp6RowOwnerModule + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] LocalAddress; + public uint LocalScopeId; + public int LocalPort; + public int OwningProcessId; + public long CreateTimestamp; + public int Flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public long[] OwningModuleInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdpStats + { + public int InDatagrams; + public int NoPorts; + public int InErrors; + public int OutDatagrams; + public int NumAddrs; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdpTableOwnerPid + { + public int NumEntries; + public MibUdpRowOwnerPid[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MibUdp6TableOwnerPid + { + public int NumEntries; + public MibUdp6RowOwnerPid[] Table; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ModuleEntry32 + { + public int dwSize; + public int th32ModuleID; + public int th32ProcessID; + public int GlblcntUsage; + public int ProccntUsage; + public int modBaseAddr; + public int modBaseSize; + public int hModule; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szModule; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szExePath; + + public int dwFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ModuleInfo + { + public IntPtr BaseOfDll; + public int SizeOfImage; + public IntPtr EntryPoint; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MonitorInformation + { + public uint Size; + public Rectangle MonitorRectangle; + public Rectangle WorkRectangle; + public uint Flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PerformanceInformation + { + public int Size; + public IntPtr CommitTotal; + public IntPtr CommitLimit; + public IntPtr CommitPeak; + public IntPtr PhysicalTotal; + public IntPtr PhysicalAvailable; + public IntPtr SystemCache; + public IntPtr KernelTotal; + public IntPtr KernelPaged; + public IntPtr KernelNonPaged; + public IntPtr PageSize; + public int HandlesCount; + public int ProcessCount; + public int ThreadCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessEntry32 + { + public int dwSize; + public int cntUsage; + public int th32ProcessID; + public int th32DefaultHeapID; + public int th32ModuleID; + public int cntThreads; + public int th32ParentProcessID; + public int pcPriClassBase; + public int dwFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szExeFile; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessInformation + { + public IntPtr ProcessHandle; + public IntPtr ThreadHandle; + public int ProcessId; + public int ThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct ProfileInformation + { + public int Size; + public int Flags; + public string UserName; + public string ProfilePath; + public string DefaultPath; + public string ServerName; + public string PolicyPath; + public int ProfileHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct QueryServiceConfig + { + public ServiceType ServiceType; + public ServiceStartType StartType; + public ServiceErrorControl ErrorControl; + + [MarshalAs(UnmanagedType.LPTStr)] + public string BinaryPathName; + + [MarshalAs(UnmanagedType.LPTStr)] + public string LoadOrderGroup; + + public int TagID; + public int Dependencies; // pointer to a string array + + [MarshalAs(UnmanagedType.LPTStr)] + public string ServiceStartName; + + [MarshalAs(UnmanagedType.LPTStr)] + public string DisplayName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public Rectangle ToRectangle() + { + return Rectangle.FromLTRB(this.Left, this.Top, this.Right, this.Bottom); + } + + public Rect(int left, int top, int right, int bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct ScAction + { + public ScActionType Type; + public int Delay; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ServiceDescription + { + [MarshalAs(UnmanagedType.LPWStr)] + public string Description; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ServiceStatus + { + public ServiceType ServiceType; + public ServiceState CurrentState; + public ServiceAccept ControlsAccepted; + public int Win32ExitCode; + public int ServiceSpecificExitCode; + public int CheckPoint; + public int WaitHint; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ServiceStatusProcess + { + public ServiceType ServiceType; + public ServiceState CurrentState; + public ServiceAccept ControlsAccepted; + public int Win32ExitCode; + public int ServiceSpecificExitCode; + public int CheckPoint; + public int WaitHint; + public int ProcessID; + public ServiceFlags ServiceFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ShellExecuteInfo + { + public int cbSize; + public uint fMask; + public IntPtr hWnd; + public string lpVerb; + public string lpFile; + public string lpParameters; + public string lpDirectory; + public ShowWindowType nShow; + public IntPtr hInstApp; + + public IntPtr lpIDList; + public string lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIcon; + public IntPtr hProcess; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ShFileInfo + { + public IntPtr hIcon; + public IntPtr iIcon; + public uint dwAttributes; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szDisplayName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SiAccess + { + public IntPtr Guid; + public int Mask; + public IntPtr Name; // string + public SiAccessFlags Flags; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SiInheritType + { + public IntPtr Guid; + public int Flags; + public IntPtr Name; // string + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SiObjectInfo + { + public SiObjectInfoFlags Flags; + public IntPtr Instance; + public IntPtr ServerName; // string + public IntPtr ObjectName; // string + public IntPtr PageTitle; // string + public Guid ObjectType; + } + + [StructLayout(LayoutKind.Sequential)] + public struct StackFrame64 + { + public Address64 AddrPC; + public Address64 AddrReturn; + public Address64 AddrFrame; + public Address64 AddrStack; + public Address64 AddrBStore; + + public IntPtr FuncTableEntry; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public long[] Params; + + public int Far; + public int Virtual; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public long[] Reserved; + + public KdHelp64 KdHelp; + } + + [StructLayout(LayoutKind.Sequential)] + public struct StartupInfo + { + public int Size; + [MarshalAs(UnmanagedType.LPWStr)] + public string Reserved; + [MarshalAs(UnmanagedType.LPWStr)] + public string Desktop; + [MarshalAs(UnmanagedType.LPWStr)] + public string Title; + public int X; + public int Y; + public int XSize; + public int YSize; + public int XCountChars; + public int YCountChars; + public int FillAttribute; + public StartupFlags Flags; + public short ShowWindow; + public short Reserved2; + public IntPtr Reserved3; + public IntPtr StdInputHandle; + public IntPtr StdOutputHandle; + public IntPtr StdErrorHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SymbolInfo + { + public int SizeOfStruct; + public int TypeIndex; + public unsafe fixed long Reserved[2]; + public int Index; + public int Size; + public ulong ModBase; + public SymbolFlags Flags; + public long Value; + public long Address; + public int Register; + public int Scope; + public int Tag; + public int NameLen; + public int MaxNameLen; + public char Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ThreadEntry32 + { + public int dwSize; + public int cntUsage; + public int th32ThreadID; + public int th32OwnerProcessID; + public int tpBasePri; + public int tpDeltaPri; + public int dwFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szExeFile; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WindowClass + { + public int Styles; + [MarshalAs(UnmanagedType.FunctionPtr)] + public WndProcDelegate WindowsProc; + private int ExtraClassData; + private int ExtraWindowData; + public IntPtr InstanceHandle; + public IntPtr IconHandle; + public IntPtr CursorHandle; + public IntPtr backgroundBrush; + [MarshalAs(UnmanagedType.LPTStr)] + public string MenuName; + [MarshalAs(UnmanagedType.LPTStr)] + public string ClassName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WindowPlacement + { + public int Length; + public WindowPlacementFlags Flags; + public ShowWindowType ShowState; + public Point MinPosition; + public Point MaxPosition; + public Rect NormalPosition; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WintrustCatalogInfo + { + public int Size; + public int CatalogVersion; + public string CatalogFilePath; + public string MemberTag; + public string MemberFilePath; + public IntPtr MemberFile; + public byte[] CalculatedFileHash; + public int CalculatedFileHashSize; + public IntPtr CatalogContext; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WintrustData + { + public int Size; + public IntPtr PolicyCallbackData; + public IntPtr SIPClientData; + public int UIChoice; + public WtRevocationChecks RevocationChecks; + public int UnionChoice; + public IntPtr UnionData; + public int StateAction; + public IntPtr StateData; + public IntPtr URLReference; + public WtProvFlags ProvFlags; + public int UIContext; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WintrustFileInfo + { + public int Size; + public IntPtr FilePath; + public IntPtr FileHandle; + public IntPtr KnownSubject; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WtsClientAddress + { + public int AddressFamily; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public byte[] Address; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WtsClientDisplay + { + public int HorizontalResolution; + public int VerticalResolution; + public int ColorDepth; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WtsProcessInfo + { + public int SessionId; + public int ProcessId; + public IntPtr ProcessName; + public IntPtr Sid; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WtsSessionInfo + { + public int SessionID; + public string WinStationName; + public WtsConnectStateClass State; + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Win32.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Win32.cs new file mode 100644 index 000000000..87a55cd02 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Win32.cs @@ -0,0 +1,350 @@ +/* + * Process Hacker - + * windows API wrapper code + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2009 Dean + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using ProcessHacker.Common.Threading; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Api +{ + public delegate bool EnumWindowsProc(IntPtr hWnd, uint param); + public delegate bool EnumChildProc(IntPtr hWnd, uint param); + public delegate bool EnumThreadWndProc(IntPtr hWnd, uint param); + public delegate IntPtr WndProcDelegate(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam); + + public delegate bool SymEnumSymbolsProc(IntPtr SymInfo, int SymbolSize, int UserContext); + public unsafe delegate bool ReadProcessMemoryProc64(IntPtr ProcessHandle, ulong BaseAddress, IntPtr Buffer, + int Size, out int BytesRead); + public delegate IntPtr FunctionTableAccessProc64(IntPtr ProcessHandle, ulong AddrBase); + public delegate ulong GetModuleBaseProc64(IntPtr ProcessHandle, ulong Address); + + /// + /// Provides interfacing to the Win32 and Native APIs. + /// + [SuppressUnmanagedCodeSecurity] + public static partial class Win32 + { + private static FastMutex _dbgHelpLock = new FastMutex(); + + /// + /// A mutex which controls access to the dbghelp.dll functions. + /// + public static FastMutex DbgHelpLock + { + get { return _dbgHelpLock; } + } + + #region Consts + + public const int DontResolveDllReferences = 0x1; + public const int ErrorNoMoreItems = 259; + public const int SeeMaskInvokeIdList = 0xc; + public const uint ServiceNoChange = 0xffffffff; + public const uint ShgFiIcon = 0x100; + public const uint ShgFiLargeIcon = 0x0; + public const uint ShgFiSmallIcon = 0x1; + public static readonly int SymbolInfoNameOffset = Marshal.OffsetOf(typeof(SymbolInfo), "Name").ToInt32(); + + #endregion + + #region Errors + + public static Win32Error GetLastErrorCode() + { + return (Win32Error)Marshal.GetLastWin32Error(); + } + + /// + /// Gets the error message associated with the last error that occured. + /// + /// An error message. + public static string GetLastErrorMessage() + { + return GetLastErrorCode().GetMessage(); + } + + /// + /// Throws a WindowsException with the last error that occurred. + /// + public static void ThrowLastError() + { + ThrowLastError(GetLastErrorCode()); + } + + public static void ThrowLastError(NtStatus status) + { + throw new WindowsException(status); + } + + public static void ThrowLastError(int error) + { + ThrowLastError((Win32Error)error); + } + + public static void ThrowLastError(Win32Error error) + { + throw new WindowsException(error); + } + + #endregion + + #region Handles + + public unsafe static void DuplicateObject( + IntPtr sourceProcessHandle, + IntPtr sourceHandle, + int desiredAccess, + HandleFlags handleAttributes, + DuplicateOptions options + ) + { + IntPtr dummy; + + DuplicateObject( + sourceProcessHandle, + sourceHandle, + IntPtr.Zero, + out dummy, + desiredAccess, + handleAttributes, + options + ); + } + + public unsafe static void DuplicateObject( + IntPtr sourceProcessHandle, + IntPtr sourceHandle, + IntPtr targetProcessHandle, + out IntPtr targetHandle, + int desiredAccess, + HandleFlags handleAttributes, + DuplicateOptions options + ) + { + if (KProcessHacker.Instance != null) + { + int target; + + KProcessHacker.Instance.KphDuplicateObject( + sourceProcessHandle.ToInt32(), + sourceHandle.ToInt32(), + targetProcessHandle.ToInt32(), + out target, + desiredAccess, + handleAttributes, + options); + targetHandle = new IntPtr(target); + } + else + { + NtStatus status; + + if ((status = NtDuplicateObject( + sourceProcessHandle, + sourceHandle, + targetProcessHandle, + out targetHandle, + desiredAccess, + handleAttributes, + options)) >= NtStatus.Error) + ThrowLastError(status); + } + } + + #endregion + + #region Processes + + public static int GetProcessSessionId(int ProcessId) + { + int sessionId; + + try + { + if (!ProcessIdToSessionId(ProcessId, out sessionId)) + ThrowLastError(); + } + catch + { + using (ProcessHandle phandle = new ProcessHandle(ProcessId, OSVersion.MinProcessQueryInfoAccess)) + { + return phandle.GetToken(TokenAccess.Query).GetSessionId(); + } + } + + return sessionId; + } + + #endregion + + #region TCP + + public static MibTcpStats GetTcpStats() + { + MibTcpStats tcpStats; + GetTcpStatistics(out tcpStats); + return tcpStats; + } + + public static MibTcpTableOwnerPid GetTcpTable() + { + MibTcpTableOwnerPid table = new MibTcpTableOwnerPid(); + int length = 0; + + GetExtendedTcpTable(IntPtr.Zero, ref length, false, AiFamily.INet, TcpTableClass.OwnerPidAll, 0); + + using (MemoryAlloc mem = new MemoryAlloc(length)) + { + GetExtendedTcpTable(mem, ref length, false, AiFamily.INet, TcpTableClass.OwnerPidAll, 0); + + int count = mem.ReadInt32(0); + + table.NumEntries = count; + table.Table = new MibTcpRowOwnerPid[count]; + + for (int i = 0; i < count; i++) + table.Table[i] = mem.ReadStruct(sizeof(int), i); + } + + return table; + } + + #endregion + + #region Terminal Server + + public struct WtsEnumProcessesFastData + { + public int[] PIDs; + public IntPtr[] SIDs; + public WtsMemoryAlloc Memory; + } + + public unsafe static WtsEnumProcessesFastData TSEnumProcessesFast() + { + IntPtr processes; + int count; + int[] pids; + IntPtr[] sids; + + WTSEnumerateProcesses(IntPtr.Zero, 0, 1, out processes, out count); + + pids = new int[count]; + sids = new IntPtr[count]; + + WtsMemoryAlloc data = new WtsMemoryAlloc(processes); + WtsProcessInfo* dataP = (WtsProcessInfo*)data.Memory; + + for (int i = 0; i < count; i++) + { + pids[i] = dataP[i].ProcessId; + sids[i] = dataP[i].Sid; + } + + return new WtsEnumProcessesFastData() { PIDs = pids, SIDs = sids, Memory = data }; + } + + #endregion + + #region UDP + + public static MibUdpStats GetUdpStats() + { + MibUdpStats udpStats; + GetUdpStatistics(out udpStats); + return udpStats; + } + + public static MibUdpTableOwnerPid GetUdpTable() + { + MibUdpTableOwnerPid table = new MibUdpTableOwnerPid(); + int length = 0; + + GetExtendedUdpTable(IntPtr.Zero, ref length, false, AiFamily.INet, UdpTableClass.OwnerPid, 0); + + using (MemoryAlloc mem = new MemoryAlloc(length)) + { + GetExtendedUdpTable(mem, ref length, false, AiFamily.INet, UdpTableClass.OwnerPid, 0); + + int count = mem.ReadInt32(0); + + table.NumEntries = count; + table.Table = new MibUdpRowOwnerPid[count]; + + for (int i = 0; i < count; i++) + table.Table[i] = mem.ReadStruct(sizeof(int), i); + } + + return table; + } + + #endregion + + #region Unsafe + + /// + /// Converts a multi-string into a managed string array. A multi-string + /// consists of an array of null-terminated strings plus an extra null to + /// terminate the array. + /// + /// The pointer to the array. + /// A string array. + public unsafe static string[] GetMultiString(IntPtr ptr) + { + List list = new List(); + char* chptr = (char*)ptr; + StringBuilder currentString = new StringBuilder(); + + while (true) + { + while (*chptr != 0) + { + currentString.Append(*chptr); + chptr++; + } + + string str = currentString.ToString(); + + if (str == "") + { + break; + } + else + { + list.Add(str); + currentString = new StringBuilder(); + } + } + + return list.ToArray(); + } + + #endregion + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Api/Win32Error.cs b/branches/ph-plugins/ProcessHacker.Native/Api/Win32Error.cs new file mode 100644 index 000000000..5319aee49 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Api/Win32Error.cs @@ -0,0 +1,132 @@ +/* + * Process Hacker - + * Win32 error codes + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Api +{ + /// + /// A Win32 error code. + /// + public enum Win32Error : uint + { + Success = 0x0, + InvalidFunction = 0x1, + FileNotFound = 0x2, + PathNotFound = 0x3, + TooManyOpenFiles = 0x4, + AccessDenied = 0x5, + InvalidHandle = 0x6, + ArenaTrashed = 0x7, + NotEnoughMemory = 0x8, + InvalidBlock = 0x9, + BadEnvironment = 0xa, + BadFormat = 0xb, + InvalidAccess = 0xc, + InvalidData = 0xd, + OutOfMemory = 0xe, + InvalidDrive = 0xf, + CurrentDirectory = 0x10, + NotSameDevice = 0x11, + NoMoreFiles = 0x12, + WriteProtect = 0x13, + BadUnit = 0x14, + NotReady = 0x15, + BadCommand = 0x16, + Crc = 0x17, + BadLength = 0x18, + Seek = 0x19, + NotDosDisk = 0x1a, + SectorNotFound = 0x1b, + OutOfPaper = 0x1c, + WriteFault = 0x1d, + ReadFault = 0x1e, + GenFailure = 0x1f, + SharingViolation = 0x20, + LockViolation = 0x21, + WrongDisk = 0x22, + SharingBufferExceeded = 0x24, + HandleEof = 0x26, + HandleDiskFull = 0x27, + NotSupported = 0x32, + RemNotList = 0x33, + DupName = 0x34, + BadNetPath = 0x35, + NetworkBusy = 0x36, + DevNotExist = 0x37, + TooManyCmds = 0x38, + FileExists = 0x50, + CannotMake = 0x52, + AlreadyAssigned = 0x55, + InvalidPassword = 0x56, + InvalidParameter = 0x57, + NetWriteFault = 0x58, + NoProcSlots = 0x59, + TooManySemaphores = 0x64, + ExclSemAlreadyOwned = 0x65, + SemIsSet = 0x66, + TooManySemRequests = 0x67, + InvalidAtInterruptTime = 0x68, + SemOwnerDied = 0x69, + SemUserLimit = 0x6a + } + + public static class Win32ErrorExtensions + { + public static HResult GetHResult(this Win32Error errorCode) + { + int error = (int)errorCode; + + if ((error & 0x80000000) == 0x80000000) + return (HResult)error; + + return (HResult)(0x80070000 | (uint)(error & 0xffff)); + } + + public static string GetMessage(this Win32Error errorCode) + { + StringBuilder buffer = new StringBuilder(0x100); + + if (Win32.FormatMessage(0x3200, IntPtr.Zero, (int)errorCode, 0, buffer, buffer.Capacity, IntPtr.Zero) == 0) + return "Unknown error (0x" + ((int)errorCode).ToString("x") + ")"; + + StringBuilder result = new StringBuilder(); + int i = 0; + + while (i < buffer.Length) + { + if (!char.IsLetterOrDigit(buffer[i]) && + !char.IsPunctuation(buffer[i]) && + !char.IsSymbol(buffer[i]) && + !char.IsWhiteSpace(buffer[i])) + break; + + result.Append(buffer[i]); + i++; + } + + return result.ToString().Replace("\r\n", ""); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Cryptography.cs b/branches/ph-plugins/ProcessHacker.Native/Cryptography.cs new file mode 100644 index 000000000..c24525110 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Cryptography.cs @@ -0,0 +1,241 @@ +/* + * Process Hacker - + * cryptography functions + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + public enum VerifyResult : int + { + Unknown = 0, + NoSignature, + Trusted, + TrustedInstaller, + Expired, + Revoked, + Distrust, + SecuritySettings + } + + public static class Cryptography + { + public static readonly Guid DriverActionVerify = + new Guid("{f750e6c3-38ee-11d1-85e5-00c04fc295ee}"); + public static readonly Guid HttpsProvAction = + new Guid("{573e31f8-aaba-11d0-8ccb-00c04fc295ee}"); + public static readonly Guid OfficeSignActionVerify = + new Guid("{5555c2cd-17fb-11d1-85c4-00c04fc295ee}"); + public static readonly Guid WintrustActionGenericCertVerify = + new Guid("{189a3842-3041-11d1-85e1-00c04fc295ee}"); + public static readonly Guid WintrustActionGenericChainVerify = + new Guid("{fc451c16-ac75-11d1-b4b8-00c04fb66ea0}"); + public static readonly Guid WintrustActionGenericVerifyV2 = + new Guid("{00aac56b-cd44-11d0-8cc2-00c04fc295ee}"); + public static readonly System.Guid WintrustActionTrustProviderTest = + new Guid("{573e31f8-ddba-11d0-8ccb-00c04fc295ee}"); + + public static string GetFileSubjectValue(string fileName, string keyName) + { + X509Certificate cert = X509Certificate.CreateFromSignedFile(fileName); + Tokenizer t = new Tokenizer(cert.Subject); + + // Use the "tokenizer" to get the Common Name (CN). + while (true) + { + t.EatWhitespace(); + string key = t.EatId(); + + if (string.IsNullOrEmpty(key)) + return null; + + t.EatWhitespace(); + string equals = t.EatSymbol(); + + if (equals != "=") + return null; + + t.EatWhitespace(); + string value = t.EatQuotedString(); + + if (string.IsNullOrEmpty(value)) + { + // The value probably isn't quoted. + value = t.EatUntil(','); + } + + if (string.IsNullOrEmpty(value)) + return null; + + if (key == keyName) + return value; + } + } + + public static VerifyResult StatusToVerifyResult(uint status) + { + if (status == 0) + return VerifyResult.Trusted; + else if (status == 0x800b0100) + return VerifyResult.NoSignature; + else if (status == 0x800b0101) + return VerifyResult.Expired; + else if (status == 0x800b010c) + return VerifyResult.Revoked; + else if (status == 0x800b0111) + return VerifyResult.Distrust; + else if (status == 0x80092026) + return VerifyResult.SecuritySettings; + else + return VerifyResult.SecuritySettings; + } + + public static VerifyResult VerifyFile(string fileName) + { + VerifyResult result = VerifyResult.NoSignature; + + using (MemoryAlloc strMem = new MemoryAlloc(fileName.Length * 2 + 2)) + { + WintrustFileInfo fileInfo = new WintrustFileInfo(); + + strMem.WriteUnicodeString(0, fileName); + strMem.WriteByte(fileName.Length * 2, 0); + strMem.WriteByte(fileName.Length * 2 + 1, 0); + + fileInfo.Size = Marshal.SizeOf(fileInfo); + fileInfo.FilePath = strMem; + + WintrustData trustData = new WintrustData(); + + trustData.Size = 12 * 4; + trustData.UIChoice = 2; // WTD_UI_NONE + trustData.UnionChoice = 1; // WTD_CHOICE_FILE + trustData.RevocationChecks = WtRevocationChecks.None; + trustData.ProvFlags = WtProvFlags.Safer; + + if (OSVersion.IsAboveOrEqual(WindowsVersion.Vista)) + trustData.ProvFlags |= WtProvFlags.CacheOnlyUrlRetrieval; + + using (MemoryAlloc mem = new MemoryAlloc(fileInfo.Size)) + { + Marshal.StructureToPtr(fileInfo, mem, false); + trustData.UnionData = mem; + + uint winTrustResult = Win32.WinVerifyTrust(IntPtr.Zero, WintrustActionGenericVerifyV2, ref trustData); + + result = StatusToVerifyResult(winTrustResult); + } + } + + if (result == VerifyResult.NoSignature) + { + using (FileHandle sourceFile = FileHandle.CreateWin32(fileName, FileAccess.GenericRead, FileShareMode.Read, + FileCreationDispositionWin32.OpenExisting)) + { + byte[] hash = new byte[256]; + int hashLength = 256; + + if (!Win32.CryptCATAdminCalcHashFromFileHandle(sourceFile, ref hashLength, hash, 0)) + { + hash = new byte[hashLength]; + + if (!Win32.CryptCATAdminCalcHashFromFileHandle(sourceFile, ref hashLength, hash, 0)) + return VerifyResult.NoSignature; + } + + StringBuilder memberTag = new StringBuilder(hashLength * 2); + + for (int i = 0; i < hashLength; i++) + memberTag.Append(hash[i].ToString("X2")); + + IntPtr catAdmin; + + if (!Win32.CryptCATAdminAcquireContext(out catAdmin, DriverActionVerify, 0)) + return VerifyResult.NoSignature; + + IntPtr catInfo = Win32.CryptCATAdminEnumCatalogFromHash(catAdmin, hash, hashLength, 0, IntPtr.Zero); + + if (catInfo == IntPtr.Zero) + { + Win32.CryptCATAdminReleaseContext(catAdmin, 0); + return VerifyResult.NoSignature; + } + + CatalogInfo ci; + + if (!Win32.CryptCATCatalogInfoFromContext(catInfo, out ci, 0)) + { + Win32.CryptCATAdminReleaseCatalogContext(catAdmin, catInfo, 0); + Win32.CryptCATAdminReleaseContext(catAdmin, 0); + return VerifyResult.NoSignature; + } + + WintrustCatalogInfo wci = new WintrustCatalogInfo(); + + wci.Size = Marshal.SizeOf(wci); + wci.CatalogFilePath = ci.CatalogFile; + wci.MemberFilePath = fileName; + wci.MemberTag = memberTag.ToString(); + + WintrustData trustData = new WintrustData(); + + trustData.Size = 12 * 4; + trustData.UIChoice = 1; + trustData.UnionChoice = 2; + trustData.RevocationChecks = WtRevocationChecks.None; + + if (OSVersion.IsAboveOrEqual(WindowsVersion.Vista)) + trustData.ProvFlags = WtProvFlags.CacheOnlyUrlRetrieval; + + using (MemoryAlloc mem = new MemoryAlloc(wci.Size)) + { + Marshal.StructureToPtr(wci, mem, false); + + try + { + trustData.UnionData = mem; + + uint winTrustResult = Win32.WinVerifyTrust(IntPtr.Zero, DriverActionVerify, ref trustData); + + result = StatusToVerifyResult(winTrustResult); + } + finally + { + Win32.CryptCATAdminReleaseCatalogContext(catAdmin, catInfo, 0); + Win32.CryptCATAdminReleaseContext(catAdmin, 0); + Marshal.DestroyStructure(mem, typeof(WintrustCatalogInfo)); + } + } + } + } + + return result; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Debugging/DebugBuffer.cs b/branches/ph-plugins/ProcessHacker.Native/Debugging/DebugBuffer.cs new file mode 100644 index 000000000..31eb0b8bd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Debugging/DebugBuffer.cs @@ -0,0 +1,268 @@ +/* + * Process Hacker - + * run-time library debug buffer + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Debugging +{ + public delegate bool DebugEnumHeapsDelegate(HeapInformation heapInfo); + public delegate bool DebugEnumLocksDelegate(LockInformation lockInfo); + public delegate bool DebugEnumModulesDelegate(ModuleInformation moduleInfo); + + /// + /// Represents a debug buffer managed by the run-time library. + /// + public sealed class DebugBuffer : BaseObject + { + private IntPtr _buffer; + + /// + /// Creates a new debug buffer. + /// + public DebugBuffer() + { + _buffer = Win32.RtlCreateQueryDebugBuffer(0, true); + + if (_buffer == IntPtr.Zero) + { + this.DisableOwnership(false); + throw new WindowsException(NtStatus.Unsuccessful); + } + } + + protected override void DisposeObject(bool disposing) + { + Win32.RtlDestroyQueryDebugBuffer(_buffer); + } + + /// + /// Enumerates heap information. + /// + /// The callback for the enumeration. + public void EnumHeaps(DebugEnumHeapsDelegate callback) + { + var debugInfo = this.GetDebugInformation(); + + if (debugInfo.Heaps == IntPtr.Zero) + throw new InvalidOperationException("Heap information does not exist."); + + MemoryRegion heapInfo = new MemoryRegion(debugInfo.Heaps); + var heaps = heapInfo.ReadStruct(); + + for (int i = 0; i < heaps.NumberOfHeaps; i++) + { + var heap = heapInfo.ReadStruct(RtlProcessHeaps.HeapsOffset, i); + + if (!callback(new HeapInformation(heap))) + break; + } + } + + /// + /// Enumerates lock information. + /// + /// The callback for the enumeration. + public void EnumLocks(DebugEnumLocksDelegate callback) + { + var debugInfo = this.GetDebugInformation(); + + if (debugInfo.Locks == IntPtr.Zero) + throw new InvalidOperationException("Lock information does not exist."); + + MemoryRegion locksInfo = new MemoryRegion(debugInfo.Locks); + var locks = locksInfo.ReadStruct(); + + for (int i = 0; i < locks.NumberOfLocks; i++) + { + var lock_ = locksInfo.ReadStruct(sizeof(int), i); + + if (!callback(new LockInformation(lock_))) + break; + } + } + + /// + /// Enumerates module information. + /// + /// The callback for the enumeration. + public void EnumModules(DebugEnumModulesDelegate callback) + { + var debugInfo = this.GetDebugInformation(); + + if (debugInfo.Modules == IntPtr.Zero) + throw new InvalidOperationException("Module information does not exist."); + + MemoryRegion modulesInfo = new MemoryRegion(debugInfo.Modules); + var modules = modulesInfo.ReadStruct(); + + for (int i = 0; i < modules.NumberOfModules; i++) + { + var module = modulesInfo.ReadStruct(RtlProcessModules.ModulesOffset, i); + + if (!callback(new ModuleInformation(module))) + break; + } + } + + /// + /// Reads the debug information structure from the buffer. + /// + /// A RtlDebugInformation structure. + private RtlDebugInformation GetDebugInformation() + { + MemoryRegion data = new MemoryRegion(_buffer); + + return data.ReadStruct(); + } + + /// + /// Gets heap information. + /// + /// An array of heap information objects. + public HeapInformation[] GetHeaps() + { + List heaps = new List(); + + this.EnumHeaps((heap) => + { + heaps.Add(heap); + return true; + }); + + return heaps.ToArray(); + } + + /// + /// Gets lock information. + /// + /// An array of lock information objects. + public LockInformation[] GetLocks() + { + List locks = new List(); + + this.EnumLocks((lock_) => + { + locks.Add(lock_); + return true; + }); + + return locks.ToArray(); + } + + /// + /// Gets module information. + /// + /// An array of module information objects. + public ModuleInformation[] GetModules() + { + List modules = new List(); + + this.EnumModules((module) => + { + modules.Add(module); + return true; + }); + + return modules.ToArray(); + } + + /// + /// Queries debug information for the current process. + /// + /// The information to query. + public void Query(RtlQueryProcessDebugFlags flags) + { + this.Query(ProcessHandle.GetCurrentId(), flags); + } + + /// + /// Queries debug information for the specified process. + /// + /// The PID of the process to query. + /// The information to query. + public void Query(int pid, RtlQueryProcessDebugFlags flags) + { + NtStatus status; + + if ((status = Win32.RtlQueryProcessDebugInformation( + pid.ToIntPtr(), + flags, + _buffer + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Queries back trace information for the current process. + /// + public void QueryBackTraces() + { + NtStatus status; + + if ((status = Win32.RtlQueryProcessBackTraceInformation(_buffer)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Queries heap information for the current process. + /// + public void QueryHeaps() + { + NtStatus status; + + if ((status = Win32.RtlQueryProcessHeapInformation(_buffer)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Queries lock information for the current process. + /// + public void QueryLocks() + { + NtStatus status; + + if ((status = Win32.RtlQueryProcessLockInformation(_buffer)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + //public void QueryModules() + //{ + // this.QueryModules(null, RtlQueryProcessDebugFlags.Modules); + //} + + //public void QueryModules(ProcessHandle processHandle, RtlQueryProcessDebugFlags flags) + //{ + // NtStatus status; + + // if ((status = Win32.RtlQueryProcessModuleInformation( + // processHandle ?? IntPtr.Zero, + // flags, + // _buffer + // )) >= NtStatus.Error) + // Win32.ThrowLastError(status); + //} + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Debugging/HeapInformation.cs b/branches/ph-plugins/ProcessHacker.Native/Debugging/HeapInformation.cs new file mode 100644 index 000000000..2cd9dd0b3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Debugging/HeapInformation.cs @@ -0,0 +1,63 @@ +/* + * Process Hacker - + * heap information + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Debugging +{ + public class HeapInformation + { + internal HeapInformation(RtlHeapInformation heapInfo) + { + this.Address = heapInfo.BaseAddress; + this.BytesAllocated = heapInfo.BytesAllocated.ToInt64(); + this.BytesCommitted = heapInfo.BytesCommitted.ToInt64(); + this.TagCount = heapInfo.NumberOfTags; + this.EntryCount = heapInfo.NumberOfEntries; + this.PseudoTagCount = heapInfo.NumberOfPseudoTags; + } + + public HeapInformation( + IntPtr address, + long bytesAllocated, + long bytesCommitted, + int tagCount, + int entryCount, + int pseudoTagCount) + { + this.Address = address; + this.BytesAllocated = bytesAllocated; + this.BytesCommitted = bytesCommitted; + this.TagCount = tagCount; + this.EntryCount = entryCount; + this.PseudoTagCount = pseudoTagCount; + } + + public IntPtr Address { get; private set; } + public long BytesAllocated { get; private set; } + public long BytesCommitted { get; private set; } + public int TagCount { get; private set; } + public int EntryCount { get; private set; } + public int PseudoTagCount { get; private set; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Debugging/LockInformation.cs b/branches/ph-plugins/ProcessHacker.Native/Debugging/LockInformation.cs new file mode 100644 index 000000000..7ffc182c7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Debugging/LockInformation.cs @@ -0,0 +1,57 @@ +/* + * Process Hacker - + * lock information + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Debugging +{ + public class LockInformation + { + internal LockInformation(RtlProcessLockInformation lockInfo) + { + this.Address = lockInfo.Address; + this.Type = lockInfo.Type; + this.OwningThreadId = lockInfo.OwningThread.ToInt32(); + this.LockCount = lockInfo.LockCount; + this.ContentionCount = lockInfo.ContentionCount; + this.EntryCount = lockInfo.EntryCount; + + this.RecursionCount = lockInfo.RecursionCount; + + this.SharedWaiters = lockInfo.NumberOfWaitingShared; + this.ExclusiveWaiters = lockInfo.NumberOfWaitingExclusive; + } + + public IntPtr Address { get; private set; } + public RtlLockType Type { get; private set; } + public int OwningThreadId { get; private set; } + public int LockCount { get; private set; } + public int ContentionCount { get; private set; } + public int EntryCount { get; private set; } + + public int RecursionCount { get; private set; } + + public int SharedWaiters { get; private set; } + public int ExclusiveWaiters { get; private set; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Debugging/ModuleInformation.cs b/branches/ph-plugins/ProcessHacker.Native/Debugging/ModuleInformation.cs new file mode 100644 index 000000000..0beb03f84 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Debugging/ModuleInformation.cs @@ -0,0 +1,54 @@ +/* + * Process Hacker - + * module information + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Debugging +{ + public class ModuleInformation : ILoadedModule + { + internal ModuleInformation(RtlProcessModuleInformation moduleInfo) + { + this.BaseAddress = moduleInfo.ImageBase; + this.Size = moduleInfo.ImageSize; + this.Flags = moduleInfo.Flags; + this.LoadCount = moduleInfo.LoadCount; + + int nullIndex = Array.IndexOf(moduleInfo.FullPathName, '\0'); + + if (nullIndex != -1) + this.FileName = new string(moduleInfo.FullPathName, 0, nullIndex); + else + this.FileName = new string(moduleInfo.FullPathName); + + this.BaseName = this.FileName.Substring(moduleInfo.OffsetToFileName); + } + + public IntPtr BaseAddress { get; private set; } + public int Size { get; private set; } + public LdrpDataTableEntryFlags Flags { get; private set; } + public ushort LoadCount { get; private set; } + public string BaseName { get; private set; } + public string FileName { get; private set; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/FileUtils.cs b/branches/ph-plugins/ProcessHacker.Native/FileUtils.cs new file mode 100644 index 000000000..e142d8f99 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/FileUtils.cs @@ -0,0 +1,161 @@ +/* + * Process Hacker - + * file-related utility functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Provides utility methods for managing files. + /// + public static class FileUtils + { + static FileUtils() + { + RefreshFileNamePrefixes(); + } + + /// + /// Used to resolve device prefixes (\Device\Harddisk1) into DOS drive names. + /// + private static Dictionary _fileNamePrefixes = new Dictionary(); + + public static Icon GetFileIcon(string fileName) + { + return GetFileIcon(fileName, false); + } + + public static Icon GetFileIcon(string fileName, bool large) + { + ShFileInfo shinfo = new ShFileInfo(); + + if (string.IsNullOrEmpty(fileName)) + throw new Exception("File name cannot be empty."); + + try + { + if (Win32.SHGetFileInfo(fileName, 0, out shinfo, + (uint)Marshal.SizeOf(shinfo), + Win32.ShgFiIcon | + (large ? Win32.ShgFiLargeIcon : Win32.ShgFiSmallIcon)) == 0) + { + return null; + } + else + { + return Icon.FromHandle(shinfo.hIcon); + } + } + catch + { + return null; + } + } + + public static string GetFileName(string fileName) + { + return GetFileName(fileName, false); + } + + public static string GetFileName(string fileName, bool canonicalize) + { + bool alreadyCanonicalized = false; + + // If the path starts with "\SystemRoot", we can replace it with C:\ (or whatever it is). + if (fileName.ToLower().StartsWith("\\systemroot")) + { + fileName = System.IO.Path.GetFullPath(Environment.SystemDirectory + "\\.." + fileName.Substring(11)); + alreadyCanonicalized = true; + } + // If the path starts with "\??\", we can remove it and we will have the path. + else if (fileName.StartsWith("\\??\\")) + { + fileName = fileName.Substring(4); + } + + // If the path still starts with a backslash, we probably need to + // resolve any native object name to a DOS drive letter. + if (fileName.StartsWith("\\")) + { + var prefixes = _fileNamePrefixes; + + foreach (var pair in prefixes) + { + if (fileName.StartsWith(pair.Key + "\\")) + { + fileName = pair.Value + "\\" + fileName.Substring(pair.Key.Length + 1); + break; + } + else if (fileName == pair.Key) + { + fileName = pair.Value; + break; + } + } + } + + if (canonicalize && !alreadyCanonicalized) + fileName = System.IO.Path.GetFullPath(fileName); + + return fileName; + } + + public static void RefreshFileNamePrefixes() + { + // Just create a new dictionary to avoid having to lock the existing one. + var newPrefixes = new Dictionary(); + + for (char c = 'A'; c <= 'Z'; c++) + { + using (var data = new MemoryAlloc(1024)) + { + int length; + + if ((length = Win32.QueryDosDevice(c.ToString() + ":", data, data.Size / 2)) > 2) + { + newPrefixes.Add(data.ReadUnicodeString(0, length - 2), c.ToString() + ":"); + } + } + } + + _fileNamePrefixes = newPrefixes; + } + + public static void ShowProperties(string fileName) + { + var info = new ShellExecuteInfo(); + + info.cbSize = Marshal.SizeOf(info); + info.lpFile = fileName; + info.nShow = ShowWindowType.Show; + info.fMask = Win32.SeeMaskInvokeIdList; + info.lpVerb = "properties"; + + Win32.ShellExecuteEx(ref info); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/ILoadedModule.cs b/branches/ph-plugins/ProcessHacker.Native/ILoadedModule.cs new file mode 100644 index 000000000..5dd450ca2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/ILoadedModule.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + public interface ILoadedModule + { + IntPtr BaseAddress { get; } + int Size { get; } + LdrpDataTableEntryFlags Flags { get; } + string BaseName { get; } + string FileName { get; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Image/ImageDirectoryEntry.cs b/branches/ph-plugins/ProcessHacker.Native/Image/ImageDirectoryEntry.cs new file mode 100644 index 000000000..896327cff --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Image/ImageDirectoryEntry.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Image +{ + public enum ImageDataEntry : int + { + Export = 0, + Import = 1, + Resource = 2, + Exception = 3, + Security = 4, + BaseRelocation = 5, + Debug = 6, + Copyright = 7, + Architecture = 7, + GlobalPtr = 8, + Tls = 9, + LoadConfig = 10, + BoundImport = 11, + Iat = 12, + DelayImport = 13, + ComDescriptor = 14 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Image/ImageExports.cs b/branches/ph-plugins/ProcessHacker.Native/Image/ImageExports.cs new file mode 100644 index 000000000..8cb4349f0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Image/ImageExports.cs @@ -0,0 +1,171 @@ +/* + * Process Hacker - + * image exports reader + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Image +{ + public unsafe sealed class ImageExports + { + public delegate bool EnumEntriesDelegate(ImageExportEntry entry); + + private MappedImage _mappedImage; + private ImageDataDirectory* _dataDirectory; + private ImageExportDirectory* _exportDirectory; + private int* _addressTable; + private int* _namePointerTable; + private short* _ordinalTable; + + internal ImageExports(MappedImage mappedImage) + { + _mappedImage = mappedImage; + _dataDirectory = mappedImage.GetDataEntry(ImageDataEntry.Export); + _exportDirectory = mappedImage.GetExportDirectory(); + + if (_exportDirectory != null) + { + _addressTable = (int*)mappedImage.RvaToVa(_exportDirectory->AddressOfFunctions); + _namePointerTable = (int*)mappedImage.RvaToVa(_exportDirectory->AddressOfNames); + _ordinalTable = (short*)mappedImage.RvaToVa(_exportDirectory->AddressOfNameOrdinals); + } + } + + public int Count + { + get + { + if (_exportDirectory != null) + return _exportDirectory->NumberOfFunctions; + else + return 0; + } + } + + public ImageExportEntry GetEntry(int index) + { + if (_exportDirectory == null || _namePointerTable == null || _ordinalTable == null) + return ImageExportEntry.Empty; + if (index >= _exportDirectory->NumberOfFunctions) + return ImageExportEntry.Empty; + + ImageExportEntry entry = new ImageExportEntry(); + + entry.Ordinal = (short)(_ordinalTable[index] + _exportDirectory->Base); + + if (index < _exportDirectory->NumberOfNames) + entry.Name = new string((sbyte*)_mappedImage.RvaToVa(_namePointerTable[index])); + + return entry; + } + + public ImageExportFunction GetFunction(string name) + { + if (_exportDirectory == null || _namePointerTable == null || _ordinalTable == null) + return ImageExportFunction.Empty; + + int index; + + index = this.LookupName(name); + + if (index == -1) + return ImageExportFunction.Empty; + + return this.GetFunction((short)(_ordinalTable[index] + _exportDirectory->Base)); + } + + public ImageExportFunction GetFunction(short ordinal) + { + if (_exportDirectory == null || _addressTable == null) + return ImageExportFunction.Empty; + if (ordinal - _exportDirectory->Base >= _exportDirectory->NumberOfFunctions) + return ImageExportFunction.Empty; + + int rva = _addressTable[ordinal - _exportDirectory->Base]; + + if ( + rva >= _dataDirectory->VirtualAddress && + rva < _dataDirectory->VirtualAddress + _dataDirectory->Size + ) + { + // This is a forwarder RVA. + return new ImageExportFunction() { ForwardedName = new string((sbyte*)_mappedImage.RvaToVa(rva)) }; + } + else + { + // This is a function RVA. + return new ImageExportFunction() { Function = (IntPtr)_mappedImage.RvaToVa(rva) }; + } + } + + private int LookupName(string name) + { + int low = 0; + int high = _exportDirectory->NumberOfNames - 1; + + // Do a binary search. + while (low <= high) + { + int i; + string n; + + i = (low + high) / 2; + n = new string((sbyte*)_mappedImage.RvaToVa(_namePointerTable[i])); + + if (name == n) + { + return i; + } + else if (name.CompareTo(n) > 0) + { + low = i + 1; + } + else + { + high = i - 1; + } + } + + return -1; + } + } + + public struct ImageExportEntry + { + public static readonly ImageExportEntry Empty = new ImageExportEntry(); + + public string Name; + public short Ordinal; + } + + public struct ImageExportFunction + { + public static readonly ImageExportFunction Empty = new ImageExportFunction(); + + public IntPtr Function; + public string ForwardedName; + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Image/ImageImports.cs b/branches/ph-plugins/ProcessHacker.Native/Image/ImageImports.cs new file mode 100644 index 000000000..db466a7fb --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Image/ImageImports.cs @@ -0,0 +1,200 @@ +/* + * Process Hacker - + * image imports reader + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Image +{ + public unsafe sealed class ImageImports + { + public delegate bool EnumEntriesDelegate(ImageExportEntry entry); + + private MappedImage _mappedImage; + private int _count; + private ImageImportDescriptor* _descriptorTable; + private ImageImportDll[] _dlls; + + internal ImageImports(MappedImage mappedImage) + { + _mappedImage = mappedImage; + _descriptorTable = mappedImage.GetImportDirectory(); + + // Do a quick scan. + if (_descriptorTable != null) + { + int i = 0; + + while (_descriptorTable[i].OriginalFirstThunk != 0 || _descriptorTable[i].FirstThunk != 0) + i++; + + _count = i; + _dlls = new ImageImportDll[i]; + } + } + + public ImageImportDll this[int index] + { + get { return this.GetDll(index); } + } + + public int Count + { + get { return _count; } + } + + public ImageImportDll GetDll(int index) + { + if (_descriptorTable == null) + return null; + + if (index < _count) + { + if (_dlls[index] == null) + _dlls[index] = new ImageImportDll(_mappedImage, &_descriptorTable[index]); + + return _dlls[index]; + } + else + { + return null; + } + } + } + + public unsafe sealed class ImageImportDll + { + private MappedImage _mappedImage; + private ImageImportDescriptor* _descriptor; + private string _name; + private void* _lookupTable; + private int _count; + + internal ImageImportDll(MappedImage mappedImage, ImageImportDescriptor* descriptor) + { + _mappedImage = mappedImage; + _descriptor = descriptor; + + if (_descriptor->OriginalFirstThunk != 0) + _lookupTable = _mappedImage.RvaToVa(_descriptor->OriginalFirstThunk); + else + _lookupTable = _mappedImage.RvaToVa(_descriptor->FirstThunk); + + // Do a quick scan. + if (_lookupTable != null) + { + int i = 0; + + if (_mappedImage.Magic == Win32.Pe32Magic) + { + while (((int*)_lookupTable)[i] != 0) + i++; + } + else if (_mappedImage.Magic == Win32.Pe32PlusMagic) + { + while (((long*)_lookupTable)[i] != 0) + i++; + } + + _count = i; + } + } + + public ImageImportEntry this[int index] + { + get { return this.GetEntry(index); } + } + + public int Count + { + get { return _count; } + } + + public string Name + { + get + { + if (_name == null) + _name = new string((sbyte*)_mappedImage.RvaToVa(_descriptor->Name)); + + return _name; + } + } + + public ImageImportEntry GetEntry(int index) + { + if (index >= _count) + return ImageImportEntry.Empty; + + if (_mappedImage.Magic == Win32.Pe32Magic) + { + int entry = ((int*)_lookupTable)[index]; + + // Is this entry using an ordinal? + if ((entry & 0x80000000) != 0) + { + return new ImageImportEntry() { Ordinal = (short)(entry & 0xffff) }; + } + else + { + ImageImportByName* nameEntry = (ImageImportByName*)_mappedImage.RvaToVa(entry); + + return new ImageImportEntry() + { + NameHint = nameEntry->Hint, + Name = new string((sbyte*)&nameEntry->Name) + }; + } + } + else if (_mappedImage.Magic == Win32.Pe32PlusMagic) + { + long entry = ((long*)_lookupTable)[index]; + + // Is this entry using an ordinal? + if (((ulong)entry & 0x8000000000000000) != 0) + { + return new ImageImportEntry() { Ordinal = (short)(entry & 0xffff) }; + } + else + { + ImageImportByName* nameEntry = (ImageImportByName*)_mappedImage.RvaToVa((int)(entry & 0xffffffff)); + + return new ImageImportEntry() + { + NameHint = nameEntry->Hint, + Name = new string((sbyte*)&nameEntry->Name) + }; + } + } + + return ImageImportEntry.Empty; + } + } + + public struct ImageImportEntry + { + public static readonly ImageImportEntry Empty; + + public short Ordinal; + public short NameHint; + public string Name; + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Image/MappedImage.cs b/branches/ph-plugins/ProcessHacker.Native/Image/MappedImage.cs new file mode 100644 index 000000000..f9ed43ac0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Image/MappedImage.cs @@ -0,0 +1,314 @@ +/* + * Process Hacker - + * image mapper and reader + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Image +{ + public unsafe sealed class MappedImage : BaseObject + { + private SectionView _view; + private int _size; + private void* _memory; + private ImageNtHeaders* _ntHeaders; + private ImageSectionHeader* _sections; + private short _magic; + + private ImageExports _exports; + private ImageImports _imports; + + public MappedImage(string fileName) + : this(fileName, true) + { } + + public MappedImage(string fileName, bool readOnly) + { + using (var fhandle = FileHandle.CreateWin32( + fileName, + readOnly ? (FileAccess.Execute | FileAccess.ReadAttributes | FileAccess.ReadData) : + (FileAccess.AppendData | FileAccess.Execute | FileAccess.ReadAttributes | FileAccess.ReadData | FileAccess.WriteAttributes | FileAccess.WriteData), + FileShareMode.Read, + FileCreationDispositionWin32.OpenExisting + )) + this.MapAndLoad(fhandle, readOnly); + } + + public MappedImage(FileHandle fileHandle, bool readOnly) + { + this.MapAndLoad(fileHandle, readOnly); + } + + protected override void DisposeObject(bool disposing) + { + if (_view != null) + _view.Dispose(disposing); + } + + public ImageExports Exports + { + get + { + if (_exports == null) + _exports = new ImageExports(this); + + return _exports; + } + } + + public ImageImports Imports + { + get + { + if (_imports == null) + _imports = new ImageImports(this); + + return _imports; + } + } + + public short Magic + { + get { return _magic; } + } + + public void* Memory + { + get { return _memory; } + } + + public int NumberOfDataEntries + { + get + { + if (_magic == Win32.Pe32Magic) + return this.GetOptionalHeader()->NumberOfRvaAndSizes; + else if (_magic == Win32.Pe32PlusMagic) + return this.GetOptionalHeader64()->NumberOfRvaAndSizes; + else + return 0; + } + } + + public int NumberOfSections + { + get { return _ntHeaders->FileHeader.NumberOfSections; } + } + + public ImageNtHeaders* NtHeaders + { + get { return _ntHeaders; } + } + + public ImageSectionHeader* Sections + { + get { return _sections; } + } + + public int Size + { + get { return _size; } + } + + public int GetChecksum() + { + int oldChecksum; + + return this.GetChecksum(out oldChecksum); + } + + public int GetChecksum(out int oldChecksum) + { + int checksum; + + if (Win32.CheckSumMappedFile(_view, _size, out oldChecksum, out checksum) == IntPtr.Zero) + Win32.ThrowLastError(); + + return checksum; + } + + public ImageDataDirectory* GetDataEntry(ImageDataEntry entry) + { + if (_magic == Win32.Pe32Magic) + { + if ((int)entry >= _ntHeaders->OptionalHeader.NumberOfRvaAndSizes) + return null; + + return &(&_ntHeaders->OptionalHeader.DataDirectory)[(int)entry]; + } + else if (_magic == Win32.Pe32PlusMagic) + { + if ((int)entry >= this.GetOptionalHeader64()->NumberOfRvaAndSizes) + return null; + + return &(&this.GetOptionalHeader64()->DataDirectory)[(int)entry]; + } + else + { + return null; + } + } + + public ImageExportDirectory* GetExportDirectory() + { + ImageDataDirectory* dataEntry; + + dataEntry = this.GetDataEntry(ImageDataEntry.Export); + + return (ImageExportDirectory*)this.RvaToVa(dataEntry->VirtualAddress); + } + + public ImageImportDescriptor* GetImportDirectory() + { + ImageDataDirectory* dataEntry; + + dataEntry = this.GetDataEntry(ImageDataEntry.Import); + + return (ImageImportDescriptor*)this.RvaToVa(dataEntry->VirtualAddress); + } + + private void* GetLoadConfig(short magic) + { + ImageDataDirectory* dataEntry; + + if (_magic != magic) + return null; + + dataEntry = this.GetDataEntry(ImageDataEntry.LoadConfig); + + if (dataEntry == null) + return null; + + return this.RvaToVa(dataEntry->VirtualAddress); + } + + public ImageLoadConfigDirectory* GetLoadConfig() + { + return (ImageLoadConfigDirectory*)this.GetLoadConfig(Win32.Pe32Magic); + } + + public ImageLoadConfigDirectory64* GetLoadConfig64() + { + return (ImageLoadConfigDirectory64*)this.GetLoadConfig(Win32.Pe32PlusMagic); + } + + private ImageNtHeaders* GetNtHeaders() + { + int offset; + ImageNtHeaders* ntHeaders; + + offset = *((int*)((byte*)_memory + 0x3c)); + + if (offset == 0) + throw new Exception("Invalid NT headers offset."); + if (offset >= 0x10000000 || offset >= _size) + throw new Exception("The NT headers offset is too large."); + + ntHeaders = (ImageNtHeaders*)((byte*)_memory + offset); + + return ntHeaders; + } + + private void* GetOptionalHeader(short magic) + { + if (_magic != magic) + return null; + + return &_ntHeaders->OptionalHeader; + } + + public ImageOptionalHeader* GetOptionalHeader() + { + return (ImageOptionalHeader*)this.GetOptionalHeader(Win32.Pe32Magic); + } + + public ImageOptionalHeader64* GetOptionalHeader64() + { + return (ImageOptionalHeader64*)this.GetOptionalHeader(Win32.Pe32PlusMagic); + } + + public string GetSectionName(ImageSectionHeader* section) + { + return new string((sbyte*)section->Name, 0, 8).TrimEnd('\0'); + } + + private void MapAndLoad(FileHandle fileHandle, bool readOnly) + { + using (Section section = new Section( + fileHandle, + false, + readOnly ? MemoryProtection.ExecuteRead : MemoryProtection.ExecuteReadWrite + )) + { + _size = (int)fileHandle.GetSize(); + _view = section.MapView(_size); + _memory = _view; + + byte* start = (byte*)_memory; + + if (start[0] != 'M' || start[1] != 'Z') + throw new Exception("The file is not a valid executable image."); + + _ntHeaders = this.GetNtHeaders(); + _sections = (ImageSectionHeader*)((byte*)&_ntHeaders->OptionalHeader + _ntHeaders->FileHeader.SizeOfOptionalHeader); + _magic = _ntHeaders->OptionalHeader.Magic; + + if (_magic != Win32.Pe32Magic && _magic != Win32.Pe32PlusMagic) + throw new Exception("The file is not a PE32 or PE32+ image."); + } + } + + public ImageSectionHeader* RvaToSection(int rva) + { + if (_ntHeaders->FileHeader.NumberOfSections == 0) + return null; + + for (int i = 0; i < _ntHeaders->FileHeader.NumberOfSections; i++) + { + if ( + rva >= _sections[i].VirtualAddress && + rva < (_sections[i].VirtualAddress + _sections[i].SizeOfRawData) + ) + { + return &_sections[i]; + } + } + + return null; + } + + public void* RvaToVa(int rva) + { + ImageSectionHeader* section; + + section = this.RvaToSection(rva); + + if (section == null) + return null; + + return (byte*)_memory + section->PointerToRawData - section->VirtualAddress + rva; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/ImpersonationContext.cs b/branches/ph-plugins/ProcessHacker.Native/ImpersonationContext.cs new file mode 100644 index 000000000..a0d3a0d33 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/ImpersonationContext.cs @@ -0,0 +1,26 @@ +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native +{ + public class ImpersonationContext : IDisposable + { + private bool _disposed = false; + + public ImpersonationContext(TokenHandle token) + { + if (!Win32.ImpersonateLoggedOnUser(token)) + Win32.ThrowLastError(); + } + + public void Dispose() + { + if (!_disposed) + { + Win32.RevertToSelf(); + _disposed = true; + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/IntPtrExtensions.cs b/branches/ph-plugins/ProcessHacker.Native/IntPtrExtensions.cs new file mode 100644 index 000000000..f23e9f93a --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/IntPtrExtensions.cs @@ -0,0 +1,246 @@ +/* + * Process Hacker - + * IntPtr extension functions + * + * Copyright (C) 2009 wj32 + * Copyright (C) 2009 Flavio Erlich + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native +{ + public static class IntPtrExtensions + { + public static IntPtr And(this IntPtr ptr, int value) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() & value); + else + return new IntPtr(ptr.ToInt64() & value); + } + + public static IntPtr And(this IntPtr ptr, IntPtr value) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() & value.ToInt32()); + else + return new IntPtr(ptr.ToInt64() & value.ToInt64()); + } + + public static int CompareTo(this IntPtr ptr, IntPtr ptr2) + { + if (ptr.ToUInt64() > ptr2.ToUInt64()) + return 1; + if (ptr.ToUInt64() < ptr2.ToUInt64()) + return -1; + return 0; + } + + public static int CompareTo(this IntPtr ptr, int ptr2) + { + return ptr.CompareTo((uint)ptr2); + } + + public static int CompareTo(this IntPtr ptr, uint ptr2) + { + if (ptr.ToUInt64() > ptr2) + return 1; + if (ptr.ToUInt64() < ptr2) + return -1; + return 0; + } + + public static IntPtr Decrement(this IntPtr ptr, IntPtr ptr2) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() - ptr2.ToInt32()); + else + return new IntPtr(ptr.ToInt64() - ptr2.ToInt64()); + } + + public static IntPtr Decrement(this IntPtr ptr, int value) + { + return Increment(ptr, -value); + } + + public static IntPtr Decrement(this IntPtr ptr, long value) + { + return Increment(ptr, -value); + } + + public static T ElementAt(this IntPtr ptr, int index) + { + var offset = Marshal.SizeOf(typeof(T)) * index; + var offsetPtr = ptr.Increment(offset); + return (T)Marshal.PtrToStructure(offsetPtr, typeof(T)); + } + + public static bool Equals(this IntPtr ptr, IntPtr ptr2) + { + return ptr == ptr2; + } + + public static bool Equals(this IntPtr ptr, int value) + { + return ptr.ToInt32() == value; + } + + public static bool Equals(this IntPtr ptr, uint value) + { + return ptr.ToUInt32() == value; + } + + public static bool Equals(this IntPtr ptr, long value) + { + return ptr.ToInt64() == value; + } + + public static bool Equals(this IntPtr ptr, ulong value) + { + return ptr.ToUInt64() == value; + } + + public static IntPtr Increment(this IntPtr ptr, int value) + { + unchecked + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() + value); + else + return new IntPtr(ptr.ToInt64() + value); + } + } + + public static IntPtr Increment(this IntPtr ptr, long value) + { + unchecked + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr((int)(ptr.ToInt32() + value)); + else + return new IntPtr(ptr.ToInt64() + value); + } + } + + public static IntPtr Increment(this IntPtr ptr, IntPtr ptr2) + { + unchecked + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() + ptr2.ToInt32()); + else + return new IntPtr(ptr.ToInt64() + ptr2.ToInt64()); + } + } + + public static IntPtr Increment(this IntPtr ptr) + { + return ptr.Increment(Marshal.SizeOf(typeof(T))); + } + + public static bool IsGreaterThanOrEqualTo(this IntPtr ptr, IntPtr ptr2) + { + return ptr.CompareTo(ptr2) >= 0; + } + + public static bool IsLessThanOrEqualTo(this IntPtr ptr, IntPtr ptr2) + { + return ptr.CompareTo(ptr2) <= 0; + } + + public static IntPtr Not(this IntPtr ptr) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(~ptr.ToInt32()); + else + return new IntPtr(~ptr.ToInt64()); + } + + public static IntPtr Or(this IntPtr ptr, IntPtr value) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() | value.ToInt32()); + else + return new IntPtr(ptr.ToInt64() | value.ToInt64()); + } + + public static uint ToUInt32(this IntPtr ptr) + { + // Avoid sign-extending the pointer - we want it zero-extended. + unsafe + { + void* voidPtr = (void*)ptr; + + return (uint)voidPtr; + } + } + + public static ulong ToUInt64(this IntPtr ptr) + { + // Avoid sign-extending the pointer - we want it zero-extended. + unsafe + { + void* voidPtr = (void*)ptr; + + return (ulong)voidPtr; + } + } + + public static IntPtr ToIntPtr(this int value) + { + return new IntPtr(value); + } + + public static IntPtr ToIntPtr(this uint value) + { + unchecked + { + return new IntPtr((int)value); + } + } + + public static IntPtr ToIntPtr(this long value) + { + unchecked + { + if (value > 0 && value <= 0xffffffff) + return new IntPtr((int)value); + } + + return new IntPtr(value); + } + + public static IntPtr ToIntPtr(this ulong value) + { + unchecked + { + return ((long)value).ToIntPtr(); + } + } + + public static IntPtr Xor(this IntPtr ptr, IntPtr value) + { + if (IntPtr.Size == sizeof(Int32)) + return new IntPtr(ptr.ToInt32() ^ value.ToInt32()); + else + return new IntPtr(ptr.ToInt64() ^ value.ToInt64()); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Io/BeepDevice.cs b/branches/ph-plugins/ProcessHacker.Native/Io/BeepDevice.cs new file mode 100644 index 000000000..d120109ea --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Io/BeepDevice.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Io +{ + public static class BeepDevice + { + public struct BeepSetParameters + { + public int Frequency; + public int Duration; + } + + public const int BeepFrequencyMinimum = 0x25; + public const int BeepFrequencyMaximum = 0x7fff; + + public static readonly int IoCtlSet = Win32.CtlCode(DeviceType.Beep, 0, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + + public static void Beep(int frequency, int duration) + { + unsafe + { + BeepSetParameters p; + + p.Frequency = frequency; + p.Duration = duration; + + using (var fhandle = OpenBeep(FileAccess.GenericRead)) + fhandle.IoControl(IoCtlSet, &p, Marshal.SizeOf(typeof(BeepSetParameters)), null, 0); + } + } + + private static FileHandle OpenBeep(FileAccess access) + { + return new FileHandle( + Win32.BeepDeviceName, + FileShareMode.ReadWrite, + access + ); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Io/MountManager.cs b/branches/ph-plugins/ProcessHacker.Native/Io/MountManager.cs new file mode 100644 index 000000000..ed53ffd26 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Io/MountManager.cs @@ -0,0 +1,191 @@ +/* + * Process Hacker - + * mount point manager API + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Io +{ + public static class MountManager + { + // Input for IoCtlCreatePoint + public struct MountMgrCreatePointInput + { + public ushort SymbolicLinkNameOffset; + public ushort SymbolicLinkNameLength; + public ushort DeviceNameOffset; + public ushort DeviceNameLength; + } + + // Input for IoCtlDeletePoints, IoCtlQueryPoints and IoCtlDeletePointsDbOnly + public struct MountMgrMountPoint + { + public int SymbolicLinkNameOffset; + public ushort SymbolicLinkNameLength; + public int UniqueIdOffset; + public ushort UniqueIdLength; + public int DeviceNameOffset; + public ushort DeviceNameLength; + } + + // Output for IoCtlDeletePoints, IoCtlQueryPoints and IoCtlDeletePointsDbOnly + public struct MountMgrMountPoints + { + public static int MountPointsOffset = + Marshal.OffsetOf(typeof(MountMgrMountPoints), "MountPoints").ToInt32(); + + public int Size; + public int NumberOfMountPoints; + public MountMgrMountPoint MountPoints; + } + + // Input for IoCtlNextDriveLetter + public struct MountMgrDriveLetterTarget + { + public static int DeviceNameOffset = + Marshal.OffsetOf(typeof(MountMgrDriveLetterTarget), "DeviceName").ToInt32(); + + public ushort DeviceNameLength; + public short DeviceName; + } + + // Output for IoCtlNextDriveLetter + public struct MountMgrDriveLetterInformation + { + [MarshalAs(UnmanagedType.I1)] + public bool DriveLetterWasAssigned; + [MarshalAs(UnmanagedType.I1)] + public char CurrentDriveLetter; + } + + // Input for IoCtlVolumeMountPointCreated and IoCtlVolumeMountPointDeleted + public struct MountMgrVolumeMountPoint + { + public ushort SourceVolumeNameOffset; + public ushort SourceVolumeNameLength; + public ushort TargetVolumeNameOffset; + public ushort TargetVolumeNameLength; + } + + // Input, output for IoCtlChangeNotify + public struct MountMgrChangeNotifyInfo + { + public int EpicNumber; + } + + // Input for IoCtlKeepLinksWhenOffline, IoCtlVolumeArrivalNotification, + // IoCtlQueryDosVolumePath, IoCtlQueryDosVolumePaths + public struct MountMgrTargetName + { + public static int DeviceNameOffset = + Marshal.OffsetOf(typeof(MountMgrTargetName), "DeviceName").ToInt32(); + + public ushort DeviceNameLength; + public short DeviceName; + } + + // Output for IoCtlQueryDosVolumePath, IoCtlQueryDosVolumePaths + public struct MountMgrVolumePaths + { + public static int MultiSzOffset = + Marshal.OffsetOf(typeof(MountMgrVolumePaths), "MultiSz").ToInt32(); + + public int MultiSzLength; + public short MultiSz; + } + + // Output for IoCtlQueryDeviceName + public struct MountDevName + { + public static int NameOffset = + Marshal.OffsetOf(typeof(MountDevName), "Name").ToInt32(); + + public ushort NameLength; + public short Name; + } + + public static readonly int IoCtlCreatePoint = Win32.CtlCode(DeviceType.MountMgr, 0, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlDeletePoints = Win32.CtlCode(DeviceType.MountMgr, 1, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlQueryPoints = Win32.CtlCode(DeviceType.MountMgr, 2, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int IoCtlDeletePointsDbOnly = Win32.CtlCode(DeviceType.MountMgr, 3, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlNextDriveLetter = Win32.CtlCode(DeviceType.MountMgr, 4, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlAutoDlAssignments = Win32.CtlCode(DeviceType.MountMgr, 5, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlVolumeMountPointCreated = Win32.CtlCode(DeviceType.MountMgr, 6, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlVolumeMountPointDeleted = Win32.CtlCode(DeviceType.MountMgr, 7, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlChangeNotify = Win32.CtlCode(DeviceType.MountMgr, 8, DeviceControlMethod.Buffered, DeviceControlAccess.Read); + public static readonly int IoCtlKeepLinksWhenOffline = Win32.CtlCode(DeviceType.MountMgr, 9, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlCheckUnprocessedVolumes = Win32.CtlCode(DeviceType.MountMgr, 10, DeviceControlMethod.Buffered, DeviceControlAccess.Read); + public static readonly int IoCtlVolumeArrivalNotification = Win32.CtlCode(DeviceType.MountMgr, 11, DeviceControlMethod.Buffered, DeviceControlAccess.Read); + public static readonly int IoCtlQueryDosVolumePath = Win32.CtlCode(DeviceType.MountMgr, 12, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int IoCtlQueryDosVolumePaths = Win32.CtlCode(DeviceType.MountMgr, 13, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int IoCtlScrubRegistry = Win32.CtlCode(DeviceType.MountMgr, 14, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int IoCtlQueryAutoMount = Win32.CtlCode(DeviceType.MountMgr, 15, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int IoCtlSetAutoMount = Win32.CtlCode(DeviceType.MountMgr, 16, DeviceControlMethod.Buffered, DeviceControlAccess.Read | DeviceControlAccess.Write); + + public static readonly int IoCtlQueryDeviceName = Win32.CtlCode(DeviceType.MountMgrDevice, 2, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + + public static bool IsDriveLetterPath(string path) + { + if ( + path.Length == 14 && + path.StartsWith(@"\DosDevices\") && + path[12] >= 'A' && path[12] <= 'Z' && + path[13] == ':' + ) + return true; + else + return false; + } + + public static bool IsVolumePath(string path) + { + if ( + (path.Length == 48 || (path.Length == 49 && path[48] == '\\')) && + (path.StartsWith(@"\??\Volume") || path.StartsWith(@"\\?\Volume")) && + path[10] == '{' && + path[19] == '-' && + path[24] == '-' && + path[29] == '-' && + path[34] == '-' && + path[47] == '}' + ) + return true; + else + return false; + } + + private static FileHandle OpenMountManager(FileAccess access) + { + return new FileHandle( + Win32.MountMgrDeviceName, + FileShareMode.ReadWrite, + access + ); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Ipc/IpcCircularBuffer.cs b/branches/ph-plugins/ProcessHacker.Native/Ipc/IpcCircularBuffer.cs new file mode 100644 index 000000000..d8f935285 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ipc/IpcCircularBuffer.cs @@ -0,0 +1,223 @@ +/* + * Process Hacker - + * inter-process circular buffer + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Threading; + +namespace ProcessHacker.Native.Ipc +{ + public unsafe class IpcCircularBuffer + { + [StructLayout(LayoutKind.Sequential)] + private struct BufferHeader + { + public int BlockSize; + public int NumberOfBlocks; + + public long ReadSemaphoreId; + public long WriteSemaphoreId; + + public int ReadPosition; + public int WritePosition; + + public long Data; + } + + public static IpcCircularBuffer Create(string name, int blockSize, int numberOfBlocks) + { + Random r = new Random(); + long readSemaphoreId = ((long)r.Next() << 32) + r.Next(); + long writeSemaphoreId = ((long)r.Next() << 32) + r.Next(); + Section section; + + section = new Section(name, blockSize * numberOfBlocks, MemoryProtection.ReadWrite); + + using (var view = section.MapView(Marshal.SizeOf(typeof(BufferHeader)))) + { + BufferHeader header = new BufferHeader(); + + header.BlockSize = blockSize; + header.NumberOfBlocks = numberOfBlocks; + header.ReadSemaphoreId = readSemaphoreId; + header.WriteSemaphoreId = writeSemaphoreId; + header.ReadPosition = 0; + header.WritePosition = 0; + + view.WriteStruct(header); + } + + return new IpcCircularBuffer( + section, + name, + new Semaphore(name + "_" + readSemaphoreId.ToString("x"), 0, numberOfBlocks), + new Semaphore(name + "_" + writeSemaphoreId.ToString("x"), numberOfBlocks, numberOfBlocks) + ); + } + + public static IpcCircularBuffer Open(string name) + { + return new IpcCircularBuffer(new Section(name, SectionAccess.All), name, null, null); + } + + private Section _section; + private SectionView _sectionView; + private Semaphore _readSemaphore; + private Semaphore _writeSemaphore; + + private BufferHeader* _header; + private void* _data; + + private IpcCircularBuffer(Section section, string sectionName, Semaphore readSemaphore, Semaphore writeSemaphore) + { + BufferHeader header; + + _section = section; + + _sectionView = section.MapView(Marshal.SizeOf(typeof(BufferHeader))); + header = _sectionView.ReadStruct(); + _sectionView.Dispose(); + + if (readSemaphore == null || writeSemaphore == null) + { + _readSemaphore = new Semaphore(sectionName + "_" + header.ReadSemaphoreId.ToString("x")); + _writeSemaphore = new Semaphore(sectionName + "_" + header.WriteSemaphoreId.ToString("x")); + } + else + { + _readSemaphore = readSemaphore; + _writeSemaphore = writeSemaphore; + } + + _sectionView = _section.MapView(header.BlockSize * header.NumberOfBlocks); + _header = (BufferHeader*)_sectionView.Memory; + _data = &_header->Data; + } + + public T Read() + where T : struct + { + using (var data = this.Read()) + return data.ReadStruct(); + } + + public MemoryAlloc Read() + { + var data = new MemoryAlloc(_header->BlockSize); + + this.Read(data); + + return data; + } + + public void Read(MemoryRegion data) + { + this.Read((void*)data.Memory); + } + + public void Read(void* buffer) + { + int readPosition; + + // Wait for a block to read. + _readSemaphore.Wait(); + + // Get a read position while simultaneously incrementing it + // and wrapping it if necessary. + while (true) + { + readPosition = _header->ReadPosition; + + if (System.Threading.Interlocked.CompareExchange( + ref _header->ReadPosition, + (readPosition + 1) % _header->NumberOfBlocks, + readPosition + ) == readPosition) + break; + } + + // Copy the data across. + Win32.RtlMoveMemory( + new IntPtr(buffer), + (new IntPtr(_data)).Increment(readPosition * _header->BlockSize), + _header->BlockSize.ToIntPtr() + ); + + // Release the write semaphore to allow a writer to write one more block. + _writeSemaphore.Release(); + } + + public void Write(T s) + where T : struct + { + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(T)))) + { + data.WriteStruct(s); + this.Write((MemoryRegion)data); + } + } + + public void Write(MemoryRegion data) + { + this.Write(data, 0); + } + + public void Write(MemoryRegion data, int offset) + { + this.Write((void*)data.Memory.Increment(offset)); + } + + public void Write(void* buffer) + { + int writePosition; + + // Wait for an available write slot. + _writeSemaphore.Wait(); + + // Get a write position while simultaneously incrementing it + // and wrapping it if necessary. + while (true) + { + writePosition = _header->WritePosition; + + if (System.Threading.Interlocked.CompareExchange( + ref _header->WritePosition, + (writePosition + 1) % _header->NumberOfBlocks, + writePosition + ) == writePosition) + break; + } + + // Copy the data across. + Win32.RtlMoveMemory( + (new IntPtr(_data)).Increment(writePosition * _header->BlockSize), + new IntPtr(buffer), + _header->BlockSize.ToIntPtr() + ); + + // Release the read semaphore to allow a reader to read one more block. + _readSemaphore.Release(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/KProcessHacker.cs b/branches/ph-plugins/ProcessHacker.Native/KProcessHacker.cs new file mode 100644 index 000000000..ed4a0977b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/KProcessHacker.cs @@ -0,0 +1,1258 @@ +/* + * Process Hacker - + * KProcessHacker interfacing code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// The private field 'field' is assigned but its value is never used +#pragma warning disable 0414 + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + /// + /// Provides an interface to KProcessHacker. + /// + public sealed unsafe class KProcessHacker + { + private static KProcessHacker _instance; + + public static KProcessHacker Instance + { + get { return _instance; } + set { _instance = value; } + } + + /// + /// A control code used by KProcessHacker to represent a specific function. + /// + private enum Control : uint + { + ClientCloseHandle = 0, + SsQueryClientEntry, + Reserved1, + KphOpenProcess, + KphOpenThread, + KphOpenProcessToken, + GetProcessProtected, + SetProcessProtected, + KphTerminateProcess, + KphSuspendProcess, + KphResumeProcess, + KphReadVirtualMemory, + KphWriteVirtualMemory, + SetProcessToken, + GetThreadStartAddress, + SetHandleAttributes, + GetHandleObjectName, + KphOpenProcessJob, + KphGetContextThread, + KphSetContextThread, + KphGetThreadWin32Thread, + KphDuplicateObject, + ZwQueryObject, + KphGetProcessId, + KphGetThreadId, + KphTerminateThread, + GetFeatures, + KphSetHandleGrantedAccess, + KphAssignImpersonationToken, + ProtectAdd, + ProtectRemove, + ProtectQuery, + KphUnsafeReadVirtualMemory, + SetExecuteOptions, + KphQueryProcessHandles, + KphOpenThreadProcess, + KphCaptureStackBackTraceThread, + KphDangerousTerminateThread, + KphOpenDevice, + KphOpenDriver, + KphQueryInformationDriver, + KphOpenDirectoryObject, + SsRef, + SsUnref, + SsCreateClientEntry, + SsCreateRuleSetEntry, + SsRemoveRule, + SsAddProcessIdRule, + SsAddThreadIdRule, + SsAddPreviousModeRule, + SsAddNumberRule, + SsEnableClientEntry + } + + [Flags] + public enum KphFeatures : int + { + PsTerminateProcess = 0x1, + PspTerminateThreadByPointer = 0x2 + } + + private string _deviceName; + private FileHandle _fileHandle; + private uint _baseControlNumber; + private KphFeatures _features; + + /// + /// Creates a connection to KProcessHacker. + /// + public KProcessHacker() + : this("KProcessHacker") + { } + + /// + /// Creates a connection to KProcessHacker. + /// + /// The name of the KProcessHacker service and device. + public KProcessHacker(string deviceName) + : this(deviceName, Application.StartupPath + "\\kprocesshacker.sys") + { } + + /// + /// Creates a connection to KProcessHacker. + /// + /// The name of the KProcessHacker service and device. + /// The file name of the KProcessHacker driver. + public KProcessHacker(string deviceName, string fileName) + { + _deviceName = deviceName; + + if (IntPtr.Size != 4) + throw new NotSupportedException("KProcessHacker does not support 64-bit Windows."); + + try + { + _fileHandle = new FileHandle( + @"\Device\" + deviceName, + 0, + FileAccess.GenericRead | FileAccess.GenericWrite + ); + } + catch (WindowsException ex) + { + if ( + ex.Status == NtStatus.NoSuchDevice || + ex.Status == NtStatus.NoSuchFile || + ex.Status == NtStatus.ObjectNameNotFound + ) + { + // Attempt to load the driver, then try again. + ServiceHandle shandle; + + using (var scm = new ServiceManagerHandle(ScManagerAccess.CreateService)) + { + shandle = scm.CreateService( + deviceName, + deviceName, + ServiceType.KernelDriver, + fileName + ); + shandle.Start(); + } + + try + { + _fileHandle = new FileHandle( + @"\Device\" + deviceName, + 0, + FileAccess.GenericRead | FileAccess.GenericWrite + ); + } + finally + { + // The SCM will delete the service when it is stopped. + shandle.Delete(); + } + } + else + { + throw ex; + } + } + + _fileHandle.SetHandleFlags(Win32HandleFlags.ProtectFromClose, Win32HandleFlags.ProtectFromClose); + + byte[] bytes = _fileHandle.Read(4); + + fixed (byte* bytesPtr = bytes) + _baseControlNumber = *(uint*)bytesPtr; + + try + { + _features = this.GetFeatures(); + } + catch + { } + } + + public string DeviceName + { + get { return _deviceName; } + } + + public KphFeatures Features + { + get { return _features; } + } + + private int CtlCode(Control ctl) + { + return (int)(_baseControlNumber + ((uint)ctl * 4)); + } + + /// + /// Closes the connection to KProcessHacker. + /// + public void Close() + { + _fileHandle.SetHandleFlags(Win32HandleFlags.ProtectFromClose, 0); + _fileHandle.Dispose(); + } + + public void ClientCloseHandle(IntPtr handle) + { + byte* inData = stackalloc byte[4]; + + *(int*)inData = handle.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.ClientCloseHandle), inData, 4, null, 0); + } + + public KphFeatures GetFeatures() + { + byte* outData = stackalloc byte[4]; + + _fileHandle.IoControl(CtlCode(Control.GetFeatures), null, 0, outData, 4); + + return (KphFeatures)(*(int*)outData); + } + + public string GetHandleObjectName(ProcessHandle processHandle, IntPtr handle) + { + byte* inData = stackalloc byte[8]; + byte[] outData = new byte[2048]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = handle.ToInt32(); + + try + { + int len = _fileHandle.IoControl(CtlCode(Control.GetHandleObjectName), + inData, 8, outData); + + return UnicodeEncoding.Unicode.GetString(outData, 8, len - 8).TrimEnd('\0'); + } + catch + { } + + return null; + } + + public bool GetProcessProtected(int pid) + { + byte[] result = new byte[1]; + + _fileHandle.IoControl(CtlCode(Control.GetProcessProtected), + (byte*)&pid, 4, result); + + return result[0] != 0; + } + + public uint GetThreadStartAddress(ThreadHandle threadHandle) + { + byte* outData = stackalloc byte[4]; + int threadHandleInt = threadHandle; + + _fileHandle.IoControl(CtlCode(Control.GetThreadStartAddress), + (byte*)&threadHandleInt, 4, outData, 4); + + return *(uint*)outData; + } + + public void KphAssignImpersonationToken(ThreadHandle threadHandle, TokenHandle tokenHandle) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = threadHandle; + *(int*)(inData + 4) = tokenHandle; + + _fileHandle.IoControl(CtlCode(Control.KphAssignImpersonationToken), inData, 8, null, 0); + } + + public unsafe int KphCaptureStackBackTraceThread( + ThreadHandle threadHandle, + int framesToSkip, + int framesToCapture, + IntPtr[] backTrace, + out int backTraceHash + ) + { + byte* inData = stackalloc byte[6 * sizeof(int)]; + int capturedFramesLocal; + int backTraceHashLocal; + + if (framesToCapture > backTrace.Length) + throw new ArgumentOutOfRangeException("Back trace buffer is too small."); + + fixed (IntPtr* backTracePtr = backTrace) + { + *(int*)inData = threadHandle; + *(int*)(inData + 0x4) = framesToSkip; + *(int*)(inData + 0x8) = framesToCapture; + *(int*)(inData + 0xc) = (int)backTracePtr; + *(int*)(inData + 0x10) = (int)&capturedFramesLocal; + *(int*)(inData + 0x14) = (int)&backTraceHashLocal; + + _fileHandle.IoControl(CtlCode(Control.KphCaptureStackBackTraceThread), inData, 6 * sizeof(int), null, 0); + backTraceHash = backTraceHashLocal; + + return capturedFramesLocal; + } + } + + public void KphDangerousTerminateThread(ThreadHandle threadHandle, NtStatus exitStatus) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = threadHandle; + *(int*)(inData + 4) = (int)exitStatus; + + _fileHandle.IoControl(CtlCode(Control.KphDangerousTerminateThread), inData, 8, null, 0); + } + + public void KphDuplicateObject( + int sourceProcessHandle, + int sourceHandle, + int targetProcessHandle, + out int targetHandle, + int desiredAccess, + HandleFlags handleAttributes, + DuplicateOptions options + ) + { + int handle; + + KphDuplicateObject( + sourceProcessHandle, + sourceHandle, + targetProcessHandle, + (int)&handle, + desiredAccess, + handleAttributes, + options + ); + + targetHandle = handle; + } + + public void KphDuplicateObject( + int sourceProcessHandle, + int sourceHandle, + int targetProcessHandle, + int targetHandle, + int desiredAccess, + HandleFlags handleAttributes, + DuplicateOptions options + ) + { + byte[] data = new byte[7 * sizeof(int)]; + + fixed (byte* dataPtr = data) + { + *(int*)(dataPtr + 0x0) = sourceProcessHandle; + *(int*)(dataPtr + 0x4) = sourceHandle; + *(int*)(dataPtr + 0x8) = targetProcessHandle; + *(int*)(dataPtr + 0xc) = targetHandle; + *(int*)(dataPtr + 0x10) = desiredAccess; + *(int*)(dataPtr + 0x14) = (int)handleAttributes; + *(int*)(dataPtr + 0x18) = (int)options; + + _fileHandle.IoControl(CtlCode(Control.KphDuplicateObject), data, null); + } + } + + public void KphGetContextThread(ThreadHandle threadHandle, Context* context) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = threadHandle; + *(int*)(inData + 4) = (int)context; + + _fileHandle.IoControl(CtlCode(Control.KphGetContextThread), inData, 8, null, 0); + } + + public int KphGetProcessId(ProcessHandle processHandle, IntPtr handle) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = handle.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.KphGetProcessId), inData, 8, outData, 4); + + return *(int*)outData; + } + + public int KphGetThreadId(ProcessHandle processHandle, IntPtr handle, out int processId) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[8]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = handle.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.KphGetThreadId), inData, 8, outData, 8); + processId = *(int*)(outData + 4); + + return *(int*)outData; + } + + public int KphGetThreadWin32Thread(ThreadHandle threadHandle) + { + int threadHandleInt = threadHandle; + byte* outData = stackalloc byte[4]; + + _fileHandle.IoControl(CtlCode(Control.KphGetThreadWin32Thread), (byte*)&threadHandleInt, 4, outData, 4); + + return *(int*)outData; + } + + public int KphOpenDevice(ObjectAttributes objectAttributes) + { + byte* inData = stackalloc byte[8]; + int deviceHandle; + + *(int*)inData = (int)&deviceHandle; + *(int*)(inData + 4) = (int)&objectAttributes; + + _fileHandle.IoControl(CtlCode(Control.KphOpenDevice), inData, 8, null, 0); + + return deviceHandle; + } + + public int KphOpenDirectoryObject(DirectoryAccess access, ObjectAttributes objectAttributes) + { + byte* inData = stackalloc byte[0xc]; + int directoryObjectHandle; + + *(int*)inData = (int)&directoryObjectHandle; + *(int*)(inData + 0x4) = (int)access; + *(int*)(inData + 0x8) = (int)&objectAttributes; + + _fileHandle.IoControl(CtlCode(Control.KphOpenDirectoryObject), inData, 0xc, null, 0); + + return directoryObjectHandle; + } + + public int KphOpenDriver(ObjectAttributes objectAttributes) + { + byte* inData = stackalloc byte[8]; + int driverHandle; + + *(int*)inData = (int)&driverHandle; + *(int*)(inData + 4) = (int)&objectAttributes; + + _fileHandle.IoControl(CtlCode(Control.KphOpenDriver), inData, 8, null, 0); + + return driverHandle; + } + + public int KphOpenProcess(int pid, ProcessAccess desiredAccess) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = pid; + *(uint*)(inData + 4) = (uint)desiredAccess; + + _fileHandle.IoControl(CtlCode(Control.KphOpenProcess), inData, 8, outData, 4); + + return *(int*)outData; + } + + public int KphOpenProcessJob(ProcessHandle processHandle, JobObjectAccess desiredAccess) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = processHandle; + *(uint*)(inData + 4) = (uint)desiredAccess; + + _fileHandle.IoControl(CtlCode(Control.KphOpenProcessJob), inData, 8, outData, 4); + + return *(int*)outData; + } + + public int KphOpenProcessToken(ProcessHandle processHandle, TokenAccess desiredAccess) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = processHandle; + *(uint*)(inData + 4) = (uint)desiredAccess; + + _fileHandle.IoControl(CtlCode(Control.KphOpenProcessToken), inData, 8, outData, 4); + + return *(int*)outData; + } + + public int KphOpenThread(int tid, ThreadAccess desiredAccess) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = tid; + *(uint*)(inData + 4) = (uint)desiredAccess; + + _fileHandle.IoControl(CtlCode(Control.KphOpenThread), inData, 8, outData, 4); + + return *(int*)outData; + } + + public int KphOpenThreadProcess(ThreadHandle threadHandle, ProcessAccess desiredAccess) + { + byte* inData = stackalloc byte[8]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = threadHandle; + *(uint*)(inData + 4) = (uint)desiredAccess; + + _fileHandle.IoControl(CtlCode(Control.KphOpenThreadProcess), inData, 8, outData, 4); + + return *(int*)outData; + } + + public void KphQueryInformationDriver( + DriverHandle driverHandle, + DriverInformationClass driverInformationClass, + IntPtr driverInformation, + int driverInformationLength, + out int returnLength + ) + { + byte* inData = stackalloc byte[0x14]; + int returnLengthLocal; + + *(int*)inData = driverHandle; + *(int*)(inData + 0x4) = (int)driverInformationClass; + *(int*)(inData + 0x8) = driverInformation.ToInt32(); + *(int*)(inData + 0xc) = driverInformationLength; + *(int*)(inData + 0x10) = (int)&returnLengthLocal; + + try + { + _fileHandle.IoControl(CtlCode(Control.KphQueryInformationDriver), inData, 0x14, null, 0); + } + finally + { + returnLength = returnLengthLocal; + } + } + + public void KphQueryProcessHandles(ProcessHandle processHandle, IntPtr buffer, int bufferLength, out int returnLength) + { + byte* inData = stackalloc byte[0x10]; + int returnLengthLocal; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = buffer.ToInt32(); + *(int*)(inData + 0x8) = bufferLength; + *(int*)(inData + 0xc) = (int)&returnLengthLocal; + + try + { + _fileHandle.IoControl(CtlCode(Control.KphQueryProcessHandles), inData, 0x10, null, 0); + } + finally + { + returnLength = returnLengthLocal; + } + } + + public void KphReadVirtualMemory(ProcessHandle processHandle, int baseAddress, byte[] buffer, int length, out int bytesRead) + { + fixed (byte* bufferPtr = buffer) + { + this.KphReadVirtualMemory(processHandle, baseAddress, new IntPtr(bufferPtr), length, out bytesRead); + } + } + + public void KphReadVirtualMemory(ProcessHandle processHandle, int baseAddress, IntPtr buffer, int length, out int bytesRead) + { + if (!KphReadVirtualMemorySafe(processHandle, baseAddress, buffer, length, out bytesRead)) + Win32.ThrowLastError(); + } + + public bool KphReadVirtualMemorySafe(ProcessHandle processHandle, int baseAddress, IntPtr buffer, int length, out int bytesRead) + { + byte* inData = stackalloc byte[0x14]; + int returnLength; + int br; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = baseAddress; + *(int*)(inData + 0x8) = (int)buffer; + *(int*)(inData + 0xc) = length; + *(int*)(inData + 0x10) = (int)&br; + + bool r = Win32.DeviceIoControl(_fileHandle, (int)CtlCode(Control.KphReadVirtualMemory), + inData, 0x14, null, 0, out returnLength, IntPtr.Zero); + + bytesRead = br; + + return r; + } + + public bool KphReadVirtualMemoryUnsafe(ProcessHandle processHandle, int baseAddress, void* buffer, int length, out int bytesRead) + { + return KphReadVirtualMemoryUnsafe(processHandle, baseAddress, new IntPtr(buffer), length, out bytesRead); + } + + public bool KphReadVirtualMemoryUnsafe(ProcessHandle processHandle, int baseAddress, IntPtr buffer, int length, out int bytesRead) + { + byte* inData = stackalloc byte[0x14]; + int returnLength; + int br; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = baseAddress; + *(int*)(inData + 0x8) = (int)buffer; + *(int*)(inData + 0xc) = length; + *(int*)(inData + 0x10) = (int)&br; + + bool r = Win32.DeviceIoControl(_fileHandle, (int)CtlCode(Control.KphUnsafeReadVirtualMemory), + inData, 0x14, null, 0, out returnLength, IntPtr.Zero); + + bytesRead = br; + + return r; + } + + public void KphResumeProcess(ProcessHandle processHandle) + { + int processHandleInt = processHandle; + + _fileHandle.IoControl(CtlCode(Control.KphResumeProcess), + (byte*)&processHandleInt, 4, null, 0); + } + + public void KphSetContextThread(ThreadHandle threadHandle, Context* context) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = threadHandle; + *(int*)(inData + 4) = (int)context; + + _fileHandle.IoControl(CtlCode(Control.KphSetContextThread), inData, 8, null, 0); + } + + public void KphSetHandleGrantedAccess(IntPtr handle, int grantedAccess) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = handle.ToInt32(); + *(int*)(inData + 4) = grantedAccess; + + _fileHandle.IoControl(CtlCode(Control.KphSetHandleGrantedAccess), inData, 8, null, 0); + } + + public void KphSuspendProcess(ProcessHandle processHandle) + { + int processHandleInt = processHandle; + + _fileHandle.IoControl(CtlCode(Control.KphSuspendProcess), + (byte*)&processHandleInt, 4, null, 0); + } + + public void KphTerminateProcess(ProcessHandle processHandle, NtStatus exitStatus) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = (int)exitStatus; + + try + { + _fileHandle.IoControl(CtlCode(Control.KphTerminateProcess), inData, 8, null, 0); + } + catch (WindowsException ex) + { + // STATUS_CANT_TERMINATE_SELF means we tried to terminate ourself. Kernel-mode can't do it, + // so we do it now. + if (ex.Status == NtStatus.CantTerminateSelf) + Win32.TerminateProcess(new IntPtr(-1), (int)exitStatus); + else + throw ex; + } + } + + public void KphTerminateThread(ThreadHandle threadHandle, NtStatus exitStatus) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = threadHandle; + *(int*)(inData + 4) = (int)exitStatus; + + try + { + _fileHandle.IoControl(CtlCode(Control.KphTerminateThread), inData, 8, null, 0); + } + catch (WindowsException ex) + { + if (ex.Status == NtStatus.CantTerminateSelf) + Win32.TerminateThread(new IntPtr(-2), (int)exitStatus); + else + throw ex; + } + } + + public void KphWriteVirtualMemory(ProcessHandle processHandle, int baseAddress, byte[] buffer, int length, out int bytesWritten) + { + fixed (byte* bufferPtr = buffer) + this.KphWriteVirtualMemory(processHandle, baseAddress, new IntPtr(bufferPtr), length, out bytesWritten); + } + + public void KphWriteVirtualMemory(ProcessHandle processHandle, int baseAddress, IntPtr buffer, int length, out int bytesWritten) + { + byte* inData = stackalloc byte[0x14]; + int returnLength; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = baseAddress; + *(int*)(inData + 0x8) = (int)buffer; + *(int*)(inData + 0xc) = length; + *(int*)(inData + 0x10) = (int)&returnLength; + + try + { + _fileHandle.IoControl(CtlCode(Control.KphWriteVirtualMemory), inData, 0x14, null, 0); + } + finally + { + bytesWritten = returnLength; + } + } + + public void ProtectAdd(ProcessHandle processHandle, bool allowKernelMode, ProcessAccess ProcessAllowMask, ThreadAccess ThreadAllowMask) + { + byte* inData = stackalloc byte[16]; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = allowKernelMode ? 1 : 0; + *(int*)(inData + 0x8) = (int)ProcessAllowMask; + *(int*)(inData + 0xc) = (int)ThreadAllowMask; + + _fileHandle.IoControl(CtlCode(Control.ProtectAdd), inData, 16, null, 0); + } + + public void ProtectQuery(ProcessHandle processHandle, out bool AllowKernelMode, out ProcessAccess ProcessAllowMask, out ThreadAccess ThreadAllowMask) + { + byte* inData = stackalloc byte[16]; + int allowKernelMode; + ProcessAccess processAllowMask; + ThreadAccess threadAllowMask; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = (int)&allowKernelMode; + *(int*)(inData + 0x8) = (int)&processAllowMask; + *(int*)(inData + 0xc) = (int)&threadAllowMask; + + _fileHandle.IoControl(CtlCode(Control.ProtectQuery), inData, 16, null, 0); + + AllowKernelMode = allowKernelMode != 0; + ProcessAllowMask = processAllowMask; + ThreadAllowMask = threadAllowMask; + } + + public void ProtectRemove(ProcessHandle processHandle) + { + int processHandleInt = processHandle; + + _fileHandle.IoControl(CtlCode(Control.ProtectRemove), + (byte*)&processHandleInt, 4, null, 0); + } + + public void SetExecuteOptions(ProcessHandle processHandle, MemExecuteOptions executeOptions) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = (int)executeOptions; + + _fileHandle.IoControl(CtlCode(Control.SetExecuteOptions), inData, 8, null, 0); + } + + public void SetHandleAttributes(ProcessHandle processHandle, IntPtr handle, HandleFlags flags) + { + byte* inData = stackalloc byte[12]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = handle.ToInt32(); + *(int*)(inData + 8) = (int)flags; + + _fileHandle.IoControl(CtlCode(Control.SetHandleAttributes), inData, 12, null, 0); + } + + public void SetProcessProtected(int pid, bool protecte) + { + byte* inData = stackalloc byte[5]; + + *(int*)inData = pid; + inData[4] = (byte)(protecte ? 1 : 0); + + _fileHandle.IoControl(CtlCode(Control.SetProcessProtected), inData, 5, null, 0); + } + + public void SetProcessToken(int sourcePid, int targetPid) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = sourcePid; + *(int*)(inData + 4) = targetPid; + + _fileHandle.IoControl(CtlCode(Control.SetProcessToken), inData, 8, null, 0); + } + + public IntPtr SsAddProcessIdRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + KphSsFilterType filterType, + IntPtr processId + ) + { + byte* inData = stackalloc byte[0xc]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)filterType; + *(int*)(inData + 0x8) = processId.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.SsAddProcessIdRule), inData, 0xc, outData, 4); + + return (*(int*)outData).ToIntPtr(); + } + + public IntPtr SsAddThreadIdRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + KphSsFilterType filterType, + IntPtr threadId + ) + { + byte* inData = stackalloc byte[0xc]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)filterType; + *(int*)(inData + 0x8) = threadId.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.SsAddThreadIdRule), inData, 0xc, outData, 4); + + return (*(int*)outData).ToIntPtr(); + } + + public IntPtr SsAddPreviousModeRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + KphSsFilterType filterType, + KProcessorMode previousMode + ) + { + byte* inData = stackalloc byte[0x9]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)filterType; + *(byte*)(inData + 0x8) = (byte)previousMode; + + _fileHandle.IoControl(CtlCode(Control.SsAddPreviousModeRule), inData, 0x9, outData, 4); + + return (*(int*)outData).ToIntPtr(); + } + + public IntPtr SsAddNumberRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + KphSsFilterType filterType, + int number + ) + { + byte* inData = stackalloc byte[0xc]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)filterType; + *(int*)(inData + 0x8) = number; + + _fileHandle.IoControl(CtlCode(Control.SsAddNumberRule), inData, 0xc, outData, 4); + + return (*(int*)outData).ToIntPtr(); + } + + public KphSsClientEntryHandle SsCreateClientEntry( + ProcessHandle processHandle, + SemaphoreHandle readSemaphoreHandle, + SemaphoreHandle writeSemaphoreHandle, + IntPtr bufferBase, + int bufferSize + ) + { + byte* inData = stackalloc byte[0x14]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = processHandle; + *(int*)(inData + 0x4) = readSemaphoreHandle; + *(int*)(inData + 0x8) = writeSemaphoreHandle; + *(int*)(inData + 0xc) = bufferBase.ToInt32(); + *(int*)(inData + 0x10) = bufferSize; + + _fileHandle.IoControl(CtlCode(Control.SsCreateClientEntry), inData, 0x14, outData, 4); + + return new KphSsClientEntryHandle((*(int*)outData).ToIntPtr()); + } + + public KphSsRuleSetEntryHandle SsCreateRuleSetEntry( + KphSsClientEntryHandle clientEntryHandle, + KphSsFilterType defaultFilterType, + KphSsRuleSetAction action + ) + { + byte* inData = stackalloc byte[0xc]; + byte* outData = stackalloc byte[4]; + + *(int*)inData = clientEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)defaultFilterType; + *(int*)(inData + 0x8) = (int)action; + + _fileHandle.IoControl(CtlCode(Control.SsCreateRuleSetEntry), inData, 0xc, outData, 4); + + return new KphSsRuleSetEntryHandle((*(int*)outData).ToIntPtr()); + } + + public void SsEnableClientEntry( + KphSsClientEntryHandle clientEntryHandle, + bool enable + ) + { + byte* inData = stackalloc byte[5]; + + *(int*)inData = clientEntryHandle.Handle.ToInt32(); + *(byte*)(inData + 4) = (byte)(enable ? 1 : 0); + + _fileHandle.IoControl(CtlCode(Control.SsEnableClientEntry), inData, 5, null, 0); + } + + public void SsQueryClientEntry( + KphSsClientEntryHandle clientEntryHandle, + out KphSsClientInformation clientInformation, + int clientInformationLength, + out int returnLength + ) + { + fixed (KphSsClientInformation *clientInfoPtr = &clientInformation) + fixed (int* retLengthPtr = &returnLength) + { + byte* inData = stackalloc byte[0x10]; + + *(int*)inData = clientEntryHandle.Handle.ToInt32(); + *(int*)(inData + 0x4) = (int)clientInfoPtr; + *(int*)(inData + 0x8) = clientInformationLength; + *(int*)(inData + 0xc) = (int)retLengthPtr; + + _fileHandle.IoControl(CtlCode(Control.SsQueryClientEntry), inData, 0x10, null, 0); + } + } + + public void SsRemoveRule( + KphSsRuleSetEntryHandle ruleSetEntryHandle, + IntPtr ruleEntryHandle + ) + { + byte* inData = stackalloc byte[8]; + + *(int*)inData = ruleSetEntryHandle.Handle.ToInt32(); + *(int*)(inData + 4) = ruleEntryHandle.ToInt32(); + + _fileHandle.IoControl(CtlCode(Control.SsRemoveRule), inData, 8, null, 0); + } + + public void SsRef() + { + _fileHandle.IoControl(CtlCode(Control.SsRef), null, null); + } + + public void SsUnref() + { + _fileHandle.IoControl(CtlCode(Control.SsUnref), null, null); + } + + public NtStatus ZwQueryObject( + ProcessHandle processHandle, + IntPtr handle, + ObjectInformationClass objectInformationClass, + IntPtr buffer, + int bufferLength, + out int returnLength, + out int baseAddress + ) + { + byte* inData = stackalloc byte[12]; + byte[] outData = new byte[bufferLength + 12]; + + *(int*)inData = processHandle; + *(int*)(inData + 4) = handle.ToInt32(); + *(int*)(inData + 8) = (int)objectInformationClass; + + _fileHandle.IoControl(CtlCode(Control.ZwQueryObject), inData, 12, outData); + + NtStatus status; + + fixed (byte* outDataPtr = outData) + { + status = *(NtStatus*)outDataPtr; + returnLength = *(int*)(outDataPtr + 4); + baseAddress = *(int*)(outDataPtr + 8); + } + + if (buffer != IntPtr.Zero) + Marshal.Copy(outData, 12, buffer, bufferLength); + + return status; + } + } + + public enum DriverInformationClass + { + DriverBasicInformation = 0, + DriverNameInformation, + DriverServiceKeyNameInformation + } + + public enum KphSsArgumentType : byte + { + Normal = 0, + Int8, + Int16, + Int32, + Int64, + Handle, + String, + WString, + AnsiString, + UnicodeString, + ObjectAttributes, + ClientId, + Context, + InitialTeb + } + + public enum KphSsBlockType : ushort + { + Reset, + Event, + Argument + } + + [Flags] + public enum KphSsEventFlags : ushort + { + ProbeArgumentsFailed = 0x1, + CopyArgumentsFailed = 0x2, + KernelMode = 0x4, + UserMode = 0x8 + } + + public enum KphSsFilterType : int + { + Include, + Exclude + } + + [Flags] + public enum KphSsModeFlags : int + { + UserMode = 0x1, + KernelMode = 0x2 + } + + public enum KphSsRuleSetAction : int + { + Log + } + + public class KphHandle : BaseObject + { + private IntPtr _handle; + + protected KphHandle(IntPtr handle) + { + _handle = handle; + } + + protected override void DisposeObject(bool disposing) + { + KProcessHacker.Instance.ClientCloseHandle(_handle); + } + + public IntPtr Handle + { + get { return _handle; } + } + } + + public class KphSsClientEntryHandle : KphHandle + { + internal KphSsClientEntryHandle(IntPtr handle) + : base(handle) + { } + } + + public class KphSsRuleSetEntryHandle : KphHandle + { + internal KphSsRuleSetEntryHandle(IntPtr handle) + : base(handle) + { } + } + + [StructLayout(LayoutKind.Sequential)] + public struct DriverBasicInformation + { + public int Flags; + public IntPtr DriverStart; + public int DriverSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsArgumentBlock + { + public static readonly int DataOffset = Marshal.OffsetOf(typeof(KphSsArgumentBlock), "Data").ToInt32(); + + [StructLayout(LayoutKind.Explicit)] + public struct KphSsArgumentUnion + { + [FieldOffset(0)] + public int Normal; + [FieldOffset(0)] + public byte Int8; + [FieldOffset(0)] + public short Int16; + [FieldOffset(0)] + public int Int32; + [FieldOffset(0)] + public long Int64; + } + + public KphSsBlockHeader Header; + public byte Index; + public KphSsArgumentType Type; + public KphSsArgumentUnion Data; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsBlockHeader + { + public ushort Size; + public KphSsBlockType Type; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsClientInformation + { + public IntPtr ProcessId; + public IntPtr BufferBase; + public int BufferSize; + public int NumberOfBlocksWritten; + public int NumberOfBlocksDropped; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsEventBlock + { + public KphSsBlockHeader Header; + public KphSsEventFlags Flags; + public long Time; + public ClientId ClientId; + + public int Number; + public ushort NumberOfArguments; + public ushort ArgumentsOffset; + + public ushort TraceCount; + public ushort TraceOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsHandle + { + public ClientId ClientId; + public ushort TypeNameOffset; + public ushort NameOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsObjectAttributes + { + public ObjectAttributes ObjectAttributes; + public ushort RootDirectoryOffset; + public ushort ObjectNameOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsUnicodeString + { + public static readonly int BufferOffset = Marshal.OffsetOf(typeof(KphSsUnicodeString), "Buffer").ToInt32(); + + public ushort Length; + public ushort MaximumLength; + public IntPtr Pointer; + public byte Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KphSsWString + { + public static readonly int BufferOffset = Marshal.OffsetOf(typeof(KphSsWString), "Buffer").ToInt32(); + + public ushort Length; + public byte Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessHandleInformation + { + public IntPtr Handle; + public IntPtr Object; + public int GrantedAccess; + public HandleFlags HandleAttributes; // should be an int + private byte Pad1; + private short Pad2; + + private void Dummy() + { + Pad1 = 0; + Pad2 = 0; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Loader.cs b/branches/ph-plugins/ProcessHacker.Native/Loader.cs new file mode 100644 index 000000000..3d26f1dec --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Loader.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + public static class Loader + { + public static IntPtr GetProcedure(string dllName, string procedureName) + { + return GetProcedure(GetDllHandle(dllName), procedureName); + } + + public static IntPtr GetProcedure(IntPtr dllHandle, string procedureName) + { + return Win32.GetProcAddress(dllHandle, procedureName); + } + + public static IntPtr GetProcedure(IntPtr dllHandle, int procedureNumber) + { + return Win32.GetProcAddress(dllHandle, (ushort)procedureNumber); + } + + public static IntPtr GetDllHandle(string dllName) + { + return Win32.GetModuleHandle(dllName); + } + + public static IntPtr LoadDll(string dllName) + { + return Win32.LoadLibrary(dllName); + } + + public static bool UnloadDll(IntPtr dllHandle) + { + return Win32.FreeLibrary(dllHandle); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Lpc/Port.cs b/branches/ph-plugins/ProcessHacker.Native/Lpc/Port.cs new file mode 100644 index 000000000..2bbde2d9e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Lpc/Port.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Lpc +{ + public class Port : NativeObject + { + public Port(string name) + { + this.Handle = PortHandle.Create( + name, + ObjectFlags.OpenIf, + null, + Win32.PortMessageMaxDataLength, + Win32.PortMessageMaxLength, + 0 + ); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Lpc/PortMessage.cs b/branches/ph-plugins/ProcessHacker.Native/Lpc/PortMessage.cs new file mode 100644 index 000000000..1087d4467 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Lpc/PortMessage.cs @@ -0,0 +1,128 @@ +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Lpc +{ + public class PortMessage : BaseObject + { + private static readonly int _portMessageSize = Marshal.SizeOf(typeof(PortMessageStruct)); + + public static MemoryAlloc AllocateBuffer() + { + return new MemoryAlloc(Win32.PortMessageMaxLength); + } + + private PortMessageStruct _message; + private MemoryRegion _data; + private MemoryRegion _referencedData; + + public PortMessage(byte[] data) + : this(null, data) + { } + + public PortMessage(PortMessage existingMessage, byte[] data) + { + using (var alloc = new MemoryAlloc(data.Length)) + { + alloc.WriteBytes(0, data); + this.InitializeMessage(existingMessage, alloc, (short)alloc.Size); + } + } + + public PortMessage(MemoryRegion data, short dataLength) + : this(null, data, dataLength) + { } + + public PortMessage(PortMessage existingMessage, MemoryRegion data, short dataLength) + { + this.InitializeMessage(existingMessage, data, dataLength); + } + + internal PortMessage(MemoryRegion headerAndData) + { + _message = headerAndData.ReadStruct(); + _data = new MemoryRegion(headerAndData, _portMessageSize, _message.DataLength); + + _referencedData = headerAndData; + _referencedData.Reference(); + } + + protected override void DisposeObject(bool disposing) + { + _referencedData.Dereference(disposing); + } + + public ClientId ClientId + { + get { return _message.ClientId; } + set { _message.ClientId = value; } + } + + public MemoryRegion Data + { + get { return _data; } + } + + public int DataLength + { + get { return _message.DataLength; } + } + + internal PortMessageStruct Header + { + get { return _message; } + } + + public int MessageId + { + get { return _message.MessageId; } + } + + public PortMessageType Type + { + get { return _message.Type; } + } + + private void InitializeMessage(PortMessage existingMessage, MemoryRegion data, short dataLength) + { + if (dataLength > Win32.PortMessageMaxDataLength) + throw new ArgumentOutOfRangeException("Data length is too large."); + if (dataLength < 0) + throw new ArgumentOutOfRangeException("Data length cannot be negative."); + + _message = new PortMessageStruct(); + + _message.DataLength = dataLength; + _message.TotalLength = (short)(_portMessageSize + dataLength); + _message.DataInfoOffset = 0; + + if (existingMessage != null) + { + _message.ClientId = existingMessage.ClientId; + _message.MessageId = existingMessage.MessageId; + } + + _data = data; + + _referencedData = data; + _referencedData.Reference(); + } + + internal void SetHeader(MemoryRegion data) + { + _message = data.ReadStruct(); + } + + public MemoryAlloc ToMemory() + { + MemoryAlloc data = new MemoryAlloc(_portMessageSize + _message.DataLength); + + data.WriteStruct(_message); + data.WriteMemory(_portMessageSize, _data, 0, _message.DataLength); + + return data; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/AlignedMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/AlignedMemoryAlloc.cs new file mode 100644 index 000000000..6a8f1780e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/AlignedMemoryAlloc.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Common; + +namespace ProcessHacker.Native +{ + public class AlignedMemoryAlloc : MemoryAlloc + { + private IntPtr _realMemory; + + public AlignedMemoryAlloc(int size, int alignment) + { + // Make sure the alignment is positive and a power of two. + if (alignment <= 0 || Utils.CountBits(alignment) != 1) + throw new ArgumentOutOfRangeException("alignment"); + + // Since we are going to align our pointer, we need to account for + // any padding at the beginning. + _realMemory = MemoryAlloc.PrivateHeap.Allocate(0, size + alignment - 1); + + // aligned memory = (memory + alignment - 1) & ~(alignment - 1) + this.Memory = _realMemory.Increment(alignment - 1).And((alignment - 1).ToIntPtr().Not()); + this.Size = size; + } + + protected override void Free() + { + MemoryAlloc.PrivateHeap.Free(0, _realMemory); + } + + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/Heap.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/Heap.cs new file mode 100644 index 000000000..7696c7215 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/Heap.cs @@ -0,0 +1,134 @@ +/* + * Process Hacker - + * run-time library heap + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + public struct Heap + { + public static Heap FromHandle(IntPtr handle) + { + return new Heap(handle); + } + + public static Heap GetDefault() + { + return new Heap(Win32.GetProcessHeap()); + } + + public static Heap[] GetHeaps() + { + IntPtr[] heapAddresses = new IntPtr[64]; + int retHeaps; + + retHeaps = Win32.RtlGetProcessHeaps(heapAddresses.Length, heapAddresses); + + // Reallocate the buffer if it wasn't large enough. + if (retHeaps > heapAddresses.Length) + { + heapAddresses = new IntPtr[retHeaps]; + retHeaps = Win32.RtlGetProcessHeaps(heapAddresses.Length, heapAddresses); + } + + int numberOfHeaps = Math.Min(heapAddresses.Length, retHeaps); + Heap[] heaps = new Heap[numberOfHeaps]; + + for (int i = 0; i < numberOfHeaps; i++) + heaps[i] = new Heap(heapAddresses[i]); + + return heaps; + } + + private IntPtr _heap; + + private Heap(IntPtr heap) + { + _heap = heap; + } + + public Heap(HeapFlags flags) + : this(flags, 0, 0) + { } + + public Heap(HeapFlags flags, int reserveSize, int commitSize) + { + _heap = Win32.RtlCreateHeap( + flags, + IntPtr.Zero, + reserveSize.ToIntPtr(), + commitSize.ToIntPtr(), + IntPtr.Zero, + IntPtr.Zero + ); + + if (_heap == IntPtr.Zero) + throw new OutOfMemoryException(); + } + + public IntPtr Address + { + get { return _heap; } + } + + public IntPtr Allocate(HeapFlags flags, int size) + { + IntPtr memory = Win32.RtlAllocateHeap(_heap, flags, size.ToIntPtr()); + + if (memory == IntPtr.Zero) + throw new OutOfMemoryException(); + + return memory; + } + + public int Compact(HeapFlags flags) + { + return Win32.RtlCompactHeap(_heap, flags).ToInt32(); + } + + public void Destroy() + { + Win32.RtlDestroyHeap(_heap); + } + + public void Free(HeapFlags flags, IntPtr memory) + { + Win32.RtlFreeHeap(_heap, flags, memory); + } + + public int GetBlockSize(HeapFlags flags, IntPtr memory) + { + return Win32.RtlSizeHeap(_heap, flags, memory).ToInt32(); + } + + public IntPtr Reallocate(HeapFlags flags, IntPtr memory, int size) + { + IntPtr newMemory = Win32.RtlReAllocateHeap(_heap, flags, memory, size.ToIntPtr()); + + if (newMemory == IntPtr.Zero) + throw new OutOfMemoryException(); + + return newMemory; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/LocalMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/LocalMemoryAlloc.cs new file mode 100644 index 000000000..76ba3e758 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/LocalMemoryAlloc.cs @@ -0,0 +1,51 @@ +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents a LocalAlloc() memory allocation. + /// + public sealed class LocalMemoryAlloc : MemoryAlloc + { + public LocalMemoryAlloc(IntPtr memory) + : this(memory, true) + { } + + public LocalMemoryAlloc(IntPtr memory, bool owned) + : base(memory, owned) + { } + + public LocalMemoryAlloc(int size) + : this(size, AllocFlags.LPtr) + { } + + public LocalMemoryAlloc(int size, AllocFlags flags) + { + this.Memory = Win32.LocalAlloc(flags, size); + + if (this.Memory == IntPtr.Zero) + throw new OutOfMemoryException(); + + this.Size = size; + } + + protected override void Free() + { + Win32.LocalFree(this); + } + + public override void Resize(int newSize) + { + IntPtr newMemory; + + newMemory = Win32.LocalReAlloc(this, AllocFlags.LMemFixed, newSize); + + if (newMemory == IntPtr.Zero) + throw new OutOfMemoryException(); + + this.Memory = newMemory; + this.Size = newSize; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/LsaMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/LsaMemoryAlloc.cs new file mode 100644 index 000000000..b9503fab7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/LsaMemoryAlloc.cs @@ -0,0 +1,79 @@ +/* + * Process Hacker - + * local security authority memory allocation wrapper + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.ComponentModel; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents a memory allocation managed by the Local Security Authority (LSA). + /// + public sealed class LsaMemoryAlloc : MemoryAlloc + { + private bool _secur32; + + public LsaMemoryAlloc(IntPtr memory) + : this(memory, false) + { } + + public LsaMemoryAlloc(IntPtr memory, bool secur32) + : this(memory, secur32, true) + { } + + /// + /// Creates a memory allocation from an existing LSA managed allocation. + /// + /// A pointer to the allocated memory. + /// True if the memory was allocated by secur32, otherwise false. + /// Whether the memory allocation should be freed automatically. + public LsaMemoryAlloc(IntPtr memory, bool secur32, bool owned) + : base(memory, owned) + { + _secur32 = secur32; + } + + protected override void Free() + { + if (!_secur32) + Win32.LsaFreeMemory(this); + else + Win32.LsaFreeReturnBuffer(this); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int Size + { + get + { + throw new NotSupportedException(); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryAlloc.cs new file mode 100644 index 000000000..c77ab2dfb --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryAlloc.cs @@ -0,0 +1,133 @@ +/* + * Process Hacker - + * memory allocation wrapper + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define ENABLE_STATISTICS + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents an unmanaged memory allocation from the heap. + /// + public class MemoryAlloc : MemoryRegion + { + private static int _allocatedCount = 0; + private static int _freedCount = 0; + private static int _reallocatedCount = 0; + + // A private heap just for the client. + private static Heap _privateHeap = new Heap(HeapFlags.Class1 | HeapFlags.Growable); + private static Heap _processHeap = Heap.GetDefault(); + + public static int AllocatedCount + { + get { return _allocatedCount; } + } + + public static new int FreedCount + { + get { return _freedCount; } + } + + public static Heap PrivateHeap + { + get { return _privateHeap; } + } + + public static int ReallocatedCount + { + get { return _reallocatedCount; } + } + + /// + /// Creates a new, invalid memory allocation. + /// You must set the pointer using the Memory property. + /// + protected MemoryAlloc() + : base() + { } + + public MemoryAlloc(IntPtr memory) + : this(memory, true) + { } + + public MemoryAlloc(IntPtr memory, bool owned) + : this(memory, 0, owned) + { } + + public MemoryAlloc(IntPtr memory, int size, bool owned) + : base(memory, size, owned) + { } + + /// + /// Creates a new memory allocation with the specified size. + /// + /// The amount of memory, in bytes, to allocate. + public MemoryAlloc(int size) + : this(size, 0) + { } + + /// + /// Creates a new memory allocation with the specified size. + /// + /// The amount of memory, in bytes, to allocate. + /// Any flags to use. + public MemoryAlloc(int size, HeapFlags flags) + { + this.Memory = _privateHeap.Allocate(flags, size); + this.Size = size; + +#if ENABLE_STATISTICS + System.Threading.Interlocked.Increment(ref _allocatedCount); +#endif + } + + protected override void Free() + { + _privateHeap.Free(0, this); + +#if ENABLE_STATISTICS + System.Threading.Interlocked.Increment(ref _freedCount); +#endif + } + + /// + /// Resizes the memory allocation. + /// + /// The new size of the allocation. + public virtual void Resize(int newSize) + { + this.Memory = _privateHeap.Reallocate(0, this.Memory, newSize); + this.Size = newSize; + +#if ENABLE_STATISTICS + System.Threading.Interlocked.Increment(ref _reallocatedCount); +#endif + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegion.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegion.cs new file mode 100644 index 000000000..3f9ef5d30 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegion.cs @@ -0,0 +1,367 @@ +/* + * Process Hacker - + * memory region + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Common.Objects; + +namespace ProcessHacker.Native +{ + public class MemoryRegion : BaseObject + { + private static Dictionary _sizeCache = new Dictionary(); + + public static implicit operator IntPtr(MemoryRegion memory) + { + return memory.Memory; + } + + public unsafe static implicit operator void*(MemoryRegion memory) + { + return memory.Memory.ToPointer(); + } + + private MemoryRegion _parent; + private IntPtr _memory; + private int _size; + + /// + /// Creates a new, invalid memory allocation. + /// You must set the pointer using the Memory property. + /// + protected MemoryRegion() + { } + + public MemoryRegion(IntPtr memory) + : this(memory, 0) + { } + + public MemoryRegion(IntPtr memory, int offset) + : this(memory, offset, 0) + { } + + public MemoryRegion(IntPtr memory, int offset, int size) + : this(memory.Increment(offset), size, false) + { } + + protected MemoryRegion(IntPtr memory, int size, bool owned) + : this(null, memory, size, owned) + { } + + protected MemoryRegion(MemoryRegion parent, IntPtr memory, int size, bool owned) + : base(owned) + { + if (parent != null) + parent.Reference(); + + _parent = parent; + _memory = memory; + _size = size; + } + + protected sealed override void DisposeObject(bool disposing) + { + this.Free(); + + if (_parent != null) + _parent.Dereference(disposing); + } + + protected virtual void Free() + { } + + /// + /// Gets a pointer to the allocated memory. + /// + public IntPtr Memory + { + get { return _memory; } + protected set { _memory = value; } + } + + public MemoryRegion Parent + { + get { return _parent; } + } + + /// + /// Gets the size of the allocated memory. + /// + public virtual int Size + { + get { return _size; } + protected set { _size = value; } + } + + public void Fill(int offset, int length, byte value) + { + ProcessHacker.Native.Api.Win32.RtlFillMemory( + _memory.Increment(offset), + length.ToIntPtr(), + value + ); + } + + public MemoryRegionStream GetStream() + { + return new MemoryRegionStream(this); + } + + private int GetStructSizeCached(Type structType) + { + if (!_sizeCache.ContainsKey(structType)) + _sizeCache.Add(structType, Marshal.SizeOf(structType)); + + return _sizeCache[structType]; + } + + public MemoryRegion MakeChild(int offset, int size) + { + return new MemoryRegion(this, _memory.Increment(offset), size, true); + } + + public string ReadAnsiString(int offset) + { + return Marshal.PtrToStringAnsi(_memory.Increment(offset)); + } + + public string ReadAnsiString(int offset, int length) + { + return Marshal.PtrToStringAnsi(_memory.Increment(offset), length); + } + + public byte[] ReadBytes(int length) + { + return this.ReadBytes(0, length); + } + + public byte[] ReadBytes(int offset, int length) + { + byte[] buffer = new byte[length]; + + this.ReadBytes(offset, buffer, 0, length); + + return buffer; + } + + public void ReadBytes(byte[] buffer, int startIndex, int length) + { + this.ReadBytes(0, buffer, startIndex, length); + } + + public void ReadBytes(int offset, byte[] buffer, int startIndex, int length) + { + Marshal.Copy(_memory.Increment(offset), buffer, startIndex, length); + } + + /// + /// Reads a signed integer. + /// + /// The offset at which to begin reading. + /// The integer. + public int ReadInt32(int offset) + { + return this.ReadInt32(offset, 0); + } + + /// + /// Reads a signed integer. + /// + /// The offset at which to begin reading. + /// The index at which to begin reading, after the offset is added. + /// The integer. + public int ReadInt32(int offset, int index) + { + return Marshal.ReadInt32(_memory, offset + index * sizeof(int)); + } + + public IntPtr ReadIntPtr(int offset) + { + return this.ReadIntPtr(offset, 0); + } + + public IntPtr ReadIntPtr(int offset, int index) + { + return Marshal.ReadIntPtr(_memory, offset + index * IntPtr.Size); + } + + public void ReadMemory(IntPtr buffer, int destOffset, int srcOffset, int length) + { + ProcessHacker.Native.Api.Win32.RtlMoveMemory( + buffer.Increment(destOffset), + _memory.Increment(srcOffset), + length.ToIntPtr() + ); + } + + /// + /// Reads an unsigned integer. + /// + /// The offset at which to begin reading. + /// The integer. + public uint ReadUInt32(int offset) + { + return this.ReadUInt32(offset, 0); + } + + /// + /// Reads an unsigned integer. + /// + /// The offset at which to begin reading. + /// The index at which to begin reading, after the offset is added. + /// The integer. + public uint ReadUInt32(int offset, int index) + { + return (uint)this.ReadInt32(offset, index); + } + + /// + /// Creates a struct from the memory allocation. + /// + /// The type of the struct. + /// The new struct. + public T ReadStruct() + where T : struct + { + return this.ReadStruct(0); + } + + /// + /// Creates a struct from the memory allocation. + /// + /// The type of the struct. + /// The index at which to begin reading to the struct. This is multiplied by + /// the size of the struct. + /// The new struct. + public T ReadStruct(int index) + where T : struct + { + return this.ReadStruct(0, index); + } + + /// + /// Creates a struct from the memory allocation. + /// + /// The type of the struct. + /// The offset to add before reading. + /// The index at which to begin reading to the struct. This is multiplied by + /// the size of the struct. + /// The new struct. + public T ReadStruct(int offset, int index) + where T : struct + { + return (T)Marshal.PtrToStructure( + _memory.Increment(offset + this.GetStructSizeCached(typeof(T)) * index), typeof(T)); + } + + public string ReadUnicodeString(int offset) + { + return Marshal.PtrToStringUni(_memory.Increment(offset)); + } + + public string ReadUnicodeString(int offset, int length) + { + return Marshal.PtrToStringUni(_memory.Increment(offset), length); + } + + /// + /// Writes a single byte to the memory allocation. + /// + /// The offset at which to write. + /// The value of the byte. + public void WriteByte(int offset, byte b) + { + Marshal.WriteByte(this, offset, b); + } + + public void WriteBytes(int offset, byte[] b) + { + Marshal.Copy(b, 0, _memory.Increment(offset), b.Length); + } + + public void WriteInt16(int offset, short i) + { + Marshal.WriteInt16(this, offset, i); + } + + public void WriteInt32(int offset, int i) + { + Marshal.WriteInt32(this, offset, i); + } + + public void WriteIntPtr(int offset, IntPtr i) + { + Marshal.WriteIntPtr(this, offset, i); + } + + public void WriteMemory(int destOffset, IntPtr buffer, int srcOffset, int length) + { + ProcessHacker.Native.Api.Win32.RtlMoveMemory( + _memory.Increment(destOffset), + buffer.Increment(srcOffset), + length.ToIntPtr() + ); + } + + public void WriteStruct(T s) + where T : struct + { + this.WriteStruct(0, s); + } + + public void WriteStruct(int index, T s) + where T : struct + { + this.WriteStruct(0, index, s); + } + + public void WriteStruct(int offset, int index, T s) + where T : struct + { + Marshal.StructureToPtr(s, + _memory.Increment(offset + this.GetStructSizeCached(typeof(T)) * index), false); + } + + /// + /// Writes a Unicode string to the allocated memory. + /// + /// The offset to add. + /// The string to write. + public void WriteUnicodeString(int offset, string s) + { + byte[] b = UnicodeEncoding.Unicode.GetBytes(s); + + for (int i = 0; i < b.Length; i++) + Marshal.WriteByte(this.Memory, offset + i, b[i]); + } + + public void Zero(int offset, int length) + { + ProcessHacker.Native.Api.Win32.RtlZeroMemory( + _memory.Increment(offset), + length.ToIntPtr() + ); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegionStream.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegionStream.cs new file mode 100644 index 000000000..103fc20fe --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/MemoryRegionStream.cs @@ -0,0 +1,114 @@ +/* + * Process Hacker - + * memory region stream + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native +{ + public class MemoryRegionStream : Stream + { + private MemoryRegion _memory; + private long _position = 0; + + public MemoryRegionStream(MemoryRegion memory) + { + _memory = memory; + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanTimeout + { + get { return false; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override void Flush() + { + // Do nothing + } + + public override long Length + { + get { return _memory.Size; } + } + + public override long Position + { + get { return _position; } + set { _position = value; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + Marshal.Copy(_memory.Memory.Increment(_position += count), buffer, offset, count); + + return count; + } + + public override int ReadByte() + { + return Marshal.ReadByte(_memory.Memory.Increment(_position++)); + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.Begin) + _position = offset; + else if (origin == SeekOrigin.Current) + _position += offset; + else if (origin == SeekOrigin.End) + _position = _memory.Size + offset; + + return _position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + Marshal.Copy(buffer, offset, _memory.Memory.Increment(_position += count), count); + } + + public override void WriteByte(byte value) + { + Marshal.WriteByte(_memory.Memory.Increment(_position++), value); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/PebMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/PebMemoryAlloc.cs new file mode 100644 index 000000000..f160ecdf4 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/PebMemoryAlloc.cs @@ -0,0 +1,60 @@ +/* + * Process Hacker - + * PEB memory allocation + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.ComponentModel; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents a memory allocation from the PEB. + /// + public sealed class PebMemoryAlloc : MemoryAlloc + { + public PebMemoryAlloc(int size) + { + NtStatus status; + IntPtr block; + + if ((status = Win32.RtlAllocateFromPeb(size, out block)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.Memory = block; + this.Size = size; + } + + protected override void Free() + { + NtStatus status; + + if ((status = Win32.RtlFreeToPeb(this, this.Size)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPages.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPages.cs new file mode 100644 index 000000000..a0acc1032 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPages.cs @@ -0,0 +1,132 @@ +using System; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Memory +{ + /// + /// Represents an allocation of physical pages. + /// + public sealed class PhysicalPages : BaseObject + { + private ProcessHandle _processHandle; + private int _count; + private IntPtr[] _pfnArray; + + /// + /// Allocates physical pages. + /// + /// The number of pages to allocate. + public PhysicalPages(int pageCount) + : this(pageCount, true) + { } + + /// + /// Allocates physical pages. + /// + /// + /// The number of bytes to allocate, or the number of pages to allocate + /// if is true. If a number of bytes is used, + /// it will be rounded up to the system page size. + /// + /// Whether specifies bytes or pages. + /// + public PhysicalPages(int count, bool pages) + : this(ProcessHandle.Current, count, pages) + { } + + /// + /// Allocates physical pages. + /// + /// The process to allocate the pages in. + /// The number of pages to allocate. + public PhysicalPages(ProcessHandle processHandle, int pageCount) + : this(processHandle, pageCount, true) + { } + + /// + /// Allocates physical pages. + /// + /// The process to allocate the pages in. + /// + /// The number of bytes to allocate, or the number of pages to allocate + /// if is true. If a number of bytes is used, + /// it will be rounded up to the system page size. + /// + /// Whether specifies bytes or pages. + /// + public PhysicalPages(ProcessHandle processHandle, int count, bool pages) + { + if (pages) + _count = count; + else + _count = Windows.BytesToPages(count); + + IntPtr pageCount = new IntPtr(_count); + + _pfnArray = new IntPtr[_count]; + + if (!Win32.AllocateUserPhysicalPages(processHandle, ref pageCount, _pfnArray)) + Win32.ThrowLastError(); + + if (pageCount.ToInt32() != _count) + throw new Exception("Could not allocate all pages."); + + _processHandle = processHandle; + _processHandle.Reference(); + } + + protected override void DisposeObject(bool disposing) + { + IntPtr freedPages = new IntPtr(_count); + + _processHandle.Dereference(); + + if (!Win32.FreeUserPhysicalPages(_processHandle, ref freedPages, _pfnArray)) + Win32.ThrowLastError(); + + if (freedPages.ToInt32() != _count) + throw new Exception("Could not free all pages."); + } + + public PhysicalPagesMapping Map(MemoryProtection protection) + { + return this.Map(IntPtr.Zero, protection); + } + + public PhysicalPagesMapping Map(IntPtr address, MemoryProtection protection) + { + // Reserve an address range. + IntPtr allocAddress = ProcessHandle.Current.AllocateMemory( + address, + _count * Windows.PageSize, + MemoryFlags.Reserve | MemoryFlags.Physical, + protection + ); + + // Map the physical memory into the address range. + if (!Win32.MapUserPhysicalPages( + allocAddress, + new IntPtr(_count), + _pfnArray + )) + Win32.ThrowLastError(); + + return new PhysicalPagesMapping(this, allocAddress); + } + + internal void Unmap(IntPtr address) + { + // Unmap the physical memory from the address range. + if (!Win32.MapUserPhysicalPages( + address, + new IntPtr(_count), + null + )) + Win32.ThrowLastError(); + + ProcessHandle.Current.FreeMemory(address, 0, false); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPagesMapping.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPagesMapping.cs new file mode 100644 index 000000000..ee1d5c134 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/PhysicalPagesMapping.cs @@ -0,0 +1,23 @@ +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Memory +{ + public sealed class PhysicalPagesMapping : MemoryAlloc + { + private PhysicalPages _physicalPages; + + internal PhysicalPagesMapping(PhysicalPages physicalPages, IntPtr baseAddress) + { + _physicalPages = physicalPages; + _physicalPages.Reference(); + this.Memory = baseAddress; + } + + protected override void Free() + { + _physicalPages.Unmap(this); + _physicalPages.Dereference(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/PinnedObject.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/PinnedObject.cs new file mode 100644 index 000000000..c1c675c22 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/PinnedObject.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Common.Objects; + +namespace ProcessHacker.Native +{ + public sealed class PinnedObject : BaseObject + { + private T _object; + private GCHandle _handle; + + public PinnedObject(T obj) + { + _object = obj; + _handle = GCHandle.Alloc(obj, GCHandleType.Pinned); + } + + protected override void DisposeObject(bool disposing) + { + _handle.Free(); + } + + public IntPtr Address + { + get { return _handle.AddrOfPinnedObject(); } + } + + public T Object + { + get { return _object; } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/Section.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/Section.cs new file mode 100644 index 000000000..7710ce9ce --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/Section.cs @@ -0,0 +1,167 @@ +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + /// + /// Represents a section, a memory mapping. + /// + public sealed class Section : NativeObject + { + private MemoryProtection _originalProtection = MemoryProtection.ReadWrite; + + /// + /// Opens an existing section. + /// + /// The name of an existing section. + /// The desired access to the section. + public Section(string name, SectionAccess access) + { + this.Handle = new SectionHandle(name, access); + } + + /// + /// Creates a section backed by a file. + /// + /// A file handle. + public Section(FileHandle fileHandle) + : this(fileHandle, MemoryProtection.ReadWrite) + { } + + /// + /// Creates a section backed by a file. + /// + /// A file handle. + /// The page protection to apply to mappings. + public Section(FileHandle fileHandle, MemoryProtection protection) + : this(fileHandle, false, protection) + { } + + /// + /// Creates a section backed by a file. + /// + /// A file handle. + /// Whether to treat the file as an executable image. + /// The page protection to apply to mappings. + public Section(FileHandle fileHandle, bool image, MemoryProtection protection) + : this(null, fileHandle, image, protection) + { } + + /// + /// Creates a section backed by a file. + /// + /// The name of the section. + /// A file handle. + /// Whether to treat the file as an executable image. + /// The page protection to apply to mappings. + public Section(string name, FileHandle fileHandle, bool image, MemoryProtection protection) + { + _originalProtection = protection; + + this.Handle = SectionHandle.Create( + SectionAccess.All, + name, + ObjectFlags.OpenIf, + null, + fileHandle.GetSize(), + image ? SectionAttributes.Image : SectionAttributes.Commit, + protection, + fileHandle + ); + } + + /// + /// Creates a section backed by the page file (i.e. in memory). + /// + /// The maximum size of the section. + public Section(long maximumSize) + : this(maximumSize, MemoryProtection.ReadWrite) + { } + + /// + /// Creates a section backed by the page file (i.e. in memory). + /// + /// The maximum size of the section. + /// The page protection to apply to mappings. + public Section(long maximumSize, MemoryProtection protection) + : this(null, maximumSize, protection) + { } + + /// + /// Creates a section backed by the page file (i.e. in memory). + /// + /// The name of the section. + /// The maximum size of the section. + /// The page protection to apply to mappings. + public Section(string name, long maximumSize, MemoryProtection protection) + { + _originalProtection = protection; + + this.Handle = SectionHandle.Create( + SectionAccess.All, + name, + ObjectFlags.OpenIf, + null, + maximumSize, + SectionAttributes.Commit, + protection, + null + ); + } + + /// + /// Extends the size of the section. + /// + /// The new size of the section. + public void Extend(long newSize) + { + this.Handle.Extend(newSize); + } + + /// + /// Creates a view of the section. + /// + /// + /// The number of bytes to map. This value will be rounded up to the + /// page size. + /// + /// A view of the section. + public SectionView MapView(int size) + { + return this.MapView(size, _originalProtection); + } + + /// + /// Creates a view of the section. + /// + /// + /// The number of bytes to map. This value will be rounded up to the + /// page size. + /// + /// The page protection to apply to the mapping. + /// A view of the section. + public SectionView MapView(int size, MemoryProtection protection) + { + return this.Handle.MapView(0, size, protection); + } + + /// + /// Creates a view of the section. + /// + /// + /// The offset from the beginning of the section to map. This value + /// must be a multiple of 0x10000 (65536). + /// + /// + /// The number of bytes to map. This value will be rounded up to the + /// page size. + /// + /// The page protection to apply to the mapping. + /// A view of the section. + public SectionView MapView(int offset, int size, MemoryProtection protection) + { + return this.Handle.MapView(offset, size, protection); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/SectionView.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/SectionView.cs new file mode 100644 index 000000000..e43cd3e27 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/SectionView.cs @@ -0,0 +1,78 @@ +/* + * Process Hacker - + * mapped view of section + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.ComponentModel; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native +{ + /// + /// Represents a mapped view of a section. + /// + public sealed class SectionView : MemoryAlloc + { + internal SectionView(IntPtr baseAddress, IntPtr commitSize) + { + this.Memory = baseAddress; + this.Size = commitSize.ToInt32(); + } + + protected override void Free() + { + NtStatus status; + + if ((status = Win32.NtUnmapViewOfSection(ProcessHandle.Current, this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Flushes the section view. + /// + /// A NT status value. + public NtStatus Flush() + { + return ProcessHandle.Current.FlushMemory(this, this.Size); + } + + /// + /// Determines whether the image section is the same as + /// another file section. + /// + /// A section mapped as a file. + /// Whether the two sections are the same. + public bool IsSameFile(SectionView mappedAsFile) + { + if ((uint)Win32.NtAreMappedFilesTheSame(this, mappedAsFile) == this.Memory.ToUInt32()) + return true; + else + return false; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/VirtualMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/VirtualMemoryAlloc.cs new file mode 100644 index 000000000..f842a460c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/VirtualMemoryAlloc.cs @@ -0,0 +1,25 @@ +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native +{ + public class VirtualMemoryAlloc : MemoryAlloc + { + public VirtualMemoryAlloc(int size) + { + this.Memory = ProcessHandle.Current.AllocateMemory(size, MemoryProtection.ReadWrite); + this.Size = size; + } + + protected override void Free() + { + ProcessHandle.Current.FreeMemory(this, this.Size); + } + + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Memory/WtsMemoryAlloc.cs b/branches/ph-plugins/ProcessHacker.Native/Memory/WtsMemoryAlloc.cs new file mode 100644 index 000000000..170a86e9f --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Memory/WtsMemoryAlloc.cs @@ -0,0 +1,67 @@ +/* + * Process Hacker - + * terminal server memory allocation wrapper + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.ComponentModel; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents a memory allocation managed by the Terminal Server API. + /// + public class WtsMemoryAlloc : MemoryAlloc + { + public WtsMemoryAlloc(IntPtr memory) + : this(memory, true) + { } + + /// + /// Creates a memory allocation from an existing Terminal Server managed allocation. + /// + /// A pointer to the allocated memory. + /// Whether the memory allocation should be freed automatically. + public WtsMemoryAlloc(IntPtr memory, bool owned) + : base(memory, owned) + { } + + protected override void Free() + { + Win32.WTSFreeMemory(this); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Resize(int newSize) + { + throw new NotSupportedException(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int Size + { + get + { + throw new NotSupportedException(); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/NProcessHacker.cs b/branches/ph-plugins/ProcessHacker.Native/NProcessHacker.cs new file mode 100644 index 000000000..3783746de --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/NProcessHacker.cs @@ -0,0 +1,89 @@ +/* + * Process Hacker - + * interfacing code to native library + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using System.Security; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + [SuppressUnmanagedCodeSecurity] + public static class NProcessHacker + { + public enum WsInformationClass + { + WsCount = 0, + WsPrivateCount, + WsSharedCount, + WsShareableCount, + WsAllCounts + } + + [StructLayout(LayoutKind.Sequential)] + public struct WsAllCounts + { + public int Count; + public int PrivateCount; + public int SharedCount; + public int ShareableCount; + } + + [DllImport("nprocesshacker.dll")] + public static extern void KphHookDeinit(); + + [DllImport("nprocesshacker.dll")] + public static extern void KphHookInit(); + + [DllImport("nprocesshacker.dll", SetLastError = true)] + public static extern NtStatus PhQueryProcessWs( + [In] IntPtr ProcessHandle, + [In] WsInformationClass WsInformationClass, + [Out] out int WsInformation, + [In] int WsInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("nprocesshacker.dll", SetLastError = true)] + public static extern NtStatus PhQueryProcessWs( + [In] IntPtr ProcessHandle, + [In] WsInformationClass WsInformationClass, + [Out] out WsAllCounts WsInformation, + [In] int WsInformationLength, + [Out] out int ReturnLength + ); + + [DllImport("nprocesshacker.dll", SetLastError = true)] + public static extern NtStatus PhQueryNameFileObject( + [In] IntPtr FileHandle, + [In] IntPtr FileObjectNameInformation, + [In] int FileObjectNameInformationLength, + [Out] [Optional] out int ReturnLength + ); + + [DllImport("nprocesshacker.dll")] + public static extern void PhVoid(); + + [DllImport("nprocesshacker.dll", CharSet = CharSet.Unicode)] + public static extern VerifyResult PhVerifyFile(string FileName); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/NativeBitmap.cs b/branches/ph-plugins/ProcessHacker.Native/NativeBitmap.cs new file mode 100644 index 000000000..f0b5f4bff --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/NativeBitmap.cs @@ -0,0 +1,236 @@ +/* + * Process Hacker - + * bitmap + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Common; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + public class NativeBitmap : BaseObject + { + public struct BitmapRun + { + public BitmapRun(int index, int length) + { + _index = index; + _length = length; + } + + private int _index; + private int _length; + + public int Index + { + get { return _index; } + set { _index = value; } + } + + public int Length + { + get { return _length; } + set { _length = value; } + } + } + + private RtlBitmap _bitmap; + private MemoryAlloc _buffer; + + public NativeBitmap(int bits) + { + if (bits <= 0) + throw new ArgumentOutOfRangeException("The number of bits must be positive."); + + _buffer = new MemoryAlloc(Utils.DivideUp(bits, 32) * 4); + Win32.RtlInitializeBitMap(out _bitmap, _buffer, bits); + } + + protected override void DisposeObject(bool disposing) + { + if (_buffer != null) + _buffer.Dispose(disposing); + } + + public bool AreClear(int index, int length) + { + return Win32.RtlAreBitsClear(ref _bitmap, index, length); + } + + public bool AreSet(int index, int length) + { + return Win32.RtlAreBitsSet(ref _bitmap, index, length); + } + + public int Check(int index) + { + return Win32.RtlCheckBit(ref _bitmap, index); + } + + public void Clear() + { + Win32.RtlClearAllBits(ref _bitmap); + } + + public void Clear(int index) + { + Win32.RtlClearBit(ref _bitmap, index); + } + + public void Clear(int index, int length) + { + Win32.RtlClearBits(ref _bitmap, index, length); + } + + public int FindClear(int length) + { + return this.FindClear(length, 0); + } + + public int FindClear(int length, int hintIndex) + { + return Win32.RtlFindClearBits(ref _bitmap, length, hintIndex); + } + + public int FindClearAndSet(int length) + { + return this.FindClearAndSet(length, 0); + } + + public int FindClearAndSet(int length, int hintIndex) + { + return Win32.RtlFindClearBitsAndSet(ref _bitmap, length, hintIndex); + } + + public BitmapRun[] FindClearRuns(int count) + { + return this.FindClearRuns(count, false); + } + + public BitmapRun[] FindClearRuns(int count, bool locateLongest) + { + RtlBitmapRun[] runs = new RtlBitmapRun[count]; + int numberOfRuns; + + numberOfRuns = Win32.RtlFindClearRuns(ref _bitmap, runs, count, locateLongest); + + BitmapRun[] returnRuns = new BitmapRun[numberOfRuns]; + + for (int i = 0; i < numberOfRuns; i++) + returnRuns[i] = new BitmapRun(runs[i].StartingIndex, runs[i].NumberOfBits); + + return returnRuns; + } + + public BitmapRun FindBackwardClearRun(int index) + { + int startingIndex; + int numberOfBits; + + numberOfBits = Win32.RtlFindLastBackwardRunClear(ref _bitmap, index, out startingIndex); + + return new BitmapRun(startingIndex, numberOfBits); + } + + public BitmapRun FindFirstClearRun() + { + int startingIndex; + int numberOfBits; + + numberOfBits = Win32.RtlFindFirstRunClear(ref _bitmap, out startingIndex); + + return new BitmapRun(startingIndex, numberOfBits); + } + + public BitmapRun FindForwardClearRun(int index) + { + int startingIndex; + int numberOfBits; + + numberOfBits = Win32.RtlFindNextForwardRunClear(ref _bitmap, index, out startingIndex); + + return new BitmapRun(startingIndex, numberOfBits); + } + + public BitmapRun FindLongestClearRun() + { + int startingIndex; + int numberOfBits; + + numberOfBits = Win32.RtlFindLongestRunClear(ref _bitmap, out startingIndex); + + return new BitmapRun(startingIndex, numberOfBits); + } + + public int FindSet(int length) + { + return this.FindSet(length, 0); + } + + public int FindSet(int length, int hintIndex) + { + return Win32.RtlFindSetBits(ref _bitmap, length, hintIndex); + } + + public int FindSetAndClear(int length) + { + return this.FindSetAndClear(length, 0); + } + + public int FindSetAndClear(int length, int hintIndex) + { + return Win32.RtlFindSetBitsAndClear(ref _bitmap, length, hintIndex); + } + + public int GetClearCount() + { + return Win32.RtlNumberOfClearBits(ref _bitmap); + } + + public int GetSetCount() + { + return Win32.RtlNumberOfSetBits(ref _bitmap); + } + + public void Set() + { + Win32.RtlSetAllBits(ref _bitmap); + } + + public void Set(int index) + { + Win32.RtlSetBit(ref _bitmap, index); + } + + public void Set(int index, int length) + { + Win32.RtlSetBits(ref _bitmap, index, length); + } + + public bool Test(int index) + { + return Win32.RtlTestBit(ref _bitmap, index); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/NativeObject.cs b/branches/ph-plugins/ProcessHacker.Native/NativeObject.cs new file mode 100644 index 000000000..c8760992b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/NativeObject.cs @@ -0,0 +1,182 @@ +/* + * Process Hacker - + * native object wrapper code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native +{ + public class NativeObject : IDisposable + { + public static void WaitAll(NativeObject[] objects) + { + NativeHandle.WaitAll(ObjectsToISync(objects)); + } + + public static void WaitAll(NativeObject[] objects, int timeout) + { + NativeHandle.WaitAll(ObjectsToISync(objects), false, timeout * Win32.TimeMsTo100Ns, true); + } + + public static void WaitAll(NativeObject[] objects, DateTime timeout) + { + NativeHandle.WaitAll(ObjectsToISync(objects), false, timeout.ToFileTime(), false); + } + + public static void WaitAny(NativeObject[] objects) + { + NativeHandle.WaitAny(ObjectsToISync(objects)); + } + + public static void WaitAny(NativeObject[] objects, int timeout) + { + NativeHandle.WaitAny(ObjectsToISync(objects), false, timeout * Win32.TimeMsTo100Ns, true); + } + + public static void WaitAny(NativeObject[] objects, DateTime timeout) + { + NativeHandle.WaitAny(ObjectsToISync(objects), false, timeout.ToFileTime(), false); + } + + private static ISynchronizable[] ObjectsToISync(NativeObject[] objects) + { + ISynchronizable[] newArray = new ISynchronizable[objects.Length]; + + for (int i = 0; i < newArray.Length; i++) + newArray[i] = objects[i].Handle; + + return newArray; + } + + private NativeHandle _handle; + + /// + /// Closes the reference to the object. + /// + public void Dispose() + { + _handle.Dispose(); + } + + /// + /// Gets the underlying handle for the object. + /// + public NativeHandle Handle + { + get { return _handle; } + protected set { _handle = value; } + } + + /// + /// Signals the object and waits for another. + /// + /// The object to wait for. + public WaitStatus SignalAndWait(NativeObject obj) + { + return (WaitStatus)_handle.SignalAndWait(obj.Handle); + } + + /// + /// Signals the object and waits for another. + /// + /// The object to wait for. + /// A timeout value, in milliseconds. + public WaitStatus SignalAndWait(NativeObject obj, int timeout) + { + return (WaitStatus)_handle.SignalAndWait(obj.Handle, false, timeout * Win32.TimeMsTo100Ns); + } + + /// + /// Signals the object and waits for another. + /// + /// The object to wait for. + /// A time to wait until. + public WaitStatus SignalAndWait(NativeObject obj, DateTime timeout) + { + return (WaitStatus)_handle.SignalAndWait(obj.Handle, false, timeout.ToFileTime(), false); + } + + /// + /// Waits for the object to be signaled. + /// + public WaitStatus Wait() + { + return (WaitStatus)_handle.Wait(); + } + + /// + /// Waits for the object to be signaled. + /// + /// A timeout value, in milliseconds. + public WaitStatus Wait(int timeout) + { + return (WaitStatus)_handle.Wait(timeout * Win32.TimeMsTo100Ns, true); + } + + /// + /// Waits for the object to be signaled. + /// + /// A time to wait until. + public WaitStatus Wait(DateTime timeout) + { + return (WaitStatus)_handle.Wait(timeout.ToFileTime(), false); + } + } + + public class NativeObject : NativeObject + where THandle : NativeHandle + { + protected new THandle Handle + { + get { return base.Handle as THandle; } + set { base.Handle = value; } + } + } + + public enum WaitStatus : uint + { + Wait0 = 0x00000000, + Wait1 = 0x00000001, + Wait2 = 0x00000002, + Wait3 = 0x00000003, + Wait4 = 0x00000004, + Wait5 = 0x00000005, + Wait6 = 0x00000006, + Wait7 = 0x00000007, + Wait63 = 0x0000003f, + Abandoned = 0x00000080, + AbandonedWait0 = 0x00000080, + AbandonedWait1 = 0x00000081, + AbandonedWait2 = 0x00000082, + AbandonedWait3 = 0x00000083, + AbandonedWait4 = 0x00000084, + AbandonedWait5 = 0x00000085, + AbandonedWait6 = 0x00000086, + AbandonedWait7 = 0x00000087, + AbandonedWait63 = 0x000000bf, + UserApc = 0x000000c0, + KernelApc = 0x00000100, + Alerted = 0x00000101, + Timeout = 0x00000102 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/NativeTypeFactory.cs b/branches/ph-plugins/ProcessHacker.Native/NativeTypeFactory.cs new file mode 100644 index 000000000..e61bf6230 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/NativeTypeFactory.cs @@ -0,0 +1,607 @@ +/* + * Process Hacker - + * type factory + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Text; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native +{ + public static class NativeTypeFactory + { + private class FlagName + { + public string Name { get; set; } + public long Value { get; set; } + public bool Enabled { get; set; } + } + + public enum ObjectType + { + Adapter, + AlpcPort, + Callback, + DebugObject, + Desktop, + Device, + Directory, + Driver, + EtwRegistration, + Event, + EventPair, + File, + FilterCommunicationPort, + FilterConnectionPort, + IoCompletion, + Job, + Key, + KeyedEvent, + Mutant, + Process, + Profile, + Section, + Semaphore, + Service, + SymbolicLink, + Thread, + Timer, + TmEn, + TmRm, + TmTm, + TmTx, + Token, + TpWorkerFactory, + Type, + WindowStation, + WmiGuid + } + + #region Base Functions + + public static AccessEntry[] GetAccessEntries(ObjectType type) + { + AccessEntry[] entries; + + switch (type) + { + case ObjectType.AlpcPort: + entries = new AccessEntry[] + { + new AccessEntry("Full control", PortAccess.All, true, true), + new AccessEntry("Connect", PortAccess.Connect, true, true) + }; + break; + case ObjectType.DebugObject: + entries = new AccessEntry[] + { + new AccessEntry("Full control", DebugObjectAccess.All, true, true), + new AccessEntry("Read events", DebugObjectAccess.ReadEvent, true, true), + new AccessEntry("Assign processes", DebugObjectAccess.ProcessAssign, true, true), + new AccessEntry("Query information", DebugObjectAccess.QueryInformation, true, true), + new AccessEntry("Set information", DebugObjectAccess.SetInformation, true, true) + }; + break; + case ObjectType.Desktop: + entries = new AccessEntry[] + { + new AccessEntry("Full control", DesktopAccess.All, true, true), + new AccessEntry("Read", DesktopAccess.GenericRead, true, false), + new AccessEntry("Write", DesktopAccess.GenericWrite, true, false), + new AccessEntry("Execute", DesktopAccess.GenericExecute, true, false), + new AccessEntry("Enumerate", DesktopAccess.Enumerate, false, true), + new AccessEntry("Read objects", DesktopAccess.ReadObjects, false, true), + new AccessEntry("Playback journals", DesktopAccess.JournalPlayback, false, true), + new AccessEntry("Write objects", DesktopAccess.WriteObjects, false, true), + new AccessEntry("Create windows", DesktopAccess.CreateWindow, false, true), + new AccessEntry("Create menus", DesktopAccess.CreateMenu, false, true), + new AccessEntry("Create window hooks", DesktopAccess.HookControl, false, true), + new AccessEntry("Record journals", DesktopAccess.JournalRecord, false, true), + new AccessEntry("Switch desktop", DesktopAccess.SwitchDesktop, false, true) + }; + break; + case ObjectType.Directory: + entries = new AccessEntry[] + { + new AccessEntry("Full control", DirectoryAccess.All, true, true), + new AccessEntry("Query", DirectoryAccess.Query, true, true), + new AccessEntry("Traverse", DirectoryAccess.Traverse, true, true), + new AccessEntry("Create objects", DirectoryAccess.CreateObject, true, true), + new AccessEntry("Create subdirectories", DirectoryAccess.CreateSubdirectory, true, true) + }; + break; + case ObjectType.Event: + entries = new AccessEntry[] + { + new AccessEntry("Full control", EventAccess.All, true, true), + new AccessEntry("Query", EventAccess.QueryState, true, true), + new AccessEntry("Modify", EventAccess.ModifyState, true, true) + }; + break; + case ObjectType.EventPair: + entries = new AccessEntry[] + { + new AccessEntry("Full control", EventPairAccess.All, true, true) + }; + break; + case ObjectType.File: + entries = new AccessEntry[] + { + new AccessEntry("Full control", FileAccess.All, true, true), + new AccessEntry("Read & execute", FileAccess.GenericRead | FileAccess.GenericExecute, true, false), + new AccessEntry("Read", FileAccess.GenericRead, true, false), + new AccessEntry("Write", FileAccess.GenericWrite, true, false), + new AccessEntry("Traverse folder / execute file", FileAccess.Execute, false, true), + new AccessEntry("List folder / read data", FileAccess.ReadData, false, true), + new AccessEntry("Read attributes", FileAccess.ReadAttributes, false, true), + new AccessEntry("Read extended attributes", FileAccess.ReadEa, false, true), + new AccessEntry("Create files / write data", FileAccess.WriteData, false, true), + new AccessEntry("Create folders / append data", FileAccess.AppendData, false, true), + new AccessEntry("Write attributes", FileAccess.WriteAttributes, false, true), + new AccessEntry("Write extended attributes", FileAccess.WriteEa, false, true), + new AccessEntry("Delete subfolders and files", FileAccess.DeleteChild, false, true) + }; + break; + case ObjectType.IoCompletion: + entries = new AccessEntry[] + { + new AccessEntry("Full control", IoCompletionAccess.All, true, true), + new AccessEntry("Query", IoCompletionAccess.QueryState, true, true), + new AccessEntry("Modify", IoCompletionAccess.ModifyState, true, true) + }; + break; + case ObjectType.Job: + entries = new AccessEntry[] + { + new AccessEntry("Full control", JobObjectAccess.All, true, true), + new AccessEntry("Query", JobObjectAccess.Query, true, true), + new AccessEntry("Assign processes", JobObjectAccess.AssignProcess, true, true), + new AccessEntry("Set attributes", JobObjectAccess.SetAttributes, true, true), + new AccessEntry("Set security attributes", JobObjectAccess.SetSecurityAttributes, true, true), + new AccessEntry("Terminate", JobObjectAccess.Terminate, true, true) + }; + break; + case ObjectType.Key: + entries = new AccessEntry[] + { + new AccessEntry("Full control", KeyAccess.All, true, true), + new AccessEntry("Read", KeyAccess.GenericRead, true, false), + new AccessEntry("Write", KeyAccess.GenericWrite, true, false), + new AccessEntry("Execute", KeyAccess.GenericExecute, true, false), + new AccessEntry("Enumerate subkeys", KeyAccess.EnumerateSubKeys, false, true), + new AccessEntry("Query values", KeyAccess.QueryValue, false, true), + new AccessEntry("Notify", KeyAccess.Notify, false, true), + new AccessEntry("Set values", KeyAccess.SetValue, false, true), + new AccessEntry("Create subkeys", KeyAccess.CreateSubKey, false, true), + new AccessEntry("Create links", KeyAccess.CreateLink, false, true) + }; + break; + case ObjectType.KeyedEvent: + entries = new AccessEntry[] + { + new AccessEntry("Full control", KeyedEventAccess.All, true, true), + new AccessEntry("Wait", KeyedEventAccess.Wait, true, true), + new AccessEntry("Wake", KeyedEventAccess.Wake, true, true) + }; + break; + case ObjectType.Mutant: + entries = new AccessEntry[] + { + new AccessEntry("Full control", MutantAccess.All, true, true), + new AccessEntry("Query", MutantAccess.QueryState, true, true) + }; + break; + case ObjectType.Process: + entries = new AccessEntry[] + { + new AccessEntry("Full control", + OSVersion.HasQueryLimitedInformation ? + (ProcessAccess.All | ProcessAccess.QueryLimitedInformation) : + ProcessAccess.All, true, true), + OSVersion.HasQueryLimitedInformation ? + new AccessEntry("Query limited information", ProcessAccess.QueryLimitedInformation, true, true) : + new AccessEntry(null, 0, false, false), + new AccessEntry("Query information", + OSVersion.HasQueryLimitedInformation ? + (ProcessAccess.QueryInformation | ProcessAccess.QueryLimitedInformation) : + ProcessAccess.QueryInformation, true, true), + new AccessEntry("Set information", ProcessAccess.SetInformation, true, true), + new AccessEntry("Set quotas", ProcessAccess.SetQuota, true, true), + new AccessEntry("Set session ID", ProcessAccess.SetSessionId, true, true), + new AccessEntry("Create threads", ProcessAccess.CreateThread, true, true), + new AccessEntry("Create processes", ProcessAccess.CreateProcess, true, true), + new AccessEntry("Modify memory", ProcessAccess.VmOperation, true, true), + new AccessEntry("Read memory", ProcessAccess.VmRead, true, true), + new AccessEntry("Write memory", ProcessAccess.VmWrite, true, true), + new AccessEntry("Duplicate handles", ProcessAccess.DupHandle, true, true), + new AccessEntry("Suspend / resume / set port", ProcessAccess.SuspendResume, true, true), + new AccessEntry("Terminate", ProcessAccess.Terminate, true, true), + }; + break; + case ObjectType.Profile: + entries = new AccessEntry[] + { + new AccessEntry("Full control", ProfileAccess.All, true, true), + new AccessEntry("Control", ProfileAccess.Control, true, true) + }; + break; + case ObjectType.Section: + entries = new AccessEntry[] + { + new AccessEntry("Full control", SectionAccess.All, true, true), + new AccessEntry("Query", SectionAccess.Query, true, true), + new AccessEntry("Map for read", SectionAccess.MapRead, true, true), + new AccessEntry("Map for write", SectionAccess.MapWrite, true, true), + new AccessEntry("Map for execute", SectionAccess.MapExecute, true, true), + new AccessEntry("Map for execute (explicit)", SectionAccess.MapExecuteExplicit, true, true), + new AccessEntry("Extend size", SectionAccess.ExtendSize, true, true) + }; + break; + case ObjectType.Semaphore: + entries = new AccessEntry[] + { + new AccessEntry("Full control", SemaphoreAccess.All, true, true), + new AccessEntry("Query", SemaphoreAccess.QueryState, true, true), + new AccessEntry("Modify", SemaphoreAccess.ModifyState, true, true) + }; + break; + case ObjectType.Service: + entries = new AccessEntry[] + { + new AccessEntry("Full control", ServiceAccess.All, true, true), + new AccessEntry("Query status", ServiceAccess.QueryStatus, true, true), + new AccessEntry("Query configuration", ServiceAccess.QueryConfig, true, true), + new AccessEntry("Modify configuration", ServiceAccess.ChangeConfig, true, true), + new AccessEntry("Enumerate dependents", ServiceAccess.EnumerateDependents, true, true), + new AccessEntry("Start", ServiceAccess.Start, true, true), + new AccessEntry("Stop", ServiceAccess.Stop, true, true), + new AccessEntry("Pause / continue", ServiceAccess.PauseContinue, true, true), + new AccessEntry("Interrogate", ServiceAccess.Interrogate, true, true), + new AccessEntry("User-defined control", ServiceAccess.UserDefinedControl, true, true) + }; + break; + case ObjectType.SymbolicLink: + entries = new AccessEntry[] + { + new AccessEntry("Full control", SymbolicLinkAccess.All, true, true), + new AccessEntry("Query", SymbolicLinkAccess.Query, true, true) + }; + break; + case ObjectType.Thread: + entries = new AccessEntry[] + { + new AccessEntry("Full control", + OSVersion.HasQueryLimitedInformation ? + (ThreadAccess.All | ThreadAccess.QueryLimitedInformation | ThreadAccess.SetLimitedInformation) : + ThreadAccess.All, true, true), + OSVersion.HasQueryLimitedInformation ? + new AccessEntry("Query limited information", ThreadAccess.QueryLimitedInformation, true, true) : + new AccessEntry(null, 0, false, false), + new AccessEntry("Query information", ThreadAccess.QueryInformation, true, true), + OSVersion.HasQueryLimitedInformation ? + new AccessEntry("Set limited information", ThreadAccess.SetLimitedInformation, true, true) : + new AccessEntry(null, 0, false, false), + new AccessEntry("Set information", ThreadAccess.SetInformation, true, true), + new AccessEntry("Get context", ThreadAccess.GetContext, true, true), + new AccessEntry("Set context", ThreadAccess.SetContext, true, true), + new AccessEntry("Set token", ThreadAccess.SetThreadToken, true, true), + new AccessEntry("Alert", ThreadAccess.Alert, true, true), + new AccessEntry("Impersonate", ThreadAccess.Impersonate, true, true), + new AccessEntry("Direct impersonate", ThreadAccess.DirectImpersonation, true, true), + new AccessEntry("Suspend / resume", ThreadAccess.SuspendResume, true, true), + new AccessEntry("Terminate", ThreadAccess.Terminate, true, true), + }; + break; + case ObjectType.Timer: + entries = new AccessEntry[] + { + new AccessEntry("Full control", TimerAccess.All, true, true), + new AccessEntry("Query", TimerAccess.QueryState, true, true), + new AccessEntry("Modify", TimerAccess.ModifyState, true, true) + }; + break; + case ObjectType.TmEn: + entries = new AccessEntry[] + { + new AccessEntry("Full control", EnlistmentAccess.All, true, true), + new AccessEntry("Read", EnlistmentAccess.GenericRead, true, false), + new AccessEntry("Write", EnlistmentAccess.GenericWrite, true, false), + new AccessEntry("Execute", EnlistmentAccess.GenericExecute, true, false), + new AccessEntry("Query information", EnlistmentAccess.QueryInformation, false, true), + new AccessEntry("Set information", EnlistmentAccess.SetInformation, false, true), + new AccessEntry("Recover", EnlistmentAccess.Recover, false, true), + new AccessEntry("Subordinate rights", EnlistmentAccess.SubordinateRights, false, true), + new AccessEntry("Superior rights", EnlistmentAccess.SuperiorRights, false, true) + }; + break; + case ObjectType.TmRm: + entries = new AccessEntry[] + { + new AccessEntry("Full control", ResourceManagerAccess.All, true, true), + new AccessEntry("Read", ResourceManagerAccess.GenericRead, true, false), + new AccessEntry("Write", ResourceManagerAccess.GenericWrite, true, false), + new AccessEntry("Execute", ResourceManagerAccess.GenericExecute, true, false), + new AccessEntry("Query information", ResourceManagerAccess.QueryInformation, false, true), + new AccessEntry("Set information", ResourceManagerAccess.SetInformation, false, true), + new AccessEntry("Get notifications", ResourceManagerAccess.GetNotification, false, true), + new AccessEntry("Enlist", ResourceManagerAccess.Enlist, false, true), + new AccessEntry("Recover", ResourceManagerAccess.Recover, false, true), + new AccessEntry("Register protocols", ResourceManagerAccess.RegisterProtocol, false, true), + new AccessEntry("Complete propagation", ResourceManagerAccess.CompletePropagation, false, true) + }; + break; + case ObjectType.TmTm: + entries = new AccessEntry[] + { + new AccessEntry("Full control", TmAccess.All, true, true), + new AccessEntry("Read", TmAccess.GenericRead, true, false), + new AccessEntry("Write", TmAccess.GenericWrite, true, false), + new AccessEntry("Execute", TmAccess.GenericExecute, true, false), + new AccessEntry("Query information", TmAccess.QueryInformation, true, false), + new AccessEntry("Set information", TmAccess.SetInformation, true, false), + new AccessEntry("Recover", TmAccess.Recover, true, false), + new AccessEntry("Rename", TmAccess.Rename, true, false), + new AccessEntry("Create resource manager", TmAccess.CreateRm, true, false), + new AccessEntry("Bind transactions", TmAccess.BindTransaction, true, false) + }; + break; + case ObjectType.TmTx: + entries = new AccessEntry[] + { + new AccessEntry("Full control", TransactionAccess.All, true, true), + new AccessEntry("Read", TransactionAccess.GenericRead, true, false), + new AccessEntry("Write", TransactionAccess.GenericWrite, true, false), + new AccessEntry("Execute", TransactionAccess.GenericExecute, true, false), + new AccessEntry("Query information", TransactionAccess.QueryInformation, false, true), + new AccessEntry("Set information", TransactionAccess.SetInformation, false, true), + new AccessEntry("Enlist", TransactionAccess.Enlist, false, true), + new AccessEntry("Commit", TransactionAccess.Commit, false, true), + new AccessEntry("Rollback", TransactionAccess.Rollback, false, true), + new AccessEntry("Propagate", TransactionAccess.Propagate, false, true), + }; + break; + case ObjectType.Token: + entries = new AccessEntry[] + { + new AccessEntry("Full control", TokenAccess.All, true, true), + new AccessEntry("Read", TokenAccess.GenericRead, true, false), + new AccessEntry("Write", TokenAccess.GenericWrite, true, false), + new AccessEntry("Execute", TokenAccess.GenericExecute, true, false), + new AccessEntry("Adjust privileges", TokenAccess.AdjustPrivileges, false, true), + new AccessEntry("Adjust groups", TokenAccess.AdjustGroups, false, true), + new AccessEntry("Adjust defaults", TokenAccess.AdjustDefault, false, true), + new AccessEntry("Adjust session ID", TokenAccess.AdjustSessionId, false, true), + new AccessEntry("Assign as primary token", TokenAccess.AssignPrimary, false, true), + new AccessEntry("Duplicate", TokenAccess.Duplicate, false, true), + new AccessEntry("Impersonate", TokenAccess.Impersonate, false, true), + new AccessEntry("Query", TokenAccess.Query, false, true), + new AccessEntry("Query source", TokenAccess.QuerySource, false, true) + }; + break; + case ObjectType.Type: + entries = new AccessEntry[] + { + new AccessEntry("Full control", ObjectTypeAccess.All, true, true), + new AccessEntry("Create", ObjectTypeAccess.Create, true, true) + }; + break; + case ObjectType.WindowStation: + entries = new AccessEntry[] + { + new AccessEntry("Full control", WindowStationAccess.All, true, true), + new AccessEntry("Read", WindowStationAccess.GenericRead, true, false), + new AccessEntry("Write", WindowStationAccess.GenericWrite, true, false), + new AccessEntry("Execute", WindowStationAccess.GenericExecute, true, false), + new AccessEntry("Enumerate", WindowStationAccess.Enumerate, false, true), + new AccessEntry("Enumerate desktops", WindowStationAccess.EnumDesktops, false, true), + new AccessEntry("Read attributes", WindowStationAccess.ReadAttributes, false, true), + new AccessEntry("Read screen", WindowStationAccess.ReadScreen, false, true), + new AccessEntry("Access clipboard", WindowStationAccess.AccessClipboard, false, true), + new AccessEntry("Access global atoms", WindowStationAccess.AccessGlobalAtoms, false, true), + new AccessEntry("Create desktop", WindowStationAccess.CreateDesktop, false, true), + new AccessEntry("Write attributes", WindowStationAccess.WriteAttributes, false, true), + new AccessEntry("Exit windows", WindowStationAccess.ExitWindows, false, true) + }; + break; + default: + entries = null; + break; + } + + // Add the standard rights. + return Utils.Concat(entries, new AccessEntry[] + { + new AccessEntry("Synchronize", StandardRights.Synchronize, false, true), + new AccessEntry("Delete", StandardRights.Delete, false, true), + new AccessEntry("Read permissions", StandardRights.ReadControl, false, true), + new AccessEntry("Change permissions", StandardRights.WriteDac, false, true), + new AccessEntry("Take ownership", StandardRights.WriteOwner, false, true) + }); + } + + public static Type GetAccessType(ObjectType type) + { + switch (type) + { + case ObjectType.AlpcPort: + return typeof(PortAccess); + case ObjectType.DebugObject: + return typeof(DebugObjectAccess); + case ObjectType.Desktop: + return typeof(DesktopAccess); + case ObjectType.Directory: + return typeof(DirectoryAccess); + case ObjectType.Event: + return typeof(EventAccess); + case ObjectType.EventPair: + return typeof(EventPairAccess); + case ObjectType.File: + return typeof(FileAccess); + case ObjectType.FilterCommunicationPort: + case ObjectType.FilterConnectionPort: + return typeof(FltPortAccess); + case ObjectType.IoCompletion: + return typeof(IoCompletionAccess); + case ObjectType.Job: + return typeof(JobObjectAccess); + case ObjectType.Key: + return typeof(KeyAccess); + case ObjectType.KeyedEvent: + return typeof(KeyedEventAccess); + case ObjectType.Mutant: + return typeof(MutantAccess); + case ObjectType.Process: + return typeof(ProcessAccess); + case ObjectType.Profile: + return typeof(ProfileAccess); + case ObjectType.Section: + return typeof(SectionAccess); + case ObjectType.Semaphore: + return typeof(SemaphoreAccess); + case ObjectType.Service: + return typeof(ServiceAccess); + case ObjectType.SymbolicLink: + return typeof(SymbolicLinkAccess); + case ObjectType.Thread: + return typeof(ThreadAccess); + case ObjectType.Timer: + return typeof(TimerAccess); + case ObjectType.TmEn: + return typeof(EnlistmentAccess); + case ObjectType.TmRm: + return typeof(ResourceManagerAccess); + case ObjectType.TmTm: + return typeof(TmAccess); + case ObjectType.TmTx: + return typeof(TransactionAccess); + case ObjectType.Token: + return typeof(TokenAccess); + case ObjectType.Type: + return typeof(ObjectTypeAccess); + case ObjectType.WindowStation: + return typeof(WindowStationAccess); + default: + throw new NotSupportedException(); + } + } + + public static ObjectType GetObjectType(string typeName) + { + foreach (string value in Enum.GetNames(typeof(ObjectType))) + { + if (string.Equals(value, typeName, StringComparison.InvariantCultureIgnoreCase)) + return (ObjectType)Enum.Parse(typeof(ObjectType), value); + } + + if (string.Equals(typeName, "ALPC Port", StringComparison.InvariantCultureIgnoreCase)) + return ObjectType.AlpcPort; + if (string.Equals(typeName, "Port", StringComparison.InvariantCultureIgnoreCase)) + return ObjectType.AlpcPort; + if (string.Equals(typeName, "WaitablePort", StringComparison.InvariantCultureIgnoreCase)) + return ObjectType.AlpcPort; + + throw new NotSupportedException(); + } + + public static SeObjectType GetSeObjectType(ObjectType type) + { + switch (type) + { + case ObjectType.Desktop: + case ObjectType.WindowStation: + return SeObjectType.WindowObject; + case ObjectType.Service: + return SeObjectType.Service; + default: + return SeObjectType.KernelObject; + } + } + + #endregion + + public static string GetAccessString(Type accessType, object access) + { + StringBuilder accessSb = new StringBuilder(); + long accessLong = Convert.ToInt64(access); + var accessTypeNames = Utils.SortFlagNames(accessType). + ConvertAll((kvp) => new FlagName() { Name = kvp.Key, Value = kvp.Value, Enabled = true }); + var srNames = Utils.SortFlagNames(typeof(StandardRights)). + ConvertAll((kvp) => new FlagName() { Name = kvp.Key, Value = kvp.Value, Enabled = true }); + + // Get the strings for the matching bits in the given enum type. + foreach (var fn in accessTypeNames) + { + if ( + fn.Enabled && + (accessLong & fn.Value) == fn.Value + ) + { + accessSb.Append(fn.Name + ", "); + // Disable equal or more specific flag names in the lists. + accessTypeNames.ForEach((fn2) => + { + if ((fn.Value | fn2.Value) == fn.Value) + fn2.Enabled = false; + }); + srNames.ForEach((fn2) => + { + if ((fn.Value | fn2.Value) == fn.Value) + fn2.Enabled = false; + }); + } + } + + // Get the strings for the matching bits in standard rights. + foreach (var fn in srNames) + { + if ( + fn.Enabled && + (accessLong & fn.Value) == fn.Value + ) + { + accessSb.Append(fn.Name + ", "); + // Disable equal or more specific flag names in the lists. + srNames.ForEach((fn2) => + { + if ((fn.Value | fn2.Value) == fn.Value) + fn2.Enabled = false; + }); + } + } + + string accessString = accessSb.ToString(); + + // Removing trailing ", ". + if (accessString.EndsWith(", ")) + return accessString.Remove(accessString.Length - 2, 2); + else + return accessString; + } + + public static Type GetAccessType(string typeName) + { + return GetAccessType(GetObjectType(typeName)); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/NativeUtils.cs b/branches/ph-plugins/ProcessHacker.Native/NativeUtils.cs new file mode 100644 index 000000000..6a0c49bcd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/NativeUtils.cs @@ -0,0 +1,229 @@ +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native +{ + /// + /// Provides various utility methods. + /// + public static class NativeUtils + { + /// + /// Calls a function. + /// + /// The address of the function. + /// The first parameter to pass. + /// The second parameter to pass. + /// The third parameter to pass. + public static void Call(IntPtr address, IntPtr param1, IntPtr param2, IntPtr param3) + { + // Queue a user-mode APC to the current thread. + ThreadHandle.Current.QueueApc(address, param1, param2, param3); + // Flush the APC queue. + ThreadHandle.TestAlert(); + } + + public unsafe static void CopyProcessParameters( + ProcessHandle processHandle, + IntPtr peb, + ProcessCreationFlags creationFlags, + string imagePathName, + string dllPath, + string currentDirectory, + string commandLine, + EnvironmentBlock environment, + string windowTitle, + string desktopInfo, + string shellInfo, + string runtimeInfo, + ref StartupInfo startupInfo + ) + { + UnicodeString imagePathNameStr; + UnicodeString dllPathStr; + UnicodeString currentDirectoryStr; + UnicodeString commandLineStr; + UnicodeString windowTitleStr; + UnicodeString desktopInfoStr; + UnicodeString shellInfoStr; + UnicodeString runtimeInfoStr; + + // Create the unicode strings. + + imagePathNameStr = new UnicodeString(imagePathName); + dllPathStr = new UnicodeString(dllPath); + currentDirectoryStr = new UnicodeString(currentDirectory); + commandLineStr = new UnicodeString(commandLine); + windowTitleStr = new UnicodeString(windowTitle); + desktopInfoStr = new UnicodeString(desktopInfo); + shellInfoStr = new UnicodeString(shellInfo); + runtimeInfoStr = new UnicodeString(runtimeInfo); + + try + { + NtStatus status; + IntPtr processParameters; + + // Create the process parameter block. + + status = Win32.RtlCreateProcessParameters( + out processParameters, + ref imagePathNameStr, + ref dllPathStr, + ref currentDirectoryStr, + ref commandLineStr, + environment, + ref windowTitleStr, + ref desktopInfoStr, + ref shellInfoStr, + ref runtimeInfoStr + ); + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + try + { + // Allocate a new memory region in the remote process for + // the environment block and copy it over. + + int environmentLength; + IntPtr newEnvironment; + + environmentLength = environment.GetLength(); + newEnvironment = processHandle.AllocateMemory( + environmentLength, + MemoryProtection.ReadWrite + ); + + processHandle.WriteMemory( + newEnvironment, + environment, + environmentLength + ); + + // Copy over the startup info data. + RtlUserProcessParameters* paramsStruct = (RtlUserProcessParameters*)processParameters; + + paramsStruct->Environment = newEnvironment; + paramsStruct->StartingX = startupInfo.X; + paramsStruct->StartingY = startupInfo.Y; + paramsStruct->CountX = startupInfo.XSize; + paramsStruct->CountY = startupInfo.YSize; + paramsStruct->CountCharsX = startupInfo.XCountChars; + paramsStruct->CountCharsY = startupInfo.YCountChars; + paramsStruct->FillAttribute = startupInfo.FillAttribute; + paramsStruct->WindowFlags = startupInfo.Flags; + paramsStruct->ShowWindowFlags = startupInfo.ShowWindow; + + if ((startupInfo.Flags & StartupFlags.UseStdHandles) == StartupFlags.UseStdHandles) + { + paramsStruct->StandardInput = startupInfo.StdInputHandle; + paramsStruct->StandardOutput = startupInfo.StdOutputHandle; + paramsStruct->StandardError = startupInfo.StdErrorHandle; + } + + // TODO: Add console support. + + // Allocate a new memory region in the remote process for + // the process parameters. + + IntPtr newProcessParameters; + IntPtr regionSize = paramsStruct->Length.ToIntPtr(); + + newProcessParameters = processHandle.AllocateMemory( + IntPtr.Zero, + ref regionSize, + MemoryFlags.Commit, + MemoryProtection.ReadWrite + ); + + paramsStruct->MaximumLength = regionSize.ToInt32(); + + processHandle.WriteMemory(newProcessParameters, processParameters, paramsStruct->Length); + + // Modify the process parameters pointer in the PEB. + processHandle.WriteMemory( + peb.Increment(Peb.ProcessParametersOffset), + &newProcessParameters, + IntPtr.Size + ); + } + finally + { + Win32.RtlDestroyProcessParameters(processParameters); + } + } + finally + { + imagePathNameStr.Dispose(); + dllPathStr.Dispose(); + currentDirectoryStr.Dispose(); + commandLineStr.Dispose(); + windowTitleStr.Dispose(); + desktopInfoStr.Dispose(); + shellInfoStr.Dispose(); + runtimeInfoStr.Dispose(); + } + } + + public static string FormatNativeKeyName(string nativeKeyName) + { + const string hklmString = "\\registry\\machine"; + const string hkcrString = "\\registry\\machine\\software\\classes"; + string hkcuString = "\\registry\\user\\" + + System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower(); + string hkcucrString = "\\registry\\user\\" + + System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower() + "_classes"; + const string hkuString = "\\registry\\user"; + + if (nativeKeyName.ToLower().StartsWith(hkcrString)) + return "HKCR" + nativeKeyName.Substring(hkcrString.Length); + else if (nativeKeyName.ToLower().StartsWith(hklmString)) + return "HKLM" + nativeKeyName.Substring(hklmString.Length); + else if (nativeKeyName.ToLower().StartsWith(hkcucrString)) + return "HKCU\\Software\\Classes" + nativeKeyName.Substring(hkcucrString.Length); + else if (nativeKeyName.ToLower().StartsWith(hkcuString)) + return "HKCU" + nativeKeyName.Substring(hkcuString.Length); + else if (nativeKeyName.ToLower().StartsWith(hkuString)) + return "HKU" + nativeKeyName.Substring(hkuString.Length); + else + return nativeKeyName; + } + + public static string GetMessage(IntPtr dllHandle, int messageTableId, int messageLanguageId, int messageId) + { + NtStatus status; + IntPtr messageEntry; + string message; + + status = Win32.RtlFindMessage( + dllHandle, + messageTableId, + messageLanguageId, + messageId, + out messageEntry + ); + + if (status.IsError()) + return null; + + var region = new MemoryRegion(messageEntry); + var entry = region.ReadStruct(); + + // Read the message, depending on format. + if ((entry.Flags & MessageResourceFlags.Unicode) == MessageResourceFlags.Unicode) + { + message = region.ReadUnicodeString(MessageResourceEntry.TextOffset); + } + else + { + message = region.ReadAnsiString(MessageResourceEntry.TextOffset); + } + + return message; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/OSVersion.cs b/branches/ph-plugins/ProcessHacker.Native/OSVersion.cs new file mode 100644 index 000000000..fcd5159fb --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/OSVersion.cs @@ -0,0 +1,233 @@ +/* + * Process Hacker - + * operating system version information + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + public enum OSArch + { + I386, + Amd64 + } + + public enum WindowsVersion + { + /// + /// Windows XP SP2, SP3. + /// + XP = 51, + + /// + /// Windows Server 2003. + /// + Server2003 = 52, + + /// + /// Windows Vista SP0, SP1, SP2, Windows Server 2008. + /// + Vista = 60, + + /// + /// Windows 7 SP0. + /// + Seven = 61, + + /// + /// An unreleased version of Windows. + /// + Unreleased = int.MaxValue + } + + public static class OSVersion + { + private static int _bits = IntPtr.Size * 8; + private static OSArch _arch = IntPtr.Size == 4 ? OSArch.I386 : OSArch.Amd64; + private static WindowsVersion _windowsVersion; + + private static ProcessAccess _minProcessQueryInfoAccess = ProcessAccess.QueryInformation; + private static ThreadAccess _minThreadQueryInfoAccess = ThreadAccess.QueryInformation; + private static ThreadAccess _minThreadSetInfoAccess = ThreadAccess.SetInformation; + + private static bool _hasCycleTime = false; + private static bool _hasExtendedTaskbar = false; + private static bool _hasProtectedProcesses = false; + private static bool _hasPsSuspendResumeProcess = false; + private static bool _hasQueryLimitedInformation = false; + private static bool _hasSetAccessToken = false; + private static bool _hasTaskDialogs = false; + private static bool _hasUac = false; + private static bool _hasWin32ImageFileName = false; + + static OSVersion() + { + System.Version version = Environment.OSVersion.Version; + + if (version.Major == 5 && version.Minor == 1) + _windowsVersion = WindowsVersion.XP; + else if (version.Major == 5 && version.Minor == 2) + _windowsVersion = WindowsVersion.Server2003; + else if (version.Major == 6 && version.Minor == 0) + _windowsVersion = WindowsVersion.Vista; + else if (version.Major == 6 && version.Minor == 1) + _windowsVersion = WindowsVersion.Seven; + else if ((version.Major == 6 && version.Minor > 1) || version.Major > 6) + _windowsVersion = WindowsVersion.Unreleased; + + if (IsBelow(WindowsVersion.Vista)) + { + _hasSetAccessToken = true; + } + + if (IsAboveOrEqual(WindowsVersion.Vista)) + { + _minProcessQueryInfoAccess = ProcessAccess.QueryLimitedInformation; + _minThreadQueryInfoAccess = ThreadAccess.QueryLimitedInformation; + _minThreadSetInfoAccess = ThreadAccess.SetLimitedInformation; + + _hasCycleTime = true; + _hasProtectedProcesses = true; + _hasPsSuspendResumeProcess = true; + _hasQueryLimitedInformation = true; + _hasTaskDialogs = true; + _hasUac = true; + _hasWin32ImageFileName = true; + } + + if (IsAboveOrEqual(WindowsVersion.Seven)) + { + _hasExtendedTaskbar = true; + } + } + + public static int Bits + { + get { return _bits; } + } + + public static string BitsString + { + get { return _bits.ToString() + "-" + "bit"; } + } + + public static OSArch Architecture + { + get { return _arch; } + } + + public static WindowsVersion WindowsVersion + { + get { return _windowsVersion; } + } + + public static ProcessAccess MinProcessQueryInfoAccess + { + get { return _minProcessQueryInfoAccess; } + } + + public static ThreadAccess MinThreadQueryInfoAccess + { + get { return _minThreadQueryInfoAccess; } + } + + public static ThreadAccess MinThreadSetInfoAccess + { + get { return _minThreadSetInfoAccess; } + } + + public static bool HasCycleTime + { + get { return _hasCycleTime; } + } + + public static bool HasExtendedTaskbar + { + get { return _hasExtendedTaskbar; } + } + + public static bool HasProtectedProcesses + { + get { return _hasProtectedProcesses; } + } + + public static bool HasPsSuspendResumeProcess + { + get { return _hasPsSuspendResumeProcess; } + } + + public static bool HasQueryLimitedInformation + { + get { return _hasQueryLimitedInformation; } + } + + public static bool HasSetAccessToken + { + get { return _hasSetAccessToken; } + } + + public static bool HasTaskDialogs + { + get { return _hasTaskDialogs; } + } + + public static bool HasUac + { + get { return _hasUac; } + } + + public static bool HasWin32ImageFileName + { + get { return _hasWin32ImageFileName; } + } + + public static bool IsAmd64() + { + return _arch == OSArch.Amd64; + } + + public static bool IsI386() + { + return _arch == OSArch.I386; + } + + public static bool IsAbove(WindowsVersion version) + { + return (int)_windowsVersion > (int)version; + } + + public static bool IsAboveOrEqual(WindowsVersion version) + { + return (int)_windowsVersion >= (int)version; + } + + public static bool IsBelowOrEqual(WindowsVersion version) + { + return (int)_windowsVersion <= (int)version; + } + + public static bool IsBelow(WindowsVersion version) + { + return (int)_windowsVersion < (int)version; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/DebugObjectHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/DebugObjectHandle.cs new file mode 100644 index 000000000..03f81eaed --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/DebugObjectHandle.cs @@ -0,0 +1,134 @@ +/* + * Process Hacker - + * debug object handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class DebugObjectHandle : NativeHandle + { + public static DebugObjectHandle Create(DebugObjectAccess access, DebugObjectFlags flags) + { + return Create(access, null, flags); + } + + public static DebugObjectHandle Create(DebugObjectAccess access, string name, DebugObjectFlags flags) + { + return Create(access, name, 0, null, flags); + } + + public static DebugObjectHandle Create(DebugObjectAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, DebugObjectFlags flags) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateDebugObject( + out handle, + access, + ref oa, + flags + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new DebugObjectHandle(handle, true); + } + + public DebugObjectHandle FromHandle(IntPtr handle) + { + return new DebugObjectHandle(handle, false); + } + + internal DebugObjectHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public DebugObjectHandle(ProcessHandle processHandle) + { + this.Handle = processHandle.GetDebugObjectHandle(); + + // Check if we got a handle. If we didn't the process is not being debugged. + if (this.Handle == IntPtr.Zero) + throw new WindowsException(NtStatus.DebuggerInactive); + } + + public void Continue(ClientId cid, NtStatus continueStatus) + { + NtStatus status; + + if ((status = Win32.NtDebugContinue( + this, + ref cid, + continueStatus + )) > NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void SetFlags(DebugObjectFlags flags) + { + unsafe + { + NtStatus status; + int retLength; + + if ((status = Win32.NtSetInformationDebugObject( + this, + DebugObjectInformationClass.DebugObjectFlags, + new IntPtr(&flags), + sizeof(DebugObjectFlags), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + public IntPtr WaitForDebugEvent(bool alertable, long timeout, bool timeoutRelative) + { + // FIXME + throw new NotImplementedException(); + + //NtStatus status; + //long realTimeout = timeoutRelative ? -timeout : timeout; + + //if ((status = Win32.NtWaitForDebugEvent( + // this, + // alertable, + // ref realTimeout, + // IntPtr.Zero + // )) >= NtStatus.Error) + // Win32.ThrowLastError(status); + + //return IntPtr.Zero; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/DesktopHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/DesktopHandle.cs new file mode 100644 index 000000000..3121bf3d6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/DesktopHandle.cs @@ -0,0 +1,77 @@ +/* + * Process Hacker - + * desktop handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class DesktopHandle : UserHandle + { + public static DesktopHandle GetCurrent() + { + return GetThreadDesktop(Win32.GetCurrentThreadId()); + } + + public static DesktopHandle GetThreadDesktop(int threadId) + { + IntPtr handle = Win32.GetThreadDesktop(threadId); + + if (handle == IntPtr.Zero) + Win32.ThrowLastError(); + + return new DesktopHandle(handle, false); + } + + public DesktopHandle(string name, bool allowOtherAccountHook, DesktopAccess access) + { + this.Handle = Win32.OpenDesktop(name, allowOtherAccountHook ? 1 : 0, false, access); + + if (this.Handle == IntPtr.Zero) + Win32.ThrowLastError(); + } + + private DesktopHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + protected override void Close() + { + Win32.CloseDesktop(this); + } + + public void SetCurrent() + { + if (!Win32.SetThreadDesktop(this)) + Win32.ThrowLastError(); + } + + public void Switch() + { + if (!Win32.SwitchDesktop(this)) + Win32.ThrowLastError(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/DirectoryHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/DirectoryHandle.cs new file mode 100644 index 000000000..92d0f35cf --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/DirectoryHandle.cs @@ -0,0 +1,181 @@ +/* + * Process Hacker - + * directory handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a directory object, which contains a collection of objects. + /// + public class DirectoryHandle : NativeHandle + { + public delegate bool EnumObjectsDelegate(ObjectEntry obj); + + public struct ObjectEntry + { + private string _name; + private string _typeName; + + public ObjectEntry(string name, string typeName) + { + _name = name; + _typeName = typeName; + } + + public string Name { get { return _name; } } + public string TypeName { get { return _typeName; } } + } + + public static DirectoryHandle Create(DirectoryAccess access, string name) + { + return Create(access, name, 0, null); + } + + public static DirectoryHandle Create(DirectoryAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateDirectoryObject(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new DirectoryHandle(handle, true); + } + + protected DirectoryHandle() + { } + + protected DirectoryHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public DirectoryHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, DirectoryAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenDirectoryObject(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public DirectoryHandle(string name, DirectoryAccess access) + : this(name, 0, null, access) + { } + + public void EnumObjects(EnumObjectsDelegate callback) + { + NtStatus status; + int context = 0; + bool firstTime = true; + int retLength; + + using (var data = new MemoryAlloc(0x200)) + { + while (true) + { + while ((status = Win32.NtQueryDirectoryObject( + this, + data, + data.Size, + false, + firstTime, + ref context, + out retLength + )) == NtStatus.MoreEntries) + { + // Check if we have at least one entry. If not, + // we need to double the buffer size and try again. + if (data.ReadStruct(0).Name.Buffer != IntPtr.Zero) + break; + + if (data.Size > 16 * 1024 * 1024) + Win32.ThrowLastError(status); + + data.Resize(data.Size * 2); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + int i = 0; + + while (true) + { + ObjectDirectoryInformation info = data.ReadStruct(i); + + if (info.Name.Buffer == IntPtr.Zero) + break; + + if (!callback(new ObjectEntry(info.Name.Read(), info.TypeName.Read()))) + return; + + i++; + } + + if (status != NtStatus.MoreEntries) + break; + + firstTime = false; + } + } + } + + /// + /// Gets the objects contained in the directory object. + /// + /// An array of object entries. + public ObjectEntry[] GetObjects() + { + var objects = new List(); + + this.EnumObjects((obj) => + { + objects.Add(obj); + return true; + }); + + return objects.ToArray(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/DriverHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/DriverHandle.cs new file mode 100644 index 000000000..cc77c30f1 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/DriverHandle.cs @@ -0,0 +1,111 @@ +/* + * Process Hacker - + * driver handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Objects +{ + public class DriverHandle : NativeHandle + { + public DriverHandle(string name) + : this(name, 0, null) + { } + + public DriverHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory) + { + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + + try + { + this.Handle = KProcessHacker.Instance.KphOpenDriver(oa).ToIntPtr(); + } + finally + { + oa.Dispose(); + } + } + + public DriverBasicInformation GetBasicInformation() + { + unsafe + { + DriverBasicInformation basicInfo; + int retLength; + + KProcessHacker.Instance.KphQueryInformationDriver( + this, + DriverInformationClass.DriverBasicInformation, + new IntPtr(&basicInfo), + Marshal.SizeOf(typeof(DriverBasicInformation)), + out retLength + ); + + return basicInfo; + } + } + + public string GetDriverName() + { + return this.GetInformationUnicodeString(DriverInformationClass.DriverNameInformation); + } + + private string GetInformationUnicodeString(DriverInformationClass infoClass) + { + using (MemoryAlloc data = new MemoryAlloc(0x1000)) + { + int retLength = 0; + + try + { + KProcessHacker.Instance.KphQueryInformationDriver( + this, + infoClass, + data, + data.Size, + out retLength + ); + } + catch (WindowsException) + { + data.Resize(retLength); + + KProcessHacker.Instance.KphQueryInformationDriver( + this, + infoClass, + data, + data.Size, + out retLength + ); + } + + return data.ReadStruct().Read(); + } + } + + public string GetServiceKeyName() + { + return this.GetInformationUnicodeString(DriverInformationClass.DriverServiceKeyNameInformation); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/EnlistmentHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/EnlistmentHandle.cs new file mode 100644 index 000000000..a58d6723c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/EnlistmentHandle.cs @@ -0,0 +1,217 @@ +/* + * Process Hacker - + * enlistment handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public class EnlistmentHandle : NativeHandle + { + public static EnlistmentHandle Create( + EnlistmentAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ResourceManagerHandle resourceManagerHandle, + TransactionHandle transactionHandle, + EnlistmentOptions createOptions, + NotificationMask notificationMask, + IntPtr enlistmentKey + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateEnlistment( + out handle, + access, + resourceManagerHandle, + transactionHandle, + ref oa, + createOptions, + notificationMask, + enlistmentKey + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new EnlistmentHandle(handle, true); + } + + public static EnlistmentHandle FromHandle(IntPtr handle) + { + return new EnlistmentHandle(handle, false); + } + + private EnlistmentHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public EnlistmentHandle( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ResourceManagerHandle resourceManagerHandle, + Guid guid, + EnlistmentAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenEnlistment( + out handle, + access, + resourceManagerHandle, + ref guid, + ref oa + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public void Commit(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtCommitEnlistment(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void CommitComplete(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtCommitComplete(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public EnlistmentBasicInformation GetBasicInformation() + { + NtStatus status; + EnlistmentBasicInformation basicInfo; + int retLength; + + if ((status = Win32.NtQueryInformationEnlistment( + this, + EnlistmentInformationClass.EnlistmentBasicInformation, + out basicInfo, + Marshal.SizeOf(typeof(EnlistmentBasicInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return basicInfo; + } + + public void Prepare(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtPrepareEnlistment(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void PrepareComplete(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtPrepareComplete(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void PrePrepare(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtPrePrepareEnlistment(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void PrePrepareComplete(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtPrePrepareComplete(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void ReadOnly(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtReadOnlyEnlistment(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void Recover(IntPtr enlistmentKey) + { + NtStatus status; + + if ((status = Win32.NtRecoverEnlistment(this, enlistmentKey)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void RejectSinglePhase(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtSinglePhaseReject(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void Rollback(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtRollbackEnlistment(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void RollbackComplete(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtRollbackComplete(this, ref virtualClock)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/EnvironmentBlock.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/EnvironmentBlock.cs new file mode 100644 index 000000000..e02ae3007 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/EnvironmentBlock.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.Objects +{ + public struct EnvironmentBlock + { + public static EnvironmentBlock GetCurrent() + { + unsafe + { + return new EnvironmentBlock(ProcessHandle.GetCurrentProcessParameters()->Environment); + } + } + + public static string GetCurrentVariable(string name) + { + return GetCurrent().GetVariable(name); + } + + public static void SetCurrentVariable(string name, string value) + { + NtStatus status; + UnicodeString nameStr; + UnicodeString valueStr; + + nameStr = new UnicodeString(name); + + try + { + valueStr = new UnicodeString(value); + + try + { + if ((status = Win32.RtlSetEnvironmentVariable( + IntPtr.Zero, + ref nameStr, + ref valueStr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + valueStr.Dispose(); + } + } + finally + { + nameStr.Dispose(); + } + } + + public static implicit operator IntPtr(EnvironmentBlock environmentBlock) + { + return environmentBlock.Memory; + } + + private IntPtr _environment; + + public EnvironmentBlock(bool cloneCurrent) + { + NtStatus status; + + if ((status = Win32.RtlCreateEnvironment( + cloneCurrent, + out _environment + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public EnvironmentBlock(IntPtr environment) + { + _environment = environment; + } + + public IntPtr Memory + { + get { return _environment; } + } + + public void Destroy() + { + Win32.RtlDestroyEnvironment(this); + } + + public unsafe int GetLength() + { + short* ptr = (short*)_environment; + + while (*ptr != 0) + while (*ptr++ != 0) + ; + + ptr++; + + return (new IntPtr(ptr)).Decrement(_environment).ToInt32(); + } + + public string GetVariable(string name) + { + NtStatus status; + UnicodeString nameStr; + UnicodeString valueStr; + + nameStr = new UnicodeString(name); + + try + { + using (var data = new MemoryAlloc(100)) + { + valueStr = new UnicodeString(); + valueStr.Buffer = data; + valueStr.MaximumLength = (ushort)data.Size; + + status = Win32.RtlQueryEnvironmentVariable_U( + this, + ref nameStr, + ref valueStr + ); + + if (status == NtStatus.BufferTooSmall) + { + // Resize and try again (+2 for the null terminator). + data.Resize(valueStr.Length + 2); + valueStr.Buffer = data; + valueStr.MaximumLength = (ushort)(valueStr.Length + 2); + + status = Win32.RtlQueryEnvironmentVariable_U( + this, + ref nameStr, + ref valueStr + ); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return valueStr.Read(); + } + } + finally + { + nameStr.Dispose(); + } + } + + public EnvironmentBlock SetCurrent() + { + NtStatus status; + IntPtr previousEnvironment; + + if ((status = Win32.RtlSetCurrentEnvironment( + this, + out previousEnvironment + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new EnvironmentBlock(previousEnvironment); + } + + public void SetVariable(string name, string value) + { + NtStatus status; + IntPtr environment = _environment; + UnicodeString nameStr; + UnicodeString valueStr; + + nameStr = new UnicodeString(name); + + try + { + valueStr = new UnicodeString(value); + + try + { + if ((status = Win32.RtlSetEnvironmentVariable( + ref environment, + ref nameStr, + ref valueStr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + valueStr.Dispose(); + } + } + finally + { + nameStr.Dispose(); + } + + _environment = environment; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/EventHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/EventHandle.cs new file mode 100644 index 000000000..d8de8f698 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/EventHandle.cs @@ -0,0 +1,159 @@ +/* + * Process Hacker - + * event handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class EventHandle : NativeHandle + { + public static EventHandle Create(EventAccess access, EventType type, bool initialState) + { + return Create(access, null, type, initialState); + } + + public static EventHandle Create(EventAccess access, string name, EventType type, bool initialState) + { + return Create(access, name, 0, null, type, initialState); + } + + public static EventHandle Create(EventAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, EventType type, bool initialState) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateEvent(out handle, access, ref oa, type, initialState)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new EventHandle(handle, true); + } + + public static EventHandle FromHandle(IntPtr handle) + { + return new EventHandle(handle, false); + } + + private EventHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public EventHandle(string name, EventAccess access) + : this(name, 0, null, access) + { } + + public EventHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, EventAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenEvent(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public void Clear() + { + NtStatus status; + + if ((status = Win32.NtClearEvent(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public EventBasicInformation GetBasicInformation() + { + NtStatus status; + EventBasicInformation ebi; + int retLength; + + if ((status = Win32.NtQueryEvent(this, EventInformationClass.EventBasicInformation, + out ebi, Marshal.SizeOf(typeof(EventBasicInformation)), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return ebi; + } + + public int Pulse() + { + NtStatus status; + int previousState; + + if ((status = Win32.NtPulseEvent(this, out previousState)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousState; + } + + public int Reset() + { + NtStatus status; + int previousState; + + if ((status = Win32.NtResetEvent(this, out previousState)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousState; + } + + public int Set() + { + NtStatus status; + int previousState; + + if ((status = Win32.NtSetEvent(this, out previousState)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousState; + } + + /// + /// Sets the event and causes the waiting thread to be context switched + /// to regardless of its priority. + /// + public void SetBoostPriority() + { + NtStatus status; + + if ((status = Win32.NtSetEventBoostPriority(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/EventPairHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/EventPairHandle.cs new file mode 100644 index 000000000..2cae45480 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/EventPairHandle.cs @@ -0,0 +1,191 @@ +/* + * Process Hacker - + * event pair handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents an event pair, an object consisting of two events, high and low. + /// + public sealed class EventPairHandle : NativeHandle + { + /// + /// Creates an unnamed event pair. + /// + /// The desired access to the event pair. + /// A handle to an event pair. + public static EventPairHandle Create(EventPairAccess access) + { + return Create(access, null, 0, null); + } + + /// + /// Creates an event pair. + /// + /// The desired access to the event pair. + /// + /// The name of the event pair. If rootDirectory is null, you must specify a fully + /// qualified name. Example: \BaseNamedObjects\MyEventPair. + /// + /// The flags to use when creating the object. + /// + /// The directory in which to place the event pair. This can be null. + /// + /// A handle to an event pair. + public static EventPairHandle Create(EventPairAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateEventPair(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new EventPairHandle(handle, true); + } + + public static EventPairHandle FromHandle(IntPtr handle) + { + return new EventPairHandle(handle, false); + } + + private EventPairHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens a named event pair. + /// + /// + /// The name of the event pair. If rootDirectory is null, + /// you must specify a fully qualified name. + /// The flags to use when opening the object. + /// The directory object in which the event pair can be found. + /// The desired access to the event pair. + public EventPairHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, EventPairAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenEventPair(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public EventPairHandle(string name, EventPairAccess access) + : this(name, 0, null, access) + { } + + /// + /// Sets the high event. + /// + public void SetHigh() + { + NtStatus status; + + if ((status = Win32.NtSetHighEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets the high event and waits for the low event. + /// + public NtStatus SetHighWaitLow() + { + NtStatus status; + + if ((status = Win32.NtSetHighWaitLowEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + /// + /// Sets the low event. + /// + public void SetLow() + { + NtStatus status; + + if ((status = Win32.NtSetLowEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets the low event and waits for the high event. + /// + public NtStatus SetLowWaitHigh() + { + NtStatus status; + + if ((status = Win32.NtSetLowWaitHighEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + /// + /// Waits for the high event. + /// + public NtStatus WaitHigh() + { + NtStatus status; + + if ((status = Win32.NtWaitHighEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + /// + /// Waits for the low event. + /// + public NtStatus WaitLow() + { + NtStatus status; + + if ((status = Win32.NtWaitLowEventPair(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/FileHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/FileHandle.cs new file mode 100644 index 000000000..faa7becdf --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/FileHandle.cs @@ -0,0 +1,1813 @@ +/* + * Process Hacker - + * file handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ProcessHacker.Common; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a file. + /// + public class FileHandle : NativeHandle + { + public delegate bool EnumFilesDelegate(FileEntry file); + public delegate bool EnumStreamsDelegate(FileStreamEntry stream); + + public static FileHandle FromFileStream(System.IO.FileStream fileStream) + { + return FromHandle(fileStream.SafeFileHandle.DangerousGetHandle()); + } + + public static FileHandle FromHandle(IntPtr handle) + { + return new FileHandle(handle, false); + } + + /// + /// Creates or opens a file. + /// + /// The desired access to the file. + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// Options to use when creating the file. + public static FileHandle Create(FileAccess access, string fileName, FileCreateOptions createOptions) + { + return Create(access, fileName, FileShareMode.Exclusive, FileCreationDisposition.OpenIf, createOptions); + } + + /// + /// Creates or opens a file. + /// + /// The desired access to the file. + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// The types of access to the file to grant to other threads. + /// Options to use when creating the file. + public static FileHandle Create(FileAccess access, string fileName, FileShareMode shareMode, FileCreateOptions createOptions) + { + return Create(access, fileName, shareMode, FileCreationDisposition.OpenIf, createOptions); + } + + public static FileHandle Create( + FileAccess access, + string fileName, + FileShareMode shareMode, + FileCreationDisposition creationDisposition, + FileCreateOptions createOptions + ) + { + return Create(access, fileName, null, shareMode, creationDisposition, createOptions); + } + + public static FileHandle Create( + FileAccess access, + string fileName, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreationDisposition creationDisposition, + FileCreateOptions createOptions + ) + { + FileIoStatus status; + + return Create( + access, + fileName, + ObjectFlags.CaseInsensitive, + rootDirectory, + shareMode, + creationDisposition, + 0, + FileAttributes.Normal, + createOptions, + out status + ); + } + + public static FileHandle Create( + FileAccess access, + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreationDisposition creationDisposition, + long allocationSize, + FileAttributes attributes, + FileCreateOptions createOptions, + out FileIoStatus ioStatus + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(fileName, objectFlags, rootDirectory); + IoStatusBlock isb; + IntPtr handle; + + try + { + if ((status = Win32.NtCreateFile( + out handle, + access, + ref oa, + out isb, + ref allocationSize, + attributes, + shareMode, + creationDisposition, + createOptions, + IntPtr.Zero, + 0 + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + ioStatus = (FileIoStatus)isb.Information.ToInt32(); + } + finally + { + oa.Dispose(); + } + + return new FileHandle(handle, true); + } + + public static FileHandle CreateWin32(string fileName, FileAccess desiredAccess) + { + return CreateWin32(fileName, desiredAccess, FileShareMode.Exclusive); + } + + public static FileHandle CreateWin32(string fileName, FileAccess desiredAccess, FileShareMode shareMode) + { + return CreateWin32(fileName, desiredAccess, shareMode, FileCreationDispositionWin32.OpenAlways); + } + + public static FileHandle CreateWin32(string fileName, FileAccess desiredAccess, FileShareMode shareMode, + FileCreationDispositionWin32 creationDisposition) + { + IntPtr handle; + + handle = Win32.CreateFile(fileName, desiredAccess, shareMode, 0, creationDisposition, 0, IntPtr.Zero); + + if (handle == NativeHandle.MinusOne) + Win32.ThrowLastError(); + + return new FileHandle(handle, true); + } + + public static void Delete(string fileName, ObjectFlags objectFlags) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(fileName, objectFlags, null); + + try + { + if ((status = Win32.NtDeleteFile(ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + } + + protected FileHandle() + { } + + protected FileHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens an existing file for synchronous access. + /// + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// The desired access to the file. + public FileHandle(string fileName, FileAccess access) + : this(fileName, FileShareMode.Exclusive, access) + { } + + /// + /// Opens an existing file for synchronous access. + /// + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// The share mode to use. + /// The desired access to the file. + public FileHandle(string fileName, FileShareMode shareMode, FileAccess access) + : this(fileName, shareMode, FileCreateOptions.NonDirectoryFile | FileCreateOptions.SynchronousIoNonAlert, access | (FileAccess)StandardRights.Synchronize) + { } + + /// + /// Opens an existing file. + /// + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// The share mode to use. + /// Open options to use. + /// The desired access to the file. + public FileHandle(string fileName, FileShareMode shareMode, FileCreateOptions openOptions, FileAccess access) + : this(fileName, ObjectFlags.CaseInsensitive, null, shareMode, openOptions, access) + { } + + /// + /// Opens an existing file. + /// + /// + /// An object name identifying the file to open. To use a DOS format + /// file name, prepend "\??\" to the file name. + /// + /// Flags to use when opening the object. + /// The directory to open the file relative to. + /// The share mode to use. + /// Open options to use. + /// The desired access to the file. + public FileHandle( + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreateOptions openOptions, + FileAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(fileName, objectFlags, rootDirectory); + IoStatusBlock isb; + IntPtr handle; + + try + { + if ((status = Win32.NtOpenFile( + out handle, + access, + ref oa, + out isb, + shareMode, + openOptions + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public AsyncIoContext BeginFsControl( + int controlCode, + byte[] inBuffer, + int inBufferOffset, + int inBufferLength, + byte[] outBuffer, + int outBufferOffset, + int outBufferLength + ) + { + AsyncIoContext asyncContext; + PinnedObject pinnedInBuffer = null; + PinnedObject pinnedOutBuffer = null; + + Utils.ValidateBuffer(inBuffer, inBufferOffset, inBufferLength, true); + Utils.ValidateBuffer(outBuffer, outBufferOffset, outBufferLength, true); + + asyncContext = new AsyncIoContext(this); + + if (inBuffer != null) + { + pinnedInBuffer = new PinnedObject(inBuffer); + asyncContext.KeepAlive(pinnedInBuffer); + } + + if (outBuffer != null) + { + pinnedOutBuffer = new PinnedObject(outBuffer); + asyncContext.KeepAlive(pinnedOutBuffer); + } + + this.BeginFsControl( + asyncContext, + controlCode, + pinnedInBuffer != null ? pinnedInBuffer.Address.Increment(inBufferOffset) : IntPtr.Zero, + inBufferLength, + pinnedOutBuffer != null ? pinnedOutBuffer.Address.Increment(outBufferOffset) : IntPtr.Zero, + outBufferLength + ); + + return asyncContext; + } + + public AsyncIoContext BeginFsControl(int controlCode, MemoryRegion inBuffer, MemoryRegion outBuffer) + { + AsyncIoContext asyncContext = new AsyncIoContext(this); + + asyncContext.KeepAlive(inBuffer); + asyncContext.KeepAlive(outBuffer); + this.BeginFsControl( + asyncContext, + controlCode, + inBuffer ?? IntPtr.Zero, + inBuffer != null ? inBuffer.Size : 0, + outBuffer ?? IntPtr.Zero, + outBuffer != null ? outBuffer.Size : 0 + ); + + return asyncContext; + } + + protected void BeginFsControl( + AsyncIoContext asyncContext, + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength + ) + { + NtStatus status; + + status = Win32.NtFsControlFile( + this, + asyncContext.EventHandle, + null, + IntPtr.Zero, + asyncContext.StatusMemory, + controlCode, + inBuffer, + inBufferLength, + outBuffer, + outBufferLength + ); + + asyncContext.NotifyBegin(); + + if (status != NtStatus.Pending) + { + // The operation finished synchronously. + asyncContext.CompletedSynchronously = true; + asyncContext.Status = status; + } + } + + public AsyncIoContext BeginIoControl( + int controlCode, + byte[] inBuffer, + int inBufferOffset, + int inBufferLength, + byte[] outBuffer, + int outBufferOffset, + int outBufferLength + ) + { + AsyncIoContext asyncContext; + PinnedObject pinnedInBuffer = null; + PinnedObject pinnedOutBuffer = null; + + Utils.ValidateBuffer(inBuffer, inBufferOffset, inBufferLength, true); + Utils.ValidateBuffer(outBuffer, outBufferOffset, outBufferLength, true); + + asyncContext = new AsyncIoContext(this); + + if (inBuffer != null) + { + pinnedInBuffer = new PinnedObject(inBuffer); + asyncContext.KeepAlive(pinnedInBuffer); + } + + if (outBuffer != null) + { + pinnedOutBuffer = new PinnedObject(outBuffer); + asyncContext.KeepAlive(pinnedOutBuffer); + } + + this.BeginIoControl( + asyncContext, + controlCode, + pinnedInBuffer != null ? pinnedInBuffer.Address.Increment(inBufferOffset) : IntPtr.Zero, + inBufferLength, + pinnedOutBuffer != null ? pinnedOutBuffer.Address.Increment(outBufferOffset) : IntPtr.Zero, + outBufferLength + ); + + return asyncContext; + } + + public AsyncIoContext BeginIoControl(int controlCode, MemoryRegion inBuffer, MemoryRegion outBuffer) + { + AsyncIoContext asyncContext = new AsyncIoContext(this); + + asyncContext.KeepAlive(inBuffer); + asyncContext.KeepAlive(outBuffer); + this.BeginIoControl( + asyncContext, + controlCode, + inBuffer ?? IntPtr.Zero, + inBuffer != null ? inBuffer.Size : 0, + outBuffer ?? IntPtr.Zero, + outBuffer != null ? outBuffer.Size : 0 + ); + + return asyncContext; + } + + protected void BeginIoControl( + AsyncIoContext asyncContext, + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength + ) + { + NtStatus status; + + status = Win32.NtDeviceIoControlFile( + this, + asyncContext.EventHandle, + null, + IntPtr.Zero, + asyncContext.StatusMemory, + controlCode, + inBuffer, + inBufferLength, + outBuffer, + outBufferLength + ); + + asyncContext.NotifyBegin(); + + if (status != NtStatus.Pending) + { + // The operation finished synchronously. + asyncContext.CompletedSynchronously = true; + asyncContext.Status = status; + } + } + + public AsyncIoContext BeginLock(long offset, long length) + { + return this.BeginLock(offset, length, false); + } + + public AsyncIoContext BeginLock(long offset, long length, bool wait) + { + return this.BeginLock(offset, length, wait, true); + } + + public AsyncIoContext BeginLock(long offset, long length, bool wait, bool exclusive) + { + NtStatus status; + AsyncIoContext asyncContext = new AsyncIoContext(this); + + status = Win32.NtLockFile( + this, + asyncContext.EventHandle, + null, + IntPtr.Zero, + asyncContext.StatusMemory, + ref offset, + ref length, + 0, + !wait, + exclusive + ); + + asyncContext.NotifyBegin(); + + if (status != NtStatus.Pending) + { + // The operation finished synchronously. + asyncContext.CompletedSynchronously = true; + asyncContext.Status = status; + } + + return asyncContext; + } + + public AsyncIoContext BeginRead(byte[] buffer) + { + return this.BeginRead(buffer, 0, buffer.Length); + } + + public AsyncIoContext BeginRead(byte[] buffer, int offset, int length) + { + AsyncIoContext asyncContext; + PinnedObject pinnedBuffer; + + Utils.ValidateBuffer(buffer, offset, length); + + // Pin the buffer because the I/O system may be writing to it after + // this call returns. + pinnedBuffer = new PinnedObject(buffer); + asyncContext = new AsyncIoContext(this); + asyncContext.KeepAlive(pinnedBuffer); + this.BeginRead(asyncContext, pinnedBuffer.Address.Increment(offset), length); + + return asyncContext; + } + + public AsyncIoContext BeginRead(MemoryRegion buffer) + { + AsyncIoContext asyncContext = new AsyncIoContext(this); + + asyncContext.KeepAlive(buffer); + this.BeginRead(asyncContext, buffer, buffer.Size); + + return asyncContext; + } + + protected void BeginRead(AsyncIoContext asyncContext, IntPtr buffer, int length) + { + NtStatus status; + + status = Win32.NtReadFile( + this, + asyncContext.EventHandle, + null, + IntPtr.Zero, + asyncContext.StatusMemory, + buffer, + length, + IntPtr.Zero, + IntPtr.Zero + ); + + asyncContext.NotifyBegin(); + + if (status != NtStatus.Pending) + { + // The operation finished synchronously. + asyncContext.CompletedSynchronously = true; + asyncContext.Status = status; + } + } + + public AsyncIoContext BeginWrite(byte[] buffer) + { + return this.BeginWrite(buffer, 0, buffer.Length); + } + + public AsyncIoContext BeginWrite(byte[] buffer, int offset, int length) + { + AsyncIoContext asyncContext; + PinnedObject pinnedBuffer; + + Utils.ValidateBuffer(buffer, offset, length); + + pinnedBuffer = new PinnedObject(buffer); + asyncContext = new AsyncIoContext(this); + asyncContext.KeepAlive(pinnedBuffer); + this.BeginWrite(asyncContext, pinnedBuffer.Address.Increment(offset), length); + + return asyncContext; + } + + public AsyncIoContext BeginWrite(MemoryRegion buffer) + { + AsyncIoContext asyncContext = new AsyncIoContext(this); + + asyncContext.KeepAlive(buffer); + this.BeginWrite(asyncContext, buffer, buffer.Size); + + return asyncContext; + } + + protected void BeginWrite(AsyncIoContext asyncContext, IntPtr buffer, int length) + { + NtStatus status; + + status = Win32.NtWriteFile( + this, + asyncContext.EventHandle, + null, + IntPtr.Zero, + asyncContext.StatusMemory, + buffer, + length, + IntPtr.Zero, + IntPtr.Zero + ); + + asyncContext.NotifyBegin(); + + if (status != NtStatus.Pending) + { + // The operation finished synchronously. + asyncContext.CompletedSynchronously = true; + asyncContext.Status = status; + } + } + + public IoStatusBlock CancelIo() + { + NtStatus status; + IoStatusBlock isb; + + if ((status = Win32.NtCancelIoFile(this, out isb)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return isb; + } + + internal void CancelIo(IntPtr isb) + { + NtStatus status; + + if ((status = Win32.NtCancelIoFile(this, isb)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Deletes the file when the handle is closed. + /// + public void Delete() + { + this.SetStruct( + FileInformationClass.FileDispositionInformation, + new FileDispositionInformation() { DeleteFile = true } + ); + } + + protected int EndCommonIo(AsyncIoContext asyncContext) + { + asyncContext.Wait(); + asyncContext.NotifyEnd(); + + if (asyncContext.Status >= NtStatus.Error) + Win32.ThrowLastError(asyncContext.Status); + + return asyncContext.StatusBlock.Information.ToInt32(); + } + + public int EndFsControl(AsyncIoContext asyncContext) + { + return this.EndCommonIo(asyncContext); + } + + public int EndIoControl(AsyncIoContext asyncContext) + { + return this.EndCommonIo(asyncContext); + } + + public bool EndLock(AsyncIoContext asyncContext) + { + asyncContext.Wait(); + asyncContext.NotifyEnd(); + + if (asyncContext.Status == NtStatus.LockNotGranted) + return false; + + if (asyncContext.Status >= NtStatus.Error) + Win32.ThrowLastError(asyncContext.Status); + + return true; + } + + public int EndRead(AsyncIoContext asyncContext) + { + return this.EndCommonIo(asyncContext); + } + + public int EndWrite(AsyncIoContext asyncContext) + { + return this.EndCommonIo(asyncContext); + } + + public void EnumFiles(EnumFilesDelegate callback) + { + NtStatus status; + IoStatusBlock isb; + bool firstTime = true; + + using (var data = new MemoryAlloc(0x400)) + { + while (true) + { + // Query the directory, doubling the buffer size each + // time NtQueryDirectoryFile fails. We will also handle + // any pending status. + + while (true) + { + status = Win32.NtQueryDirectoryFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + data, + data.Size, + FileInformationClass.FileDirectoryInformation, + false, + IntPtr.Zero, + firstTime + ); + + // Our ISB is on the stack, so we have to wait for the operation to complete + // before continuing. + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + if (status == NtStatus.BufferOverflow || status == NtStatus.InfoLengthMismatch) + data.Resize(data.Size * 2); + else + break; + } + + // If we don't have any entries to read, exit. + if (status == NtStatus.NoMoreFiles) + break; + + // Handle any errors. + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + // Read the list of files we got in this batch. + + int i = 0; + + while (true) + { + FileDirectoryInformation info = data.ReadStruct(i, 0); + string name = data.ReadUnicodeString( + i + FileDirectoryInformation.FileNameOffset, + info.FileNameLength / 2 + ); + + if (!callback(new FileEntry( + name, + info.FileIndex, + DateTime.FromFileTime(info.CreationTime), + DateTime.FromFileTime(info.LastAccessTime), + DateTime.FromFileTime(info.LastWriteTime), + DateTime.FromFileTime(info.ChangeTime), + info.EndOfFile, + info.AllocationSize, + info.FileAttributes + ))) + return; + + if (info.NextEntryOffset == 0) + break; + else + i += info.NextEntryOffset; + } + + firstTime = false; + + // Go back and get another batch of file entries. + } + } + } + + public void EnumStreams(EnumStreamsDelegate callback) + { + using (var data = this.QueryVariableSize(FileInformationClass.FileStreamInformation)) + { + int i = 0; + + while (true) + { + FileStreamInformation info = data.ReadStruct(i, 0); + string name = data.ReadUnicodeString( + i + FileStreamInformation.StreamNameOffset, + info.StreamNameLength / 2 + ); + + if (!callback(new FileStreamEntry(name, info.StreamSize, info.StreamAllocationSize))) + return; + + if (info.NextEntryOffset == 0) + break; + else + i += info.NextEntryOffset; + } + } + } + + public void Flush() + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtFlushBuffersFile( + this, + out isb + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public int FsControl(int controlCode, byte[] inBuffer, byte[] outBuffer) + { + return this.FsControl( + controlCode, + inBuffer, + 0, + inBuffer != null ? inBuffer.Length : 0, + outBuffer, + 0, + outBuffer != null ? outBuffer.Length : 0 + ); + } + + public int FsControl( + int controlCode, + byte[] inBuffer, + int inBufferOffset, + int inBufferLength, + byte[] outBuffer, + int outBufferOffset, + int outBufferLength + ) + { + Utils.ValidateBuffer(inBuffer, inBufferOffset, inBufferLength, true); + Utils.ValidateBuffer(outBuffer, outBufferOffset, outBufferLength, true); + + unsafe + { + fixed (byte* inBufferPtr = inBuffer) + { + fixed (byte* outBufferPtr = outBuffer) + { + return this.FsControl( + controlCode, + &inBufferPtr[inBufferOffset], + inBuffer != null ? inBuffer.Length : 0, + &outBufferPtr[outBufferOffset], + outBuffer != null ? outBuffer.Length : 0 + ); + } + } + } + } + + public unsafe int FsControl( + int controlCode, + void* inBuffer, + int inBufferLength, + void* outBuffer, + int outBufferLength + ) + { + return this.FsControl(controlCode, new IntPtr(inBuffer), inBufferLength, new IntPtr(outBuffer), outBufferLength); + } + + public int FsControl( + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength + ) + { + NtStatus status; + int returnLength; + + status = this.FsControl(controlCode, inBuffer, inBufferLength, outBuffer, outBufferLength, out returnLength); + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return returnLength; + } + + public NtStatus FsControl( + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength, + out int returnLength + ) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtFsControlFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + controlCode, + inBuffer, + inBufferLength, + outBuffer, + outBufferLength + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + // Information contains the return length. + returnLength = isb.Information.ToInt32(); + + return status; + } + + public FileAttributes GetAttributes() + { + return this.GetBasicInformation().FileAttributes; + } + + public FileBasicInformation GetBasicInformation() + { + return this.QueryStruct(FileInformationClass.FileBasicInformation); + } + + public string GetFileName() + { + using (var data = this.QueryVariableSize(FileInformationClass.FileNameInformation)) + { + FileNameInformation info = data.ReadStruct(); + + return data.ReadUnicodeString( + FileNameInformation.FileNameOffset, + info.FileNameLength / 2 + ); + } + } + + public FileEntry[] GetFiles() + { + List files = new List(); + + this.EnumFiles((file) => + { + files.Add(file); + return true; + }); + + return files.ToArray(); + } + + public long GetPosition() + { + return this.QueryStruct(FileInformationClass.FilePositionInformation).CurrentByteOffset; + } + + public FileStreamEntry[] GetStreams() + { + List streams = new List(); + + this.EnumStreams((file) => + { + streams.Add(file); + return true; + }); + + return streams.ToArray(); + } + + public long GetSize() + { + return this.GetStandardInformation().EndOfFile; + } + + public FileStandardInformation GetStandardInformation() + { + return this.QueryStruct(FileInformationClass.FileStandardInformation); + } + + public string GetVolumeFsName() + { + NtStatus status; + IoStatusBlock isb; + + using (var data = new MemoryAlloc(0x200)) + { + if ((status = Win32.NtQueryVolumeInformationFile( + this, + out isb, + data, + data.Size, + FsInformationClass.FileFsAttributeInformation + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + FileFsAttributeInformation info = data.ReadStruct(); + + return Marshal.PtrToStringUni( + data.Memory.Increment(Marshal.OffsetOf(typeof(FileFsAttributeInformation), "FileSystemName")), + info.FileSystemNameLength / 2 + ); + } + } + + public string GetVolumeLabel() + { + NtStatus status; + IoStatusBlock isb; + + using (var data = new MemoryAlloc(0x200)) + { + if ((status = Win32.NtQueryVolumeInformationFile( + this, + out isb, + data, + data.Size, + FsInformationClass.FileFsVolumeInformation + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + FileFsVolumeInformation info = data.ReadStruct(); + + return Marshal.PtrToStringUni( + data.Memory.Increment(Marshal.OffsetOf(typeof(FileFsVolumeInformation), "VolumeLabel")), + info.VolumeLabelLength / 2 + ); + } + } + + /// + /// Sends an I/O control message to the device's associated driver. + /// + /// The device-specific control code. + /// The input. + /// The output buffer. + /// The bytes returned in the output buffer. + public int IoControl(int controlCode, byte[] inBuffer, byte[] outBuffer) + { + return this.IoControl( + controlCode, + inBuffer, + 0, + inBuffer != null ? inBuffer.Length : 0, + outBuffer, + 0, + outBuffer != null ? outBuffer.Length : 0 + ); + } + + public int IoControl( + int controlCode, + byte[] inBuffer, + int inBufferOffset, + int inBufferLength, + byte[] outBuffer, + int outBufferOffset, + int outBufferLength + ) + { + Utils.ValidateBuffer(inBuffer, inBufferOffset, inBufferLength, true); + Utils.ValidateBuffer(outBuffer, outBufferOffset, outBufferLength, true); + + unsafe + { + fixed (byte* inBufferPtr = inBuffer) + { + fixed (byte* outBufferPtr = outBuffer) + { + return this.IoControl( + controlCode, + &inBufferPtr[inBufferOffset], + inBuffer != null ? inBuffer.Length : 0, + &outBufferPtr[outBufferOffset], + outBuffer != null ? outBuffer.Length : 0 + ); + } + } + } + } + + public unsafe int IoControl( + int controlCode, + byte* inBuffer, + int inBufferLength, + byte[] outBuffer + ) + { + fixed (byte* outBufferPtr = outBuffer) + { + return this.IoControl( + controlCode, + inBuffer, + inBufferLength, + outBufferPtr, + outBuffer != null ? outBuffer.Length : 0 + ); + } + } + + public unsafe int IoControl( + int controlCode, + void* inBuffer, + int inBufferLength, + void* outBuffer, + int outBufferLength + ) + { + return this.IoControl(controlCode, new IntPtr(inBuffer), inBufferLength, new IntPtr(outBuffer), outBufferLength); + } + + public int IoControl( + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength + ) + { + NtStatus status; + int returnLength; + + status = this.IoControl(controlCode, inBuffer, inBufferLength, outBuffer, outBufferLength, out returnLength); + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return returnLength; + } + + public NtStatus IoControl( + int controlCode, + IntPtr inBuffer, + int inBufferLength, + IntPtr outBuffer, + int outBufferLength, + out int returnLength + ) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtDeviceIoControlFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + controlCode, + inBuffer, + inBufferLength, + outBuffer, + outBufferLength + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + // Information contains the return length. + returnLength = isb.Information.ToInt32(); + + return status; + } + + public bool Lock(long offset, long length) + { + return this.Lock(offset, length, false); + } + + public bool Lock(long offset, long length, bool wait) + { + return this.Lock(offset, length, wait, true); + } + + public bool Lock(long offset, long length, bool wait, bool exclusive) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtLockFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + ref offset, + ref length, + 0, + !wait, + exclusive + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + if (status == NtStatus.LockNotGranted) + return false; + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return true; + } + + protected T QueryStruct(FileInformationClass infoClass) + where T : struct + { + NtStatus status; + IoStatusBlock isb; + + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(T)))) + { + if ((status = Win32.NtQueryInformationFile( + this, + out isb, + data, + data.Size, + infoClass + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return data.ReadStruct(); + } + } + + protected MemoryAlloc QueryVariableSize(FileInformationClass infoClass) + { + NtStatus status; + IoStatusBlock isb; + var data = new MemoryAlloc(0x200); + + while (true) + { + status = Win32.NtQueryInformationFile( + this, + out isb, + data, + data.Size, + infoClass + ); + + if ( + status == NtStatus.BufferOverflow || + status == NtStatus.BufferTooSmall || + status == NtStatus.InfoLengthMismatch + ) + data.Resize(data.Size * 2); + else + break; + } + + if (status >= NtStatus.Error) + { + data.Dispose(); + Win32.ThrowLastError(status); + } + + return data; + } + + /// + /// Reads data from the file. + /// + /// The length to read. + /// The read data. + public byte[] Read(int length) + { + byte[] buffer = new byte[length]; + + this.Read(buffer); + + return buffer; + } + + /// + /// Reads data from the file. + /// + /// The buffer to store the data in. + /// The number of bytes read from the file. + public int Read(byte[] buffer) + { + return this.Read(buffer, 0, buffer.Length); + } + + public int Read(byte[] buffer, int offset, int length) + { + Utils.ValidateBuffer(buffer, offset, length); + + unsafe + { + fixed (byte* bufferPtr = buffer) + { + return this.Read(&bufferPtr[offset], length); + } + } + } + + public unsafe int Read(void* buffer, int length) + { + return this.Read(new IntPtr(buffer), length); + } + + public int Read(IntPtr buffer, int length) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtReadFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + buffer, + length, + IntPtr.Zero, + IntPtr.Zero + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return isb.Information.ToInt32(); + } + + public void SetEnd(long offset) + { + this.SetStruct( + FileInformationClass.FileEndOfFileInformation, + new FileEndOfFileInformation() { EndOfFile = offset } + ); + } + + public void SetIoCompletion(IoCompletionHandle ioCompletionHandle, IntPtr keyContext) + { + FileCompletionInformation info = new FileCompletionInformation(); + + info.Port = ioCompletionHandle; + info.Key = keyContext; + this.SetStruct(FileInformationClass.FileCompletionInformation, info); + } + + public void SetPosition(long offset) + { + this.SetStruct( + FileInformationClass.FilePositionInformation, + new FilePositionInformation() { CurrentByteOffset = offset } + ); + } + + public long SetPosition(long offset, PositionOrigin origin) + { + long currentPosition; + + currentPosition = this.GetPosition(); + + switch (origin) + { + case PositionOrigin.Current: + currentPosition += offset; + break; + case PositionOrigin.Start: + currentPosition = offset; + break; + case PositionOrigin.End: + currentPosition = this.GetSize() + offset; + break; + } + + this.SetPosition(currentPosition); + + return currentPosition; + } + + protected void SetStruct(FileInformationClass infoClass, T info) + where T : struct + { + NtStatus status; + IoStatusBlock isb; + + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(T)))) + { + data.WriteStruct(info); + + if ((status = Win32.NtSetInformationFile( + this, + out isb, + data, + data.Size, + infoClass + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + public void Unlock(long offset, long length) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtUnlockFile( + this, + out isb, + ref offset, + ref length, + 0 + ); + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Writes data to the file. + /// + /// The data. + /// The number of bytes written to the file. + public int Write(byte[] buffer) + { + return this.Write(buffer, 0, buffer.Length); + } + + public int Write(byte[] buffer, int offset, int length) + { + Utils.ValidateBuffer(buffer, offset, length); + + unsafe + { + fixed (byte* bufferPtr = buffer) + { + return this.Write(&bufferPtr[offset], length); + } + } + } + + public unsafe int Write(void* buffer, int length) + { + return this.Write(new IntPtr(buffer), length); + } + + /// + /// Writes data to the file. + /// + /// The data. + /// The number of bytes to write. + /// The number of bytes written to the file. + public int Write(IntPtr buffer, int length) + { + NtStatus status; + IoStatusBlock isb; + + status = Win32.NtWriteFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + buffer, + length, + IntPtr.Zero, + IntPtr.Zero + ); + + if (status == NtStatus.Pending) + { + this.Wait(); + status = isb.Status; + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return isb.Information.ToInt32(); + } + } + + public enum PositionOrigin + { + Start, + Current, + End + } + + public sealed class AsyncIoContext : BaseObject, ISynchronizable + { + private unsafe sealed class UnmanagedIsb : BaseObject + { + private static readonly int _isbSize = Marshal.SizeOf(typeof(IoStatusBlock)); + + public static implicit operator IntPtr(UnmanagedIsb isb) + { + return isb.Memory; + } + + private IoStatusBlock* _ioStatusBlock; + + public UnmanagedIsb() + { + _ioStatusBlock = (IoStatusBlock*)MemoryAlloc.PrivateHeap.Allocate(0, _isbSize); + } + + protected override void DisposeObject(bool disposing) + { + if (_ioStatusBlock != null) + MemoryAlloc.PrivateHeap.Free(0, new IntPtr(_ioStatusBlock)); + } + + public IntPtr Information + { + get { return _ioStatusBlock->Information; } + set { _ioStatusBlock->Information = value; } + } + + public IntPtr Memory + { + get { return new IntPtr(_ioStatusBlock); } + } + + public IntPtr Pointer + { + get { return _ioStatusBlock->Pointer; } + set { _ioStatusBlock->Pointer = value; } + } + + public NtStatus Status + { + get { return _ioStatusBlock->Status; } + set { _ioStatusBlock->Status = value; } + } + + public IoStatusBlock Struct + { + get { return *_ioStatusBlock; } + set { *_ioStatusBlock = value; } + } + } + + private EventHandle _eventHandle; + private FileHandle _fileHandle; + private UnmanagedIsb _isb; + private bool _completedSynchronously = false; + private bool _started = false; + + private List _keepAliveList = new List(); + private object _tag; + + public AsyncIoContext(FileHandle fileHandle) + { + _eventHandle = EventHandle.Create(EventAccess.All, EventType.NotificationEvent, false); + _fileHandle = fileHandle; + _isb = new UnmanagedIsb(); + _isb.Status = NtStatus.Pending; + + _fileHandle.Reference(); + } + + protected override void DisposeObject(bool disposing) + { + if (_started && !this.Completed) + { + throw new InvalidOperationException( + "An attempt was made to dispose an asynchronous I/O context object " + + "before the I/O operation has finished." + ); + } + + this.ClearKeepAlive(); + + if (_eventHandle != null) + _eventHandle.Dispose(); + if (_fileHandle != null) + _fileHandle.Dereference(); + if (_isb != null) + _isb.Dispose(); + } + + public bool Cancelled + { + get { return this.Status == NtStatus.Cancelled; } + } + + public bool Completed + { + get + { + return _isb.Status != NtStatus.Pending; + } + } + + public bool CompletedSynchronously + { + get { return _completedSynchronously; } + internal set + { + _completedSynchronously = value; + _eventHandle.Set(); + } + } + + internal EventHandle EventHandle + { + get { return _eventHandle; } + } + + public FileHandle FileHandle + { + get { return _fileHandle; } + } + + public int Information + { + get { return _isb.Information.ToInt32(); } + } + + public bool Started + { + get { return _started; } + } + + public NtStatus Status + { + get { return _isb.Status; } + internal set { _isb.Status = value; } + } + + public IoStatusBlock StatusBlock + { + get + { + return _isb.Struct; + } + internal set + { + _isb.Struct = value; + } + } + + internal IntPtr StatusMemory + { + get { return _isb; } + } + + public object Tag + { + get { return _tag; } + set { _tag = value; } + } + + public void Cancel() + { + if (!_started) + return; + + _fileHandle.CancelIo(); + this.Wait(); + this.NotifyEnd(); + } + + private void ClearKeepAlive() + { + foreach (var obj in _keepAliveList) + obj.Dereference(); + + _keepAliveList.Clear(); + } + + internal void KeepAlive(BaseObject obj) + { + _keepAliveList.Add(obj); + obj.Reference(); + } + + internal void NotifyBegin() + { + _started = true; + } + + internal void NotifyEnd() + { + this.ClearKeepAlive(); + } + + #region ISynchronizable Members + + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public IntPtr Handle + { + get { return _eventHandle.Handle; } + } + + public NtStatus Wait() + { + return _eventHandle.Wait(); + } + + public NtStatus Wait(bool alertable) + { + return _eventHandle.Wait(alertable); + } + + public NtStatus Wait(bool alertable, long timeout) + { + return _eventHandle.Wait(alertable, timeout); + } + + #endregion + } + + public class FileEntry + { + public FileEntry( + string name, + int index, + DateTime creationTime, + DateTime lastAccessTime, + DateTime lastWriteTime, + DateTime changeTime, + long size, + long allocationSize, + FileAttributes attributes + ) + { + this.Name = name; + this.Index = index; + this.CreationTime = creationTime; + this.LastAccessTime = lastAccessTime; + this.LastWriteTime = lastWriteTime; + this.ChangeTime = changeTime; + this.Size = size; + this.AllocationSize = allocationSize; + this.Attributes = attributes; + } + + public string Name { get; private set; } + public int Index { get; private set; } + + public DateTime CreationTime { get; private set; } + public DateTime LastAccessTime { get; private set; } + public DateTime LastWriteTime { get; private set; } + public DateTime ChangeTime { get; private set; } + + public long Size { get; private set; } + public long AllocationSize { get; private set; } + + public FileAttributes Attributes { get; private set; } + } + + public class FileStreamEntry + { + public FileStreamEntry(string name, long size, long allocationSize) + { + this.Name = name; + this.Size = size; + this.AllocationSize = allocationSize; + } + + public string Name { get; private set; } + public long Size { get; private set; } + public long AllocationSize { get; private set; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ISynchronizable.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ISynchronizable.cs new file mode 100644 index 000000000..0b45e0698 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ISynchronizable.cs @@ -0,0 +1,42 @@ +/* + * Process Hacker - + * object with synchronize functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a Windows object that can be synchronized with. + /// + public interface ISynchronizable + { + IntPtr Handle { get; } + + //NtStatus SignalAndWait(ISynchronizable waitObject); + //NtStatus SignalAndWait(ISynchronizable waitObject, bool alertable); + //NtStatus SignalAndWait(ISynchronizable waitObject, bool alertable, long timeout); + NtStatus Wait(); + NtStatus Wait(bool alertable); + NtStatus Wait(bool alertable, long timeout); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/IWithToken.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/IWithToken.cs new file mode 100644 index 000000000..bdf209b6b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/IWithToken.cs @@ -0,0 +1,49 @@ +/* + * Process Hacker - + * object with token + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a Windows object that contains a token. + /// + /// + /// This interface is useful because both processes and threads have + /// tokens, but the method used to open their tokens are different. + /// + public interface IWithToken + { + /// + /// Opens and returns the object's token. + /// + /// A handle to the token. + TokenHandle GetToken(); + + /// + /// Opens and returns the object's token. + /// + /// Specifies the desired access to the token. + /// A handle to the token. + TokenHandle GetToken(TokenAccess access); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/IoCompletionHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/IoCompletionHandle.cs new file mode 100644 index 000000000..a0e905c32 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/IoCompletionHandle.cs @@ -0,0 +1,122 @@ +/* + * Process Hacker - + * I/O completion handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class IoCompletionHandle : NativeHandle + { + public static IoCompletionHandle Create(IoCompletionAccess access) + { + return Create(access, 0); + } + + public static IoCompletionHandle Create(IoCompletionAccess access, int count) + { + return Create(access, null, count); + } + + public static IoCompletionHandle Create(IoCompletionAccess access, string name, int count) + { + return Create(access, name, 0, null, count); + } + + public static IoCompletionHandle Create(IoCompletionAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, int count) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateIoCompletion(out handle, access, ref oa, count)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new IoCompletionHandle(handle, true); + } + + private IoCompletionHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public IoCompletionHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, IoCompletionAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenIoCompletion(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public IoCompletionHandle(string name, IoCompletionAccess access) + : this(name, 0, null, access) + { } + + public IoStatusBlock Remove(out IntPtr keyContext, out IntPtr apcContext, long timeout) + { + return this.Remove(out keyContext, out apcContext, timeout, true); + } + + public IoStatusBlock Remove(out IntPtr keyContext, out IntPtr apcContext, long timeout, bool relative) + { + NtStatus status; + IoStatusBlock ioStatus; + long realTimeout = relative ? -timeout : timeout; + + if ((status = Win32.NtRemoveIoCompletion( + this, out keyContext, out apcContext, out ioStatus, ref realTimeout)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return ioStatus; + } + + public void Set(IntPtr keyContext, IntPtr apcContext, NtStatus ioStatus, IntPtr ioInformation) + { + NtStatus status; + + if ((status = Win32.NtSetIoCompletion( + this, keyContext, apcContext, ioStatus, ioInformation)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/JobObjectHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/JobObjectHandle.cs new file mode 100644 index 000000000..74d30e61f --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/JobObjectHandle.cs @@ -0,0 +1,226 @@ +/* + * Process Hacker - + * job handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a Windows job object. + /// + public sealed class JobObjectHandle : NativeHandle + { + public static JobObjectHandle Create(JobObjectAccess access) + { + return Create(access, null); + } + + public static JobObjectHandle Create(JobObjectAccess access, string name) + { + return Create(access, name, 0, null); + } + + public static JobObjectHandle Create(JobObjectAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateJobObject( + out handle, + access, + ref oa + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new JobObjectHandle(handle, true); + } + + /// + /// Creates a service handle using an existing handle. + /// The handle will not be closed automatically. + /// + /// The handle value. + /// The job handle. + public static JobObjectHandle FromHandle(IntPtr handle) + { + return new JobObjectHandle(handle, false); + } + + private JobObjectHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public JobObjectHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, JobObjectAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenJobObject( + out handle, + access, + ref oa + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public JobObjectHandle(string name, JobObjectAccess access) + : this(name, 0, null, access) + { } + + /// + /// Opens the job object associated with the specified process. + /// + /// The process. + /// The desired access to the job object. + public JobObjectHandle(ProcessHandle processHandle, JobObjectAccess access) + { + try + { + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcessJob(processHandle, access)); + } + catch (WindowsException) + { + // Use KPH to set the handle's granted access. + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcessJob(processHandle, + (JobObjectAccess)StandardRights.Synchronize)); + if (this.Handle != IntPtr.Zero) + KProcessHacker.Instance.KphSetHandleGrantedAccess(this.Handle, (int)access); + } + + // If we don't have a handle assume the process isn't in a job. + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(NtStatus.ProcessNotInJob); + } + } + + private T QueryStruct(JobObjectInformationClass informationClass) + where T : struct + { + int retLength; + + using (MemoryAlloc data = new MemoryAlloc(Marshal.SizeOf(typeof(T)))) + { + if (!Win32.QueryInformationJobObject(this, informationClass, data, data.Size, out retLength)) + { + data.Resize(retLength); + + if (!Win32.QueryInformationJobObject(this, informationClass, data, data.Size, out retLength)) + Win32.ThrowLastError(); + } + + return data.ReadStruct(); + } + } + + public JobObjectBasicAccountingInformation GetBasicAccountingInformation() + { + return this.QueryStruct( + JobObjectInformationClass.JobObjectBasicAccountingInformation); + } + + public JobObjectBasicAndIoAccountingInformation GetBasicAndIoAccountingInformation() + { + return this.QueryStruct( + JobObjectInformationClass.JobObjectBasicAndIoAccountingInformation); + } + + public JobObjectBasicLimitInformation GetBasicLimitInformation() + { + return this.QueryStruct(JobObjectInformationClass.JobObjectBasicLimitInformation); + } + + public int[] GetProcessIdList() + { + List processIds = new List(); + int retLength; + + // FIXME: Fixed buffer + using (MemoryAlloc data = new MemoryAlloc(0x1000)) + { + if (!Win32.QueryInformationJobObject(this, JobObjectInformationClass.JobObjectBasicProcessIdList, + data, data.Size, out retLength)) + Win32.ThrowLastError(); + + JobObjectBasicProcessIdList listInfo = data.ReadStruct(); + + for (int i = 0; i < listInfo.NumberOfProcessIdsInList; i++) + { + processIds.Add(data.ReadInt32(8, i)); + } + } + + return processIds.ToArray(); + } + + public JobObjectBasicUiRestrictions GetBasicUiRestrictions() + { + JobObjectBasicUiRestrictions uiRestrictions; + int retLength; + + if (!Win32.QueryInformationJobObject(this, JobObjectInformationClass.JobObjectBasicUIRestrictions, + out uiRestrictions, 4, out retLength)) + Win32.ThrowLastError(); + + return uiRestrictions; + } + + public JobObjectExtendedLimitInformation GetExtendedLimitInformation() + { + return this.QueryStruct(JobObjectInformationClass.JobObjectExtendedLimitInformation); + } + + public void Terminate() + { + this.Terminate(0); + } + + public void Terminate(int exitCode) + { + if (!Win32.TerminateJobObject(this, exitCode)) + Win32.ThrowLastError(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/KeyHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/KeyHandle.cs new file mode 100644 index 000000000..c926cc617 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/KeyHandle.cs @@ -0,0 +1,145 @@ +/* + * Process Hacker - + * key handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public class KeyHandle : NativeHandle + { + public static KeyHandle Create( + KeyAccess access, + string name, + RegOptions createOptions + ) + { + return Create(access, name, 0, null, createOptions); + } + + public static KeyHandle Create( + KeyAccess access, + string name, + ObjectFlags objectFlags, + KeyHandle rootDirectory, + RegOptions createOptions + ) + { + KeyCreationDisposition creationDisposition; + + return Create(access, name, objectFlags, rootDirectory, createOptions, out creationDisposition); + } + + public static KeyHandle Create( + KeyAccess access, + string name, + ObjectFlags objectFlags, + KeyHandle rootDirectory, + RegOptions createOptions, + out KeyCreationDisposition creationDisposition + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateKey( + out handle, + access, + ref oa, + 0, + IntPtr.Zero, + createOptions, + out creationDisposition + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new KeyHandle(handle, true); + } + + private KeyHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public KeyHandle(string name, KeyAccess access) + : this(name, 0, null, access) + { } + + public KeyHandle(string name, ObjectFlags objectFlags, KeyHandle rootDirectory, KeyAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenKey( + out handle, + access, + ref oa + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public void Delete() + { + NtStatus status; + + if ((status = Win32.NtDeleteKey(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void DeleteValue(string name) + { + NtStatus status; + UnicodeString nameStr = new UnicodeString(name); + + try + { + if ((status = Win32.NtDeleteValueKey(this, ref nameStr)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + nameStr.Dispose(); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/KeyedEventHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/KeyedEventHandle.cs new file mode 100644 index 000000000..5daea1837 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/KeyedEventHandle.cs @@ -0,0 +1,167 @@ +/* + * Process Hacker - + * keyed event handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class KeyedEventHandle : NativeHandle + { + public static KeyedEventHandle Create(KeyedEventAccess access) + { + return Create(access, null); + } + + public static KeyedEventHandle Create(KeyedEventAccess access, string name) + { + return Create(access, name, 0, null); + } + + public static KeyedEventHandle Create(KeyedEventAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateKeyedEvent(out handle, access, ref oa, 0)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new KeyedEventHandle(handle, true); + } + + private KeyedEventHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public KeyedEventHandle(string name, KeyedEventAccess access) + : this(name, null, 0, access) + { } + + public KeyedEventHandle(string name, DirectoryHandle rootDirectory, ObjectFlags objectFlags, KeyedEventAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenKeyedEvent(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public NtStatus ReleaseKey(int key) + { + return this.ReleaseKey(key, false); + } + + public NtStatus ReleaseKey(int key, long timeout) + { + return this.ReleaseKey(key, false, timeout); + } + + public NtStatus ReleaseKey(int key, bool alertable) + { + return this.ReleaseKey(new IntPtr(key), alertable, long.MinValue, false); + } + + public NtStatus ReleaseKey(int key, bool alertable, long timeout) + { + return this.ReleaseKey(new IntPtr(key), alertable, timeout, true); + } + + public NtStatus ReleaseKey(IntPtr key, bool alertable, long timeout, bool relative) + { + NtStatus status; + long realTimeout = relative ? -timeout : timeout; + + if (key.ToInt64() % 2 != 0) + throw new ArgumentException("Key must be divisible by 2."); + + if ((status = Win32.NtReleaseKeyedEvent( + this, + key, + alertable, + ref realTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + public NtStatus WaitKey(int key) + { + return this.WaitKey(key, false); + } + + public NtStatus WaitKey(int key, long timeout) + { + return this.WaitKey(key, false, timeout); + } + + public NtStatus WaitKey(int key, bool alertable) + { + return this.WaitKey(new IntPtr(key), alertable, long.MinValue, false); + } + + public NtStatus WaitKey(int key, bool alertable, long timeout) + { + return this.WaitKey(new IntPtr(key), alertable, timeout, true); + } + + public NtStatus WaitKey(IntPtr key, bool alertable, long timeout, bool relative) + { + NtStatus status; + long realTimeout = relative ? -timeout : timeout; + + if (key.ToInt64() % 2 != 0) + throw new ArgumentException("Key must be divisible by 2."); + + if ((status = Win32.NtWaitForKeyedEvent( + this, + key, + alertable, + ref realTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/LsaAccountHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaAccountHandle.cs new file mode 100644 index 000000000..62215bed7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaAccountHandle.cs @@ -0,0 +1,185 @@ +/* + * Process Hacker - + * LSA account handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a LSA account. + /// + public sealed class LsaAccountHandle : LsaHandle + { + public static LsaAccountHandle Create(LsaAccountAccess access, LsaPolicyHandle policyHandle, Sid sid) + { + NtStatus status; + IntPtr handle; + + if ((status = Win32.LsaCreateAccount( + policyHandle, + sid, + access, + out handle + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new LsaAccountHandle(handle, true); + } + + private LsaAccountHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens a LSA account. + /// + /// A handle to a LSA policy. + /// The SID of the account to open. + /// The desired access to the account. + public LsaAccountHandle(LsaPolicyHandle policyHandle, Sid sid, LsaAccountAccess access) + { + NtStatus status; + IntPtr handle; + + if ((status = Win32.LsaOpenAccount( + policyHandle, + sid, + access, + out handle + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.Handle = handle; + } + + public void AddPrivileges(PrivilegeSet privileges) + { + NtStatus status; + + using (var privilegeSetMemory = privileges.ToMemory()) + { + if ((status = Win32.LsaAddPrivilegesToAccount( + this, + privilegeSetMemory + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + public PrivilegeSet GetPrivileges() + { + NtStatus status; + IntPtr privileges; + + if ((status = Win32.LsaEnumeratePrivilegesOfAccount( + this, + out privileges + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var privilegesAlloc = new LsaMemoryAlloc(privileges)) + { + return new PrivilegeSet(privilegesAlloc); + } + } + + public QuotaLimits GetQuotas() + { + NtStatus status; + QuotaLimits quotas; + + if ((status = Win32.LsaGetQuotasForAccount( + this, + out quotas + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return quotas; + } + + public SecuritySystemAccess GetSystemAccess() + { + NtStatus status; + SecuritySystemAccess access; + + if ((status = Win32.LsaGetSystemAccessAccount( + this, + out access + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return access; + } + + public void RemovePrivileges() + { + NtStatus status; + + if ((status = Win32.LsaRemovePrivilegesFromAccount( + this, + true, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + private void RemovePrivileges(PrivilegeSet privileges) + { + NtStatus status; + + using (var privilegeSetMemory = privileges.ToMemory()) + { + if ((status = Win32.LsaRemovePrivilegesFromAccount( + this, + false, + privilegeSetMemory + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + public void SetQuotas(QuotaLimits quotas) + { + NtStatus status; + + if ((status = Win32.LsaSetQuotasForAccount( + this, + ref quotas + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void SetSystemAccess(SecuritySystemAccess access) + { + NtStatus status; + + if ((status = Win32.LsaSetSystemAccessAccount( + this, + access + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/LsaHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaHandle.cs new file mode 100644 index 000000000..47c2644bd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaHandle.cs @@ -0,0 +1,82 @@ +/* + * Process Hacker - + * local security authority handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle managed by the Local Security Authority. + /// + public class LsaHandle : NativeHandle + where TAccess : struct + { + protected LsaHandle() + { } + + protected LsaHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + protected override void Close() + { + Win32.LsaClose(this); + } + + public void Delete() + { + NtStatus status; + + if ((status = Win32.LsaDelete(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public override SecurityDescriptor GetSecurity(SecurityInformation securityInformation) + { + NtStatus status; + IntPtr securityDescriptor; + + if ((status = Win32.LsaQuerySecurityObject( + this, + securityInformation, + out securityDescriptor + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new SecurityDescriptor(new LsaMemoryAlloc(securityDescriptor)); + } + + public override void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + NtStatus status; + + if ((status = Win32.LsaSetSecurityObject( + this, + securityInformation, + securityDescriptor + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/LsaPolicyHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaPolicyHandle.cs new file mode 100644 index 000000000..3450c0c52 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/LsaPolicyHandle.cs @@ -0,0 +1,504 @@ +/* + * Process Hacker - + * LSA policy handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a LSA policy. + /// + public sealed class LsaPolicyHandle : LsaHandle + { + private static WeakReference _lookupPolicyHandle; + private static int _lookupPolicyHandleMisses = 0; + + public static LsaPolicyHandle LookupPolicyHandle + { + get + { + WeakReference weakRef = _lookupPolicyHandle; + LsaPolicyHandle policyHandle = null; + + if (weakRef != null) + { + policyHandle = weakRef.Target; + } + + if (policyHandle == null) + { + System.Threading.Interlocked.Increment(ref _lookupPolicyHandleMisses); + + policyHandle = new LsaPolicyHandle(LsaPolicyAccess.LookupNames); + + if (policyHandle != null) + _lookupPolicyHandle = new WeakReference(policyHandle); + } + + return policyHandle; + } + } + + public static int LookupPolicyHandleMisses + { + get { return _lookupPolicyHandleMisses; } + } + + public delegate bool EnumAccountsDelegate(Sid sid); + public delegate bool EnumPrivilegesDelegate(Privilege privilege); + + /// + /// Opens the local LSA policy object. + /// + /// The desired access to the policy. + public LsaPolicyHandle(LsaPolicyAccess access) + : this(null, access) + { } + + /// + /// Opens a LSA policy object. + /// + /// The name of the system on which the policy resides. + /// The desired access to the policy. + public LsaPolicyHandle(string systemName, LsaPolicyAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(); + UnicodeString systemNameStr; + IntPtr handle; + + systemNameStr = new UnicodeString(systemName); + + try + { + if ((status = Win32.LsaOpenPolicy( + ref systemNameStr, + ref oa, + access, + out handle + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + systemNameStr.Dispose(); + } + + this.Handle = handle; + } + + /// + /// Enumerates the accounts in the policy. This requires + /// ViewLocalInformation access. + /// + /// The callback for the enumeration. + public void EnumAccounts(EnumAccountsDelegate callback) + { + NtStatus status; + int enumerationContext = 0; + IntPtr buffer; + int count; + + while (true) + { + status = Win32.LsaEnumerateAccounts( + this, + ref enumerationContext, + out buffer, + 0x100, + out count + ); + + if (status == NtStatus.NoMoreEntries) + break; + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var bufferAlloc = new LsaMemoryAlloc(buffer)) + { + for (int i = 0; i < count; i++) + { + if (!callback(new Sid(bufferAlloc.ReadIntPtr(0, i)))) + return; + } + } + } + } + + /// + /// Enumerates the accounts in the policy with the specified privilege. + /// This requires LookupNames, ViewLocalInformation and usually + /// administrator access. + /// + /// The name of the required privilege. + /// The callback for the enumeration. + public void EnumAccountsWithPrivilege(string privilegeName, EnumAccountsDelegate callback) + { + NtStatus status; + UnicodeString privilegeNameStr; + IntPtr buffer; + int count; + + privilegeNameStr = new UnicodeString(privilegeName); + + try + { + if ((status = Win32.LsaEnumerateAccountsWithUserRight( + this, + ref privilegeNameStr, + out buffer, + out count + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + privilegeNameStr.Dispose(); + } + + Sid[] sids = new Sid[count]; + + using (var bufferAlloc = new LsaMemoryAlloc(buffer)) + { + for (int i = 0; i < count; i++) + { + if (!callback(new Sid(bufferAlloc.ReadIntPtr(0, i)))) + break; + } + } + } + + /// + /// Enumerates the privileges in the policy. This requires + /// ViewLocalInformation access. + /// + /// The callback for the enumeration. + public void EnumPrivileges(EnumPrivilegesDelegate callback) + { + NtStatus status; + int enumerationContext = 0; + IntPtr buffer; + int count; + + while (true) + { + status = Win32.LsaEnumeratePrivileges( + this, + ref enumerationContext, + out buffer, + 0x100, + out count + ); + + if (status == NtStatus.NoMoreEntries) + break; + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var bufferAlloc = new LsaMemoryAlloc(buffer)) + { + for (int i = 0; i < count; i++) + { + if (!callback(new Privilege(bufferAlloc.ReadStruct(i).Name.Read()))) + return; + } + } + } + } + + /// + /// Gets the accounts in the policy. This requires + /// ViewLocalInformation access. + /// + public Sid[] GetAccounts() + { + List sids = new List(); + + this.EnumAccounts((sid) => + { + sids.Add(sid); + return true; + }); + + return sids.ToArray(); + } + + /// + /// Gets the accounts in the policy with the specified privilege. + /// This requires LookupNames, ViewLocalInformation and usually + /// administrator access. + /// + /// The name of the required privilege. + public Sid[] GetAccountsWithPrivilege(string privilegeName) + { + List sids = new List(); + + this.EnumAccountsWithPrivilege(privilegeName, (sid) => + { + sids.Add(sid); + return true; + }); + + return sids.ToArray(); + } + + public Privilege[] GetPrivileges() + { + List privileges = new List(); + + this.EnumPrivileges((privilege) => + { + privileges.Add(privilege); + return true; + }); + + return privileges.ToArray(); + } + + public string LookupName(Sid sid) + { + SidNameUse nameUse; + + return this.LookupName(sid, out nameUse); + } + + public string LookupName(Sid sid, out SidNameUse nameUse) + { + string domainName; + + return this.LookupName(sid, out nameUse, out domainName); + } + + public string LookupName(Sid sid, out string domainName) + { + SidNameUse nameUse; + + return this.LookupName(sid, out nameUse, out domainName); + } + + public string LookupName(Sid sid, out SidNameUse nameUse, out string domainName) + { + NtStatus status; + IntPtr referencedDomains; + IntPtr names; + + if ((status = Win32.LsaLookupSids( + this, + 1, + new IntPtr[] { sid }, + out referencedDomains, + out names + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var referencedDomainsAlloc = new LsaMemoryAlloc(referencedDomains)) + using (var namesAlloc = new LsaMemoryAlloc(names)) + { + LsaTranslatedName translatedName = namesAlloc.ReadStruct(); + + nameUse = translatedName.Use; + + if (nameUse == SidNameUse.Invalid || nameUse == SidNameUse.Unknown) + { + domainName = null; + + return null; + } + + if (translatedName.DomainIndex != -1) + { + LsaReferencedDomainList domains = referencedDomainsAlloc.ReadStruct(); + MemoryRegion trustArray = new MemoryRegion(domains.Domains); + LsaTrustInformation trustInfo = trustArray.ReadStruct(translatedName.DomainIndex); + + domainName = trustInfo.Name.Read(); + } + else + { + domainName = null; + } + + return translatedName.Name.Read(); + } + } + + public string LookupPrivilegeDisplayName(Luid value) + { + return this.LookupPrivilegeDisplayName(this.LookupPrivilegeName(value)); + } + + public string LookupPrivilegeDisplayName(string name) + { + NtStatus status; + UnicodeString nameStr; + IntPtr displayName; + short language; + + nameStr = new UnicodeString(name); + + try + { + if ((status = Win32.LsaLookupPrivilegeDisplayName( + this, + ref nameStr, + out displayName, + out language + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + nameStr.Dispose(); + } + + using (var displayNameAlloc = new LsaMemoryAlloc(displayName)) + { + return displayNameAlloc.ReadStruct().Read(); + } + } + + public string LookupPrivilegeName(Luid value) + { + NtStatus status; + IntPtr name; + + if ((status = Win32.LsaLookupPrivilegeName( + this, + ref value, + out name + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var nameAlloc = new LsaMemoryAlloc(name)) + { + return nameAlloc.ReadStruct().Read(); + } + } + + public Luid LookupPrivilegeValue(string name) + { + NtStatus status; + UnicodeString nameStr; + Luid luid; + + nameStr = new UnicodeString(name); + + try + { + if ((status = Win32.LsaLookupPrivilegeValue( + this, + ref nameStr, + out luid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + nameStr.Dispose(); + } + + return luid; + } + + public Sid LookupSid(string name) + { + SidNameUse nameUse; + + return this.LookupSid(name, out nameUse); + } + + public Sid LookupSid(string name, out SidNameUse nameUse) + { + string domainName; + + return this.LookupSid(name, out nameUse, out domainName); + } + + public Sid LookupSid(string name, out string domainName) + { + SidNameUse nameUse; + + return this.LookupSid(name, out nameUse, out domainName); + } + + public Sid LookupSid(string name, out SidNameUse nameUse, out string domainName) + { + NtStatus status; + UnicodeString nameStr; + IntPtr referencedDomains; + IntPtr sids; + + nameStr = new UnicodeString(name); + + try + { + if ((status = Win32.LsaLookupNames2( + this, + 0, + 1, + new UnicodeString[] { nameStr }, + out referencedDomains, + out sids + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + nameStr.Dispose(); + } + + using (var referencedDomainsAlloc = new LsaMemoryAlloc(referencedDomains)) + using (var sidsAlloc = new LsaMemoryAlloc(sids)) + { + LsaTranslatedSid2 translatedSid = sidsAlloc.ReadStruct(); + + nameUse = translatedSid.Use; + + if (nameUse == SidNameUse.Invalid || nameUse == SidNameUse.Unknown) + { + domainName = null; + + return null; + } + + if (translatedSid.DomainIndex != -1) + { + LsaReferencedDomainList domains = referencedDomainsAlloc.ReadStruct(); + MemoryRegion trustArray = new MemoryRegion(domains.Domains); + LsaTrustInformation trustInfo = trustArray.ReadStruct(translatedSid.DomainIndex); + + domainName = trustInfo.Name.Read(); + } + else + { + domainName = null; + } + + return new Sid(translatedSid.Sid); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/MailslotHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/MailslotHandle.cs new file mode 100644 index 000000000..2a4a825be --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/MailslotHandle.cs @@ -0,0 +1,113 @@ +/* + * Process Hacker - + * mailslot handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a mailslot. + /// + public sealed class MailslotHandle : FileHandle + { + public static MailslotHandle Create(FileAccess access, string fileName, int maxMessageSize, long readTimeout) + { + return Create( + access, + fileName, + ObjectFlags.CaseInsensitive, + null, + 0, + maxMessageSize, + readTimeout, + 0 + ); + } + + public static MailslotHandle Create( + FileAccess access, + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + int quota, + int maxMessageSize, + long readTimeout, + FileCreateOptions createOptions + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(fileName, objectFlags, rootDirectory); + IoStatusBlock isb; + IntPtr handle; + + try + { + if ((status = Win32.NtCreateMailslotFile( + out handle, + access, + ref oa, + out isb, + createOptions, + quota, + maxMessageSize, + ref readTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new MailslotHandle(handle, true); + } + + private MailslotHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public MailslotHandle(string fileName, FileAccess access) + : base(fileName, access) + { } + + public MailslotHandle(string fileName, FileShareMode shareMode, FileAccess access) + : base(fileName, shareMode, access) + { } + + public MailslotHandle(string fileName, FileShareMode shareMode, FileCreateOptions openOptions, FileAccess access) + : base(fileName, shareMode, openOptions, access) + { } + + public MailslotHandle( + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreateOptions openOptions, + FileAccess access + ) + : base(fileName, objectFlags, rootDirectory, shareMode, openOptions, access) + { } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/MutantHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/MutantHandle.cs new file mode 100644 index 000000000..cba34ed8c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/MutantHandle.cs @@ -0,0 +1,132 @@ +/* + * Process Hacker - + * mutant handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.Objects +{ + public sealed class MutantHandle : NativeHandle + { + public static MutantHandle Create(MutantAccess access, bool initialOwner) + { + return Create(access, null, initialOwner); + } + + public static MutantHandle Create(MutantAccess access, string name, bool initialOwner) + { + return Create(access, name, 0, null, initialOwner); + } + + public static MutantHandle Create(MutantAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, bool initialOwner) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateMutant(out handle, access, ref oa, initialOwner)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new MutantHandle(handle, true); + } + + public static MutantHandle FromHandle(IntPtr handle) + { + return new MutantHandle(handle, false); + } + + private MutantHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public MutantHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, MutantAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenMutant(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public MutantHandle(string name, MutantAccess access) + : this(name, 0, null, access) + { } + + public MutantBasicInformation GetBasicInformation() + { + NtStatus status; + MutantBasicInformation mbi; + int retLength; + + if ((status = Win32.NtQueryMutant(this, MutantInformationClass.MutantBasicInformation, + out mbi, Marshal.SizeOf(typeof(MutantBasicInformation)), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return mbi; + } + + public MutantOwnerInformation GetOwnerInformation() + { + NtStatus status; + MutantOwnerInformation moi; + int retLength; + + if ((status = Win32.NtQueryMutant(this, MutantInformationClass.MutantOwnerInformation, + out moi, Marshal.SizeOf(typeof(MutantOwnerInformation)), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return moi; + } + + public int Release() + { + NtStatus status; + int previousCount; + + if ((status = Win32.NtReleaseMutant(this, out previousCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousCount; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/NamedPipeHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/NamedPipeHandle.cs new file mode 100644 index 000000000..f10e8b91d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/NamedPipeHandle.cs @@ -0,0 +1,400 @@ +/* + * Process Hacker - + * named pipe handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a named pipe. + /// + public sealed class NamedPipeHandle : FileHandle + { + public static readonly int FsCtlAssignEvent = Win32.CtlCode(DeviceType.NamedPipe, 0, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlDisconnect = Win32.CtlCode(DeviceType.NamedPipe, 1, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlListen = Win32.CtlCode(DeviceType.NamedPipe, 2, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlPeek = Win32.CtlCode(DeviceType.NamedPipe, 3, DeviceControlMethod.Buffered, DeviceControlAccess.Read); + public static readonly int FsCtlQueryEvent = Win32.CtlCode(DeviceType.NamedPipe, 4, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlTransceive = Win32.CtlCode(DeviceType.NamedPipe, 5, DeviceControlMethod.Neither, DeviceControlAccess.Read | DeviceControlAccess.Write); + public static readonly int FsCtlWait = Win32.CtlCode(DeviceType.NamedPipe, 6, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlImpersonate = Win32.CtlCode(DeviceType.NamedPipe, 7, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlSetClientProcess = Win32.CtlCode(DeviceType.NamedPipe, 8, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + public static readonly int FsCtlQueryClientProcess = Win32.CtlCode(DeviceType.NamedPipe, 9, DeviceControlMethod.Buffered, DeviceControlAccess.Any); + + public static NamedPipeHandle Create( + FileAccess access, + string fileName, + PipeType type, + int maximumInstances, + long defaultTimeout + ) + { + return Create( + access, + fileName, + ObjectFlags.CaseInsensitive, + null, + FileShareMode.ReadWrite, + FileCreationDisposition.OpenIf, + 0, + type, + type, + PipeCompletionMode.Queue, + maximumInstances, + 0, + 0, + defaultTimeout + ); + } + + public static NamedPipeHandle Create( + FileAccess access, + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreationDisposition creationDisposition, + FileCreateOptions createOptions, + PipeType type, + PipeType readMode, + PipeCompletionMode completionMode, + int maximumInstances, + int inboundQuota, + int outboundQuota, + long defaultTimeout + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(fileName, objectFlags, rootDirectory); + IoStatusBlock isb; + IntPtr handle; + + try + { + if ((status = Win32.NtCreateNamedPipeFile( + out handle, + access, + ref oa, + out isb, + shareMode, + creationDisposition, + createOptions, + type, + readMode, + completionMode, + maximumInstances, + inboundQuota, + outboundQuota, + ref defaultTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new NamedPipeHandle(handle, true); + } + + public new static NamedPipeHandle FromHandle(IntPtr handle) + { + return new NamedPipeHandle(handle, false); + } + + public static bool Wait(string name) + { + return Wait(name, long.MinValue, false); + } + + /// + /// Waits for an instance of the specified named pipe to + /// become available for connection. + /// + /// The short name of the named pipe. + /// + /// The timeout, in 100ns units. + /// + /// + /// True if an instance of the pipe was available before the timeout + /// interval elapsed, otherwise false. + /// + public static bool Wait(string name, long timeout) + { + return Wait(name, timeout, true); + } + + public static bool Wait(string name, long timeout, bool relative) + { + using (var npfsHandle = new FileHandle( + Win32.NamedPipePath + "\\", + FileShareMode.ReadWrite, + FileCreateOptions.SynchronousIoNonAlert, + FileAccess.ReadAttributes | (FileAccess)StandardRights.Synchronize + )) + { + using (var data = new MemoryAlloc(FilePipeWaitForBuffer.NameOffset + name.Length * 2)) + { + FilePipeWaitForBuffer info = new FilePipeWaitForBuffer(); + + info.Timeout = timeout; + info.TimeoutSpecified = true; + info.NameLength = name.Length * 2; + data.WriteStruct(info); + data.WriteUnicodeString(FilePipeWaitForBuffer.NameOffset, name); + + NtStatus status; + int returnLength; + + status = npfsHandle.FsControl(FsCtlWait, data, data.Size, IntPtr.Zero, 0, out returnLength); + + if (status == NtStatus.IoTimeout) + return false; + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return true; + } + } + } + + private NamedPipeHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public NamedPipeHandle(string fileName, FileAccess access) + : base(fileName, access) + { } + + public NamedPipeHandle(string fileName, FileShareMode shareMode, FileAccess access) + : base(fileName, shareMode, access) + { } + + public NamedPipeHandle(string fileName, FileShareMode shareMode, FileCreateOptions openOptions, FileAccess access) + : base(fileName, shareMode, openOptions, access) + { } + + public NamedPipeHandle( + string fileName, + ObjectFlags objectFlags, + FileHandle rootDirectory, + FileShareMode shareMode, + FileCreateOptions openOptions, + FileAccess access + ) + : base(fileName, objectFlags, rootDirectory, shareMode, openOptions, access) + { } + + public AsyncIoContext BeginListen() + { + return this.BeginFsControl(FsCtlListen, null, null); + } + + public AsyncIoContext BeginTransceive( + byte[] inBuffer, + int inBufferOffset, + int inBufferLength, + byte[] outBuffer, + int outBufferOffset, + int outBufferLength + ) + { + return this.BeginFsControl( + FsCtlTransceive, + inBuffer, + inBufferOffset, + inBufferLength, + outBuffer, + outBufferOffset, + outBufferLength + ); + } + + public AsyncIoContext BeginTransceive(MemoryRegion inBuffer, MemoryRegion outBuffer) + { + return this.BeginFsControl(FsCtlTransceive, inBuffer, outBuffer); + } + + public bool EndListen(AsyncIoContext asyncContext) + { + asyncContext.Wait(); + asyncContext.NotifyEnd(); + + if (asyncContext.Status == NtStatus.PipeConnected) + return true; + + if (asyncContext.StatusBlock.Status >= NtStatus.Error) + Win32.ThrowLastError(asyncContext.StatusBlock.Status); + + return false; + } + + public int EndTransceive(AsyncIoContext asyncContext) + { + return this.EndCommonIo(asyncContext); + } + + public void Disconnect() + { + this.FsControl(FsCtlDisconnect, IntPtr.Zero, 0, IntPtr.Zero, 0); + } + + private FilePipeInformation GetInformation() + { + return this.QueryStruct(FileInformationClass.FilePipeInformation); + } + + private FilePipeLocalInformation GetLocalInformation() + { + return this.QueryStruct(FileInformationClass.FilePipeLocalInformation); + } + + public PipeType GetPipeType() + { + return this.GetInformation().ReadMode; + } + + public void ImpersonateClient() + { + this.FsControl(FsCtlImpersonate, null, null); + } + + public bool Listen() + { + NtStatus status; + int returnLength; + + status = this.FsControl(FsCtlListen, IntPtr.Zero, 0, IntPtr.Zero, 0, out returnLength); + + if (status == NtStatus.PipeConnected) + return true; + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return false; + } + + public int Peek(byte[] buffer) + { + return this.Peek(buffer, 0, 0); + } + + public int Peek(byte[] buffer, int offset, int length) + { + int bytesAvailable; + + return this.Peek(buffer, offset, length, out bytesAvailable); + } + + public int Peek(IntPtr buffer, int length) + { + int bytesAvailable; + + return this.Peek(buffer, length, out bytesAvailable); + } + + public int Peek(byte[] buffer, out int bytesAvailable) + { + int bytesLeftInMessage; + + return this.Peek(buffer, out bytesAvailable, out bytesLeftInMessage); + } + + public int Peek(byte[] buffer, int offset, int length, out int bytesAvailable) + { + int bytesLeftInMessage; + + return this.Peek(buffer, offset, length, out bytesAvailable, out bytesLeftInMessage); + } + + public int Peek(IntPtr buffer, int length, out int bytesAvailable) + { + int bytesLeftInMessage; + + return this.Peek(buffer, length, out bytesAvailable, out bytesLeftInMessage); + } + + public int Peek(byte[] buffer, out int bytesAvailable, out int bytesLeftInMessage) + { + return this.Peek(buffer, 0, buffer.Length, out bytesAvailable, out bytesLeftInMessage); + } + + public int Peek(byte[] buffer, int offset, int length, out int bytesAvailable, out int bytesLeftInMessage) + { + Utils.ValidateBuffer(buffer, offset, length); + + unsafe + { + fixed (byte* bufferPtr = buffer) + { + return this.Peek(new IntPtr(&bufferPtr[offset]), length, out bytesAvailable, out bytesLeftInMessage); + } + } + } + + public int Peek(IntPtr buffer, int length, out int bytesAvailable, out int bytesLeftInMessage) + { + using (var data = new MemoryAlloc(FilePipePeekBuffer.DataOffset + length)) + { + NtStatus status; + int returnLength; + + status = this.FsControl(FsCtlPeek, IntPtr.Zero, 0, data, data.Size, out returnLength); + + // If we got a buffer overflow it simply means we didn't + // read all of the available bytes. + if (status == NtStatus.BufferOverflow) + status = NtStatus.Success; + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + FilePipePeekBuffer info = data.ReadStruct(); + int bytesRead; + + bytesAvailable = info.ReadDataAvailable; + bytesRead = returnLength - FilePipePeekBuffer.DataOffset; + bytesLeftInMessage = info.MessageLength - bytesRead; + + if (buffer != IntPtr.Zero) + data.ReadMemory(buffer, 0, FilePipePeekBuffer.DataOffset, bytesRead); + + return bytesRead; + } + } + + public int Transceive(byte[] inBuffer, byte[] outBuffer) + { + return this.FsControl(FsCtlTransceive, inBuffer, outBuffer); + } + + public int Transceive(IntPtr inBuffer, int inBufferLength, IntPtr outBuffer, int outBufferLength) + { + return this.FsControl(FsCtlTransceive, inBuffer, inBufferLength, outBuffer, outBufferLength); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/NativeHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/NativeHandle.cs new file mode 100644 index 000000000..7431a344d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/NativeHandle.cs @@ -0,0 +1,663 @@ +/* + * Process Hacker - + * windows handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a generic Windows handle which acts as a kernel handle by default. + /// + public class NativeHandle : BaseObject, IEquatable, ISecurable, ISynchronizable + { + public static IntPtr Invalid + { + get { return IntPtr.Zero; } + } + + public static IntPtr MinusOne + { + get { return (-1).ToIntPtr(); } + } + + public static bool IsInvalid(IntPtr handle) + { + return handle == Invalid; + } + + public static NtStatus WaitAll(ISynchronizable[] objects) + { + return WaitAll(objects, false, long.MinValue, false); + } + + public static NtStatus WaitAll(ISynchronizable[] objects, long timeout) + { + return WaitAll(objects, false, timeout); + } + + public static NtStatus WaitAll(ISynchronizable[] objects, bool alertable, long timeout) + { + return WaitAll(objects, alertable, timeout, true); + } + + public static NtStatus WaitAll(ISynchronizable[] objects, bool alertable, long timeout, bool relative) + { + return WaitForMultipleObjects(objects, WaitType.WaitAll, alertable, timeout, relative); + } + + public static NtStatus WaitAny(ISynchronizable[] objects) + { + return WaitAny(objects, false, long.MinValue, false); + } + + public static NtStatus WaitAny(ISynchronizable[] objects, long timeout) + { + return WaitAny(objects, false, timeout); + } + + public static NtStatus WaitAny(ISynchronizable[] objects, bool alertable, long timeout) + { + return WaitAny(objects, alertable, timeout, true); + } + + public static NtStatus WaitAny(ISynchronizable[] objects, bool alertable, long timeout, bool relative) + { + return WaitForMultipleObjects(objects, WaitType.WaitAny, alertable, timeout, relative); + } + + private static NtStatus WaitForMultipleObjects(ISynchronizable[] objects, WaitType waitType, bool alertable, long timeout, bool relative) + { + NtStatus status; + IntPtr[] handles = new IntPtr[objects.Length]; + long realTimeout = relative ? -timeout : timeout; + + for (int i = 0; i < objects.Length; i++) + handles[i] = objects[i].Handle; + + if ((status = Win32.NtWaitForMultipleObjects( + handles.Length, + handles, + waitType, + alertable, + ref realTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + public static implicit operator int(NativeHandle handle) + { + return handle.Handle.ToInt32(); + } + + public static implicit operator IntPtr(NativeHandle handle) + { + return handle.Handle; + } + + private IntPtr _handle; + + /// + /// Creates a new, invalid handle. You must set the handle using the Handle property. + /// + protected NativeHandle() + { } + + /// + /// Creates a new handle using the specified value. The handle will be closed when + /// this object is disposed or garbage-collected. + /// + /// The handle value. + public NativeHandle(IntPtr handle) + { + _handle = handle; + } + + /// + /// Creates a new handle using the specified value. If owned is set to false, the + /// handle will not be closed automatically. + /// + /// The handle value. + /// Specifies whether the handle will be closed automatically. + public NativeHandle(IntPtr handle, bool owned) + : base(owned) + { + _handle = handle; + } + + protected sealed override void DisposeObject(bool disposing) + { + this.Close(); + } + + /// + /// Closes the handle. This method must not be called directly; instead, + /// override this method in a derived class if your handle must be closed + /// with a method other than CloseHandle. + /// + protected virtual void Close() + { + if (_handle != IntPtr.Zero && _handle.ToInt32() != -1 && _handle.ToInt32() != -2) + Win32.NtClose(_handle); + } + + /// + /// Gets the handle value. + /// + public IntPtr Handle + { + get { return _handle; } + protected set { _handle = value; } + } + + /// + /// Determines if the specified object is equal to the current handle. + /// + /// The object to compare. + /// Whether the two objects are equal. + public override bool Equals(object obj) + { + return this.Equals(obj as NativeHandle); + } + + /// + /// Determines if the specified handle is equal to the current handle. + /// + /// The handle to compare. + /// Whether the two handles are equal. + public bool Equals(NativeHandle obj) + { + if (obj == null) + return false; + return obj.Handle == this.Handle; + } + + /// + /// Gets certain information about the handle. + /// + /// A HANDLE_FLAGS value. + public virtual Win32HandleFlags GetHandleFlags() + { + Win32HandleFlags flags; + + if (!Win32.GetHandleInformation(this, out flags)) + Win32.ThrowLastError(); + + return flags; + } + + /// + /// Gets a unique hash code for the handle. + /// + /// A hash code. + public override int GetHashCode() + { + return _handle.ToInt32(); + } + + /// + /// Gets the handle's name. + /// + /// A string. + public virtual string GetObjectName() + { + NtStatus status; + int retLength; + + status = Win32.NtQueryObject(this, ObjectInformationClass.ObjectNameInformation, + IntPtr.Zero, 0, out retLength); + + if (retLength > 0) + { + using (MemoryAlloc oniMem = new MemoryAlloc(retLength)) + { + if ((status = Win32.NtQueryObject(this, ObjectInformationClass.ObjectNameInformation, + oniMem, oniMem.Size, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + var oni = oniMem.ReadStruct(); + + return oni.Name.Read(); + } + } + else + { + Win32.ThrowLastError(status); + } + + return null; + } + + /// + /// Gets the handle's type name. + /// + /// A string. + public virtual string GetObjectTypeName() + { + NtStatus status; + int retLength; + + status = Win32.NtQueryObject(this, ObjectInformationClass.ObjectTypeInformation, + IntPtr.Zero, 0, out retLength); + + if (retLength > 0) + { + using (MemoryAlloc otiMem = new MemoryAlloc(retLength)) + { + if ((status = Win32.NtQueryObject(this, ObjectInformationClass.ObjectTypeInformation, + otiMem, otiMem.Size, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + var oni = otiMem.ReadStruct(); + + return oni.Name.Read(); + } + } + else + { + Win32.ThrowLastError(status); + } + + return null; + } + + /// + /// Gets the security descriptor of the object. + /// + /// The information to retrieve. + /// A security descriptor. + public virtual SecurityDescriptor GetSecurity(SecurityInformation securityInformation) + { + return SecurityDescriptor.GetSecurity(this, securityInformation); + } + + /// + /// Gets the security descriptor of the object. + /// + /// The type of the object. + /// The information to retrieve. + /// A security descriptor. + protected SecurityDescriptor GetSecurity(SeObjectType objectType, SecurityInformation securityInformation) + { + return SecurityDescriptor.GetSecurity(this, objectType, securityInformation); + } + + /// + /// Makes the object referenced by the handle permanent. + /// + public virtual void MakeObjectPermanent() + { + NtStatus status; + + if ((status = Win32.NtMakePermanentObject(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Makes the object referenced by the handle temporary. The object + /// will be deleted once the last handle to it is closed. This function + /// requires Delete access. + /// + public virtual void MakeObjectTemporary() + { + NtStatus status; + + if ((status = Win32.NtMakeTemporaryObject(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Marks the handle as invalid. This method must only be called from + /// within a derived class constructor. + /// + protected void MarkAsInvalid() + { + this.DisableOwnership(false); + } + + /// + /// Sets certain information about the handle. + /// + /// Specifies which flags to set. + /// The values of the flags to set. + public virtual void SetHandleFlags(Win32HandleFlags mask, Win32HandleFlags flags) + { + if (!Win32.SetHandleInformation(this, mask, flags)) + Win32.ThrowLastError(); + } + + /// + /// Sets the security descriptor of the object. + /// + /// The information to modify. + /// The security descriptor. + public virtual void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + SecurityDescriptor.SetSecurity(this, securityInformation, securityDescriptor); + } + + /// + /// Sets the security descriptor of the object. + /// + /// The type of the object. + /// The information to modify. + /// The security descriptor. + protected void SetSecurity(SeObjectType objectType, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + SecurityDescriptor.SetSecurity(this, objectType, securityInformation, securityDescriptor); + } + + /// + /// Signals the object and waits for another. + /// + public virtual NtStatus SignalAndWait(ISynchronizable waitObject) + { + return this.SignalAndWait(waitObject, false); + } + + /// + /// Signals the object and waits for another. + /// + public virtual NtStatus SignalAndWait(ISynchronizable waitObject, bool alertable) + { + return this.SignalAndWait(waitObject, alertable, long.MinValue, false); + } + + /// + /// Signals the object and waits for another. + /// + public virtual NtStatus SignalAndWait(ISynchronizable waitObject, bool alertable, long timeout) + { + return this.SignalAndWait(waitObject, alertable, timeout, true); + } + + /// + /// Signals the object and waits for another. + /// + public virtual NtStatus SignalAndWait(ISynchronizable waitObject, bool alertable, long timeout, bool relative) + { + NtStatus status; + long realTimeout = relative ? -timeout : timeout; + + if ((status = Win32.NtSignalAndWaitForSingleObject( + this, + waitObject.Handle, + alertable, + ref timeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + /// + /// Closes the current handle and assigns a new handle to the NativeHandle instance. + /// + /// The new handle value. + protected void SwapHandle(IntPtr newHandle) + { + if (!this.Owned || this.Disposed) + throw new InvalidOperationException(); + + this.Close(); + _handle = newHandle; + } + + /// + /// Gets a string that represents the handle. + /// + /// A string. + public override string ToString() + { + return this.GetType().Name + ": " + _handle.ToString("x"); + } + + /// + /// Waits for the object to be signaled. + /// + public virtual NtStatus Wait() + { + return this.Wait(false); + } + + /// + /// Waits for the object to be signaled. + /// + /// + /// Whether user-mode APCs can be delivered during the wait. + /// + public virtual NtStatus Wait(bool alertable) + { + /* Note that in order to wait for an infinite amount of time + * NULL should be passed as the timeout parameter to + * KeWaitForSingleObject/MultipleObjects. However, + * long.MinValue = -9223372036854775808 + * = 9223372036854775808 100ns (relative) + * = 922337203685477580.8 microseconds + * = 922337203685477.5808 ms + * = 922337203685.4775808 s + * = 15372286728.091293013333333333333 minutes + * = 256204778.80152155022222222222222 hours + * = 10675199.116730064592592592592593 days + * = 7306.7755761328299743960250462646 4 years (including one leap year) + * = 29227.102304531319897584100185058 years (average) + * = 29.227102304531319897584100185058 millennia + * That's long enough, I think... + */ + return this.Wait(alertable, long.MinValue, false); + } + + /// + /// Waits for the object to be signaled. + /// + /// The timeout, in 100ns units. + public NtStatus Wait(long timeout) + { + return this.Wait(false, timeout); + } + + /// + /// Waits for the object to be signaled. + /// + /// + /// Whether user-mode APCs can be delivered during the wait. + /// + /// The timeout, in 100ns units. + public virtual NtStatus Wait(bool alertable, long timeout) + { + return this.Wait(alertable, timeout, true); + } + + /// + /// Waits for the object to be signaled. + /// + /// The timeout, in 100ns units. + /// Whether the timeout value is relative. + public NtStatus Wait(long timeout, bool relative) + { + return this.Wait(false, timeout, relative); + } + + /// + /// Waits for the object to be signaled. + /// + /// + /// Whether user-mode APCs can be delivered during the wait. + /// + /// The timeout, in 100ns units. + /// Whether the timeout value is relative. + public virtual NtStatus Wait(bool alertable, long timeout, bool relative) + { + NtStatus status; + long realTimeout = relative ? -timeout : timeout; + + if ((status = Win32.NtWaitForSingleObject( + this, + alertable, + ref realTimeout + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } +} + + /// + /// Represents a generic Windows handle which acts as a kernel handle by default. + /// + public class NativeHandle : NativeHandle + where TAccess : struct + { + /// + /// Creates a new, invalid handle. You must set the handle using the Handle property. + /// + protected NativeHandle() + { } + + /// + /// Creates a new handle using the specified value. The handle will be closed when + /// this object is disposed or garbage-collected. + /// + /// The handle value. + public NativeHandle(IntPtr handle) + : base(handle) + { } + + /// + /// Creates a new handle using the specified value. If owned is set to false, the + /// handle will not be closed automatically. + /// + /// The handle value. + /// Specifies whether the handle will be closed automatically. + public NativeHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Creates a new handle by duplicating an existing handle. + /// + /// The existing handle. + /// The desired access to the object. + public NativeHandle(IntPtr handle, TAccess access) + { + IntPtr newHandle; + + Win32.DuplicateObject(ProcessHandle.Current, handle, ProcessHandle.Current, out newHandle, + (int)Convert.ChangeType(access, typeof(int)), 0, 0); + this.Handle = newHandle; + } + + /// + /// Creates a new handle by duplicating an existing handle from another process. + /// + /// A handle to a process. It must have the PROCESS_DUP_HANDLE permission. + /// The existing handle. + /// The desired access to the object. + public NativeHandle(ProcessHandle processHandle, IntPtr handle, TAccess access) + { + IntPtr newHandle; + + Win32.DuplicateObject(processHandle, handle, ProcessHandle.Current, out newHandle, + (int)Convert.ChangeType(access, typeof(int)), 0, 0); + this.Handle = newHandle; + } + + /// + /// Attempts to duplicate the handle with different access rights. + /// + /// The new access rights. + public void ChangeAccess(TAccess access) + { + IntPtr newHandle; + + Win32.DuplicateObject(ProcessHandle.Current, this, ProcessHandle.Current, out newHandle, + (int)Convert.ChangeType(access, typeof(int)), 0, 0); + this.SwapHandle(newHandle); + } + + /// + /// Duplicates the handle. + /// + /// The desired access to the object. + /// A handle. + public NativeHandle Duplicate(TAccess access) + { + return new NativeHandle(ProcessHandle.Current, this, access); + } + } + + /// + /// Represents a generic Windows handle which acts as a kernel handle by default. + /// + public class GenericHandle : NativeHandle + { + /// + /// Creates a new, invalid handle. You must set the handle using the Handle property. + /// + protected GenericHandle() + : base() + { } + + /// + /// Creates a new handle using the specified value. The handle will be closed when + /// this object is disposed or garbage-collected. + /// + /// The handle value. + public GenericHandle(IntPtr handle) + : base(handle) + { } + + /// + /// Creates a new handle using the specified value. If owned is set to false, the + /// handle will not be closed automatically. + /// + /// The handle value. + /// Specifies whether the handle will be closed automatically. + public GenericHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Creates a new handle by duplicating an existing handle. + /// + /// The existing handle. + /// The desired access to the object. + public GenericHandle(IntPtr handle, int desiredAccess) + : base(handle, desiredAccess) + { } + + /// + /// Creates a new handle by duplicating an existing handle from another process. + /// + /// A handle to a process. It must have the PROCESS_DUP_HANDLE permission. + /// The existing handle. + /// The desired access to the object. + public GenericHandle(ProcessHandle processHandle, IntPtr handle, int desiredAccess) + : base(processHandle, handle, desiredAccess) + { } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/PortComHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/PortComHandle.cs new file mode 100644 index 000000000..73adfe161 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/PortComHandle.cs @@ -0,0 +1,168 @@ +/* + * Process Hacker - + * port communication handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Lpc; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class PortComHandle : NativeHandle + { + public static PortComHandle Connect(string portName) + { + NtStatus status; + UnicodeString portNameStr = new UnicodeString(portName); + SecurityQualityOfService securityQos = + new SecurityQualityOfService(SecurityImpersonationLevel.SecurityImpersonation, true, false); + IntPtr handle; + + try + { + if ((status = Win32.NtConnectPort( + out handle, + ref portNameStr, + ref securityQos, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + portNameStr.Dispose(); + } + + return new PortComHandle(handle, true); + } + + internal PortComHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public void Reply(PortMessage message) + { + NtStatus status; + + using (var messageMemory = message.ToMemory()) + { + if ((status = Win32.NtReplyPort(this, messageMemory)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + message.SetHeader(messageMemory); + } + } + + public PortMessage ReplyWaitReceive() + { + return this.ReplyWaitReceive(null); + } + + public PortMessage ReplyWaitReceive(PortMessage message) + { + NtStatus status; + IntPtr context; + + using (var buffer = PortMessage.AllocateBuffer()) + { + MemoryAlloc messageMemory = null; + + if (message != null) + messageMemory = message.ToMemory(); + + try + { + if ((status = Win32.NtReplyWaitReceivePort( + this, + out context, + messageMemory ?? IntPtr.Zero, + buffer + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (message != null) + message.SetHeader(messageMemory); + } + finally + { + if (messageMemory != null) + messageMemory.Dispose(); + } + + return new PortMessage(buffer); + } + } + + public PortMessage ReplyWaitReply(PortMessage message) + { + NtStatus status; + + using (var messageMemory = message.ToMemory()) + { + if ((status = Win32.NtReplyWaitReplyPort( + this, + messageMemory + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new PortMessage(messageMemory); + } + } + + public void Request(PortMessage message) + { + NtStatus status; + + using (var messageMemory = message.ToMemory()) + { + if ((status = Win32.NtRequestPort(this, messageMemory)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + message.SetHeader(messageMemory); + } + } + + public PortMessage RequestWaitReply(PortMessage message) + { + NtStatus status; + + using (var buffer = PortMessage.AllocateBuffer()) + using (var messageMemory = message.ToMemory()) + { + if ((status = Win32.NtRequestWaitReplyPort( + this, + messageMemory, + buffer + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + message.SetHeader(messageMemory); + + return new PortMessage(buffer); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/PortHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/PortHandle.cs new file mode 100644 index 000000000..25e30284b --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/PortHandle.cs @@ -0,0 +1,177 @@ +/* + * Process Hacker - + * port handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Lpc; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class PortHandle : NativeHandle + { + public static PortHandle Create( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory + ) + { + return Create( + name, + objectFlags, + rootDirectory, + Win32.PortMessageMaxDataLength, + Win32.PortMessageMaxLength, + 0 + ); + } + + public static PortHandle Create( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + int maxConnectionInfoLength, + int maxMessageLength, + int maxPoolUsage + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreatePort( + out handle, + ref oa, + maxConnectionInfoLength, + maxMessageLength, + maxPoolUsage + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new PortHandle(handle, true); + } + + public static PortHandle CreateWaitable( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory + ) + { + return CreateWaitable( + name, + objectFlags, + rootDirectory, + Win32.PortMessageMaxDataLength, + Win32.PortMessageMaxLength, + 0 + ); + } + + public static PortHandle CreateWaitable( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + int maxConnectionInfoLength, + int maxMessageLength, + int maxPoolUsage + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateWaitablePort( + out handle, + ref oa, + maxConnectionInfoLength, + maxMessageLength, + maxPoolUsage + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new PortHandle(handle, true); + } + + private PortHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public PortComHandle AcceptConnect(PortMessage message, bool accept) + { + NtStatus status; + IntPtr portHandle; + + using (var messageMemory = message.ToMemory()) + { + if ((status = Win32.NtAcceptConnectPort( + out portHandle, + IntPtr.Zero, + messageMemory, + accept, + IntPtr.Zero, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (!NativeHandle.IsInvalid(portHandle)) + return new PortComHandle(portHandle, true); + else + return null; + } + } + + public void CompleteConnect() + { + NtStatus status; + + if ((status = Win32.NtCompleteConnectPort(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public PortMessage Listen() + { + NtStatus status; + + using (var buffer = PortMessage.AllocateBuffer()) + { + if ((status = Win32.NtListenPort(this, buffer)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new PortMessage(buffer); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/PrivateNamespaceHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/PrivateNamespaceHandle.cs new file mode 100644 index 000000000..882e146c2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/PrivateNamespaceHandle.cs @@ -0,0 +1,117 @@ +/* + * Process Hacker - + * private namespace handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a private namespace, a private directory object. + /// + public class PrivateNamespaceHandle : DirectoryHandle + { + public static PrivateNamespaceHandle Create(BoundaryDescriptor boundaryDescriptor, string aliasPrefix) + { + IntPtr handle = IntPtr.Zero; + + handle = Win32.CreatePrivateNamespace(IntPtr.Zero, boundaryDescriptor.Descriptor, aliasPrefix); + + if (handle == IntPtr.Zero) + Win32.ThrowLastError(); + + return new PrivateNamespaceHandle(handle, true); + } + + private bool _destroy = false; + + private PrivateNamespaceHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public PrivateNamespaceHandle(BoundaryDescriptor boundaryDescriptor, string aliasPrefix) + { + this.Handle = Win32.OpenPrivateNamespace(boundaryDescriptor.Descriptor, aliasPrefix); + + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + + protected override void Close() + { + Win32.ClosePrivateNamespace(this, _destroy ? PrivateNamespaceFlags.Destroy : 0); + } + + public void MarkForDestruction() + { + _destroy = true; + } + } + + public class BoundaryDescriptor : BaseObject + { + private IntPtr _descriptor; + + public BoundaryDescriptor(string name) + : this(name, null) + { } + + public BoundaryDescriptor(string name, IEnumerable sids) + { + _descriptor = Win32.CreateBoundaryDescriptor(name, 0); + + if (_descriptor == IntPtr.Zero) + { + this.DisableOwnership(false); + Win32.ThrowLastError(); + } + + if (sids != null) + { + foreach (Sid sid in sids) + this.Add(sid); + } + } + + protected override void DisposeObject(bool disposing) + { + Win32.DeleteBoundaryDescriptor(_descriptor); + } + + public IntPtr Descriptor + { + get { return _descriptor; } + } + + public void Add(Sid sid) + { + if (!Win32.AddSIDToBoundaryDescriptor(ref _descriptor, sid)) + Win32.ThrowLastError(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ProcessHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ProcessHandle.cs new file mode 100644 index 000000000..328223868 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ProcessHandle.cs @@ -0,0 +1,2917 @@ +/* + * Process Hacker - + * process handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a Windows process. + /// + /// + /// The idea of a ProcessHandle class is + /// different to the class; + /// instead of opening the process with the right permissions every + /// time a query or set function is called, this lets the users control + /// when they want to open handles with certain permissions. This + /// means that handles can be cached (by the users). + /// + public sealed class ProcessHandle : NativeHandle, IWithToken + { + /// + /// The callback for enumerating process memory regions. + /// + /// The basic information for the memory region. + /// Return true to continue enumerating; return false to stop. + public delegate bool EnumMemoryDelegate(MemoryBasicInformation info); + + /// + /// The callback for enumerating process modules. + /// + /// The module information. + /// Return true to continue enumerating; return false to stop. + public delegate bool EnumModulesDelegate(ProcessModule module); + + private static readonly ProcessHandle _current = new ProcessHandle(new IntPtr(-1), false); + + /// + /// Gets a handle to the current process. + /// + public static ProcessHandle Current + { + get { return _current; } + } + + /// + /// Creates a process. + /// + /// The desired access to the new process. + /// The process to inherit the address space and handles from. + /// Specify true to inherit handles, otherwise false. + /// A section of an executable image. + /// A handle to the new process. + public static ProcessHandle Create( + ProcessAccess access, + ProcessHandle parentProcess, + bool inheritHandles, + SectionHandle sectionHandle) + { + return Create(access, null, 0, null, parentProcess, inheritHandles, sectionHandle, null); + } + + /// + /// Creates a process. + /// + /// The desired access to the new process. + /// The name of the process. + /// The flags to use when creating the object. + /// A handle to the directory in which to place the object. + /// The process to inherit the address space and handles from. + /// Specify true to inherit handles, otherwise false. + /// A section of an executable image. + /// A debug object to attach the process to. + /// A handle to the new process. + public static ProcessHandle Create( + ProcessAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ProcessHandle parentProcess, + bool inheritHandles, + SectionHandle sectionHandle, + DebugObjectHandle debugPort + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateProcess( + out handle, + access, + ref oa, + parentProcess ?? IntPtr.Zero, + inheritHandles, + sectionHandle ?? IntPtr.Zero, + debugPort ?? IntPtr.Zero, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new ProcessHandle(handle, true); + } + + public static ProcessHandle CreateExtended( + string fileName, + ProcessHandle parentProcess, + ProcessCreationFlags creationFlags, + bool inheritHandles, + string currentDirectory, + StartupInfo startupInfo, + out ClientId clientId, + out ThreadHandle threadHandle + ) + { + return CreateExtended( + fileName, + parentProcess, + creationFlags, + true, + inheritHandles, + EnvironmentBlock.GetCurrent(), + currentDirectory, + startupInfo, + out clientId, + out threadHandle + ); + } + + public static ProcessHandle CreateExtended( + string fileName, + ProcessHandle parentProcess, + ProcessCreationFlags creationFlags, + bool notifyCsr, + bool inheritHandles, + EnvironmentBlock environment, + string currentDirectory, + StartupInfo startupInfo, + out ClientId clientId, + out ThreadHandle threadHandle + ) + { + ProcessHandle phandle; + ThreadHandle thandle; + SectionImageInformation imageInfo; + + // If we don't have a desktop, use the current one. + if (startupInfo.Desktop == null) + startupInfo.Desktop = ProcessHandle.Current.GetPebString(PebOffset.DesktopName); + + // Open the file, create a section, and create a process. + using (var fhandle = new FileHandle( + fileName, + FileShareMode.Read | FileShareMode.Delete, + FileAccess.Execute | (FileAccess)StandardRights.Synchronize + )) + { + using (var shandle = SectionHandle.Create( + SectionAccess.All, + SectionAttributes.Image, + MemoryProtection.Execute, + fhandle + )) + { + imageInfo = shandle.GetImageInformation(); + + phandle = Create( + ProcessAccess.All, + parentProcess, + inheritHandles, + shandle + ); + } + } + + IntPtr peb = phandle.GetBasicInformation().PebBaseAddress; + + // Copy the process parameters across. + NativeUtils.CopyProcessParameters( + phandle, + peb, + creationFlags, + FileUtils.GetFileName(fileName), + ProcessHandle.Current.GetPebString(PebOffset.DllPath), + currentDirectory, + fileName, + environment, + startupInfo.Title != null ? startupInfo.Title : fileName, + startupInfo.Desktop != null ? startupInfo.Desktop : "", + startupInfo.Reserved != null ? startupInfo.Reserved : "", + "", + ref startupInfo + ); + + // TODO: Duplicate the console handles (stdin, stdout, stderr). + + // Create the initial thread. + thandle = ThreadHandle.CreateUserThread( + phandle, + true, + imageInfo.StackCommit.Increment(imageInfo.StackReserved).ToInt32(), + imageInfo.StackCommit.ToInt32(), + imageInfo.TransferAddress, + IntPtr.Zero, + out clientId + ); + + // Notify CSR. + + if (notifyCsr) + { + BaseCreateProcessMsg processMsg = new BaseCreateProcessMsg(); + + processMsg.ProcessHandle = phandle; + processMsg.ThreadHandle = thandle; + processMsg.ClientId = clientId; + processMsg.CreationFlags = creationFlags; + + if ((creationFlags & (ProcessCreationFlags.DebugProcess | + ProcessCreationFlags.DebugOnlyThisProcess)) != 0) + { + NtStatus status; + + status = Win32.DbgUiConnectToDbg(); + + if (status >= NtStatus.Error) + { + phandle.Terminate(status); + Win32.ThrowLastError(status); + } + + processMsg.DebuggerClientId = ThreadHandle.GetCurrentCid(); + } + + // If this is a GUI program, set the 1 and 2 bits to turn the + // hourglass cursor on. + if (imageInfo.ImageSubsystem == 2) + processMsg.ProcessHandle = processMsg.ProcessHandle.Or((1 | 2).ToIntPtr()); + // We still have to honor the startup info settings, though. + if ((startupInfo.Flags & StartupFlags.ForceOnFeedback) == + StartupFlags.ForceOnFeedback) + processMsg.ProcessHandle = processMsg.ProcessHandle.Or((1).ToIntPtr()); + if ((startupInfo.Flags & StartupFlags.ForceOffFeedback) == + StartupFlags.ForceOffFeedback) + processMsg.ProcessHandle = processMsg.ProcessHandle.And((1).ToIntPtr().Not()); + + using (var data = new MemoryAlloc( + CsrApiMsg.ApiMessageDataOffset + Marshal.SizeOf(typeof(BaseCreateProcessMsg)) + )) + { + data.WriteStruct(CsrApiMsg.ApiMessageDataOffset, 0, processMsg); + + Win32.CsrClientCallServer( + data, + IntPtr.Zero, + Win32.CsrMakeApiNumber(Win32.BaseSrvServerDllIndex, (int)BaseSrvApiNumber.BasepCreateProcess), + Marshal.SizeOf(typeof(BaseCreateProcessMsg)) + ); + + NtStatus status = (NtStatus)data.ReadStruct().ReturnValue; + + if (status >= NtStatus.Error) + { + phandle.Terminate(status); + Win32.ThrowLastError(status); + } + } + } + + if ((creationFlags & ProcessCreationFlags.CreateSuspended) == 0) + thandle.Resume(); + + threadHandle = thandle; + + return phandle; + } + + public static ProcessHandle CreateUserProcess(string fileName, out ClientId clientId, out ThreadHandle threadHandle) + { + NtStatus status; + UnicodeString fileNameStr = new UnicodeString(fileName); + RtlUserProcessParameters processParams = new RtlUserProcessParameters(); + RtlUserProcessInformation processInfo; + + processParams.Length = Marshal.SizeOf(processParams); + processParams.MaximumLength = processParams.Length; + processParams.ImagePathName = new UnicodeString(fileName); + processParams.CommandLine = new UnicodeString(fileName); + + Win32.RtlCreateEnvironment(true, out processParams.Environment); + + try + { + if ((status = Win32.RtlCreateUserProcess( + ref fileNameStr, + 0, + ref processParams, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + false, + IntPtr.Zero, + IntPtr.Zero, + out processInfo + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + clientId = processInfo.ClientId; + threadHandle = new ThreadHandle(processInfo.Thread, true); + + return new ProcessHandle(processInfo.Process, true); + } + finally + { + fileNameStr.Dispose(); + processParams.ImagePathName.Dispose(); + processParams.CommandLine.Dispose(); + Win32.RtlDestroyEnvironment(processParams.Environment); + } + } + + public static ProcessHandle CreateWin32( + string applicationName, + string commandLine, + bool inheritHandles, + ProcessCreationFlags creationFlags, + EnvironmentBlock environment, + string currentDirectory, + StartupInfo startupInfo, + out ClientId clientId, + out ThreadHandle threadHandle + ) + { + ProcessInformation processInformation; + + if (!Win32.CreateProcess( + applicationName, + commandLine, + IntPtr.Zero, + IntPtr.Zero, + inheritHandles, + creationFlags, + environment, + currentDirectory, + ref startupInfo, + out processInformation + )) + Win32.ThrowLastError(); + + clientId = new ClientId(processInformation.ProcessId, processInformation.ThreadId); + threadHandle = new ThreadHandle(processInformation.ThreadHandle, true); + + return new ProcessHandle(processInformation.ProcessHandle, true); + } + + /// + /// Creates a process handle using an existing handle. + /// The handle will not be closed automatically. + /// + /// The handle value. + /// The process handle. + public static ProcessHandle FromHandle(IntPtr handle) + { + return new ProcessHandle(handle, false); + } + + /// + /// Gets a handle to the current process. + /// + /// A process handle. + public static ProcessHandle GetCurrent() + { + return Current; + } + + /// + /// Gets the ID of the current process. + /// + /// The ID of the current process. + public static int GetCurrentId() + { + return Win32.GetCurrentProcessId(); + } + + /// + /// Gets a pointer to the current process' environment block. + /// + /// A pointer to the current PEB. + public unsafe static Peb* GetCurrentPeb() + { + return (Peb*)ThreadHandle.GetCurrentTeb()->ProcessEnvironmentBlock; + } + + public unsafe static RtlUserProcessParameters* GetCurrentProcessParameters() + { + return (RtlUserProcessParameters*)GetCurrentPeb()->ProcessParameters; + } + + private static int GetPebOffset(PebOffset offset) + { + switch (offset) + { + case PebOffset.CommandLine: + return RtlUserProcessParameters.CommandLineOffset; + case PebOffset.CurrentDirectoryPath: + return RtlUserProcessParameters.CurrentDirectoryOffset; + case PebOffset.DesktopName: + return RtlUserProcessParameters.DesktopInfoOffset; + case PebOffset.DllPath: + return RtlUserProcessParameters.DllPathOffset; + case PebOffset.ImagePathName: + return RtlUserProcessParameters.ImagePathNameOffset; + case PebOffset.RuntimeData: + return RtlUserProcessParameters.RuntimeDataOffset; + case PebOffset.ShellInfo: + return RtlUserProcessParameters.ShellInfoOffset; + case PebOffset.WindowTitle: + return RtlUserProcessParameters.WindowTitleOffset; + default: + throw new ArgumentException("offset"); + } + } + + /// + /// Opens processes with the specified name. + /// + /// The names of the processes to open. + /// The desired access to the processes. + /// An array of process handles. + public static ProcessHandle[] OpenByName(string processName, ProcessAccess access) + { + var processes = Windows.GetProcesses(); + List processHandles = new List(); + + foreach (var process in processes.Values) + { + if (string.Equals(process.Name, processName, StringComparison.InvariantCultureIgnoreCase)) + { + try + { + processHandles.Add(new ProcessHandle(process.Process.ProcessId, access)); + } + catch + { } + } + } + + return processHandles.ToArray(); + } + + /// + /// Opens a handle to the current process. + /// + /// The desired access to the current process. + /// A handle. + public static ProcessHandle OpenCurrent(ProcessAccess access) + { + return new ProcessHandle(GetCurrentId(), access); + } + + public static ProcessHandle OpenWithAnyAccess(int pid) + { + try + { + return new ProcessHandle(pid, OSVersion.MinProcessQueryInfoAccess); + } + catch + { + try + { + return new ProcessHandle(pid, (ProcessAccess)StandardRights.Synchronize); + } + catch + { + try + { + return new ProcessHandle(pid, (ProcessAccess)StandardRights.ReadControl); + } + catch + { + try + { + return new ProcessHandle(pid, (ProcessAccess)StandardRights.WriteDac); + } + catch + { + return new ProcessHandle(pid, (ProcessAccess)StandardRights.WriteOwner); + } + } + } + } + } + + private ProcessHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens a process. + /// + /// The ID of the process to open. + public ProcessHandle(int pid) + : this(pid, ProcessAccess.All) + { } + + /// + /// Opens a process. + /// + /// The ID of the process to open. + /// The desired access to the process. + public ProcessHandle(int pid, ProcessAccess access) + { + // If we have KPH, use it. + if (KProcessHacker.Instance != null) + { + try + { + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcess(pid, access)); + } + catch (WindowsException) + { + // This would only happen if the process is DRM-protected or if + // some part of ObReferenceObjectByHandle is hooked. We can + // open the process with SYNCHRONIZE access and set the granted access + // using KPH. + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcess(pid, + (ProcessAccess)StandardRights.Synchronize)); + KProcessHacker.Instance.KphSetHandleGrantedAccess(this.Handle, (int)access); + } + } + else + { + this.Handle = Win32.OpenProcess(access, false, pid); + } + + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + + /// + /// Opens a thread's process. + /// + /// A handle to a thread. + /// The desired access to the process. + public ProcessHandle(ThreadHandle threadHandle, ProcessAccess access) + { + if (KProcessHacker.Instance == null) + throw new NotSupportedException(); + + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThreadProcess(threadHandle, access)); + } + + /// + /// Opens a process. + /// + /// The name of the process. + /// The flags to use when opening the object. + /// + /// A handle to the directory in which the object is located. + /// + /// A Client ID structure describing the process. + /// The desired access to the process. + public ProcessHandle( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ClientId clientId, + ProcessAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + // NtOpenProcess fails when both a client ID and a name is specified. + if (name != null) + { + // Name specified, don't specify a CID. + if ((status = Win32.NtOpenProcess( + out handle, + access, + ref oa, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + else + { + // No name, specify a CID. + if ((status = Win32.NtOpenProcess( + out handle, + access, + ref oa, + ref clientId + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + /// + /// Opens a process. + /// + /// The name of the process. + /// The desired access to the process. + public ProcessHandle(string name, ProcessAccess access) + : this(name, 0, null, new ClientId(), access) + { } + + /// + /// Opens a process. + /// + /// A Client ID structure describing the process. + /// The desired access to the process. + public ProcessHandle(ClientId clientId, ProcessAccess access) + : this(null, 0, null, clientId, access) + { } + + /// + /// Allocates a memory region in the process' virtual memory. The function decides where + /// to allocate the memory. + /// + /// The size of the region. + /// The protection of the region. + /// The base address of the allocated pages. + public IntPtr AllocateMemory(int size, MemoryProtection protection) + { + return this.AllocateMemory(size, MemoryFlags.Commit, protection); + } + + /// + /// Allocates a memory region in the process' virtual memory. The function decides where + /// to allocate the memory. + /// + /// The size of the region. + /// The type of allocation. + /// The protection of the region. + /// The base address of the allocated pages. + public IntPtr AllocateMemory(int size, MemoryFlags type, MemoryProtection protection) + { + return this.AllocateMemory(IntPtr.Zero, size, type, protection); + } + + /// + /// Allocates a memory region in the process' virtual memory. + /// + /// The base address of the region. + /// The size of the region. + /// The type of allocation. + /// The protection of the region. + /// The base address of the allocated pages. + public IntPtr AllocateMemory(IntPtr baseAddress, int size, MemoryFlags type, MemoryProtection protection) + { + IntPtr sizeIntPtr = new IntPtr(size); + + return this.AllocateMemory(baseAddress, ref sizeIntPtr, type, protection); + } + + /// + /// Allocates a memory region in the process' virtual memory. + /// + /// The base address of the region. + /// + /// The size of the region. This variable will be modified to contain + /// the actual allocated size. + /// + /// The type of allocation. + /// The protection of the region. + /// The base address of the allocated pages. + public IntPtr AllocateMemory(IntPtr baseAddress, ref IntPtr size, MemoryFlags type, MemoryProtection protection) + { + NtStatus status; + + if ((status = Win32.NtAllocateVirtualMemory( + this, + ref baseAddress, + IntPtr.Zero, + ref size, + type, + protection + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return baseAddress; + } + + /// + /// Assigns the process to a job object. The job handle must have the + /// JOB_OBJECT_ASSIGN_PROCESS permission and the process handle must have + /// the PROCESS_SET_QUOTA and PROCESS_TERMINATE permissions. + /// + /// The job object to assign the process to. + public void AssignToJobObject(JobObjectHandle job) + { + if (!Win32.AssignProcessToJobObject(job, this)) + Win32.ThrowLastError(); + } + + /// + /// Creates a thread in the process. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// A handle to the new thread. + /// This function will work across sessions, unlike CreateThreadWin32. + public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter) + { + return this.CreateThread(startAddress, parameter, false); + } + + /// + /// Creates a thread in the process. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// Whether to create the thread suspended. + /// A handle to the new thread. + /// This function will work across sessions, unlike CreateThreadWin32. + public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter, bool createSuspended) + { + int threadId; + + return this.CreateThread(startAddress, parameter, createSuspended, out threadId); + } + + /// + /// Creates a thread in the process. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// Whether to create the thread suspended. + /// The ID of the new thread. + /// A handle to the new thread. + /// This function will work across sessions, unlike CreateThreadWin32. + public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter, bool createSuspended, out int threadId) + { + ClientId cid; + + ThreadHandle thandle = ThreadHandle.CreateUserThread( + this, + createSuspended, + 0, + 0, + startAddress, + parameter, + out cid + ); + + threadId = cid.ThreadId; + + return thandle; + } + + /// + /// Creates a thread in the process and notifies the Win32 subsystem. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// A handle to the new thread. + public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter) + { + return this.CreateThreadWin32(startAddress, parameter, false); + } + + /// + /// Creates a thread in the process and notifies the Win32 subsystem. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// Whether to create the thread suspended. + /// A handle to the new thread. + public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter, bool createSuspended) + { + int threadId; + + return this.CreateThreadWin32(startAddress, parameter, createSuspended, out threadId); + } + + /// + /// Creates a thread in the process and notifies the Win32 subsystem. + /// + /// The address at which to begin execution. + /// The parameter to pass to the function. + /// Whether to create the thread suspended. + /// The ID of the new thread. + /// A handle to the new thread. + public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter, bool createSuspended, out int threadId) + { + IntPtr threadHandle; + + if ((threadHandle = Win32.CreateRemoteThread( + this, + IntPtr.Zero, + IntPtr.Zero, + startAddress, + parameter, + createSuspended ? ProcessCreationFlags.CreateSuspended : 0, + out threadId + )) == IntPtr.Zero) + Win32.ThrowLastError(); + + return new ThreadHandle(threadHandle, true); + } + + /// + /// Debugs the process with the specified debug object. This requires + /// PROCESS_SUSPEND_RESUME access. + /// + /// A handle to a debug object. + public void Debug(DebugObjectHandle debugObjectHandle) + { + NtStatus status; + + if ((status = Win32.NtDebugActiveProcess(this, debugObjectHandle)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Disables the collection of handle stack traces. This requires + /// PROCESS_SET_INFORMATION access. Note that this function is only + /// available on Windows Vista and above. + /// + public void DisableHandleTracing() + { + NtStatus status; + + // Length 0 and NULL disables handle tracing. + if ((status = Win32.NtSetInformationProcess( + this, + ProcessInformationClass.ProcessHandleTracing, + IntPtr.Zero, + 0 + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Removes as many pages as possible from the process' working set. This requires the + /// PROCESS_QUERY_INFORMATION and PROCESS_SET_INFORMATION permissions. + /// + public void EmptyWorkingSet() + { + if (!Win32.EmptyWorkingSet(this)) + Win32.ThrowLastError(); + } + + /// + /// Enables the collection of handle stack traces. This requires + /// PROCESS_SET_INFORMATION access. + /// + public void EnableHandleTracing() + { + NtStatus status; + ProcessHandleTracingEnable phte = new ProcessHandleTracingEnable(); + + if ((status = Win32.NtSetInformationProcess( + this, + ProcessInformationClass.ProcessHandleTracing, + ref phte, + Marshal.SizeOf(phte) + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Enumerates the memory regions of the process. + /// + /// The callback for the enumeration. + public void EnumMemory(EnumMemoryDelegate enumMemoryCallback) + { + IntPtr address = IntPtr.Zero; + MemoryBasicInformation mbi = new MemoryBasicInformation(); + int mbiSize = Marshal.SizeOf(mbi); + + while (Win32.VirtualQueryEx(this, address, out mbi, mbiSize) != 0) + { + if (!enumMemoryCallback(mbi)) + break; + + address = address.Increment(mbi.RegionSize); + } + } + + /// + /// Enumerates the modules loaded by the process. + /// + /// The callback for the enumeration. + public void EnumModules(EnumModulesDelegate enumModulesCallback) + { + this.EnumModulesNative(enumModulesCallback); + } + + /// + /// Enumerates the modules loaded by the process using PSAPI. + /// + /// The callback for the enumeration. + private void EnumModulesApi(EnumModulesDelegate enumModulesCallback) + { + IntPtr[] moduleHandles; + int requiredSize; + + Win32.EnumProcessModules(this, null, 0, out requiredSize); + moduleHandles = new IntPtr[requiredSize / 4]; + + if (!Win32.EnumProcessModules(this, moduleHandles, requiredSize, out requiredSize)) + Win32.ThrowLastError(); + + for (int i = 0; i < moduleHandles.Length; i++) + { + ModuleInfo moduleInfo = new ModuleInfo(); + StringBuilder baseName = new StringBuilder(0x400); + StringBuilder fileName = new StringBuilder(0x400); + + if (!Win32.GetModuleInformation(this, moduleHandles[i], moduleInfo, Marshal.SizeOf(moduleInfo))) + Win32.ThrowLastError(); + if (Win32.GetModuleBaseName(this, moduleHandles[i], baseName, baseName.Capacity * 2) == 0) + Win32.ThrowLastError(); + if (Win32.GetModuleFileNameEx(this, moduleHandles[i], fileName, fileName.Capacity * 2) == 0) + Win32.ThrowLastError(); + + if (!enumModulesCallback(new ProcessModule( + moduleInfo.BaseOfDll, moduleInfo.SizeOfImage, moduleInfo.EntryPoint, 0, + baseName.ToString(), FileUtils.GetFileName(fileName.ToString()) + ))) + break; + } + } + + /// + /// Enumerates the modules loaded by the process by reading the NT loader data. + /// + /// The callback for the enumeration. + private unsafe void EnumModulesNative(EnumModulesDelegate enumModulesCallback) + { + byte* buffer = stackalloc byte[IntPtr.Size]; + + // Get the loader data table address. + this.ReadMemory(this.GetBasicInformation().PebBaseAddress.Increment(Peb.LdrOffset), buffer, IntPtr.Size); + + IntPtr loaderData = *(IntPtr*)buffer; + + PebLdrData* data = stackalloc PebLdrData[1]; + // Read the loader data table structure. + this.ReadMemory(loaderData, data, Marshal.SizeOf(typeof(PebLdrData))); + + if (!data->Initialized) + throw new Exception("Loader data is not initialized."); + + IntPtr currentLink = data->InLoadOrderModuleList.Flink; + IntPtr startLink = currentLink; + LdrDataTableEntry* currentEntry = stackalloc LdrDataTableEntry[1]; + int i = 0; + + while (currentLink != IntPtr.Zero) + { + // Stop when we have reached the beginning of the linked list. + if (i > 0 && currentLink == startLink) + break; + // Safety guard. + if (i > 0x800) + break; + + // Read the loader data table entry. + this.ReadMemory(currentLink, currentEntry, Marshal.SizeOf(typeof(LdrDataTableEntry))); + + // Check if the entry is valid. + if (currentEntry->DllBase != IntPtr.Zero) + { + string baseDllName = null; + string fullDllName = null; + + // Read the two strings. + try + { + baseDllName = currentEntry->BaseDllName.Read(this).TrimEnd('\0'); + } + catch + { } + + try + { + fullDllName = FileUtils.GetFileName(currentEntry->FullDllName.Read(this).TrimEnd('\0')); + } + catch + { } + + // Execute the callback. + if (!enumModulesCallback(new ProcessModule( + currentEntry->DllBase, + currentEntry->SizeOfImage, + currentEntry->EntryPoint, + currentEntry->Flags, + baseDllName, + fullDllName + ))) + break; + } + + currentLink = currentEntry->InLoadOrderLinks.Flink; + i++; + } + } + + /// + /// Flushes the process' virtual memory. + /// + /// The base address of the region to flush. + /// The size of the region to flush. + /// A NT status value. + public NtStatus FlushMemory(IntPtr baseAddress, int size) + { + NtStatus status; + IntPtr sizeIntPtr = size.ToIntPtr(); + IoStatusBlock isb; + + if ((status = Win32.NtFlushVirtualMemory( + this, + ref baseAddress, + ref sizeIntPtr, + out isb + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return isb.Status; + } + + /// + /// Frees a memory region in the process' virtual memory. + /// + /// The address of the region to free. + /// The size to free. + public void FreeMemory(IntPtr baseAddress, int size) + { + this.FreeMemory(baseAddress, size, false); + } + + /// + /// Frees a memory region in the process' virtual memory. + /// + /// The address of the region to free. + /// The size to free. + /// Specifies whether or not to only + /// reserve the memory instead of freeing it. + public void FreeMemory(IntPtr baseAddress, int size, bool reserveOnly) + { + NtStatus status; + IntPtr sizeIntPtr = size.ToIntPtr(); + + // Size needs to be 0 if we're freeing. + if (!reserveOnly) + sizeIntPtr = IntPtr.Zero; + + if ((status = Win32.NtFreeVirtualMemory( + this, + ref baseAddress, + ref sizeIntPtr, + reserveOnly ? MemoryFlags.Decommit : MemoryFlags.Release + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Gets the processor affinity for the process. + /// + /// The processor affinity for the process. + public long GetAffinityMask() + { + long systemMask; + + return this.GetAffinityMask(out systemMask); + } + + /// + /// Gets the processor affinity for the process. + /// + /// Receives the processor affinity mask for the system. + /// The processor affinity for the process. + public long GetAffinityMask(out long systemMask) + { + IntPtr processMaskTemp; + IntPtr systemMaskTemp; + + if (!Win32.GetProcessAffinityMask(this, out processMaskTemp, out systemMaskTemp)) + Win32.ThrowLastError(); + + systemMask = systemMaskTemp.ToInt64(); + + return processMaskTemp.ToInt64(); + } + + /// + /// Gets the base priority of the process. + /// + public int GetBasePriority() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessBasePriority); + } + + /// + /// Gets the process' basic information. This requires QueryLimitedInformation + /// access. + /// + /// A PROCESS_BASIC_INFORMATION structure. + public ProcessBasicInformation GetBasicInformation() + { + NtStatus status; + ProcessBasicInformation pbi; + int retLen; + + if ((status = Win32.NtQueryInformationProcess(this, ProcessInformationClass.ProcessBasicInformation, + out pbi, Marshal.SizeOf(typeof(ProcessBasicInformation)), out retLen)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return pbi; + } + + /// + /// Gets the command line used to start the process. This requires + /// the PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ permissions. + /// + /// A string. + public string GetCommandLine() + { + if (!this.IsPosix()) + return this.GetPebString(PebOffset.CommandLine); + else + return this.GetPosixCommandLine(); + } + + /// + /// Gets the process' cookie (a random value). + /// + public int GetCookie() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessCookie); + } + + /// + /// Gets the creation time of the process. + /// + public DateTime GetCreateTime() + { + return DateTime.FromFileTime(this.GetTimes()[0]); + } + + /// + /// Gets the number of processor cycles consumed by the process' threads. + /// + public ulong GetCycleTime() + { + ulong cycles; + + if (!Win32.QueryProcessCycleTime(this, out cycles)) + Win32.ThrowLastError(); + + return cycles; + } + + /// + /// Opens the debug object associated with the process. + /// + /// A debug object handle. + public DebugObjectHandle GetDebugObject() + { + IntPtr handle; + + handle = this.GetDebugObjectHandle(); + + // Check if we got a handle. If we didn't the process is not being debugged. + if (handle == IntPtr.Zero) + return null; + + return new DebugObjectHandle(handle, true); + } + + internal IntPtr GetDebugObjectHandle() + { + return this.GetInformationIntPtr(ProcessInformationClass.ProcessDebugObjectHandle); + } + + /// + /// Gets the process' DEP policy. + /// + /// A DepStatus enum. + public DepStatus GetDepStatus() + { + MemExecuteOptions options; + + // If we're on 64-bit and the process isn't under + // WOW64, it must be under permanent DEP. + if (IntPtr.Size == 8) + { + if (!this.IsWow64()) + return DepStatus.Enabled | DepStatus.Permanent; + } + + options = (MemExecuteOptions)this.GetInformationInt32(ProcessInformationClass.ProcessExecuteFlags); + + DepStatus depStatus = 0; + + // Check if execution of data pages is enabled. + if ((options & MemExecuteOptions.ExecuteEnable) == MemExecuteOptions.ExecuteEnable) + return 0; + + // Check if execution of data pages is disabled. + if ((options & MemExecuteOptions.ExecuteDisable) == MemExecuteOptions.ExecuteDisable) + depStatus = DepStatus.Enabled; + // ExecuteDisable and ExecuteEnable are both disabled in OptOut mode. + else if ((options & MemExecuteOptions.ExecuteDisable) == 0 && + (options & MemExecuteOptions.ExecuteEnable) == 0) + depStatus = DepStatus.Enabled; + + if ((options & MemExecuteOptions.DisableThunkEmulation) == MemExecuteOptions.DisableThunkEmulation) + depStatus |= DepStatus.AtlThunkEmulationDisabled; + if ((options & MemExecuteOptions.Permanent) == MemExecuteOptions.Permanent) + depStatus |= DepStatus.Permanent; + + return depStatus; + } + + /// + /// Gets the process' environment variables. This requires the + /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. + /// + /// A dictionary of variables. + public unsafe IDictionary GetEnvironmentVariables() + { + IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; + byte* buffer = stackalloc byte[IntPtr.Size]; + + // Get a pointer to the process parameters block. + this.ReadMemory(pebBaseAddress.Increment(Peb.ProcessParametersOffset), buffer, IntPtr.Size); + IntPtr processParameters = *(IntPtr*)buffer; + + // Get a pointer to the environment block. + this.ReadMemory(processParameters.Increment(RtlUserProcessParameters.EnvironmentOffset), buffer, IntPtr.Size); + IntPtr envBase = *(IntPtr*)buffer; + int length = 0; + + { + MemoryBasicInformation mbi = this.QueryMemory(envBase); + + if (mbi.Protect == MemoryProtection.NoAccess) + throw new WindowsException(); + + length = mbi.RegionSize.Decrement(envBase.Decrement(mbi.BaseAddress)).ToInt32(); + } + + // Now we read in the entire region of memory + // And yes, some memory is wasted. + byte[] memory = this.ReadMemory(envBase, length); + + /* The environment variables block is a series of Unicode strings separated by + * two null bytes. The entire block is terminated by four null bytes. + */ + Dictionary vars = new Dictionary(); + StringBuilder currentVariable = new StringBuilder(); + int i = 0; + + while (true) + { + if (i >= memory.Length) + break; + + char currentChar = + UnicodeEncoding.Unicode.GetChars(memory, i, 2)[0]; + + i += 2; + + if (currentChar == '\0') + { + // Two nulls in a row, the env. block is finished. + if (currentVariable.Length == 0) + break; + + string[] s = currentVariable.ToString().Split(new char[] { '=' }, 2); + + if (!vars.ContainsKey(s[0]) && s.Length > 1) + vars.Add(s[0], s[1]); + + currentVariable = new StringBuilder(); + } + else + { + currentVariable.Append(currentChar); + } + } + + return vars; + } + + /// + /// Gets the process' exit code. + /// + /// A number. + public int GetExitCode() + { + int exitCode; + + if (!Win32.GetExitCodeProcess(this, out exitCode)) + Win32.ThrowLastError(); + + return exitCode; + } + + /// + /// Gets the process' exit status. + /// + /// A NT status value. + public NtStatus GetExitStatus() + { + return this.GetBasicInformation().ExitStatus; + } + + /// + /// Gets the exit time of the process. + /// + public DateTime GetExitTime() + { + return DateTime.FromFileTime(this.GetTimes()[1]); + } + + /// + /// Gets a GUI handle count. + /// + /// If true, returns the number of USER handles. Otherwise, returns + /// the number of GDI handles. + /// A handle count. + public int GetGuiResources(bool userObjects) + { + return Win32.GetGuiResources(this, userObjects ? 1 : 0); + } + + /// + /// Gets the number of handles opened by the process. + /// + public int GetHandleCount() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessHandleCount); + } + + /// + /// Gets the handles owned by the process. + /// + /// An array of handle information structures. + public ProcessHandleInformation[] GetHandles() + { + int returnLength = 0; + int attempts = 0; + + using (var data = new MemoryAlloc(0x1000)) + { + while (true) + { + try + { + KProcessHacker.Instance.KphQueryProcessHandles(this, data, data.Size, out returnLength); + } + catch (WindowsException ex) + { + if (attempts > 3) + throw ex; + + if ( + ex.Status == NtStatus.BufferTooSmall && + returnLength > data.Size + ) + data.Resize(returnLength); + + attempts++; + + continue; + } + + int handleCount = data.ReadInt32(0); + ProcessHandleInformation[] handles = new ProcessHandleInformation[handleCount]; + + for (int i = 0; i < handleCount; i++) + handles[i] = data.ReadStruct(sizeof(int), i); + + return handles; + } + } + } + + /// + /// Gets a collection of handle stack traces. This requires + /// PROCESS_QUERY_INFORMATION access. + /// + /// A collection of handle stack traces. + public ProcessHandleTraceCollection GetHandleTraces() + { + return this.GetHandleTraces(IntPtr.Zero); + } + + /// + /// Gets a collection of handle stack traces. This requires + /// PROCESS_QUERY_INFORMATION access. + /// + /// + /// A handle to the stack trace to retrieve. If this parameter is + /// zero, all stack traces will be retrieved. + /// + /// A collection of handle stack traces. + public ProcessHandleTraceCollection GetHandleTraces(IntPtr handle) + { + NtStatus status = NtStatus.Success; + int retLength; + + using (var data = new MemoryAlloc(0x10000)) + { + var query = new ProcessHandleTracingQuery(); + + // If Handle is not NULL, NtQueryInformationProcess will + // get a specific stack trace. Otherwise, it will get + // all of the stack traces. + query.Handle = handle; + data.WriteStruct(query); + + for (int i = 0; i < 8; i++) + { + status = Win32.NtQueryInformationProcess( + this, + ProcessInformationClass.ProcessHandleTracing, + data, + data.Size, + out retLength + ); + + if (status == NtStatus.InfoLengthMismatch) + { + data.Resize(data.Size * 4); + continue; + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new ProcessHandleTraceCollection(data); + } + + Win32.ThrowLastError(status); + return null; // Silences the compiler. + } + } + + /// + /// Gets the process' default heap. + /// + /// A pointer to a heap. + public unsafe IntPtr GetHeap() + { + IntPtr heap; + + this.ReadMemory( + this.GetBasicInformation().PebBaseAddress.Increment(Peb.ProcessHeapOffset), + &heap, + IntPtr.Size + ); + + return heap; + } + + /// + /// Gets the file name of the process' image. This requires + /// QueryLimitedInformation access. + /// + /// A file name, in native format. + public string GetImageFileName() + { + return this.GetInformationUnicodeString(ProcessInformationClass.ProcessImageFileName); + } + + /// + /// Gets the file name of the process' image. This requires + /// QueryLimitedInformation access. + /// + /// A file name, in DOS format. + public string GetImageFileNameWin32() + { + return this.GetInformationUnicodeString(ProcessInformationClass.ProcessImageFileNameWin32); + } + + /// + /// Gets information about the process in an Int32. + /// + /// The class of information to retrieve. + /// An int. + private int GetInformationInt32(ProcessInformationClass infoClass) + { + NtStatus status; + int value; + int retLength; + + if ((status = Win32.NtQueryInformationProcess( + this, infoClass, out value, sizeof(int), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return value; + } + + /// + /// Gets information about the process in an IntPtr. + /// + /// The class of information to retrieve. + /// An IntPtr. + private IntPtr GetInformationIntPtr(ProcessInformationClass infoClass) + { + NtStatus status; + IntPtr value; + int retLength; + + if ((status = Win32.NtQueryInformationProcess( + this, infoClass, out value, IntPtr.Size, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return value; + } + + private string GetInformationUnicodeString(ProcessInformationClass infoClass) + { + NtStatus status; + int retLen; + + Win32.NtQueryInformationProcess(this, infoClass, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if ((status = Win32.NtQueryInformationProcess(this, infoClass, data, retLen, out retLen)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return data.ReadStruct().Read(); + } + } + + /// + /// Gets the process' I/O priority, ranging from 0-7. + /// + /// + public int GetIoPriority() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessIoPriority); + } + + /// + /// Gets I/O statistics for the process. + /// + /// A IoCounters structure. + public IoCounters GetIoStatistics() + { + NtStatus status; + IoCounters counters; + int retLength; + + if ((status = Win32.NtQueryInformationProcess( + this, + ProcessInformationClass.ProcessIoCounters, + out counters, + Marshal.SizeOf(typeof(IoCounters)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return counters; + } + + /// + /// Opens the job object associated with the process. + /// + /// A job object handle. + public JobObjectHandle GetJobObject(JobObjectAccess access) + { + try + { + return new JobObjectHandle(this, access); + } + catch (WindowsException ex) + { + if (ex.Status == NtStatus.ProcessNotInJob) + return null; + else + throw ex; + } + } + + /// + /// Gets the type of well-known process. + /// + /// A known process type. + public KnownProcess GetKnownProcessType() + { + if (this.GetBasicInformation().UniqueProcessId.Equals(4)) + return KnownProcess.System; + + string fileName = FileUtils.GetFileName(this.GetImageFileName()); + + if (fileName.ToLower().StartsWith(Environment.SystemDirectory.ToLower())) + { + string baseName = fileName.Remove(0, Environment.SystemDirectory.Length).TrimStart('\\').ToLower(); + + switch (baseName) + { + case "smss.exe": + return KnownProcess.SessionManager; + case "csrss.exe": + return KnownProcess.WindowsSubsystem; + case "wininit.exe": + return KnownProcess.WindowsStartup; + case "services.exe": + return KnownProcess.ServiceControlManager; + case "lsass.exe": + return KnownProcess.LocalSecurityAuthority; + case "lsm.exe": + return KnownProcess.LocalSessionManager; + default: + return KnownProcess.None; + } + } + else + { + return KnownProcess.None; + } + } + + /// + /// Gets the main module of the process. This requires the + /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. + /// + /// A ProcessModule. + public ProcessModule GetMainModule() + { + ProcessModule mainModule = null; + + this.EnumModules((module) => + { + mainModule = module; + return false; + }); + + return mainModule; + } + + /// + /// Gets the name of a file which the process has mapped. + /// + /// The address of the mapped section. + /// A filename. + public string GetMappedFileName(IntPtr address) + { + StringBuilder sb = new StringBuilder(0x400); + int length = Win32.GetMappedFileName(this, address, sb, sb.Capacity); + + if (length > 0) + { + string fileName = sb.ToString(0, length); + + return FileUtils.GetFileName(fileName, true); + } + + return null; + } + + /// + /// Gets memory statistics for the process. + /// + /// A VmCounters structure. + public VmCounters GetMemoryStatistics() + { + NtStatus status; + VmCounters counters; + int retLength; + + if ((status = Win32.NtQueryInformationProcess( + this, + ProcessInformationClass.ProcessVmCounters, + out counters, + Marshal.SizeOf(typeof(VmCounters)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return counters; + } + + /// + /// Gets the modules loaded by the process. This requires the + /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. + /// + /// An array of ProcessModule objects. + public ProcessModule[] GetModules() + { + List modules = new List(); + + this.EnumModules((module) => + { + modules.Add(module); + return true; + }); + + return modules.ToArray(); + } + + /// + /// Opens the next linked process. + /// + /// The desired access to the next process. + /// A process handle. + public ProcessHandle GetNextProcess(ProcessAccess access) + { + NtStatus status; + IntPtr handle; + + if ((status = Win32.NtGetNextProcess( + this, + access, + 0, + 0, + out handle + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (handle != IntPtr.Zero) + return new ProcessHandle(handle, true); + else + return null; + } + + /// + /// Opens the next linked thread belonging to the process. + /// + /// A thread handle. You may specify null. + /// The desired access to the next thread. + /// A thread handle. + public ThreadHandle GetNextThread(ThreadHandle threadHandle, ThreadAccess access) + { + NtStatus status; + IntPtr handle; + + if ((status = Win32.NtGetNextThread( + this, + threadHandle != null ? threadHandle : IntPtr.Zero, + access, + 0, + 0, + out handle + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (handle != IntPtr.Zero) + return new ThreadHandle(handle, true); + else + return null; + } + + /// + /// Gets the process' page priority, ranging from 0-7. + /// + public int GetPagePriority() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessPagePriority); + } + + /// + /// Gets the process' parent's process ID. This requires + /// the PROCESS_QUERY_LIMITED_INFORMATION permission. + /// + /// The process ID. + public int GetParentPid() + { + return this.GetBasicInformation().InheritedFromUniqueProcessId.ToInt32(); + } + + /// + /// Reads a UNICODE_STRING from the process' process environment block. + /// + /// The offset to the UNICODE_STRING structure. + /// A string. + public unsafe string GetPebString(PebOffset offset) + { + byte* buffer = stackalloc byte[IntPtr.Size]; + IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; + + // Read the address of parameter information block. + this.ReadMemory(pebBaseAddress.Increment(Peb.ProcessParametersOffset), buffer, IntPtr.Size); + IntPtr processParameters = *(IntPtr*)buffer; + + // The offset of the UNICODE_STRING structure is specified in the enum. + int realOffset = GetPebOffset(offset); + + // Read the UNICODE_STRING structure. + UnicodeString pebStr; + + this.ReadMemory(processParameters.Increment(realOffset), &pebStr, Marshal.SizeOf(typeof(UnicodeString))); + + // read string and decode it + return UnicodeEncoding.Unicode.GetString( + this.ReadMemory(pebStr.Buffer, pebStr.Length), 0, pebStr.Length); + } + + /// + /// Gets the command line used to start the process. This + /// function is only valid for POSIX processes. + /// + /// A command line string. + public unsafe string GetPosixCommandLine() + { + byte* buffer = stackalloc byte[IntPtr.Size]; + IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; + + this.ReadMemory(pebBaseAddress.Increment(Peb.ProcessParametersOffset), buffer, IntPtr.Size); + IntPtr processParameters = *(IntPtr*)buffer; + + // Read the command line UNICODE_STRING structure. + UnicodeString commandLineUs; + + this.ReadMemory( + processParameters.Increment(GetPebOffset(PebOffset.CommandLine)), + &commandLineUs, + Marshal.SizeOf(typeof(UnicodeString)) + ); + IntPtr stringAddr = commandLineUs.Buffer; + + /* + * In the POSIX subsystem the command line is actually split up into bits, as in + * argv. In the command line string we don't actually have the command line - + * instead, it is filled with pointers to each command line part. For example: + * CommandLine.Buffer = 0x12345678 + * at 0x12345678 we have: + * 0x12346000 0x12347000 0x12348000 0x00000000 0x12349000 + * ^ at 0x12346000: "cat" (ASCII) + * ^ at 0x12347000: "-o" (ASCII) + * ^ at 0x12348000: "myfile" (ASCII) + * ^ signifies that there are no more pointers + * ^ pointer to environment block + * - from this we can work out + * how much memory to read + */ + // Get the list of pointers. + List strPointers = new List(); + bool zeroReached = false; + int i = 0; + + while (true) + { + this.ReadMemory(stringAddr.Increment(i), buffer, IntPtr.Size); + IntPtr value = *(IntPtr*)buffer; + + if (value != IntPtr.Zero) + strPointers.Add(value); + + i += IntPtr.Size; + + if (zeroReached) + break; + else if (value == IntPtr.Zero) + zeroReached = true; + } + + // Work out the size of the command line and read the data. + IntPtr lastPointer = strPointers[strPointers.Count - 1]; + int partsSize = lastPointer.Decrement(strPointers[0]).ToInt32(); + + // FIXME: Lazy; optimize later. + StringBuilder commandLine = new StringBuilder(); + + for (i = 0; i < strPointers.Count - 1; i++) + { + byte[] data = this.ReadMemory(strPointers[i], partsSize); + + commandLine.Append(ASCIIEncoding.ASCII.GetString(data, 0, Array.IndexOf(data, 0)) + " "); + } + + string commandLineStr = commandLine.ToString(); + + if (commandLineStr.EndsWith(" ")) + commandLineStr = commandLineStr.Remove(commandLineStr.Length - 1, 1); + + return commandLineStr; + } + + /// + /// Gets the process' priority class. + /// + /// A ProcessPriorityClass enum. + public ProcessPriorityClass GetPriorityClass() + { + switch (Win32.GetPriorityClass(this)) + { + case ProcessPriorityClassWin32.AboveNormal: + return ProcessPriorityClass.AboveNormal; + case ProcessPriorityClassWin32.BelowNormal: + return ProcessPriorityClass.BelowNormal; + case ProcessPriorityClassWin32.High: + return ProcessPriorityClass.High; + case ProcessPriorityClassWin32.Idle: + return ProcessPriorityClass.Idle; + case ProcessPriorityClassWin32.Normal: + return ProcessPriorityClass.Normal; + case ProcessPriorityClassWin32.RealTime: + return ProcessPriorityClass.RealTime; + default: + Win32.ThrowLastError(); + // Stupid compiler + return ProcessPriorityClass.Unknown; + } + + // Datatype misalignment on x64 + + //NtStatus status; + //ProcessPriorityClassStruct priorityClass; + //int retLength; + + //if ((status = Win32.NtQueryInformationProcess( + // this, + // ProcessInformationClass.ProcessPriorityClass, + // out priorityClass, + // Marshal.SizeOf(typeof(ProcessPriorityClassStruct)), + // out retLength + // )) >= NtStatus.Error) + // Win32.ThrowLastError(status); + + //return priorityClass.PriorityClass; + } + + /// + /// Gets the process' unique identifier. + /// + public int GetProcessId() + { + return this.GetBasicInformation().UniqueProcessId.ToInt32(); + } + + /// + /// Gets the process' session ID. + /// + public int GetSessionId() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessSessionInformation); + } + + /// + /// Gets an array of times for the process. + /// + /// An array of times: creation time, exit time, kernel time, user time. + private LargeInteger[] GetTimes() + { + LargeInteger[] times = new LargeInteger[4]; + + if (!Win32.GetProcessTimes(this, out times[0], out times[1], out times[2], out times[3])) + Win32.ThrowLastError(); + + return times; + } + + /// + /// Opens and returns a handle to the process' token. This requires + /// PROCESS_QUERY_LIMITED_INFORMATION access. + /// + /// A handle to the process' token. + public TokenHandle GetToken() + { + return this.GetToken(TokenAccess.All); + } + + /// + /// Opens and returns a handle to the process' token. This requires + /// PROCESS_QUERY_LIMITED_INFORMATION access. + /// + /// The desired access to the token. + /// A handle to the process' token. + public TokenHandle GetToken(TokenAccess access) + { + return new TokenHandle(this, access); + } + + /// + /// Forces the process to load the specified library. + /// + /// The path to the library. + public void InjectDll(string path) + { + this.InjectDll(path, 0xffffffff); + } + + /// + /// Forces the process to load the specified library. + /// + /// The path to the library. + /// The timeout, in milliseconds, for the process to load the library. + public void InjectDll(string path, uint timeout) + { + IntPtr stringPage = this.AllocateMemory(path.Length * 2 + 2, MemoryProtection.ReadWrite); + + this.WriteMemory(stringPage, UnicodeEncoding.Unicode.GetBytes(path)); + + // Vista seems to support non-Win32 threads better than XP can. + if (OSVersion.IsAboveOrEqual(WindowsVersion.Vista)) + { + using (var thandle = this.CreateThread( + Loader.GetProcedure("kernel32.dll", "LoadLibraryW"), + stringPage + )) + thandle.Wait(timeout * Win32.TimeMsTo100Ns); + } + else + { + using (var thandle = this.CreateThreadWin32( + Loader.GetProcedure("kernel32.dll", "LoadLibraryW"), + stringPage + )) + thandle.Wait(timeout * Win32.TimeMsTo100Ns); + } + + this.FreeMemory(stringPage, path.Length * 2 + 2, false); + } + + /// + /// Gets whether the process is currently being debugged. This requires + /// QueryInformation access. + /// + public bool IsBeingDebugged() + { + return this.GetInformationIntPtr(ProcessInformationClass.ProcessDebugPort) != IntPtr.Zero; + } + + /// + /// Gets whether the system will crash upon the process being terminated. + /// + public bool IsCritical() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessBreakOnTermination) != 0; + } + + /// + /// Determines whether the process is running in a job. + /// + /// A boolean. + public bool IsInJob() + { + bool result; + + if (!Win32.IsProcessInJob(this, IntPtr.Zero, out result)) + Win32.ThrowLastError(); + + return result; + } + + /// + /// Determines whether the process is running in the specified job. + /// + /// The job object to check. + /// A boolean. + public bool IsInJob(JobObjectHandle jobObjectHandle) + { + bool result; + + if (!Win32.IsProcessInJob(this, jobObjectHandle, out result)) + Win32.ThrowLastError(); + + return result; + } + + /// + /// Gets whether the process is a NTVDM process. + /// + public bool IsNtVdmProcess() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessWx86Information) != 0; + } + + /// + /// Gets whether the process is using the POSIX subsystem. + /// + public unsafe bool IsPosix() + { + int subsystem; + IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; + + this.ReadMemory(pebBaseAddress.Increment(Peb.ImageSubsystemOffset), &subsystem, sizeof(int)); + + return subsystem == 7; + } + + /// + /// Gets whether the process has priority boost enabled. + /// + public bool IsPriorityBoostEnabled() + { + return this.GetInformationInt32(ProcessInformationClass.ProcessPriorityBoost) == 0; + } + + /// + /// Gets whether the process is running under WOW64. + /// + public bool IsWow64() + { + return this.GetInformationIntPtr(ProcessInformationClass.ProcessWow64Information) != IntPtr.Zero; + } + + /// + /// Sets the protection for a page in the process. + /// + /// The address to modify. + /// The number of bytes to modify. + /// The new memory protection. + /// The old memory protection. + public MemoryProtection ProtectMemory(IntPtr baseAddress, int size, MemoryProtection protection) + { + NtStatus status; + IntPtr sizeIntPtr = size.ToIntPtr(); + MemoryProtection oldProtection; + + if ((status = Win32.NtProtectVirtualMemory( + this, + ref baseAddress, + ref sizeIntPtr, + protection, + out oldProtection + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return oldProtection; + } + + /// + /// Gets information about the memory region starting at the specified address. + /// + /// The address to query. + /// A MEMORY_BASIC_INFORMATION structure. + public MemoryBasicInformation QueryMemory(IntPtr baseAddress) + { + NtStatus status; + MemoryBasicInformation mbi; + IntPtr retLength; + + if ((status = Win32.NtQueryVirtualMemory( + this, + baseAddress, + MemoryInformationClass.MemoryBasicInformation, + out mbi, + Marshal.SizeOf(typeof(MemoryBasicInformation)).ToIntPtr(), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return mbi; + } + + /// + /// Reads data from the process' virtual memory. + /// + /// The offset at which to begin reading. + /// The length, in bytes, to read. + /// An array of bytes. + public byte[] ReadMemory(IntPtr baseAddress, int length) + { + byte[] buffer = new byte[length]; + + this.ReadMemory(baseAddress, buffer, length); + + return buffer; + } + + /// + /// Reads data from the process' virtual memory. + /// + /// The offset at which to begin reading. + /// The buffer to write to. + /// The length to read. + /// The number of bytes read. + public unsafe int ReadMemory(IntPtr baseAddress, byte[] buffer, int length) + { + fixed (byte* bufferPtr = buffer) + return this.ReadMemory(baseAddress, bufferPtr, length); + } + + /// + /// Reads data from the process' virtual memory. + /// + /// The offset at which to begin reading. + /// The buffer to write to. + /// The length to read. + /// The number of bytes read. + public unsafe int ReadMemory(IntPtr baseAddress, void* buffer, int length) + { + return this.ReadMemory(baseAddress, new IntPtr(buffer), length); + } + + /// + /// Reads data from the process' virtual memory. + /// + /// The offset at which to begin reading. + /// The buffer to write to. + /// The length to read. + /// The number of bytes read. + public int ReadMemory(IntPtr baseAddress, IntPtr buffer, int length) + { + int retLength; + + if (this.Handle == Current) + { + Win32.RtlMoveMemory(buffer, baseAddress, length.ToIntPtr()); + return length; + } + + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.KphReadVirtualMemory(this, baseAddress.ToInt32(), buffer, length, out retLength); + } + else + { + NtStatus status; + IntPtr retLengthIntPtr; + + if ((status = Win32.NtReadVirtualMemory( + this, + baseAddress, + buffer, + length.ToIntPtr(), + out retLengthIntPtr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + retLength = retLengthIntPtr.ToInt32(); + } + + return retLength; + } + + /// + /// Calls the specified function in the context of the process. + /// + /// The function to call. + /// The arguments to pass to the function. + public ThreadHandle RemoteCall(IntPtr address, IntPtr[] arguments) + { + IntPtr rtlExitUserThread = Loader.GetProcedure("ntdll.dll", "RtlExitUserThread"); + + // Create a suspended thread at RtlExitUserThread. + var thandle = this.CreateThread(rtlExitUserThread, IntPtr.Zero, true); + + // Do the remote call on this thread. + thandle.RemoteCall(this, address, arguments, true); + // Resume the thread. It will execute the remote call then exit. + thandle.Resume(); + + return thandle; + } + + /// + /// Stops debugging the process attached to the specified debug object. This requires + /// PROCESS_SUSPEND_RESUME access. + /// + /// The debug object which was used to debug the process. + public void RemoveDebug(DebugObjectHandle debugObjectHandle) + { + NtStatus status; + + if ((status = Win32.NtRemoveProcessDebug(this, debugObjectHandle)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Resumes the process. This requires PROCESS_SUSPEND_RESUME access. + /// + public void Resume() + { + if (KProcessHacker.Instance != null && OSVersion.HasPsSuspendResumeProcess) + { + KProcessHacker.Instance.KphResumeProcess(this); + } + else + { + NtStatus status; + + if ((status = Win32.NtResumeProcess(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Sets the processor affinity for the process. + /// + /// The processor affinity mask. + public void SetAffinityMask(long processMask) + { + if (!Win32.SetProcessAffinityMask(this, new IntPtr(processMask))) + Win32.ThrowLastError(); + } + + /// + /// Sets the process' base priority. + /// + /// The process' base priority. + public void SetBasePriority(int basePriority) + { + this.SetInformationInt32(ProcessInformationClass.ProcessBasePriority, basePriority); + } + + /// + /// Sets whether the system will crash upon the process being terminated. + /// This function requires SeTcbPrivilege. + /// + /// Whether the system will crash upon the process being terminated. + public void SetCritical(bool critical) + { + this.SetInformationInt32(ProcessInformationClass.ProcessBreakOnTermination, critical ? 1 : 0); + } + + /// + /// Sets the process' DEP policy. + /// + /// The DEP options. + public void SetDepStatus(DepStatus depStatus) + { + MemExecuteOptions executeOptions = 0; + + if ((depStatus & DepStatus.Enabled) == DepStatus.Enabled) + executeOptions |= MemExecuteOptions.ExecuteDisable; + else + executeOptions |= MemExecuteOptions.ExecuteEnable; + + if ((depStatus & DepStatus.AtlThunkEmulationDisabled) == DepStatus.AtlThunkEmulationDisabled) + executeOptions |= MemExecuteOptions.DisableThunkEmulation; + if ((depStatus & DepStatus.Permanent) == DepStatus.Permanent) + executeOptions |= MemExecuteOptions.Permanent; + + KProcessHacker.Instance.SetExecuteOptions(this, executeOptions); + } + + /// + /// Sets information about the process in an Int32. + /// + /// The class of information to set. + /// The value to set. + private void SetInformationInt32(ProcessInformationClass infoClass, int value) + { + NtStatus status; + + if ((status = Win32.NtSetInformationProcess( + this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets the reference count of a module. + /// + /// The base address of the module. + /// The new reference count. + public unsafe void SetModuleReferenceCount(IntPtr baseAddress, ushort count) + { + byte* buffer = stackalloc byte[IntPtr.Size]; + + this.ReadMemory( + this.GetBasicInformation().PebBaseAddress.Increment(Peb.LdrOffset), + buffer, + IntPtr.Size + ); + + IntPtr loaderData = *(IntPtr*)buffer; + + PebLdrData* data = stackalloc PebLdrData[1]; + this.ReadMemory(loaderData, data, Marshal.SizeOf(typeof(PebLdrData))); + + if (!data->Initialized) + throw new Exception("Loader data is not initialized."); + + List modules = new List(); + IntPtr currentLink = data->InLoadOrderModuleList.Flink; + IntPtr startLink = currentLink; + LdrDataTableEntry* currentEntry = stackalloc LdrDataTableEntry[1]; + int i = 0; + + while (currentLink != IntPtr.Zero) + { + if (modules.Count > 0 && currentLink == startLink) + break; + if (i > 0x800) + break; + + this.ReadMemory(currentLink, currentEntry, Marshal.SizeOf(typeof(LdrDataTableEntry))); + + if (currentEntry->DllBase == baseAddress) + { + this.WriteMemory(currentLink.Increment(LdrDataTableEntry.LoadCountOffset), &count, 2); + break; + } + + currentLink = currentEntry->InLoadOrderLinks.Flink; + i++; + } + } + + /// + /// Sets the process' priority boost. + /// + /// Whether priority boost will be enabled. + public void SetPriorityBoost(bool enabled) + { + // If priority boost is being enabled, we have to not disable it (hence the value of 0). + this.SetInformationInt32(ProcessInformationClass.ProcessPriorityBoost, enabled ? 0 : 1); + } + + /// + /// Sets the process' priority class. + /// + /// The process' priority class. + public void SetPriorityClass(ProcessPriorityClass priorityClass) + { + ProcessPriorityClassWin32 pcWin32; + + switch (priorityClass) + { + case ProcessPriorityClass.AboveNormal: + pcWin32 = ProcessPriorityClassWin32.AboveNormal; + break; + case ProcessPriorityClass.BelowNormal: + pcWin32 = ProcessPriorityClassWin32.BelowNormal; + break; + case ProcessPriorityClass.High: + pcWin32 = ProcessPriorityClassWin32.High; + break; + case ProcessPriorityClass.Idle: + pcWin32 = ProcessPriorityClassWin32.Idle; + break; + case ProcessPriorityClass.Normal: + pcWin32 = ProcessPriorityClassWin32.Normal; + break; + case ProcessPriorityClass.RealTime: + pcWin32 = ProcessPriorityClassWin32.RealTime; + break; + default: + throw new ArgumentException("priorityClass"); + } + + if (!Win32.SetPriorityClass(this, pcWin32)) + Win32.ThrowLastError(); + + // Datatype misalignment on x64. + //NtStatus status; + //ProcessPriorityClassStruct processPriority; + + //processPriority.Foreground = false; + //processPriority.PriorityClass = priorityClass; + + //if ((status = Win32.NtSetInformationProcess( + // this, + // ProcessInformationClass.ProcessPriorityClass, + // ref processPriority, + // Marshal.SizeOf(typeof(ProcessPriorityClassStruct)) + // )) >= NtStatus.Error) + // Win32.ThrowLastError(status); + } + + /// + /// Suspends the process. This requires PROCESS_SUSPEND_RESUME access. + /// + public void Suspend() + { + if (KProcessHacker.Instance != null && OSVersion.HasPsSuspendResumeProcess) + { + KProcessHacker.Instance.KphSuspendProcess(this); + } + else + { + NtStatus status; + + if ((status = Win32.NtSuspendProcess(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Terminates the process. This requires PROCESS_TERMINATE access. + /// + public void Terminate() + { + this.Terminate(NtStatus.Success); + } + + /// + /// Terminates the process. This requires PROCESS_TERMINATE access. + /// + /// The exit status. + public void Terminate(NtStatus exitStatus) + { + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.KphTerminateProcess(this, exitStatus); + } + else + { + NtStatus status; + + if ((status = Win32.NtTerminateProcess(this, exitStatus)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Writes a minidump of the process to the specified file. + /// + /// The destination file. + public void WriteDump(string fileName) + { + // taskmgr uses these flags + this.WriteDump(fileName, + MinidumpType.WithFullMemory | + MinidumpType.WithHandleData | + MinidumpType.WithUnloadedModules | + MinidumpType.WithFullMemoryInfo | + MinidumpType.WithThreadInfo + ); + } + + /// + /// Writes a minidump of the process to the specified file. + /// + /// The destination file. + /// The type of minidump to write. + public void WriteDump(string fileName, MinidumpType type) + { + using (var fhandle = FileHandle.CreateWin32(fileName, FileAccess.GenericWrite)) + this.WriteDump(fhandle, type); + } + + /// + /// Writes a minidump of the process to the specified file. + /// + /// A handle to the destination file. + /// The type of minidump to write. + public void WriteDump(FileHandle fileHandle, MinidumpType type) + { + if (!Win32.MiniDumpWriteDump( + this, + this.GetProcessId(), + fileHandle, + type, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero + )) + Win32.ThrowLastError(); + } + + /// + /// Writes data to the process' virtual memory. + /// + /// The offset at which to begin writing. + /// The data to write. + /// The length, in bytes, that was written. + public int WriteMemory(IntPtr baseAddress, byte[] buffer) + { + unsafe + { + fixed (byte* dataPtr = buffer) + { + return WriteMemory(baseAddress, dataPtr, buffer.Length); + } + } + } + + /// + /// Writes data to the process' virtual memory. + /// + /// The offset at which to begin writing. + /// The data to write. + /// The length to be written. + /// The length, in bytes, that was written. + public unsafe int WriteMemory(IntPtr baseAddress, void* buffer, int length) + { + return this.WriteMemory(baseAddress, new IntPtr(buffer), length); + } + + /// + /// Writes data to the process' virtual memory. + /// + /// The offset at which to begin writing. + /// The data to write. + /// The length to be written. + /// The length, in bytes, that was written. + public int WriteMemory(IntPtr baseAddress, IntPtr buffer, int length) + { + int retLength; + + if (this.Handle == Current) + { + Win32.RtlMoveMemory(baseAddress, buffer, length.ToIntPtr()); + return length; + } + + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.KphWriteVirtualMemory(this, baseAddress.ToInt32(), buffer, length, out retLength); + } + else + { + NtStatus status; + IntPtr retLengthIntPtr; + + if ((status = Win32.NtWriteVirtualMemory( + this, + baseAddress, + buffer, + length.ToIntPtr(), + out retLengthIntPtr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + retLength = retLengthIntPtr.ToInt32(); + } + + return retLength; + } + } + + /// + /// Represents a stack trace collected during a handle trace event. + /// + public class ProcessHandleTrace + { + private ClientId _clientId; + private IntPtr _handle; + private IntPtr[] _stack; + private HandleTraceType _type; + + internal ProcessHandleTrace(ProcessHandleTracingEntry entry) + { + _clientId = entry.ClientId; + _handle = entry.Handle; + _type = entry.Type; + + // Find the first occurrence of a NULL to find where the trace stops. + int zeroIndex = Array.IndexOf(entry.Stacks, IntPtr.Zero); + + // If there was no NULL, copy the entire array. + if (zeroIndex == -1) + zeroIndex = entry.Stacks.Length; + + // Copy the actual stack trace, excluding NULLs. + _stack = new IntPtr[zeroIndex]; + Array.Copy(entry.Stacks, 0, _stack, 0, zeroIndex); + } + + /// + /// The client ID of the thread which produced the event. + /// + public ClientId ClientId + { + get { return _clientId; } + } + + /// + /// The handle value associated with the event. + /// + public IntPtr Handle + { + get { return _handle; } + } + + /// + /// A stack trace of the thread at the time of the event. + /// + public IntPtr[] Stack + { + get { return _stack; } + } + + /// + /// The type of handle trace event. + /// + public HandleTraceType Type + { + get { return _type; } + } + } + + /// + /// Represents a collection of handle trace events. + /// + public class ProcessHandleTraceCollection : ReadOnlyCollection + { + private IntPtr _handle; + + internal ProcessHandleTraceCollection(MemoryAlloc data) + : base(new List()) + { + if (data.Size < Marshal.SizeOf(typeof(ProcessHandleTracingQuery))) + throw new ArgumentException("Data memory allocation is too small."); + + // Read the structure. + var query = data.ReadStruct(); + + _handle = query.Handle; + + // Get the handle traces. + IList traces = this.Items; + + for (int i = 0; i < query.TotalTraces; i++) + { + var entry = data.ReadStruct( + ProcessHandleTracingQuery.HandleTraceOffset, + i + ); + + traces.Add(new ProcessHandleTrace(entry)); + } + } + + /// + /// A unique handle representing the collection. + /// + public IntPtr Handle + { + get { return _handle; } + } + } + + /// + /// Represents a module loaded by a process. + /// + public class ProcessModule : ILoadedModule + { + public ProcessModule( + IntPtr baseAddress, + int size, + IntPtr entryPoint, + LdrpDataTableEntryFlags flags, + string baseName, + string fileName + ) + { + this.BaseAddress = baseAddress; + this.Size = size; + this.EntryPoint = entryPoint; + this.Flags = flags; + this.BaseName = baseName; + this.FileName = fileName; + } + + /// + /// The base address of the module. + /// + public IntPtr BaseAddress { get; private set; } + /// + /// The size of the module. + /// + public int Size { get; private set; } + /// + /// The entry point of the module (usually its DllMain function). + /// + public IntPtr EntryPoint { get; private set; } + /// + /// The flags set by the NT loader for this module. + /// + public LdrpDataTableEntryFlags Flags { get; private set; } + /// + /// The base name of the module (e.g. module.dll). + /// + public string BaseName { get; private set; } + /// + /// The file name of the module (e.g. C:\Windows\system32\module.dll). + /// + public string FileName { get; private set; } + } + + /// + /// Specifies the DEP status of a process. + /// + [Flags] + public enum DepStatus + { + /// + /// DEP is enabled. + /// + Enabled = 0x1, + + /// + /// DEP is permanently enabled or disabled and cannot + /// be enabled or disabled. + /// + Permanent = 0x2, + + /// + /// DEP is enabled with DEP-ATL thunk emulation disabled. + /// + AtlThunkEmulationDisabled = 0x4 + } + + /// + /// A well-known Windows process. + /// + public enum KnownProcess + { + /// + /// The process is not well-known. + /// + None, + /// + /// System Idle Process. + /// + Idle, + /// + /// NT Kernel & System. + /// + System, + /// + /// Windows Session Manager (smss) + /// + SessionManager, + /// + /// Client Server Runtime Process (csrss) + /// + WindowsSubsystem, + /// + /// Windows Start-Up Application (wininit) + /// + WindowsStartup, + /// + /// Services and Controller app (services) + /// + ServiceControlManager, + /// + /// Local Security Authority Process (lsass) + /// + LocalSecurityAuthority, + /// + /// Local Session Manager Service (lsm) + /// + LocalSessionManager + } + + /// + /// Specifies an offset in a process' process environment block (PEB). + /// + public enum PebOffset + { + /// + /// The current directory of the process. This may, as the name + /// implies, change very often. + /// + CurrentDirectoryPath, + /// + /// A copy of the PATH environment variable for the process. + /// + DllPath, + /// + /// The image file name, in kernel format (e.g. \\?\C:\..., + /// \SystemRoot\..., \Device\Harddisk1\...). + /// + ImagePathName, + /// + /// The command used to start the program, including arguments. + /// + CommandLine, + /// + /// Usually blank. + /// + WindowTitle, + /// + /// For interactive programs, contains the window station and + /// desktop name of the first thread that was started, e.g. + /// WinSta0\Default. + /// + DesktopName, + /// + /// Usually blank. + /// + ShellInfo, + /// + /// Usually blank. + /// + RuntimeData + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ProfileHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ProfileHandle.cs new file mode 100644 index 000000000..c63eb4c75 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ProfileHandle.cs @@ -0,0 +1,141 @@ +/* + * Process Hacker - + * profile handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class ProfileHandle : NativeHandle + { + public static ProfileHandle Create( + ProcessHandle processHandle, + IntPtr rangeBase, + uint rangeSize, + int bucketSize, + KProfileSource profileSource, + IntPtr affinity + ) + { + NtStatus status; + IntPtr handle; + + if (bucketSize < 2 || bucketSize > 30) + throw new ArgumentException("Bucket size must be between 2 and 30, inclusive."); + + unchecked + { + uint realBucketSize = (uint)(2 << (bucketSize - 1)); + MemoryAlloc buffer = new MemoryAlloc((int)((rangeSize - 1) / realBucketSize + 1) * sizeof(int)); // divide, round up + + if ((status = Win32.NtCreateProfile( + out handle, + processHandle ?? IntPtr.Zero, + rangeBase, + new IntPtr(rangeSize), + bucketSize, + buffer, + buffer.Size, + profileSource, + affinity + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new ProfileHandle(handle, true, rangeBase, rangeSize, realBucketSize, buffer); + } + } + + public static int GetInterval(KProfileSource profileSource) + { + NtStatus status; + int interval; + + if ((status = Win32.NtQueryIntervalProfile(profileSource, out interval)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return interval; + } + + public static void SetInterval(KProfileSource profileSource, int interval) + { + NtStatus status; + + if ((status = Win32.NtSetIntervalProfile(interval, profileSource)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + private IntPtr _rangeBase; + private uint _rangeSize; + private uint _bucketSize; // not logarithmic + private MemoryAlloc _buffer; + + private ProfileHandle( + IntPtr handle, + bool owned, + IntPtr rangeBase, + uint rangeSize, + uint bucketSize, + MemoryAlloc buffer + ) + : base(handle, owned) + { + _rangeBase = rangeBase; + _rangeSize = rangeSize; + _bucketSize = bucketSize; + _buffer = buffer; + } + + protected override void Close() + { + _buffer.Dispose(); + + base.Close(); + } + + public int[] Collect() + { + int[] counters = new int[_buffer.Size / sizeof(int)]; + + Marshal.Copy(_buffer, counters, 0, counters.Length); + + return counters; + } + + public void Start() + { + NtStatus status; + + if ((status = Win32.NtStartProfile(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void Stop() + { + NtStatus status; + + if ((status = Win32.NtStopProfile(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteHandle.cs new file mode 100644 index 000000000..2e2e0f757 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteHandle.cs @@ -0,0 +1,66 @@ +/* + * Process Hacker - + * remote handle + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + + +using System; +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle owned by another process. + /// + public class RemoteHandle + { + private ProcessHandle _phandle; + private IntPtr _handle; + + public RemoteHandle(ProcessHandle phandle, IntPtr handle) + { + _phandle = phandle; + _handle = handle; + } + + public ProcessHandle ProcessHandle + { + get { return _phandle; } + } + + public IntPtr Handle + { + get { return _handle; } + } + + /// + /// Duplicates the handle owned by the process. + /// + /// The desired access to the handle's object. + /// A local copy of the handle. + /// + /// We can't use a template for this because of C#'s rules for template + /// restrictions. Specifically, we can only specify that the type must have a + /// constructor with 0 arguments, but no more. + /// + public int GetHandle(int access) + { + return new GenericHandle(_phandle, _handle, access); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteTokenHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteTokenHandle.cs new file mode 100644 index 000000000..6f3b6ae82 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/RemoteTokenHandle.cs @@ -0,0 +1,63 @@ +/* + * Process Hacker - + * remote token handle + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using System; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a token handle owned by another process. + /// + /// + /// This is a wrapper class so that we can view information + /// about tokens other processes have handles to. TokenProperties + /// only takes an IWithToken object. + /// + public sealed class RemoteTokenHandle : RemoteHandle, IWithToken + { + public RemoteTokenHandle(ProcessHandle phandle, IntPtr handle) + : base(phandle, handle) + { } + + public new IntPtr GetHandle(int rights) + { + IntPtr newHandle = IntPtr.Zero; + + // We can use KPH here. RemoteHandle doesn't. + Win32.DuplicateObject(this.ProcessHandle, this.Handle, new IntPtr(-1), out newHandle, rights, 0, 0); + + return newHandle; + } + + public TokenHandle GetToken() + { + return GetToken(TokenAccess.All); + } + + public TokenHandle GetToken(TokenAccess access) + { + return new TokenHandle(this.GetHandle((int)access), true); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ResourceManagerHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ResourceManagerHandle.cs new file mode 100644 index 000000000..ef8d46fbc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ResourceManagerHandle.cs @@ -0,0 +1,184 @@ +/* + * Process Hacker - + * resource manager handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public class ResourceManagerHandle : NativeHandle + { + public static ResourceManagerHandle Create( + ResourceManagerAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + TmHandle tmHandle, + Guid guid, + ResourceManagerOptions createOptions, + string description + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + UnicodeString descriptionStr = new UnicodeString(description); + + try + { + if ((status = Win32.NtCreateResourceManager( + out handle, + access, + tmHandle, + ref guid, + ref oa, + createOptions, + ref descriptionStr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + descriptionStr.Dispose(); + } + } + finally + { + oa.Dispose(); + } + + return new ResourceManagerHandle(handle, true); + } + + public static ResourceManagerHandle FromHandle(IntPtr handle) + { + return new ResourceManagerHandle(handle, false); + } + + private ResourceManagerHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public ResourceManagerHandle( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + TmHandle tmHandle, + Guid guid, + ResourceManagerAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenResourceManager( + out handle, + access, + tmHandle, + ref guid, + ref oa + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + private MemoryAlloc GetBasicInformation() + { + NtStatus status; + int retLength; + + var data = new MemoryAlloc(0x1000); + + status = Win32.NtQueryInformationResourceManager( + this, + ResourceManagerInformationClass.ResourceManagerBasicInformation, + data, + data.Size, + out retLength + ); + + if (status == NtStatus.BufferTooSmall) + { + // Resize the buffer and try again. + data.Resize(retLength); + + status = Win32.NtQueryInformationResourceManager( + this, + ResourceManagerInformationClass.ResourceManagerBasicInformation, + data, + data.Size, + out retLength + ); + } + + if (status >= NtStatus.Error) + { + data.Dispose(); + Win32.ThrowLastError(status); + } + + return data; + } + + public string GetDescription() + { + using (var data = this.GetBasicInformation()) + { + var basicInfo = data.ReadStruct(); + + return data.ReadUnicodeString( + ResourceManagerBasicInformation.DescriptionOffset, + basicInfo.DescriptionLength / 2 + ); + } + } + + public Guid GetGuid() + { + using (var data = this.GetBasicInformation()) + { + return data.ReadStruct().ResourceManagerId; + } + } + + public void Recover() + { + NtStatus status; + + if ((status = Win32.NtRecoverResourceManager(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/SectionHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/SectionHandle.cs new file mode 100644 index 000000000..442cf9118 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/SectionHandle.cs @@ -0,0 +1,257 @@ +/* + * Process Hacker - + * section handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class SectionHandle : NativeHandle + { + public static SectionHandle Create( + SectionAccess access, + SectionAttributes sectionAttributes, + MemoryProtection pageAttributes, + FileHandle fileHandle + ) + { + return Create(access, 0, sectionAttributes, pageAttributes, fileHandle); + } + + public static SectionHandle Create( + SectionAccess access, + long maximumSize, + SectionAttributes sectionAttributes, + MemoryProtection pageAttributes, + FileHandle fileHandle + ) + { + return Create(access, null, maximumSize, sectionAttributes, pageAttributes, fileHandle); + } + + public static SectionHandle Create( + SectionAccess access, + long maximumSize, + SectionAttributes sectionAttributes, + MemoryProtection pageAttributes + ) + { + return Create(access, null, maximumSize, sectionAttributes, pageAttributes, null); + } + + public static SectionHandle Create( + SectionAccess access, + string name, + long maximumSize, + SectionAttributes sectionAttributes, + MemoryProtection pageAttributes, + FileHandle fileHandle + ) + { + return Create(access, name, 0, null, maximumSize, sectionAttributes, pageAttributes, fileHandle); + } + + public static SectionHandle Create( + SectionAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + long maximumSize, + SectionAttributes sectionAttributes, + MemoryProtection pageAttributes, + FileHandle fileHandle + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if (maximumSize != 0) + { + if ((status = Win32.NtCreateSection( + out handle, + access, + ref oa, + ref maximumSize, + pageAttributes, + sectionAttributes, + fileHandle ?? IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + else + { + if ((status = Win32.NtCreateSection( + out handle, + access, + ref oa, + IntPtr.Zero, + pageAttributes, + sectionAttributes, + fileHandle ?? IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + finally + { + oa.Dispose(); + } + + return new SectionHandle(handle, true); + } + + public static SectionHandle FromHandle(IntPtr handle) + { + return new SectionHandle(handle, false); + } + + private SectionHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public SectionHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, SectionAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenSection(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public SectionHandle(string name, SectionAccess access) + : this(name, 0, null, access) + { } + + public long Extend(long newSize) + { + NtStatus status; + + if ((status = Win32.NtExtendSection(this, ref newSize)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return newSize; + } + + public SectionBasicInformation GetBasicInformation() + { + NtStatus status; + SectionBasicInformation sbi; + IntPtr retLength; + + if ((status = Win32.NtQuerySection(this, SectionInformationClass.SectionBasicInformation, + out sbi, new IntPtr(Marshal.SizeOf(typeof(SectionBasicInformation))), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return sbi; + } + + public SectionImageInformation GetImageInformation() + { + NtStatus status; + SectionImageInformation sii; + IntPtr retLength; + + if ((status = Win32.NtQuerySection(this, SectionInformationClass.SectionImageInformation, + out sii, new IntPtr(Marshal.SizeOf(typeof(SectionImageInformation))), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return sii; + } + + public SectionView MapView(int sectionOffset, int size, MemoryProtection protection) + { + return this.MapView(IntPtr.Zero, sectionOffset, new IntPtr(size), protection); + } + + public SectionView MapView(IntPtr baseAddress, long sectionOffset, IntPtr size, MemoryProtection protection) + { + return this.MapView(ProcessHandle.Current, baseAddress, sectionOffset, size, protection); + } + + public SectionView MapView( + ProcessHandle processHandle, + IntPtr baseAddress, + long sectionOffset, + IntPtr size, + MemoryProtection protection + ) + { + return this.MapView( + processHandle, + baseAddress, + size, + sectionOffset, + size, + SectionInherit.ViewShare, + 0, + protection + ); + } + + public SectionView MapView( + ProcessHandle processHandle, + IntPtr baseAddress, + IntPtr commitSize, + long sectionOffset, + IntPtr viewSize, + SectionInherit inheritDisposition, + MemoryFlags allocationType, + MemoryProtection protection + ) + { + NtStatus status; + + // sectionOffset requires 2 << 15 = 0x10000 = 65536 alignment. + // viewSize will be rounded up to the page size. + if ((status = Win32.NtMapViewOfSection( + this, + processHandle, + ref baseAddress, + IntPtr.Zero, + commitSize, + ref sectionOffset, + ref viewSize, + inheritDisposition, + allocationType, + protection + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new SectionView(baseAddress, viewSize); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/SemaphoreHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/SemaphoreHandle.cs new file mode 100644 index 000000000..3f348a377 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/SemaphoreHandle.cs @@ -0,0 +1,123 @@ +/* + * Process Hacker - + * semaphore handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class SemaphoreHandle : NativeHandle + { + public static SemaphoreHandle Create(SemaphoreAccess access, int initialCount, int maximumCount) + { + return Create(access, null, initialCount, maximumCount); + } + + public static SemaphoreHandle Create(SemaphoreAccess access, string name, int initialCount, int maximumCount) + { + return Create(access, name, 0, null, initialCount, maximumCount); + } + + public static SemaphoreHandle Create(SemaphoreAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, int initialCount, int maximumCount) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateSemaphore(out handle, access, ref oa, + initialCount, maximumCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new SemaphoreHandle(handle, true); + } + + public static SemaphoreHandle FromHandle(IntPtr handle) + { + return new SemaphoreHandle(handle, false); + } + + private SemaphoreHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public SemaphoreHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, SemaphoreAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenSemaphore(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public SemaphoreHandle(string name, SemaphoreAccess access) + : this(name, 0, null, access) + { } + + public SemaphoreBasicInformation GetBasicInformation() + { + NtStatus status; + SemaphoreBasicInformation sbi; + int retLength; + + if ((status = Win32.NtQuerySemaphore(this, SemaphoreInformationClass.SemaphoreBasicInformation, + out sbi, Marshal.SizeOf(typeof(SemaphoreBasicInformation)), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return sbi; + } + + public int Release() + { + return this.Release(1); + } + + public int Release(int count) + { + NtStatus status; + int previousCount; + + if ((status = Win32.NtReleaseSemaphore(this, count, out previousCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousCount; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceBaseHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceBaseHandle.cs new file mode 100644 index 000000000..f4a091612 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceBaseHandle.cs @@ -0,0 +1,46 @@ +/* + * Process Hacker - + * service-related handle + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using ProcessHacker.Native.Api; +using System; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle managed by the Windows service manager. + /// + public class ServiceBaseHandle : NativeHandle + where TAccess : struct + { + public ServiceBaseHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + protected ServiceBaseHandle() + { } + + protected override void Close() + { + Win32.CloseServiceHandle(this); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceHandle.cs new file mode 100644 index 000000000..49609f74d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceHandle.cs @@ -0,0 +1,301 @@ +/* + * Process Hacker - + * service handle + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a Windows service. + /// + public sealed class ServiceHandle : ServiceBaseHandle + { + /// + /// Creates a service handle using an existing handle. + /// The handle will not be closed automatically. + /// + /// The handle value. + /// The service handle. + public static ServiceHandle FromHandle(IntPtr handle) + { + return new ServiceHandle(handle, false); + } + + public static ServiceHandle OpenWithAnyAccess(string serviceName) + { + try + { + return new ServiceHandle(serviceName, ServiceAccess.QueryStatus); + } + catch + { + try + { + return new ServiceHandle(serviceName, (ServiceAccess)StandardRights.Synchronize); + } + catch + { + try + { + return new ServiceHandle(serviceName, (ServiceAccess)StandardRights.ReadControl); + } + catch + { + try + { + return new ServiceHandle(serviceName, (ServiceAccess)StandardRights.WriteDac); + } + catch + { + return new ServiceHandle(serviceName, (ServiceAccess)StandardRights.WriteOwner); + } + } + } + } + } + + internal ServiceHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Creates a new service handle. + /// + /// The name of the service to open. + public ServiceHandle(string serviceName) + : this(serviceName, ServiceAccess.All) + { } + + /// + /// Creates a new service handle. + /// + /// The name of the service to open. + /// The desired access to the service. + public ServiceHandle(string serviceName, ServiceAccess access) + { + using (ServiceManagerHandle manager = + new ServiceManagerHandle(ScManagerAccess.Connect)) + { + this.Handle = Win32.OpenService(manager, serviceName, access); + + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + } + + /// + /// Sends a control message to the service. + /// + /// The message. + public void Control(ServiceControl control) + { + ServiceStatus status = new ServiceStatus(); + + if (!Win32.ControlService(this, control, out status)) + Win32.ThrowLastError(); + } + + /// + /// Deletes the service. + /// + public void Delete() + { + if (!Win32.DeleteService(this)) + Win32.ThrowLastError(); + } + + /// + /// Gets the service's configuration. + /// + public QueryServiceConfig GetConfig() + { + int requiredSize = 0; + + Win32.QueryServiceConfig(this, IntPtr.Zero, 0, out requiredSize); + + using (MemoryAlloc data = new MemoryAlloc(requiredSize)) + { + if (!Win32.QueryServiceConfig(this, data, data.Size, out requiredSize)) + Win32.ThrowLastError(); + + return data.ReadStruct(); + } + } + + /// + /// Gets the service's description. + /// + /// A string. + public string GetDescription() + { + int retLen; + + Win32.QueryServiceConfig2(this, ServiceInfoLevel.Description, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.QueryServiceConfig2(this, ServiceInfoLevel.Description, data, retLen, out retLen)) + Win32.ThrowLastError(); + + return data.ReadStruct().Description; + } + } + + public override SecurityDescriptor GetSecurity(SecurityInformation securityInformation) + { + return this.GetSecurity(SeObjectType.Service, securityInformation); + } + + /// + /// Gets the status of the service. + /// + /// A SERVICE_STATUS_PROCESS structure. + public ServiceStatusProcess GetStatus() + { + ServiceStatusProcess status; + int retLen; + + if (!Win32.QueryServiceStatusEx(this, 0, out status, Marshal.SizeOf(typeof(ServiceStatusProcess)), out retLen)) + Win32.ThrowLastError(); + + return status; + } + + public override void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + this.SetSecurity(SeObjectType.Service, securityInformation, securityDescriptor); + } + + /// + /// Starts the service. + /// + public void Start() + { + if (!Win32.StartService(this, 0, null)) + Win32.ThrowLastError(); + } + } + + public enum ServiceAccept : uint + { + NetBindChange = 0x10, + ParamChange = 0x8, + PauseContinue = 0x2, + PreShutdown = 0x100, + Shutdown = 0x4, + Stop = 0x1, + HardwareProfileChange = 0x20, + PowerEvent = 0x40, + SessionChange = 0x80 + } + + public enum ServiceControl : uint + { + Continue = 0x3, + Interrogate = 0x4, + NetBindAdd = 0x7, + NetBindDisable = 0xa, + NetBindEnable = 0x9, + NetBindRemove = 0x8, + ParamChange = 0x6, + Pause = 0x2, + Stop = 0x1 + } + + public enum ServiceErrorControl : uint + { + Critical = 0x3, + Ignore = 0x0, + Normal = 0x1, + Severe = 0x2 + } + + public enum ServiceFlags : uint + { + None = 0, + RunsInSystemProcess = 0x1 + } + + public enum ServiceInfoLevel : uint + { + Description = 1, + FailureActions = 2, + DelayedAutoStartInfo = 3, + FailureActionsFlag = 4, + SidInfo = 5, + RequiredPrivilegesInfo = 6, + PreShutdownInfo = 7, + TriggerInfo = 8, + PreferredNode = 9 + } + + public enum ServiceQueryState : uint + { + Active = 1, + Inactive = 2, + All = 3 + } + + [Flags] + public enum ServiceQueryType : uint + { + Driver = 0xb, + Win32 = 0x30 + } + + public enum ServiceStartType : uint + { + AutoStart = 0x2, + BootStart = 0x0, + DemandStart = 0x3, + Disabled = 0x4, + SystemStart = 0x1 + } + + public enum ServiceState : uint + { + ContinuePending = 0x5, + PausePending = 0x6, + Paused = 0x7, + Running = 0x4, + StartPending = 0x2, + StopPending = 0x3, + Stopped = 0x1 + } + + [Flags] + public enum ServiceType : uint + { + FileSystemDriver = 0x2, + KernelDriver = 0x1, + Win32OwnProcess = 0x10, + Win32ShareProcess = 0x20, + InteractiveProcess = 0x100 + } +} \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceManagerHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceManagerHandle.cs new file mode 100644 index 000000000..7909f4e59 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ServiceManagerHandle.cs @@ -0,0 +1,77 @@ +/* + * Process Hacker - + * service manager handle + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to the Windows service manager. + /// + public sealed class ServiceManagerHandle : ServiceBaseHandle + { + /// + /// Connects to the Windows service manager. + /// + /// The desired access to the service manager. + public ServiceManagerHandle(ScManagerAccess access) + { + this.Handle = Win32.OpenSCManager(null, null, access); + + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + + public ServiceHandle CreateService(string name, string displayName, + ServiceType type, string binaryPath) + { + return this.CreateService(name, displayName, type, ServiceStartType.DemandStart, + ServiceErrorControl.Ignore, binaryPath, null, null, null); + } + + public ServiceHandle CreateService(string name, string displayName, + ServiceType type, ServiceStartType startType, string binaryPath) + { + return this.CreateService(name, displayName, type, startType, + ServiceErrorControl.Ignore, binaryPath, null, null, null); + } + + public ServiceHandle CreateService(string name, string displayName, + ServiceType type, ServiceStartType startType, ServiceErrorControl errorControl, + string binaryPath, string group, string accountName, string password) + { + IntPtr service; + + if ((service = Win32.CreateService(this, name, displayName, ServiceAccess.All, + type, startType, errorControl, binaryPath, group, + IntPtr.Zero, IntPtr.Zero, accountName, password)) == IntPtr.Zero) + Win32.ThrowLastError(); + + return new ServiceHandle(service, true); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/SymbolicLinkHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/SymbolicLinkHandle.cs new file mode 100644 index 000000000..7b2d353c4 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/SymbolicLinkHandle.cs @@ -0,0 +1,118 @@ +/* + * Process Hacker - + * symbolic link handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class SymbolicLinkHandle : NativeHandle + { + public static SymbolicLinkHandle Create(SymbolicLinkAccess access, string name, string linkTarget) + { + return Create(access, name, 0, null, linkTarget); + } + + public static SymbolicLinkHandle Create(SymbolicLinkAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, string linkTarget) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + UnicodeString linkTargetString = new UnicodeString(linkTarget); + + try + { + if ((status = Win32.NtCreateSymbolicLinkObject(out handle, access, + ref oa, ref linkTargetString)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + linkTargetString.Dispose(); + } + } + finally + { + oa.Dispose(); + } + + return new SymbolicLinkHandle(handle, true); + } + + private SymbolicLinkHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public SymbolicLinkHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, SymbolicLinkAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenSymbolicLinkObject(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public SymbolicLinkHandle(string name, SymbolicLinkAccess access) + : this(name, 0, null, access) + { } + + public string GetTarget() + { + NtStatus status; + int retLength; + UnicodeString str = new UnicodeString(); + + using (var buffer = new MemoryAlloc(0x200)) + { + str.Length = 0; + str.MaximumLength = (ushort)buffer.Size; + str.Buffer = buffer; + + if ((status = Win32.NtQuerySymbolicLinkObject(this, ref str, out retLength)) >= NtStatus.Error) + { + buffer.Resize(retLength); + str.MaximumLength = (ushort)retLength; + str.Buffer = buffer; + } + + if ((status = Win32.NtQuerySymbolicLinkObject(this, ref str, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return str.Read(); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TerminalServerHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TerminalServerHandle.cs new file mode 100644 index 000000000..78bc4e3f8 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TerminalServerHandle.cs @@ -0,0 +1,535 @@ +/* + * Process Hacker - + * terminal server handles and objects + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class TerminalServerHandle : NativeHandle + { + private static readonly TerminalServerHandle _current = new TerminalServerHandle(IntPtr.Zero, false); + + /// + /// Gets a handle to the local terminal server. + /// + public static TerminalServerHandle Current + { + get { return _current; } + } + + /// + /// Gets a handle to the local terminal server. + /// + /// A terminal server handle. + public static TerminalServerHandle GetCurrent() + { + return Current; + } + + /// + /// Registers the specified window to receieve terminal server notifications. + /// + /// The window to receieve the notifications. + /// Whether notifications should be created for all sessions. + public static void RegisterNotificationsCurrent(IWin32Window window, bool allSessions) + { + if (!Win32.WTSRegisterSessionNotification( + window.Handle, + allSessions ? WtsNotificationFlags.AllSessions : WtsNotificationFlags.ThisSession + )) + Win32.ThrowLastError(); + } + + /// + /// Unregisters terminal server notifications for the specified window. + /// + /// The window to stop receiving notifications. + public static void UnregisterNotificationsCurrent(IWin32Window window) + { + if (!Win32.WTSUnRegisterSessionNotification(window.Handle)) + Win32.ThrowLastError(); + } + + private string _systemName; + + private TerminalServerHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens a terminal server. + /// + /// The NetBIOS name of the server. + public TerminalServerHandle(string serverName) + { + this.Handle = Win32.WTSOpenServer(serverName); + _systemName = serverName; + + if (this.Handle == IntPtr.Zero) + Win32.ThrowLastError(); + } + + protected override void Close() + { + Win32.WTSCloseServer(this); + } + + /// + /// Gets the name of the terminal server. + /// This value can be null when the server is local. + /// + public string SystemName + { + get { return _systemName; } + } + + /// + /// Gets the processes running on the terminal server. + /// + /// An array of processes. + public TerminalServerProcess[] GetProcesses() + { + IntPtr dataPtr; + int count; + TerminalServerProcess[] processes; + + if (!Win32.WTSEnumerateProcesses(this, 0, 1, out dataPtr, out count)) + Win32.ThrowLastError(); + + using (var data = new WtsMemoryAlloc(dataPtr)) + { + processes = new TerminalServerProcess[count]; + + for (int i = 0; i < count; i++) + { + var process = data.ReadStruct(i); + processes[i] = new TerminalServerProcess( + process.ProcessId, + process.SessionId, + Marshal.PtrToStringUni(process.ProcessName), + process.Sid != IntPtr.Zero ? new Sid(process.Sid, _systemName) : null + ); + } + + return processes; + } + } + + /// + /// Gets information about a session on the terminal server. + /// + /// The ID of the session. + /// Information about the session. + public TerminalServerSession GetSession(int sessionId) + { + return new TerminalServerSession(this, sessionId); + } + + /// + /// Gets the sessions on the terminal server. + /// + /// An array of sessions. + public TerminalServerSession[] GetSessions() + { + IntPtr dataPtr; + int count; + TerminalServerSession[] sessions; + + if (!Win32.WTSEnumerateSessions(this, 0, 1, out dataPtr, out count)) + Win32.ThrowLastError(); + + using (var data = new WtsMemoryAlloc(dataPtr)) + { + sessions = new TerminalServerSession[count]; + + for (int i = 0; i < count; i++) + { + var session = data.ReadStruct(i); + sessions[i] = new TerminalServerSession( + this, + session.SessionID, + session.WinStationName, + session.State + ); + } + + return sessions; + } + } + + /// + /// Registers the specified window to receieve terminal server notifications. + /// + /// The window to receieve the notifications. + /// Whether notifications should be created for all sessions. + public void RegisterNotifications(IWin32Window window, bool allSessions) + { + if (!Win32.WTSRegisterSessionNotificationEx( + this, + window.Handle, + allSessions ? WtsNotificationFlags.AllSessions : WtsNotificationFlags.ThisSession + )) + Win32.ThrowLastError(); + } + + /// + /// Causes the terminal server to shutdown. + /// + /// The action to take. + public void Shutdown(WtsShutdownFlags flag) + { + if (!Win32.WTSShutdownSystem(this, flag)) + Win32.ThrowLastError(); + } + + /// + /// Terminates the specified process on the terminal server. + /// + /// The ID of the process to terminate. + /// The exit code. + public void TerminateProcess(int pid, int exitCode) + { + if (!Win32.WTSTerminateProcess(this, pid, exitCode)) + Win32.ThrowLastError(); + } + + /// + /// Unregisters terminal server notifications for the specified window. + /// + /// The window to stop receiving notifications. + public void UnregisterNotifications(IWin32Window window) + { + if (!Win32.WTSUnRegisterSessionNotificationEx(this, window.Handle)) + Win32.ThrowLastError(); + } + } + + public class TerminalServerSession : BaseObject + { + public static int GetActiveConsoleId() + { + return Win32.WTSGetActiveConsoleSessionId(); + } + + private TerminalServerHandle _serverHandle; + private int _sessionId; + private string _name; + private WtsConnectStateClass _state = (WtsConnectStateClass)(-1); + private string _initialProgram; + private string _applicationName; + private string _workingDirectory; + private string _userName; + private string _domainName; + private string _clientName; + private string _clientDirectory; + private System.Net.IPAddress _clientAddress; + private WtsClientDisplay? _clientDisplay; + + internal TerminalServerSession(TerminalServerHandle serverHandle, int sessionId) + { + _serverHandle = serverHandle; + _sessionId = sessionId; + _serverHandle.Reference(); + } + + internal TerminalServerSession(TerminalServerHandle serverHandle, int sessionId, string name, WtsConnectStateClass state) + { + _serverHandle = serverHandle; + _sessionId = sessionId; + _serverHandle.Reference(); + _name = name; + _state = state; + } + + protected override void DisposeObject(bool disposing) + { + _serverHandle.Dereference(disposing); + } + + public int SessionId { get { return _sessionId; } } + + public string Name + { + get + { + if (_name == null) + _name = this.GetInformationString(WtsInformationClass.WinStationName); + return _name; + } + } + + public WtsConnectStateClass State + { + get + { + if ((int)_state == -1) + { + IntPtr dataPtr; + int length; + + if (!Win32.WTSQuerySessionInformation( + _serverHandle, _sessionId, WtsInformationClass.ConnectState, out dataPtr, out length)) + Win32.ThrowLastError(); + + using (var data = new WtsMemoryAlloc(dataPtr)) + _state = (WtsConnectStateClass)data.ReadInt32(0); + } + + return _state; + } + } + + public string InitialProgram + { + get + { + if (_initialProgram == null) + _initialProgram = this.GetInformationString(WtsInformationClass.InitialProgram); + return _initialProgram; + } + } + + public string ApplicationName + { + get + { + if (_applicationName == null) + _applicationName = this.GetInformationString(WtsInformationClass.ApplicationName); + return _applicationName; + } + } + + public string WorkingDirectory + { + get + { + if (_workingDirectory == null) + _workingDirectory = this.GetInformationString(WtsInformationClass.WorkingDirectory); + return _workingDirectory; + } + } + + public string UserName + { + get + { + if (_userName == null) + _userName = this.GetInformationString(WtsInformationClass.UserName); + return _userName; + } + } + + public string DomainName + { + get + { + if (_domainName == null) + _domainName = this.GetInformationString(WtsInformationClass.DomainName); + return _domainName; + } + } + + public string ClientName + { + get + { + if (_clientName == null) + _clientName = this.GetInformationString(WtsInformationClass.ClientName); + return _clientName; + } + } + + public string ClientDirectory + { + get + { + if (_clientDirectory == null) + _clientDirectory = this.GetInformationString(WtsInformationClass.ClientDirectory); + return _clientDirectory; + } + } + + public System.Net.IPAddress ClientAddress + { + get + { + if (_clientAddress == null) + { + IntPtr dataPtr; + int length; + + if (!Win32.WTSQuerySessionInformation( + _serverHandle, _sessionId, WtsInformationClass.ClientAddress, out dataPtr, out length)) + Win32.ThrowLastError(); + + if (dataPtr != IntPtr.Zero) + { + unsafe + { + using (var data = new WtsMemoryAlloc(dataPtr)) + { + var address = data.ReadStruct(); + + if (address.AddressFamily != 0) + _clientAddress = new System.Net.IPAddress(data.ReadBytes(6, 4)); + } + } + } + } + + return _clientAddress; + } + } + + public WtsClientDisplay ClientDisplay + { + get + { + if (_clientDisplay == null) + { + IntPtr dataPtr; + int length; + + if (!Win32.WTSQuerySessionInformation( + _serverHandle, _sessionId, WtsInformationClass.ClientDisplay, out dataPtr, out length)) + Win32.ThrowLastError(); + + if (dataPtr != IntPtr.Zero) + { + using (var data = new WtsMemoryAlloc(dataPtr)) + _clientDisplay = data.ReadStruct(); + } + } + + return _clientDisplay.Value; + } + } + + public void Disconnect() + { + this.Disconnect(false); + } + + public void Disconnect(bool synchronous) + { + if (!Win32.WTSDisconnectSession(_serverHandle, _sessionId, synchronous)) + Win32.ThrowLastError(); + } + + public string GetInformationString(WtsInformationClass infoClass) + { + IntPtr data; + int length; + + if (!Win32.WTSQuerySessionInformation(_serverHandle, _sessionId, infoClass, out data, out length)) + Win32.ThrowLastError(); + + if (data == IntPtr.Zero) + return null; + + using (new WtsMemoryAlloc(data)) + return Marshal.PtrToStringUni(data); + } + + public void Logoff() + { + this.Logoff(false); + } + + public void Logoff(bool synchronous) + { + if (!Win32.WTSLogoffSession(_serverHandle, _sessionId, synchronous)) + Win32.ThrowLastError(); + } + + public DialogResult SendMessage(string title, string message) + { + return this.SendMessage(title, message, MessageBoxButtons.OK, MessageBoxIcon.None); + } + + public DialogResult SendMessage( + string title, + string message, + MessageBoxButtons buttons, + MessageBoxIcon icon + ) + { + return this.SendMessage(title, message, buttons, icon, 0, 0, 0, false); + } + + public DialogResult SendMessage( + string title, + string message, + MessageBoxButtons buttons, + MessageBoxIcon icon, + MessageBoxDefaultButton defaultButton, + MessageBoxOptions options, + int secondsTimeout, + bool synchronous + ) + { + DialogResult response; + + if (!Win32.WTSSendMessage( + _serverHandle, + _sessionId, + title, + title.Length * 2, + message, + message.Length * 2, + (int)buttons | (int)icon | (int)defaultButton | (int)options, + secondsTimeout, + out response, + synchronous + )) + Win32.ThrowLastError(); + + return response; + } + } + + public class TerminalServerProcess + { + private int _processId; + private int _sessionId; + private string _name; + private Sid _sid; + + internal TerminalServerProcess(int processId, int sessionId, string name, Sid sid) + { + _processId = processId; + _sessionId = sessionId; + _name = name; + _sid = sid; + } + + public int ProcessId { get { return _processId; } } + public int SessionId { get { return _sessionId; } } + public string Name { get { return _name; } } + public Sid Sid { get { return _sid; } } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/ThreadHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/ThreadHandle.cs new file mode 100644 index 000000000..8cf3d92ce --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/ThreadHandle.cs @@ -0,0 +1,1355 @@ +/* + * Process Hacker - + * thread handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a Windows thread. + /// + public sealed class ThreadHandle : NativeHandle, IWithToken + { + public delegate bool WalkStackDelegate(ThreadStackFrame stackFrame); + + private static readonly ThreadHandle _current = new ThreadHandle(new IntPtr(-2), false); + + /// + /// Gets a handle to the current thread. + /// + public static ThreadHandle Current + { + get { return _current; } + } + + public static ThreadHandle Create( + ThreadAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ProcessHandle processHandle, + out ClientId clientId, + ref Context threadContext, + ref InitialTeb initialTeb, + bool createSuspended + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateThread( + out handle, + access, + ref oa, + processHandle, + out clientId, + ref threadContext, + ref initialTeb, + createSuspended + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new ThreadHandle(handle, true); + } + + public static ThreadHandle CreateUserThread(ProcessHandle processHandle, IntPtr startAddress, IntPtr parameter) + { + return CreateUserThread(processHandle, false, startAddress, parameter); + } + + public static ThreadHandle CreateUserThread( + ProcessHandle processHandle, + bool createSuspended, + IntPtr startAddress, + IntPtr parameter + ) + { + ClientId clientId; + + return CreateUserThread(processHandle, createSuspended, 0, 0, startAddress, parameter, out clientId); + } + + public static ThreadHandle CreateUserThread( + ProcessHandle processHandle, + bool createSuspended, + int maximumStackSize, + int initialStackSize, + IntPtr startAddress, + IntPtr parameter, + out ClientId clientId + ) + { + NtStatus status; + IntPtr threadHandle; + + if ((status = Win32.RtlCreateUserThread( + processHandle, + IntPtr.Zero, + createSuspended, + 0, + maximumStackSize.ToIntPtr(), + initialStackSize.ToIntPtr(), + startAddress, + parameter, + out threadHandle, + out clientId + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new ThreadHandle(threadHandle, true); + } + + /// + /// Creates a thread handle using an existing handle. + /// The handle will not be closed automatically. + /// + /// The handle value. + /// The thread handle. + public static ThreadHandle FromHandle(IntPtr handle) + { + return new ThreadHandle(handle, false); + } + + /// + /// Gets a handle to the current thread. + /// + /// A thread handle. + public static ThreadHandle GetCurrent() + { + return Current; + } + + /// + /// Gets the client ID of the current thread. + /// + /// A client ID. + public static ClientId GetCurrentCid() + { + return new ClientId(ProcessHandle.GetCurrentId(), ThreadHandle.GetCurrentId()); + } + + /// + /// Gets the ID of the current thread. + /// + /// A thread ID. + public static int GetCurrentId() + { + return Win32.GetCurrentThreadId(); + } + + /// + /// Gets a pointer to the current thread's environment block. + /// + /// A pointer to the current TEB. + public unsafe static Teb* GetCurrentTeb() + { + return (Teb*)Win32.NtCurrentTeb(); + } + + /// + /// Opens the current thread. + /// + /// The desired access to the thread. + /// A handle to the current thread. + public static ThreadHandle OpenCurrent(ThreadAccess access) + { + return new ThreadHandle(GetCurrentId(), access); + } + + public static ThreadHandle OpenWithAnyAccess(int tid) + { + try + { + return new ThreadHandle(tid, OSVersion.MinThreadQueryInfoAccess); + } + catch + { + try + { + return new ThreadHandle(tid, (ThreadAccess)StandardRights.Synchronize); + } + catch + { + try + { + return new ThreadHandle(tid, (ThreadAccess)StandardRights.ReadControl); + } + catch + { + try + { + return new ThreadHandle(tid, (ThreadAccess)StandardRights.WriteDac); + } + catch + { + return new ThreadHandle(tid, (ThreadAccess)StandardRights.WriteOwner); + } + } + } + } + } + + /// + /// Registers a port which will be notified when the current thread terminates. + /// + /// A handle to a port. + public static void RegisterTerminationPort(PortHandle portHandle) + { + NtStatus status; + + if ((status = Win32.NtRegisterThreadTerminatePort(portHandle)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sleeps the current thread. + /// + /// The timeout, in 100ns units. + /// Whether the timeout value is relative. + /// A NT status value. + public static NtStatus Sleep(long timeout, bool relative) + { + return Sleep(false, timeout, relative); + } + + /// + /// Sleeps the current thread. + /// + /// + /// Whether user-mode APCs can be delivered during the wait. + /// + /// The timeout, in 100ns units. + /// Whether the timeout value is relative. + /// A NT status value. + public static NtStatus Sleep(bool alertable, long timeout, bool relative) + { + if (timeout == 0) + { + Yield(); + return NtStatus.Success; + } + + long realTime = relative ? -timeout : timeout; + + return Win32.NtDelayExecution(alertable, ref realTime); + } + + /// + /// Checks whether the current thread is in an alerted state and + /// executes any pending user-mode APCs. + /// + /// + /// NtStatus.Alerted if the current thread was in an alerted state, + /// otherwise NtStatus.Success. + /// + public static NtStatus TestAlert() + { + NtStatus status; + + if ((status = Win32.NtTestAlert()) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return status; + } + + /// + /// Switches to another thread. + /// + public static void Yield() + { + Win32.NtYieldExecution(); + } + + internal ThreadHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Opens a thread. + /// + /// The ID of the thread to open. + public ThreadHandle(int tid) + : this(tid, ThreadAccess.All) + { } + + /// + /// Opens a thread. + /// + /// The ID of the thread to open. + /// The desired access to the thread. + public ThreadHandle(int tid, ThreadAccess access) + { + if (KProcessHacker.Instance != null) + { + try + { + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThread(tid, access)); + } + catch (WindowsException) + { + // Open the thread with minimum access (SYNCHRONIZE) and set the granted access. + this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThread(tid, + (ThreadAccess)StandardRights.Synchronize)); + KProcessHacker.Instance.KphSetHandleGrantedAccess(this.Handle, (int)access); + } + } + else + { + this.Handle = Win32.OpenThread(access, false, tid); + } + + if (this.Handle == IntPtr.Zero) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + + public ThreadHandle( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + ClientId clientId, + ThreadAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if (clientId.ProcessId == 0 && clientId.ThreadId == 0) + { + if ((status = Win32.NtOpenThread( + out handle, + access, + ref oa, + IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + else + { + if ((status = Win32.NtOpenThread( + out handle, + access, + ref oa, + ref clientId + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public ThreadHandle(string name, ThreadAccess access) + : this(name, 0, null, new ClientId(), access) + { } + + /// + /// Puts the thread in an alerted state. + /// + public void Alert() + { + NtStatus status; + + if ((status = Win32.NtAlertThread(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Resumes the thread in an alerted state. + /// + public int AlertResume() + { + NtStatus status; + int suspendCount; + + if ((status = Win32.NtAlertResumeThread(this, out suspendCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return suspendCount; + } + + /// + /// Captures a kernel-mode stack trace for the thread. + /// + /// An array of function addresses. + public IntPtr[] CaptureKernelStack() + { + return this.CaptureKernelStack(0); + } + + /// + /// Captures a kernel-mode stack trace for the thread. + /// + /// The number of frames to skip. + /// An array of function addresses. + public IntPtr[] CaptureKernelStack(int skipCount) + { + IntPtr[] stack = new IntPtr[62 - skipCount]; // 62 limit for XP and Server 2003 + int hash; + + // Capture a kernel-mode stack trace. + int captured = KProcessHacker.Instance.KphCaptureStackBackTraceThread( + this, + skipCount, + stack.Length, + stack, + out hash + ); + + // Create a new array with only the frames we captured. + IntPtr[] newStack = new IntPtr[captured]; + + Array.Copy(stack, 0, newStack, 0, captured); + + return newStack; + } + + /// + /// Captures a user-mode stack trace for the thread. + /// + /// An array of stack frames. + public ThreadStackFrame[] CaptureUserStack() + { + return this.CaptureUserStack(0); + } + + /// + /// Captures a user-mode stack trace for the thread. + /// + /// The number of frames to skip. + /// An array of stack frames. + public ThreadStackFrame[] CaptureUserStack(int skipCount) + { + List frames = new List(); + + // Walk the stack. + this.WalkStack((frame) => { frames.Add(frame); return true; }); + + // If we want to skip frames than we have, just return an empty array. + if (frames.Count <= skipCount) + return new ThreadStackFrame[0]; + + // Otherwise, create a new array with the frames, minus what we skipped. + ThreadStackFrame[] newFrames = new ThreadStackFrame[frames.Count - skipCount]; + + Array.Copy(frames.ToArray(), skipCount, newFrames, 0, newFrames.Length); + + return newFrames; + } + + /// + /// Attempts to terminate the thread using a dangerous method. This + /// operation may cause the system to crash. + /// + /// The exit status. + public void DangerousTerminate(NtStatus exitStatus) + { + KProcessHacker.Instance.KphDangerousTerminateThread(this, exitStatus); + } + + /// + /// Gets the thread's base priority. + /// + public int GetBasePriority() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadBasePriority); + } + + /// + /// Gets the thread's base priority. + /// + /// A ThreadPriorityLevel enum. + public ThreadPriorityLevel GetBasePriorityWin32() + { + int priority = Win32.GetThreadPriority(this); + + if (priority == 0x7fffffff) + Win32.ThrowLastError(); + + return (ThreadPriorityLevel)priority; + } + + /// + /// Gets the thread's basic information. + /// + /// A THREAD_BASIC_INFORMATION structure. + public ThreadBasicInformation GetBasicInformation() + { + NtStatus status; + ThreadBasicInformation basicInfo = new ThreadBasicInformation(); + int retLen; + + if ((status = Win32.NtQueryInformationThread(this, ThreadInformationClass.ThreadBasicInformation, + ref basicInfo, Marshal.SizeOf(basicInfo), out retLen)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return basicInfo; + } + + /// + /// Gets the thread's context. + /// + /// A CONTEXT struct. + public Context GetContext(ContextFlags flags) + { + Context context = new Context(); + + context.ContextFlags = flags; + this.GetContext(ref context); + + return context; + } + + /// + /// Gets the thread's context. + /// + /// A Context structure. The ContextFlags must be set appropriately. + public unsafe void GetContext(ref Context context) + { + if (KProcessHacker.Instance != null) + { + fixed (Context* contextPtr = &context) + KProcessHacker.Instance.KphGetContextThread(this, contextPtr); + } + else + { + NtStatus status; + + if ((status = Win32.NtGetContextThread(this, ref context)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Gets the thread's context. + /// + /// A CONTEXT struct. + public ContextAmd64 GetContext(ContextFlagsAmd64 flags) + { + ContextAmd64 context = new ContextAmd64(); + + context.ContextFlags = flags; + this.GetContext(ref context); + + return context; + } + + /// + /// Gets the thread's context. + /// + /// A Context structure. The ContextFlags must be set appropriately. + public void GetContext(ref ContextAmd64 context) + { + NtStatus status; + + // HACK: To avoid a datatype misalignment error, allocate some + // aligned memory. + using (var data = new AlignedMemoryAlloc(Utils.SizeOf(16), 16)) + { + data.WriteStruct(context); + + if ((status = Win32.NtGetContextThread(this, data)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + context = data.ReadStruct(); + } + } + + /// + /// Gets the thread's x86 context. The thread's process must be running + /// under WOW64. + /// + /// A Context structure. The ContextFlags must be set appropriately. + public void GetContextWow64(ref Context context) + { + NtStatus status; + + if ((status = Win32.RtlWow64GetThreadContext(this, ref context)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Gets the number of processor cycles consumed by the thread. + /// + public ulong GetCycleTime() + { + ulong cycles; + + if (!Win32.QueryThreadCycleTime(this, out cycles)) + Win32.ThrowLastError(); + + return cycles; + } + + /// + /// Gets the thread's exit code. + /// + /// A number. + public int GetExitCode() + { + int exitCode; + + if (!Win32.GetExitCodeThread(this, out exitCode)) + Win32.ThrowLastError(); + + return exitCode; + } + + /// + /// Gets the thread's exit status. + /// + /// A NT status value. + public NtStatus GetExitStatus() + { + return this.GetBasicInformation().ExitStatus; + } + + private int GetInformationInt32(ThreadInformationClass infoClass) + { + NtStatus status; + int value; + int retLength; + + if ((status = Win32.NtQueryInformationThread( + this, infoClass, out value, sizeof(int), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return value; + } + + private IntPtr GetInformationIntPtr(ThreadInformationClass infoClass) + { + NtStatus status; + IntPtr value; + int retLength; + + if ((status = Win32.NtQueryInformationThread( + this, infoClass, out value, IntPtr.Size, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return value; + } + + /// + /// Gets the thread's I/O priority. + /// + public int GetIoPriority() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadIoPriority); + } + + /// + /// Gets the last system call the thread made. + /// + /// A system call number. + public int GetLastSystemCall() + { + int firstArgument; + + return this.GetLastSystemCall(out firstArgument); + } + + /// + /// Gets the last system call the thread made. + /// + /// The first argument to the last system call. + /// A system call number. + public unsafe int GetLastSystemCall(out int firstArgument) + { + NtStatus status; + int* data = stackalloc int[2]; + int retLength; + + if ((status = Win32.NtQueryInformationThread( + this, ThreadInformationClass.ThreadLastSystemCall, data, sizeof(int) * 2, out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + firstArgument = data[0]; + + return data[1]; + } + + /// + /// Gets the thread's page priority. + /// + public int GetPagePriority() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadPagePriority); + } + + /// + /// Gets the thread's priority. + /// + public int GetPriority() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadPriority); + } + + /// + /// Opens the thread's process. + /// + /// A process handle. + public ProcessHandle GetProcess(ProcessAccess access) + { + return new ProcessHandle(this, access); + } + + /// + /// Gets the thread's parent process' unique identifier. + /// + /// A process ID. + public int GetProcessId() + { + return this.GetBasicInformation().ClientId.ProcessId; + } + + /// + /// Gets the thread's unique identifier. + /// + /// A thread ID. + public int GetThreadId() + { + return this.GetBasicInformation().ClientId.ThreadId; + } + + /// + /// Opens and returns a handle to the thread's token. + /// + /// A handle to the thread's token. + public TokenHandle GetToken() + { + return GetToken(TokenAccess.All); + } + + /// + /// Opens and returns a handle to the thread's token. + /// + /// The desired access to the token. + /// A handle to the thread's token. + public TokenHandle GetToken(TokenAccess access) + { + return new TokenHandle(this, access); + } + + /// + /// Gets the thread's Win32 start address. + /// + public IntPtr GetWin32StartAddress() + { + return this.GetInformationIntPtr(ThreadInformationClass.ThreadQuerySetWin32StartAddress); + } + + /// + /// Causes the thread to impersonate a client thread. + /// + /// A handle to a client thread. + /// The impersonation level to request. + public void Impersonate(ThreadHandle clientThreadHandle, SecurityImpersonationLevel impersonationLevel) + { + NtStatus status; + SecurityQualityOfService securityQos = + new SecurityQualityOfService(impersonationLevel, false, false); + + if ((status = Win32.NtImpersonateThread(this, clientThreadHandle, ref securityQos)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Causes the thread to impersonate the anonymous account. + /// + public void ImpersonateAnonymous() + { + NtStatus status; + + if ((status = Win32.NtImpersonateAnonymousToken(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Gets whether the system will break (crash) upon the thread terminating. + /// + public bool IsCritical() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadBreakOnTermination) != 0; + } + + /// + /// Gets whether any I/O request packets (IRPs) are still pending for the thread. + /// + public bool IsIoPending() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadIsIoPending) != 0; + } + + /// + /// Gets whether the thread is the last in its process. + /// + public bool IsLastThread() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadAmILastThread) != 0; + } + + /// + /// Gets whether priority boost is enabled for the thread. + /// + public bool IsPriorityBoostEnabled() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadPriorityBoost) == 0; + } + + /// + /// Gets whether the thread has terminated. + /// + public bool IsTerminated() + { + return this.GetInformationInt32(ThreadInformationClass.ThreadIsTerminated) != 0; + } + + /// + /// Adds an user-mode asynchronous procedure call (APC) to the thread's APC queue. + /// This requires THREAD_SET_CONTEXT access. + /// + /// The address of the APC procedure. + /// The parameter to pass to the procedure. + public void QueueApc(IntPtr address, IntPtr parameter) + { + if (!Win32.QueueUserAPC(address, this, parameter)) + Win32.ThrowLastError(); + } + + /// + /// Adds an user-mode asynchronous procedure call (APC) to the thread's APC queue. + /// This requires THREAD_SET_CONTEXT access. + /// + /// The delegate to execute.. + /// The parameter to pass to the procedure. + public void QueueApc(ApcRoutine action, IntPtr parameter) + { + if (!Win32.QueueUserAPC(action, this, parameter)) + Win32.ThrowLastError(); + } + + /// + /// Queues a user-mode asynchronous procedure call (APC) to the thread. + /// + /// The address of the function to execute. + /// The first parameter to pass to the function. + /// The second parameter to pass to the function. + /// The third parameter to pass to the function. + public void QueueApc(IntPtr address, IntPtr param1, IntPtr param2, IntPtr param3) + { + NtStatus status; + + if ((status = Win32.NtQueueApcThread( + this, + address, + param1, + param2, + param3 + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void RemoteCall(IntPtr address, IntPtr[] arguments) + { + this.RemoteCall(address, arguments, false); + } + + public void RemoteCall(IntPtr address, IntPtr[] arguments, bool alreadySuspended) + { + ProcessHandle processHandle; + + if (KProcessHacker.Instance != null) + processHandle = this.GetProcess(ProcessAccess.VmWrite); + else + processHandle = new ProcessHandle(this.GetProcessId(), ProcessAccess.VmWrite); + + using (processHandle) + this.RemoteCall(processHandle, address, arguments, alreadySuspended); + } + + public void RemoteCall(ProcessHandle processHandle, IntPtr address, IntPtr[] arguments, bool alreadySuspended) + { + NtStatus status; + + if ((status = Win32.RtlRemoteCall( + processHandle, + this, + address, + arguments.Length, + arguments, + false, + alreadySuspended + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Resumes the thread. + /// + public int Resume() + { + NtStatus status; + int suspendCount; + + if ((status = Win32.NtResumeThread(this, out suspendCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return suspendCount; + } + + /// + /// Sets the thread's base priority. + /// + /// The thread's base priority. + public void SetBasePriority(int basePriority) + { + this.SetInformationInt32(ThreadInformationClass.ThreadBasePriority, basePriority); + } + + /// + /// Sets the thread's base priority. + /// + /// The base priority of the thread. + public void SetBasePriorityWin32(ThreadPriorityLevel basePriority) + { + if (!Win32.SetThreadPriority(this, (int)basePriority)) + Win32.ThrowLastError(); + } + + /// + /// Sets the thread's context. + /// + /// A CONTEXT struct. + public unsafe void SetContext(Context context) + { + if (KProcessHacker.Instance != null) + { + KProcessHacker.Instance.KphSetContextThread(this, &context); + } + else + { + NtStatus status; + + if ((status = Win32.NtSetContextThread(this, ref context)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Sets the thread's context. + /// + /// A CONTEXT struct. + public void SetContext(ContextAmd64 context) + { + NtStatus status; + + // HACK: To avoid a datatype misalignment error, allocate + // some aligned memory. + using (var data = new AlignedMemoryAlloc(Utils.SizeOf(16), 16)) + { + data.WriteStruct(context); + + if ((status = Win32.NtSetContextThread(this, data)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Sets the thread's x86 context. The thread's process must + /// be running under WOW64. + /// + /// A CONTEXT struct. + public void SetContextWow64(Context context) + { + NtStatus status; + + if ((status = Win32.RtlWow64SetThreadContext(this, ref context)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets whether the thread is critical. + /// + /// Whether the thread should be critical. + public void SetCritical(bool critical) + { + this.SetInformationInt32(ThreadInformationClass.ThreadBreakOnTermination, critical ? 1 : 0); + } + + private void SetInformationInt32(ThreadInformationClass infoClass, int value) + { + NtStatus status; + + if ((status = Win32.NtSetInformationThread( + this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + private void SetInformationIntPtr(ThreadInformationClass infoClass, IntPtr value) + { + NtStatus status; + + if ((status = Win32.NtSetInformationThread( + this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets the thread's priority. + /// + /// The thread's priority. + public void SetPriority(int priority) + { + this.SetInformationInt32(ThreadInformationClass.ThreadPriority, priority); + } + + /// + /// Sets the thread's priority boost. + /// + /// Whether priority boost will be enabled. + public void SetPriorityBoost(bool enabled) + { + this.SetInformationInt32(ThreadInformationClass.ThreadPriorityBoost, enabled ? 0 : 1); + } + + /// + /// Sets the thread's impersonation token. + /// + /// + /// A handle to a token. Specify null to cause the thread to stop + /// impersonating. + /// + public void SetToken(TokenHandle tokenHandle) + { + this.SetInformationIntPtr(ThreadInformationClass.ThreadImpersonationToken, tokenHandle ?? IntPtr.Zero); + } + + /// + /// Suspends the thread. + /// + public int Suspend() + { + NtStatus status; + int suspendCount; + + if ((status = Win32.NtSuspendThread(this, out suspendCount)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return suspendCount; + } + + /// + /// Terminates the thread. + /// + public void Terminate() + { + this.Terminate(NtStatus.Success); + } + + /// + /// Terminates the thread. + /// + /// The exit status. + public void Terminate(NtStatus exitStatus) + { + if (KProcessHacker.Instance != null) + { + try + { + KProcessHacker.Instance.KphTerminateThread(this, exitStatus); + return; + } + catch (WindowsException ex) + { + if (ex.ErrorCode != Win32Error.NotSupported) + throw ex; + } + } + + NtStatus status; + + if ((status = Win32.NtTerminateThread(this, exitStatus)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Walks the call stack for the thread. + /// + /// A callback to execute. + public void WalkStack(WalkStackDelegate walkStackCallback) + { + this.WalkStack(walkStackCallback, OSVersion.Architecture); + } + + /// + /// Walks the call stack for the thread. + /// + /// A callback to execute. + /// + /// The type of stack walk. On 32-bit systems, this value is ignored. + /// On 64-bit systems, this value can be set to I386 to walk the + /// 32-bit stack. + /// + public void WalkStack(WalkStackDelegate walkStackCallback, OSArch architecture) + { + if (KProcessHacker.Instance != null) + { + // Use KPH to open the parent process. + using (var phandle = this.GetProcess(ProcessAccess.QueryInformation | ProcessAccess.VmRead)) + this.WalkStack(phandle, walkStackCallback, architecture); + } + else + { + // We need to duplicate the handle to get QueryInformation access. + using (var dupThreadHandle = this.Duplicate(OSVersion.MinThreadQueryInfoAccess)) + using (var phandle = new ProcessHandle( + ThreadHandle.FromHandle(dupThreadHandle).GetBasicInformation().ClientId.ProcessId, + ProcessAccess.QueryInformation | ProcessAccess.VmRead + )) + { + this.WalkStack(phandle, walkStackCallback, architecture); + } + } + } + + /// + /// Walks the call stack for the thread. + /// + /// A handle to the thread's parent process. + /// A callback to execute. + public unsafe void WalkStack(ProcessHandle parentProcess, WalkStackDelegate walkStackCallback) + { + this.WalkStack(parentProcess, walkStackCallback, OSVersion.Architecture); + } + + /// + /// Walks the call stack for the thread. + /// + /// A handle to the thread's parent process. + /// A callback to execute. + /// + /// The type of stack walk. On 32-bit systems, this value is ignored. + /// On 64-bit systems, this value can be set to I386 to walk the + /// 32-bit stack. + /// + public unsafe void WalkStack(ProcessHandle parentProcess, WalkStackDelegate walkStackCallback, OSArch architecture) + { + bool suspended = false; + + // Suspend the thread to avoid inaccurate thread stacks. + try + { + this.Suspend(); + suspended = true; + } + catch (WindowsException) + { + suspended = false; + } + + // Use KPH for reading memory if we can. + ReadProcessMemoryProc64 readMemoryProc = null; + + if (KProcessHacker.Instance != null) + { + readMemoryProc = new ReadProcessMemoryProc64( + delegate(IntPtr processHandle, ulong baseAddress, IntPtr buffer, int size, out int bytesRead) + { + return KProcessHacker.Instance.KphReadVirtualMemorySafe( + ProcessHandle.FromHandle(processHandle), (int)baseAddress, buffer, size, out bytesRead); + }); + } + + try + { + // x86/WOW64 stack walk. + if (IntPtr.Size == 4 || (IntPtr.Size == 8 && architecture == OSArch.I386)) + { + Context context = new Context(); + + context.ContextFlags = ContextFlags.All; + + if (IntPtr.Size == 4) + { + // Get the context. + this.GetContext(ref context); + } + else + { + // Get the WOW64 x86 context. + this.GetContextWow64(ref context); + } + + // Set up the initial stack frame structure. + var stackFrame = new StackFrame64(); + + stackFrame.AddrPC.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrPC.Offset = (ulong)context.Eip; + stackFrame.AddrStack.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrStack.Offset = (ulong)context.Esp; + stackFrame.AddrFrame.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrFrame.Offset = (ulong)context.Ebp; + + while (true) + { + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.StackWalk64( + MachineType.I386, + parentProcess, + this, + ref stackFrame, + ref context, + readMemoryProc, + Win32.SymFunctionTableAccess64, + Win32.SymGetModuleBase64, + IntPtr.Zero + )) + break; + } + + // If we got an invalid eip, break. + if (stackFrame.AddrPC.Offset == 0) + break; + + // Execute the callback. + if (!walkStackCallback(new ThreadStackFrame(ref stackFrame))) + break; + } + } + // x64 stack walk. + else if (IntPtr.Size == 8) + { + ContextAmd64 context = new ContextAmd64(); + + context.ContextFlags = ContextFlagsAmd64.All; + // Get the context. + this.GetContext(ref context); + + // Set up the initial stack frame structure. + var stackFrame = new StackFrame64(); + + stackFrame.AddrPC.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrPC.Offset = (ulong)context.Rip; + stackFrame.AddrStack.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrStack.Offset = (ulong)context.Rsp; + stackFrame.AddrFrame.Mode = AddressMode.AddrModeFlat; + stackFrame.AddrFrame.Offset = (ulong)context.Rbp; + + while (true) + { + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.StackWalk64( + MachineType.Amd64, + parentProcess, + this, + ref stackFrame, + ref context, + readMemoryProc, + Win32.SymFunctionTableAccess64, + Win32.SymGetModuleBase64, + IntPtr.Zero + )) + break; + } + + // If we got an invalid rip, break. + if (stackFrame.AddrPC.Offset == 0) + break; + + // Execute the callback. + if (!walkStackCallback(new ThreadStackFrame(ref stackFrame))) + break; + } + } + } + finally + { + // If we suspended the thread before, resume it. + if (suspended) + { + try + { + this.Resume(); + } + catch (WindowsException) + { } + } + } + } + } + + public class ThreadStackFrame + { + private IntPtr _pcAddress; + private IntPtr _returnAddress; + private IntPtr _frameAddress; + private IntPtr _stackAddress; + private IntPtr _bStoreAddress; + private IntPtr[] _params; + + internal ThreadStackFrame(ref StackFrame64 stackFrame) + { + _pcAddress = new IntPtr((long)stackFrame.AddrPC.Offset); + _returnAddress = new IntPtr((long)stackFrame.AddrReturn.Offset); + _frameAddress = new IntPtr((long)stackFrame.AddrFrame.Offset); + _stackAddress = new IntPtr((long)stackFrame.AddrStack.Offset); + _bStoreAddress = new IntPtr((long)stackFrame.AddrBStore.Offset); + _params = new IntPtr[4]; + + for (int i = 0; i < 4; i++) + _params[i] = new IntPtr(stackFrame.Params[i]); + } + + public IntPtr PcAddress { get { return _pcAddress; } } + public IntPtr ReturnAddress { get { return _returnAddress; } } + public IntPtr FrameAddress { get { return _frameAddress; } } + public IntPtr StackAddress { get { return _stackAddress; } } + public IntPtr BStoreAddress { get { return _bStoreAddress; } } + public IntPtr[] Params { get { return _params; } } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TimerHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TimerHandle.cs new file mode 100644 index 000000000..b9cde6bdc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TimerHandle.cs @@ -0,0 +1,230 @@ +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class TimerHandle : NativeHandle + { + /// + /// Creates a timer. + /// + /// The desired access to the timer. + /// + /// The type of timer; synchronization timers will be reset once waiting threads are released. + /// + /// A handle to the timer. + public static TimerHandle Create(TimerAccess access, TimerType type) + { + return Create(access, null, type); + } + + /// + /// Creates a timer. + /// + /// The desired access to the timer. + /// A name for the timer in the object manager namespace. + /// + /// The type of timer; synchronization timers will be reset once waiting threads are released. + /// + /// A handle to the timer. + public static TimerHandle Create(TimerAccess access, string name, TimerType type) + { + return Create(access, name, 0, null, type); + } + + /// + /// Creates a timer. + /// + /// The desired access to the timer. + /// A name for the timer in the object manager namespace. + /// The flags to use when creating the object. + /// The directory in which to place the timer. This can be null. + /// + /// The type of timer; synchronization timers will be reset once waiting threads are released. + /// + /// A handle to the timer. + public static TimerHandle Create(TimerAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, TimerType type) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateTimer(out handle, access, ref oa, type)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new TimerHandle(handle, true); + } + + public static TimerHandle FromHandle(IntPtr handle) + { + return new TimerHandle(handle, false); + } + + private TimerApcRoutine _routine; + + private TimerHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public TimerHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, TimerAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenTimer(out handle, access, ref oa)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public TimerHandle(string name, TimerAccess access) + : this(name, 0, null, access) + { } + + /// + /// Cancels the timer, preventing it from being signaled. + /// + /// The state of the timer (whether it is signaled). + public bool Cancel() + { + NtStatus status; + bool currentState; + + if ((status = Win32.NtCancelTimer(this, out currentState)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return currentState; + } + + /// + /// Gets information about the timer. + /// + public TimerBasicInformation GetBasicInformation() + { + NtStatus status; + TimerBasicInformation tbi; + int retLength; + + if ((status = Win32.NtQueryTimer(this, TimerInformationClass.TimerBasicInformation, + out tbi, Marshal.SizeOf(typeof(TimerBasicInformation)), out retLength)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return tbi; + } + + /// + /// Sets the timer. + /// + /// The time at which the timer is to be signaled. + /// + /// The time interval for periodic signaling of the timer, in milliseconds. + /// Specify 0 for no periodic signaling. + /// + /// The state of the timer (whether it is signaled). + public bool Set(DateTime dueTime, int period) + { + return this.Set(dueTime.ToFileTime(), false, null, IntPtr.Zero, period); + } + + /// + /// Sets the timer. + /// + /// A relative due time, in 100ns units. + /// + /// The time interval for periodic signaling of the timer, in milliseconds. + /// Specify 0 for no periodic signaling. + /// + /// The state of the timer (whether it is signaled). + public bool Set(long dueTime, int period) + { + return this.Set(dueTime, null, period); + } + + /// + /// Sets the timer. + /// + /// A relative due time, in 100ns units. + /// A routine to call when the timer is signaled. + /// + /// The time interval for periodic signaling of the timer, in milliseconds. + /// Specify 0 for no periodic signaling. + /// + /// The state of the timer (whether it is signaled). + public bool Set(long dueTime, TimerApcRoutine routine, int period) + { + return this.Set(dueTime, true, routine, IntPtr.Zero, period); + } + + /// + /// Sets the timer. + /// + /// A due time, in 100ns units. + /// Whether the due time is relative. + /// A routine to call when the timer is signaled. + /// A value to pass to the timer callback routine. + /// + /// The time interval for periodic signaling of the timer, in milliseconds. + /// Specify 0 for no periodic signaling. + /// + /// The state of the timer (whether it is signaled). + public bool Set(long dueTime, bool relative, TimerApcRoutine routine, IntPtr context, int period) + { + return this.Set(dueTime, relative, routine, context, false, period); + } + + /// + /// Sets the timer. + /// + /// A due time, in 100ns units. + /// Whether the due time is relative. + /// A routine to call when the timer is signaled. + /// A value to pass to the timer callback routine. + /// + /// Whether the power manager should restore the system when the timer is signaled. + /// + /// + /// The time interval for periodic signaling of the timer, in milliseconds. + /// Specify 0 for no periodic signaling. + /// + /// The state of the timer (whether it is signaled). + public bool Set(long dueTime, bool relative, TimerApcRoutine routine, IntPtr context, bool resume, int period) + { + NtStatus status; + long realDueTime = relative ? -dueTime : dueTime; + bool previousState; + + // Keep the APC routine alive. + _routine = routine; + + if ((status = Win32.NtSetTimer( + this, + ref realDueTime, + routine, + context, + resume, + period, + out previousState + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return previousState; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TmHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TmHandle.cs new file mode 100644 index 000000000..fed50b2e6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TmHandle.cs @@ -0,0 +1,220 @@ +/* + * Process Hacker - + * transaction manager handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public class TmHandle : NativeHandle + { + public static TmHandle Create( + TmAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + string logFileName, + TmOptions createOptions + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + UnicodeString logFileNameStr = new UnicodeString(logFileName); + + try + { + if ((status = Win32.NtCreateTransactionManager( + out handle, + access, + ref oa, + ref logFileNameStr, + createOptions, + 0 + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + logFileNameStr.Dispose(); + } + } + finally + { + oa.Dispose(); + } + + return new TmHandle(handle, true); + } + + public static TmHandle FromHandle(IntPtr handle) + { + return new TmHandle(handle, false); + } + + private TmHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public TmHandle(string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, TmAccess access) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenTransactionManager( + out handle, + access, + ref oa, + IntPtr.Zero, + IntPtr.Zero, + 0 + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public TmBasicInformation GetBasicInformation() + { + NtStatus status; + TmBasicInformation basicInfo; + int retLength; + + if ((status = Win32.NtQueryInformationTransactionManager( + this, + TmInformationClass.TransactionManagerBasicInformation, + out basicInfo, + Marshal.SizeOf(typeof(TmBasicInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return basicInfo; + } + + public long GetLastRecoveredLsn() + { + NtStatus status; + TmRecoveryInformation recoveryInfo; + int retLength; + + if ((status = Win32.NtQueryInformationTransactionManager( + this, + TmInformationClass.TransactionManagerRecoveryInformation, + out recoveryInfo, + Marshal.SizeOf(typeof(TmRecoveryInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return recoveryInfo.LastRecoveredLsn; + } + + public string GetLogFileName() + { + NtStatus status; + int retLength; + + using (var data = new MemoryAlloc(0x1000)) + { + status = Win32.NtQueryInformationTransactionManager( + this, + TmInformationClass.TransactionManagerLogPathInformation, + data, + data.Size, + out retLength + ); + + if (status == NtStatus.BufferTooSmall) + { + // Resize the buffer and try again. + data.Resize(retLength); + + status = Win32.NtQueryInformationTransactionManager( + this, + TmInformationClass.TransactionManagerLogPathInformation, + data, + data.Size, + out retLength + ); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + TmLogPathInformation logPathInfo = data.ReadStruct(); + + return data.ReadUnicodeString(TmLogPathInformation.LogPathOffset, logPathInfo.LogPathLength); + } + } + + public Guid GetLogIdentity() + { + NtStatus status; + TmLogInformation logInfo; + int retLength; + + if ((status = Win32.NtQueryInformationTransactionManager( + this, + TmInformationClass.TransactionManagerLogInformation, + out logInfo, + Marshal.SizeOf(typeof(TmLogInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return logInfo.LogIdentity; + } + + public void Recover() + { + NtStatus status; + + if ((status = Win32.NtRecoverTransactionManager(this)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void Rollforward(long virtualClock) + { + NtStatus status; + + if ((status = Win32.NtRollforwardTransactionManager( + this, + ref virtualClock + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TokenHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TokenHandle.cs new file mode 100644 index 000000000..455a2e830 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TokenHandle.cs @@ -0,0 +1,595 @@ +/* + * Process Hacker - + * token handle + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Objects +{ + /// + /// Represents a handle to a Windows token. + /// + public sealed class TokenHandle : NativeHandle, IEquatable + { + private static readonly TokenSource _phTokenSource = new TokenSource("PROCHACK", Luid.Allocate()); + + public static TokenHandle Create( + TokenAccess access, + TokenType tokenType, + Sid user, + Sid[] groups, + PrivilegeSet privileges + ) + { + using (var administratorsSid = Sid.GetWellKnownSid(WellKnownSidType.WinBuiltinAdministratorsSid)) + using (var thandle = TokenHandle.OpenCurrentPrimary(TokenAccess.Query)) + return Create(access, 0, thandle, tokenType, user, groups, privileges, administratorsSid, administratorsSid); + } + + public static TokenHandle Create( + TokenAccess access, + ObjectFlags objectFlags, + TokenHandle existingTokenHandle, + TokenType tokenType, + Sid user, + Sid[] groups, + PrivilegeSet privileges, + Sid owner, + Sid primaryGroup + ) + { + var statistics = existingTokenHandle.GetStatistics(); + + return Create( + access, + null, + objectFlags, + null, + tokenType, + statistics.AuthenticationId, + statistics.ExpirationTime, + user, + groups, + privileges, + owner, + primaryGroup, + null, + _phTokenSource + ); + } + + public static TokenHandle Create( + TokenAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + TokenType tokenType, + Luid authenticationId, + long expirationTime, + Sid user, + Sid[] groups, + PrivilegeSet privileges, + Sid owner, + Sid primaryGroup, + Acl defaultDacl, + TokenSource source + ) + { + NtStatus status; + TokenUser tokenUser = new TokenUser(user); + TokenGroups tokenGroups = new TokenGroups(groups); + TokenPrivileges tokenPrivileges = new TokenPrivileges(privileges); + TokenOwner tokenOwner = new TokenOwner(owner); + TokenPrimaryGroup tokenPrimaryGroup = new TokenPrimaryGroup(primaryGroup); + TokenDefaultDacl tokenDefaultDacl = new TokenDefaultDacl(defaultDacl); + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtCreateToken( + out handle, + access, + ref oa, + tokenType, + ref authenticationId, + ref expirationTime, + ref tokenUser, + ref tokenGroups, + ref tokenPrivileges, + ref tokenOwner, + ref tokenPrimaryGroup, + ref tokenDefaultDacl, + ref source + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + return new TokenHandle(handle, true); + } + + /// + /// Creates a token handle using an existing handle. + /// The handle will not be closed automatically. + /// + /// The handle value. + /// The token handle. + public static TokenHandle FromHandle(IntPtr handle) + { + return new TokenHandle(handle, false); + } + + public static TokenHandle Logon(string username, string domain, string password, LogonType logonType, LogonProvider logonProvider) + { + IntPtr token; + + if (!Win32.LogonUser(username, domain, password, logonType, logonProvider, out token)) + Win32.ThrowLastError(); + + return new TokenHandle(token, true); + } + + public static TokenHandle OpenCurrent(TokenAccess access) + { + return new TokenHandle(ThreadHandle.GetCurrent(), access, false); + } + + public static TokenHandle OpenCurrentPrimary(TokenAccess access) + { + return new TokenHandle(ProcessHandle.Current, access); + } + + public static TokenHandle OpenSelf(TokenAccess access) + { + return new TokenHandle(ThreadHandle.GetCurrent(), access, true); + } + + public static TokenHandle OpenSystemToken(TokenAccess access) + { + using (var phandle = new ProcessHandle(4, OSVersion.MinProcessQueryInfoAccess)) + { + return phandle.GetToken(access); + } + } + + public static TokenHandle OpenSystemToken(TokenAccess access, SecurityImpersonationLevel impersonationLevel, TokenType type) + { + using (var phandle = new ProcessHandle(4, OSVersion.MinProcessQueryInfoAccess)) + { + using (var thandle = phandle.GetToken(TokenAccess.Duplicate | access)) + { + return thandle.Duplicate(access, impersonationLevel, type); + } + } + } + + public TokenHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + /// + /// Creates a new token handle from a process. + /// + /// The process handle. + /// The desired access to the token. + public TokenHandle(ProcessHandle handle, TokenAccess access) + { + IntPtr h; + + if (KProcessHacker.Instance != null) + { + h = new IntPtr(KProcessHacker.Instance.KphOpenProcessToken(handle, access)); + } + else + { + if (!Win32.OpenProcessToken(handle, access, out h)) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + } + + this.Handle = h; + } + + /// + /// Creates a new token handle from a thread. + /// + /// The thread handle. + /// The desired access to the token. + public TokenHandle(ThreadHandle handle, TokenAccess access) + : this(handle, access, false) + { } + + /// + /// Creates a new token handle from a thread. + /// + /// The thread handle. + /// The desired access to the token. + /// If the thread is currently impersonating, opens the original token. + public TokenHandle(ThreadHandle handle, TokenAccess access, bool openAsSelf) + { + IntPtr h; + + if (!Win32.OpenThreadToken(handle, access, openAsSelf, out h)) + { + this.MarkAsInvalid(); + Win32.ThrowLastError(); + } + + this.Handle = h; + } + + public void AdjustGroups(Sid[] groups) + { + TokenGroups tokenGroups = new TokenGroups(); + + tokenGroups.GroupCount = groups.Length; + tokenGroups.Groups = new SidAndAttributes[groups.Length]; + + for (int i = 0; i < groups.Length; i++) + tokenGroups.Groups[i] = groups[i].ToSidAndAttributes(); + + if (!Win32.AdjustTokenGroups(this, false, ref tokenGroups, 0, IntPtr.Zero, IntPtr.Zero)) + Win32.ThrowLastError(); + } + + public void AdjustPrivileges(PrivilegeSet privileges) + { + var tokenPrivileges = privileges.ToTokenPrivileges(); + + Win32.AdjustTokenPrivileges(this, false, ref tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero); + + if (Marshal.GetLastWin32Error() != 0) + Win32.ThrowLastError(); + } + + public bool CheckPrivileges(PrivilegeSet privileges) + { + NtStatus status; + bool result; + + using (var privilegesMemory = privileges.ToMemory()) + { + if ((status = Win32.NtPrivilegeCheck( + this, + privilegesMemory, + out result + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return result; + } + } + + /// + /// Duplicates the token. + /// + /// The desired access to the new token. + /// The new impersonation level. + /// The new token type. + /// A new token. + public TokenHandle Duplicate(TokenAccess access, SecurityImpersonationLevel impersonationLevel, TokenType type) + { + IntPtr token; + + if (!Win32.DuplicateTokenEx(this, access, IntPtr.Zero, impersonationLevel, type, out token)) + Win32.ThrowLastError(); + + return new TokenHandle(token, true); + } + + /// + /// Determins whether the token is the same as another token. + /// + /// The other token. + /// Whether they are equal. + public bool Equals(TokenHandle other) + { + NtStatus status; + bool equal; + + if ((status = Win32.NtCompareTokens( + this, + other, + out equal + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return equal; + } + + /// + /// Gets the elevation type of the token. + /// + /// A TOKEN_ELEVATION_TYPE enum. + public TokenElevationType GetElevationType() + { + return (TokenElevationType)this.GetInformationInt32(TokenInformationClass.TokenElevationType); + } + + /// + /// Gets the token's groups. + /// + /// A TokenGroupsData struct. + public Sid[] GetGroups() + { + return this.GetGroupsInternal(TokenInformationClass.TokenGroups); + } + + private Sid[] GetGroupsInternal(TokenInformationClass infoClass) + { + int retLen = 0; + + Win32.GetTokenInformation(this, infoClass, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.GetTokenInformation(this, infoClass, data, + data.Size, out retLen)) + Win32.ThrowLastError(); + + int count = data.ReadStruct().GroupCount; + Sid[] sids = new Sid[count]; + + for (int i = 0; i < count; i++) + { + var saa = data.ReadStruct(TokenGroups.GroupsOffset, i); + sids[i] = new Sid(saa.Sid, saa.Attributes); + } + + return sids; + } + } + + private int GetInformationInt32(TokenInformationClass infoClass) + { + int value; + int retLen; + + if (!Win32.GetTokenInformation(this, infoClass, out value, sizeof(int), out retLen)) + Win32.ThrowLastError(); + + return value; + } + + /// + /// Gets the token's owner. + /// + /// A WindowsSID instance. + public Sid GetOwner() + { + int retLen; + + Win32.GetTokenInformation(this, TokenInformationClass.TokenOwner, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.GetTokenInformation(this, TokenInformationClass.TokenOwner, data, + data.Size, out retLen)) + Win32.ThrowLastError(); + + return new Sid(data.ReadIntPtr(0)); + } + } + + /// + /// Gets the token's primary group. + /// + /// A WindowsSID instance. + public Sid GetPrimaryGroup() + { + int retLen; + + Win32.GetTokenInformation(this, TokenInformationClass.TokenPrimaryGroup, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.GetTokenInformation(this, TokenInformationClass.TokenPrimaryGroup, data, + data.Size, out retLen)) + Win32.ThrowLastError(); + + return new Sid(data.ReadIntPtr(0)); + } + } + + /// + /// Gets the token's privileges. + /// + /// A TOKEN_PRIVILEGES structure. + public Privilege[] GetPrivileges() + { + int retLen; + + Win32.GetTokenInformation(this, TokenInformationClass.TokenPrivileges, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.GetTokenInformation(this, TokenInformationClass.TokenPrivileges, data, + data.Size, out retLen)) + Win32.ThrowLastError(); + + uint count = data.ReadUInt32(0); + Privilege[] privileges = new Privilege[count]; + + for (int i = 0; i < count; i++) + { + var laa = data.ReadStruct(sizeof(int), i); + privileges[i] = new Privilege(this, laa.Luid, laa.Attributes); + } + + return privileges; + } + } + + /// + /// Gets the restricted token's restricting SIDs. + /// + /// A TokenGroupsData struct. + public Sid[] GetRestrictingGroups() + { + return this.GetGroupsInternal(TokenInformationClass.TokenRestrictedSids); + } + + /// + /// Gets the token's session ID. + /// + /// The session ID. + public int GetSessionId() + { + return this.GetInformationInt32(TokenInformationClass.TokenSessionId); + } + + /// + /// Gets the token's source. + /// + /// A TOKEN_SOURCE struct. + public TokenSource GetSource() + { + TokenSource source; + int retLen; + + if (!Win32.GetTokenInformation(this, TokenInformationClass.TokenSource, + out source, Marshal.SizeOf(typeof(TokenSource)), out retLen)) + Win32.ThrowLastError(); + + return source; + } + + /// + /// Gets statistics about the token. + /// + /// A TOKEN_STATISTICS structure. + public TokenStatistics GetStatistics() + { + TokenStatistics statistics; + int retLen; + + if (!Win32.GetTokenInformation(this, TokenInformationClass.TokenStatistics, + out statistics, Marshal.SizeOf(typeof(TokenStatistics)), out retLen)) + Win32.ThrowLastError(); + + return statistics; + } + + /// + /// Gets the token's user. + /// + /// A WindowsSID instance. + public Sid GetUser() + { + int retLen; + + Win32.GetTokenInformation(this, TokenInformationClass.TokenUser, IntPtr.Zero, 0, out retLen); + + using (MemoryAlloc data = new MemoryAlloc(retLen)) + { + if (!Win32.GetTokenInformation(this.Handle, TokenInformationClass.TokenUser, data, + data.Size, out retLen)) + Win32.ThrowLastError(); + + TokenUser user = data.ReadStruct(); + + return new Sid(user.User.Sid, user.User.Attributes); + } + } + + /// + /// Gets whether the token has UAC elevation applied. + /// + /// A boolean. + public bool IsElevated() + { + return this.GetInformationInt32(TokenInformationClass.TokenElevation) != 0; + } + + /// + /// Gets whether virtualization is allowed. + /// + /// A boolean. + public bool IsVirtualizationAllowed() + { + return this.GetInformationInt32(TokenInformationClass.TokenVirtualizationAllowed) != 0; + } + + /// + /// Gets whether virtualization is enabled. + /// + /// A boolean. + public bool IsVirtualizationEnabled() + { + return this.GetInformationInt32(TokenInformationClass.TokenVirtualizationEnabled) != 0; + } + + /// + /// Sets a privilege's attributes. + /// + /// The name of the privilege. + /// The new attributes of the privilege. + public void SetPrivilege(string privilegeName, SePrivilegeAttributes attributes) + { + Luid privilegeLuid; + + if (!Win32.LookupPrivilegeValue(null, privilegeName, out privilegeLuid)) + throw new Exception("Invalid privilege name '" + privilegeName + "'."); + + this.SetPrivilege(privilegeLuid, attributes); + } + + public void SetPrivilege(Luid privilegeLuid, SePrivilegeAttributes attributes) + { + TokenPrivileges tkp = new TokenPrivileges(); + + tkp.Privileges = new LuidAndAttributes[1]; + + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = attributes; + tkp.Privileges[0].Luid = privilegeLuid; + + Win32.AdjustTokenPrivileges(this, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero); + + if (Marshal.GetLastWin32Error() != 0) + Win32.ThrowLastError(); + } + + /// + /// Sets whether virtualization is enabled. + /// + /// Whether virtualization is enabled. + public void SetVirtualizationEnabled(bool enabled) + { + int value = enabled ? 1 : 0; + + if (!Win32.SetTokenInformation(this, TokenInformationClass.TokenVirtualizationEnabled, ref value, 4)) + { + Win32.ThrowLastError(); + } + } + } +} \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TokenWithLinkedToken.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TokenWithLinkedToken.cs new file mode 100644 index 000000000..7089a519c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TokenWithLinkedToken.cs @@ -0,0 +1,55 @@ +/* + * Process Hacker - + * a token with a linked token + * + * Copyright (C) 2008 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; +using System; + +namespace ProcessHacker.Native.Objects +{ + public sealed class TokenWithLinkedToken : IWithToken + { + private TokenHandle _token; + + public TokenWithLinkedToken(TokenHandle token) + { + _token = token; + } + + public TokenHandle GetToken() + { + IntPtr linkedToken; + int retLen; + + if (!Win32.GetTokenInformation(_token, TokenInformationClass.TokenLinkedToken, + out linkedToken, IntPtr.Size, out retLen)) + Win32.ThrowLastError(); + + return new TokenHandle(linkedToken, true); + } + + public TokenHandle GetToken(TokenAccess access) + { + return this.GetToken(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/TransactionHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/TransactionHandle.cs new file mode 100644 index 000000000..887893dbd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/TransactionHandle.cs @@ -0,0 +1,213 @@ +/* + * Process Hacker - + * transaction handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public class TransactionHandle : NativeHandle + { + public static TransactionHandle Create( + TransactionAccess access, + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + Guid unitOfWorkGuid, + TmHandle tmHandle, + TransactionOptions createOptions, + long timeout, + string description + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + UnicodeString descriptionStr = new UnicodeString(description); + + try + { + if ((status = Win32.NtCreateTransaction( + out handle, + access, + ref oa, + ref unitOfWorkGuid, + tmHandle ?? IntPtr.Zero, + createOptions, + 0, + 0, + ref timeout, + ref descriptionStr + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + descriptionStr.Dispose(); + } + } + finally + { + oa.Dispose(); + } + + return new TransactionHandle(handle, true); + } + + private TransactionHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public TransactionHandle( + string name, + ObjectFlags objectFlags, + DirectoryHandle rootDirectory, + Guid unitOfWorkGuid, + TmHandle tmHandle, + TransactionAccess access + ) + { + NtStatus status; + ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); + IntPtr handle; + + try + { + if ((status = Win32.NtOpenTransaction( + out handle, + access, + ref oa, + ref unitOfWorkGuid, + tmHandle ?? IntPtr.Zero + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + oa.Dispose(); + } + + this.Handle = handle; + } + + public static TransactionHandle FromHandle(IntPtr handle) + { + return new TransactionHandle(handle, false); + } + + public void Commit(bool wait) + { + NtStatus status; + + if ((status = Win32.NtCommitTransaction(this, wait)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public TransactionBasicInformation GetBasicInformation() + { + NtStatus status; + TransactionBasicInformation basicInfo; + int retLength; + + if ((status = Win32.NtQueryInformationTransaction( + this, + TransactionInformationClass.TransactionBasicInformation, + out basicInfo, + Marshal.SizeOf(typeof(TransactionBasicInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return basicInfo; + } + + public string GetDescription() + { + using (var data = this.GetPropertiesInformation()) + { + var propertiesInfo = data.ReadStruct(); + + return data.ReadUnicodeString( + TransactionPropertiesInformation.DescriptionOffset, + propertiesInfo.DescriptionLength / 2 + ); + } + } + + private MemoryAlloc GetPropertiesInformation() + { + NtStatus status; + int retLength; + + var data = new MemoryAlloc(0x1000); + + status = Win32.NtQueryInformationTransaction( + this, + TransactionInformationClass.TransactionPropertiesInformation, + data, + data.Size, + out retLength + ); + + if (status == NtStatus.BufferTooSmall) + { + // Resize the buffer and try again. + data.Resize(retLength); + + status = Win32.NtQueryInformationTransaction( + this, + TransactionInformationClass.TransactionPropertiesInformation, + data, + data.Size, + out retLength + ); + } + + if (status >= NtStatus.Error) + { + data.Dispose(); + Win32.ThrowLastError(status); + } + + return data; + } + + public long GetTimeout() + { + using (var data = this.GetPropertiesInformation()) + return data.ReadStruct().Timeout; + } + + public void Rollback(bool wait) + { + NtStatus status; + + if ((status = Win32.NtRollbackTransaction(this, wait)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/UserHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/UserHandle.cs new file mode 100644 index 000000000..d93980054 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/UserHandle.cs @@ -0,0 +1,50 @@ +/* + * Process Hacker - + * USER handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Objects +{ + public abstract class UserHandle : NativeHandle + where TAccess : struct + { + protected UserHandle() + : base() + { } + + protected UserHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + public override SecurityDescriptor GetSecurity(SecurityInformation securityInformation) + { + return this.GetSecurity(SeObjectType.WindowObject, securityInformation); + } + + public override void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + this.SetSecurity(SeObjectType.WindowObject, securityInformation, securityDescriptor); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/WindowHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/WindowHandle.cs new file mode 100644 index 000000000..df9774f5e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/WindowHandle.cs @@ -0,0 +1,200 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Objects +{ + public delegate bool EnumerateWindowsDelegate(WindowHandle windowHandle); + + public struct WindowHandle : IEquatable, IEquatable + { + private static WindowHandle _zero = new WindowHandle(IntPtr.Zero); + + public static WindowHandle Zero + { + get { return _zero; } + } + + public static bool Enumerate(EnumerateWindowsDelegate callback) + { + return Win32.EnumWindows((hWnd, param) => callback(new WindowHandle(hWnd)), 0); + } + + public static bool EnumerateByThreadId(int tid, EnumerateWindowsDelegate callback) + { + return Win32.EnumThreadWindows(tid, (hWnd, param) => callback(new WindowHandle(hWnd)), 0); + } + + public static WindowHandle Find(string className, string windowName) + { + IntPtr handle = Win32.FindWindow(className, windowName); + + return new WindowHandle(handle); + } + + public static WindowHandle GetDesktopWindow() + { + return new WindowHandle(Win32.GetDesktopWindow()); + } + + public static WindowHandle GetForegroundWindow() + { + return new WindowHandle(Win32.GetForegroundWindow()); + } + + public static WindowHandle GetShellWindow() + { + return new WindowHandle(Win32.GetShellWindow()); + } + + public static implicit operator IntPtr(WindowHandle windowHandle) + { + return windowHandle.Handle; + } + + private IntPtr _handle; + + public WindowHandle(IntPtr handle) + { + _handle = handle; + } + + public IntPtr Handle + { + get { return _handle; } + } + + public bool IsInvalid + { + get { return _handle == IntPtr.Zero; } + } + + public bool BringToTop() + { + return Win32.BringWindowToTop(this); + } + + public bool Close() + { + return Win32.CloseWindow(this); + } + + public bool Destroy() + { + return Win32.DestroyWindow(this); + } + + public bool EndTask(bool force) + { + return Win32.EndTask(this, false, force); + } + + public bool EnumerateChildren(EnumerateWindowsDelegate callback) + { + return Win32.EnumChildWindows(this, (hWnd, param) => callback(new WindowHandle(hWnd)), 0); + } + + public bool Equals(WindowHandle other) + { + return this.Handle.Equals(other.Handle); + } + + public bool Equals(IntPtr other) + { + return this.Handle.Equals(other); + } + + public ClientId GetClientId() + { + int tid, pid; + + tid = Win32.GetWindowThreadProcessId(this, out pid); + + return new ClientId(pid, tid); + } + + public WindowHandle GetParent() + { + return new WindowHandle(Win32.GetParent(this)); + } + + public WindowPlacement GetPlacement() + { + WindowPlacement placement = new WindowPlacement(); + + placement.Length = Marshal.SizeOf(placement); + Win32.GetWindowPlacement(this, ref placement); + + return placement; + } + + public Rectangle GetRectangle() + { + Rect rect; + + if (!Win32.GetWindowRect(this, out rect)) + return Rectangle.Empty; + else + return rect.ToRectangle(); + } + + public string GetText() + { + int retChars; + + using (var data = new MemoryAlloc(0x200)) + { + retChars = Win32.InternalGetWindowText(this, data, data.Size / 2); + + return data.ReadUnicodeString(0, retChars); + } + } + + public bool IsHung() + { + return Win32.IsHungAppWindow(this); + } + + public bool IsParent() + { + return this.GetParent().Equals(WindowHandle.Zero); + } + + public bool IsWindow() + { + return Win32.IsWindow(this); + } + + public bool IsVisible() + { + return Win32.IsWindowVisible(this); + } + + public bool PostMessage(WindowMessage message, int wParam, int lParam) + { + return Win32.PostMessage(this, message, wParam, lParam); + } + + public IntPtr SendMessage(WindowMessage message, int wParam, int lParam) + { + return Win32.SendMessage(this, message, wParam, lParam); + } + + public IntPtr SendMessageTimeout(WindowMessage message, int wParam, int lParam, SmtoFlags flags, int timeout, out int result) + { + return Win32.SendMessageTimeout(this, message, wParam, lParam, flags, timeout, out result); + } + + public bool SetForeground() + { + return Win32.SetForegroundWindow(this); + } + + public bool Show(ShowWindowType flags) + { + return Win32.ShowWindow(this, flags); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Objects/WindowStationHandle.cs b/branches/ph-plugins/ProcessHacker.Native/Objects/WindowStationHandle.cs new file mode 100644 index 000000000..e71a1c090 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Objects/WindowStationHandle.cs @@ -0,0 +1,66 @@ +/* + * Process Hacker - + * window station handle + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Objects +{ + public sealed class WindowStationHandle : UserHandle + { + public static WindowStationHandle GetCurrent() + { + IntPtr handle = Win32.GetProcessWindowStation(); + + if (handle == IntPtr.Zero) + Win32.ThrowLastError(); + + return new WindowStationHandle(handle, false); + } + + public WindowStationHandle(string name, WindowStationAccess access) + { + this.Handle = Win32.OpenWindowStation(name, false, access); + + if (this.Handle == System.IntPtr.Zero) + Win32.ThrowLastError(); + } + + private WindowStationHandle(IntPtr handle, bool owned) + : base(handle, owned) + { } + + protected override void Close() + { + Win32.CloseWindowStation(this); + } + + public void SetCurrent() + { + if (!Win32.SetProcessWindowStation(this)) + Win32.ThrowLastError(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/ProcessHacker.Native.csproj b/branches/ph-plugins/ProcessHacker.Native/ProcessHacker.Native.csproj new file mode 100644 index 000000000..402258d64 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/ProcessHacker.Native.csproj @@ -0,0 +1,262 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8A448157-E1A7-4DDF-954E-287F1117832B} + Library + Properties + ProcessHacker.Native + ProcessHacker.Native + v2.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + + + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + bin\Release\ProcessHacker.Native.xml + 1591 + AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + ChooseProcessDialog.cs + + + Form + + + HandlePropertiesWindow.cs + + + + + + + + + + + + + ChooseProcessDialog.cs + + + HandlePropertiesWindow.cs + + + + + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E} + ProcessHacker.Common + + + + + \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Properties/AssemblyInfo.cs b/branches/ph-plugins/ProcessHacker.Native/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..03666e843 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Process Hacker Native Library")] +[assembly: AssemblyDescription("Process Hacker Native Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("wj32")] +[assembly: AssemblyProduct("Process Hacker")] +[assembly: AssemblyCopyright("Licensed under the GNU GPL, v3.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("55693337-3b82-490c-a33f-5cd16846b2f9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Ace.cs b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Ace.cs new file mode 100644 index 000000000..fc23705cb --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Ace.cs @@ -0,0 +1,130 @@ +/* + * Process Hacker - + * access control entry + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Security.AccessControl +{ + public class Ace : BaseObject + { + public static Ace GetAce(IntPtr ace) + { + var type = GetType(ace); + + switch (type) + { + case AceType.AccessAllowed: + case AceType.AccessDenied: + case AceType.SystemAlarm: + case AceType.SystemAudit: + return new KnownAce(ace); + default: + return new Ace(ace); + } + } + + public static AceType GetType(IntPtr ace) + { + MemoryRegion memory = new MemoryRegion(ace); + + return memory.ReadStruct().AceType; + } + + public static implicit operator IntPtr(Ace ace) + { + return ace.Memory; + } + + private MemoryRegion _memory; + private AceFlags _flags; + private int _size; + private AceType _type; + + protected Ace() + { } + + public Ace(IntPtr memory) + : this(memory, false) + { } + + public Ace(IntPtr memory, bool copy) + : base(copy) + { + if (copy) + { + Ace existingAce = new Ace(memory); + + _memory = new MemoryAlloc(existingAce.Size); + _memory.WriteMemory(0, existingAce, 0, existingAce.Size); + } + else + { + _memory = new MemoryRegion(memory); + } + + this.Read(); + } + + protected override void DisposeObject(bool disposing) + { + if (_memory != null) + _memory.Dispose(); + } + + public AceFlags Flags + { + get { return _flags; } + } + + public IntPtr Memory + { + get { return _memory; } + } + + protected MemoryRegion MemoryRegion + { + get { return _memory; } + set { _memory = value; } + } + + public int Size + { + get { return _size; } + } + + public AceType Type + { + get { return _type; } + } + + protected virtual void Read() + { + var aceHeader = _memory.ReadStruct(); + + _flags = aceHeader.AceFlags; + _size = aceHeader.AceSize; + _type = aceHeader.AceType; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Acl.cs b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Acl.cs new file mode 100644 index 000000000..ee20f7681 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/Acl.cs @@ -0,0 +1,322 @@ +/* + * Process Hacker - + * access control list + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Security.AccessControl +{ + public sealed class Acl : BaseObject, IEnumerable + { + public static Acl FromPointer(IntPtr memory) + { + return new Acl(new MemoryRegion(memory)); + } + + public static implicit operator IntPtr(Acl acl) + { + return acl.Memory; + } + + private MemoryRegion _memory; + + public Acl(int size) + { + NtStatus status; + + // Reserve 8 bytes for the ACL header. + if (size < 8) + throw new ArgumentException("Size must be greater than or equal to 8 bytes."); + + // Allocate some memory. + _memory = new MemoryAlloc(size); + + // Initialize the ACL. + if ((status = Win32.RtlCreateAcl( + _memory, + size, + Win32.AclRevision + )) >= NtStatus.Error) + { + // Dispose memory and disable ownership. + _memory.Dispose(); + _memory = null; + this.DisableOwnership(false); + } + + _memory.Reference(); + _memory.Dispose(); + } + + public Acl(Acl existingAcl) + { + // Allocate memory for the new ACL. + _memory = new MemoryAlloc(existingAcl.Size); + // Copy the ACL. + _memory.WriteMemory(0, existingAcl, 0, existingAcl.Size); + _memory.Reference(); + _memory.Dispose(); + } + + public Acl(Acl existingAcl, int newSize) + : this(newSize) + { + this.AddRange(0, existingAcl); + } + + public Acl(MemoryRegion memory) + { + _memory = memory; + _memory.Reference(); + } + + protected override void DisposeObject(bool disposing) + { + if (_memory != null) + _memory.Dereference(disposing); + } + + public Ace this[int index] + { + get { return this.GetAt(index); } + } + + public int BytesFree + { + get { return this.GetSizeInformation().AclBytesFree; } + } + + public int BytesUsed + { + get { return this.GetSizeInformation().AclBytesInUse; } + } + + public int Count + { + get { return this.GetSizeInformation().AceCount; } + } + + public IntPtr Memory + { + get { return _memory; } + } + + public int Size + { + get + { + var sizeInfo = this.GetSizeInformation(); + + return sizeInfo.AclBytesInUse + sizeInfo.AclBytesFree; + } + } + + public bool IsValid() + { + return Win32.RtlValidAcl(this); + } + + public void AddAccessAllowed(int accessMask, Sid sid) + { + NtStatus status; + + if ((status = Win32.RtlAddAccessAllowedAce( + this, + Win32.AclRevision, + accessMask, + sid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddAccessAllowed(int accessMask, Sid sid, AceFlags flags) + { + NtStatus status; + + if ((status = Win32.RtlAddAccessAllowedAceEx( + this, + Win32.AclRevision, + flags, + accessMask, + sid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddAccessDenied(int accessMask, Sid sid) + { + NtStatus status; + + if ((status = Win32.RtlAddAccessDeniedAce( + this, + Win32.AclRevision, + accessMask, + sid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddAccessDenied(int accessMask, Sid sid, AceFlags flags) + { + NtStatus status; + + if ((status = Win32.RtlAddAccessDeniedAceEx( + this, + Win32.AclRevision, + flags, + accessMask, + sid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddAuditAccess(int accessMask, Sid sid, bool auditSuccess, bool auditFailure) + { + NtStatus status; + + if ((status = Win32.RtlAddAuditAccessAce( + this, + Win32.AclRevision, + accessMask, + sid, + auditSuccess, + auditFailure + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddAuditAccess(int accessMask, Sid sid, bool auditSuccess, bool auditFailure, AceFlags flags) + { + NtStatus status; + + if ((status = Win32.RtlAddAuditAccessAceEx( + this, + Win32.AclRevision, + flags, + accessMask, + sid, + auditSuccess, + auditFailure + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddCompound(AceType type, int accessMask, Sid serverSid, Sid clientSid) + { + NtStatus status; + + if ((status = Win32.RtlAddCompoundAce( + this, + Win32.AclRevision, + type, + accessMask, + serverSid, + clientSid + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + public void AddRange(int index, IEnumerable aceList) + { + int totalSize = 0; + + // Compute the total size, in bytes. + foreach (Ace ace in aceList) + { + totalSize += ace.Size; + } + + using (var aceListMemory = new MemoryAlloc(totalSize)) + { + int i = 0; + + // Copy the ACEs into one contiguous block. + foreach (Ace ace in aceList) + { + aceListMemory.WriteMemory(i, ace, 0, ace.Size); + i += ace.Size; + } + + NtStatus status; + + // Add the ACEs to the ACL. + if ((status = Win32.RtlAddAce( + this, + Win32.AclRevision, + index, + aceListMemory, + totalSize + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + public Ace GetAt(int index) + { + NtStatus status; + IntPtr ace; + + if ((status = Win32.RtlGetAce(this, index, out ace)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return Ace.GetAce(ace); + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < this.Count; i++) + yield return this[i]; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public AclSizeInformation GetSizeInformation() + { + NtStatus status; + AclSizeInformation sizeInfo; + + if ((status = Win32.RtlQueryInformationAcl( + this, + out sizeInfo, + Marshal.SizeOf(typeof(AclSizeInformation)), + AclInformationClass.AclSizeInformation + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return sizeInfo; + } + + public void RemoveAt(int index) + { + NtStatus status; + + if ((status = Win32.RtlDeleteAce(this, index)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/KnownAce.cs b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/KnownAce.cs new file mode 100644 index 000000000..a6ee717a0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/KnownAce.cs @@ -0,0 +1,93 @@ +/* + * Process Hacker - + * known access control entry + * (access allowed, access denied, system alarm, system audit) + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Security.AccessControl +{ + public class KnownAce : Ace + { + private int _mask; + private Sid _sid; + + protected KnownAce() + { } + + public KnownAce(AceType type, AceFlags flags, int mask, Sid sid) + { + if ( + type != AceType.AccessAllowed && + type != AceType.AccessDenied && + type != AceType.SystemAlarm && + type != AceType.SystemAudit + ) + throw new ArgumentException("Invalid ACE type."); + + this.MemoryRegion = new MemoryAlloc( + Marshal.SizeOf(typeof(KnownAceStruct)) - // known ace struct size + sizeof(int) + // minus SidStart field + sid.Length // plus SID length + ); + + KnownAceStruct knownAce = new KnownAceStruct(); + + // Initialize the ACE (minus the SID). + knownAce.Header.AceType = type; + knownAce.Header.AceFlags = flags; + knownAce.Header.AceSize = (ushort)this.MemoryRegion.Size; + knownAce.Mask = mask; + // Write the ACE to memory. + this.MemoryRegion.WriteStruct(knownAce); + // Write the SID. + this.MemoryRegion.WriteMemory(Win32.KnownAceSidStartOffset.ToInt32(), sid, 0, sid.Length); + // Update the cached info. + this.Read(); + } + + public KnownAce(IntPtr memory) + : base(memory) + { } + + public int Mask + { + get { return _mask; } + } + + public Sid Sid + { + get { return _sid; } + } + + protected override void Read() + { + var knownAce = this.MemoryRegion.ReadStruct(); + + _mask = knownAce.Mask; + _sid = Sid.FromPointer(this.Memory.Increment(Win32.KnownAceSidStartOffset)); + + base.Read(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityDescriptor.cs b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityDescriptor.cs new file mode 100644 index 000000000..9f007d43d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityDescriptor.cs @@ -0,0 +1,599 @@ +/* + * Process Hacker - + * security descriptor + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Security.AccessControl +{ + /// + /// Represents a security descriptor. + /// + public sealed class SecurityDescriptor : BaseObject + { + /// + /// Gets the security descriptor of a kernel object. + /// + /// A handle to a kernel object. + /// The information to retrieve. + /// A security descriptor. + public static SecurityDescriptor GetSecurity(IntPtr handle, SecurityInformation securityInformation) + { + NtStatus status; + int retLength; + + using (var data = new MemoryAlloc(0x100)) + { + status = Win32.NtQuerySecurityObject( + handle, + securityInformation, + data, + data.Size, + out retLength + ); + + if (status == NtStatus.BufferTooSmall) + { + data.Resize(retLength); + + status = Win32.NtQuerySecurityObject( + handle, + securityInformation, + data, + data.Size, + out retLength + ); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new SecurityDescriptor(data); + } + } + + /// + /// Gets the security descriptor of an object. + /// + /// A handle to an object. + /// The type of the object. + /// The information to retrieve. + /// A security descriptor. + public static SecurityDescriptor GetSecurity(IntPtr handle, SeObjectType objectType, SecurityInformation securityInformation) + { + Win32Error result; + IntPtr dummy, securityDescriptor; + + if ((result = Win32.GetSecurityInfo( + handle, + objectType, + securityInformation, + out dummy, out dummy, out dummy, out dummy, + out securityDescriptor + )) != 0) + Win32.ThrowLastError(result); + + return new SecurityDescriptor(new LocalMemoryAlloc(securityDescriptor)); + } + + /// + /// Sets the security descriptor of a kernel object. + /// + /// A handle to a kernel object. + /// The information to modify. + /// The security descriptor. + public static void SetSecurity(IntPtr handle, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + NtStatus status; + + if ((status = Win32.NtSetSecurityObject( + handle, + securityInformation, + securityDescriptor + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + + /// + /// Sets the security descriptor of an object. + /// + /// A handle to an object. + /// The type of the object. + /// The information to modify. + /// The security descriptor. + public static void SetSecurity(IntPtr handle, SeObjectType objectType, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + Win32Error result; + IntPtr dacl = IntPtr.Zero; + IntPtr group = IntPtr.Zero; + IntPtr owner = IntPtr.Zero; + IntPtr sacl = IntPtr.Zero; + + if ((securityInformation & SecurityInformation.Dacl) == SecurityInformation.Dacl) + dacl = securityDescriptor.Dacl ?? IntPtr.Zero; + if ((securityInformation & SecurityInformation.Group) == SecurityInformation.Group) + group = securityDescriptor.Group; + if ((securityInformation & SecurityInformation.Owner) == SecurityInformation.Owner) + owner = securityDescriptor.Owner; + if ((securityInformation & SecurityInformation.Sacl) == SecurityInformation.Sacl) + sacl = securityDescriptor.Sacl ?? IntPtr.Zero; + + if ((result = Win32.SetSecurityInfo( + handle, + objectType, + securityInformation, + owner, + group, + dacl, + sacl + )) != 0) + Win32.ThrowLastError(result); + } + + public static implicit operator IntPtr(SecurityDescriptor securityDescriptor) + { + return securityDescriptor.Memory; + } + + private MemoryRegion _memory; + private Acl _dacl; + private Acl _sacl; + private Sid _owner; + private Sid _group; + + /// + /// Creates an empty security descriptor. + /// + public SecurityDescriptor() + { + NtStatus status; + + _memory = new MemoryAlloc(Win32.SecurityDescriptorMinLength); + + if ((status = Win32.RtlCreateSecurityDescriptor( + _memory, + Win32.SecurityDescriptorRevision + )) >= NtStatus.Error) + { + _memory.Dispose(); + _memory = null; + this.DisableOwnership(false); + Win32.ThrowLastError(status); + } + + _memory.Reference(); + _memory.Dispose(); + } + + /// + /// Creates a security descriptor with the specified components. + /// + /// A SID representing an owner. + /// A SID representing a group. + /// The discretionary access control list. + /// The system access control list. + public SecurityDescriptor(Sid owner, Sid group, Acl dacl, Acl sacl) + : this() + { + this.Owner = owner; + this.Group = group; + this.Dacl = dacl; + this.Sacl = sacl; + } + + /// + /// Creates a security descriptor from memory. + /// + /// The memory region to use. This object will be referenced. + public SecurityDescriptor(MemoryRegion memory) + { + _memory = memory; + _memory.Reference(); + this.Read(); + } + + protected override void DisposeObject(bool disposing) + { + if (_dacl != null) + _dacl.Dereference(disposing); + if (_sacl != null) + _sacl.Dereference(disposing); + if (_owner != null) + _owner.Dereference(disposing); + if (_group != null) + _group.Dereference(disposing); + if (_memory != null) + _memory.Dereference(disposing); + } + + /// + /// Gets or sets the control flags. + /// + public SecurityDescriptorControlFlags ControlFlags + { + get + { + NtStatus status; + SecurityDescriptorControlFlags control; + int revision; + + if ((status = Win32.RtlGetControlSecurityDescriptor( + this, + out control, + out revision + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return control; + } + set + { + NtStatus status; + + if ((status = Win32.RtlSetControlSecurityDescriptor( + this, + value, + value + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + } + + /// + /// Gets or sets the DACL. + /// + public Acl Dacl + { + get { return _dacl; } + set + { + NtStatus status; + + if ((status = Win32.RtlSetDaclSecurityDescriptor( + this, + value != null, + value ?? IntPtr.Zero, + false + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.SwapDacl(value); + } + } + + /// + /// Gets or sets whether the DACL has been defaulted. + /// + public bool DaclDefaulted + { + get + { + return (this.ControlFlags & SecurityDescriptorControlFlags.DaclDefaulted) == + SecurityDescriptorControlFlags.DaclDefaulted; + } + set + { + if (value) + this.ControlFlags |= SecurityDescriptorControlFlags.DaclDefaulted; + else + this.ControlFlags &= ~SecurityDescriptorControlFlags.DaclDefaulted; + } + } + + /// + /// Gets or sets the group. + /// + public Sid Group + { + get { return _group; } + set + { + NtStatus status; + + if ((status = Win32.RtlSetGroupSecurityDescriptor( + this, + value, + false + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.SwapGroup(value); + } + } + + /// + /// Gets or sets whether the group has been defaulted. + /// + public bool GroupDefaulted + { + get + { + return (this.ControlFlags & SecurityDescriptorControlFlags.GroupDefaulted) == + SecurityDescriptorControlFlags.GroupDefaulted; + } + set + { + if (value) + this.ControlFlags |= SecurityDescriptorControlFlags.GroupDefaulted; + else + this.ControlFlags &= ~SecurityDescriptorControlFlags.GroupDefaulted; + } + } + + /// + /// Gets the size of the security descriptor, in bytes. + /// + public int Length + { + get { return Win32.RtlLengthSecurityDescriptor(this); } + } + + /// + /// Gets a pointer to the associated memory of the security descriptor. + /// + public IntPtr Memory + { + get { return _memory; } + } + + /// + /// Gets or sets the owner. + /// + public Sid Owner + { + get { return _owner; } + set + { + NtStatus status; + + if ((status = Win32.RtlSetOwnerSecurityDescriptor( + this, + value, + false + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.SwapOwner(value); + } + } + + /// + /// Gets or sets whether the owner has been defaulted. + /// + public bool OwnerDefaulted + { + get + { + return (this.ControlFlags & SecurityDescriptorControlFlags.OwnerDefaulted) == + SecurityDescriptorControlFlags.OwnerDefaulted; + } + set + { + if (value) + this.ControlFlags |= SecurityDescriptorControlFlags.OwnerDefaulted; + else + this.ControlFlags &= ~SecurityDescriptorControlFlags.OwnerDefaulted; + } + } + + /// + /// Gets or sets the SACL. + /// + public Acl Sacl + { + get { return _sacl; } + set + { + NtStatus status; + + if ((status = Win32.RtlSetSaclSecurityDescriptor( + this, + value != null, + value ?? IntPtr.Zero, + false + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + this.SwapSacl(value); + } + } + + /// + /// Gets or sets whether the SACL has been defaulted. + /// + public bool SaclDefaulted + { + get + { + return (this.ControlFlags & SecurityDescriptorControlFlags.SaclDefaulted) == + SecurityDescriptorControlFlags.SaclDefaulted; + } + set + { + if (value) + this.ControlFlags |= SecurityDescriptorControlFlags.SaclDefaulted; + else + this.ControlFlags &= ~SecurityDescriptorControlFlags.SaclDefaulted; + } + } + + /// + /// Gets whether the security descriptor is in self-relative form. + /// + public bool SelfRelative + { + get + { + return (this.ControlFlags & SecurityDescriptorControlFlags.SelfRelative) == + SecurityDescriptorControlFlags.SelfRelative; + } + } + + /// + /// Checks whether the security descriptor grants a set of access rights to a client. + /// + /// A handle to a token which represents the client. + /// The access rights requested by the client. + /// A structure which defines how generic access rights are to be mapped. + /// A variable which receives the granted access rights. + /// Success if access was granted, otherwise another NT status value. + public NtStatus CheckAccess(TokenHandle tokenHandle, int desiredAccess, GenericMapping genericMapping, out int grantedAccess) + { + NtStatus status; + NtStatus accessStatus; + int privilegeSetLength = 0; + + if ((status = Win32.NtAccessCheck( + this, + tokenHandle, + desiredAccess, + ref genericMapping, + IntPtr.Zero, + ref privilegeSetLength, + out grantedAccess, + out accessStatus + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return accessStatus; + } + + /// + /// Checks whether the security descriptor is valid. + /// + /// True if the security descriptor is valid, otherwise false. + public bool IsValid() + { + return Win32.RtlValidSecurityDescriptor(this); + } + + private void Read() + { + NtStatus status; + bool present, defaulted; + IntPtr dacl, group, owner, sacl; + + // Read the DACL. + if ((status = Win32.RtlGetDaclSecurityDescriptor( + this, + out present, + out dacl, + out defaulted + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (present) + this.SwapDacl(new Acl(Acl.FromPointer(dacl))); + else + this.SwapDacl(null); + + // Read the SACL. + if ((status = Win32.RtlGetSaclSecurityDescriptor( + this, + out present, + out sacl, + out defaulted + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (present) + this.SwapSacl(new Acl(Acl.FromPointer(sacl))); + else + this.SwapSacl(null); + + // Read the group. + if ((status = Win32.RtlGetGroupSecurityDescriptor( + this, + out group, + out defaulted + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (group != IntPtr.Zero) + this.SwapGroup(new Sid(group)); + else + this.SwapGroup(null); + + // Read the owner. + if ((status = Win32.RtlGetOwnerSecurityDescriptor( + this, + out owner, + out defaulted + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + if (owner != IntPtr.Zero) + this.SwapOwner(new Sid(owner)); + else + this.SwapOwner(null); + } + + private void SwapDacl(Acl dacl) + { + BaseObject.SwapRef(ref _dacl, dacl); + } + + private void SwapGroup(Sid group) + { + BaseObject.SwapRef(ref _group, group); + } + + private void SwapOwner(Sid owner) + { + BaseObject.SwapRef(ref _owner, owner); + } + + private void SwapSacl(Acl sacl) + { + BaseObject.SwapRef(ref _sacl, sacl); + } + + /// + /// Creates a copy of the security descriptor in self-relative form. + /// + /// A new self-relative security descriptor. + public SecurityDescriptor ToSelfRelative() + { + NtStatus status; + int retLength; + + using (var data = new MemoryAlloc(Win32.SecurityDescriptorMinLength)) + { + retLength = data.Size; + status = Win32.RtlMakeSelfRelativeSD(this, data, ref retLength); + + if (status == NtStatus.BufferTooSmall) + { + data.Resize(retLength); + status = Win32.RtlMakeSelfRelativeSD(this, data, ref retLength); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return new SecurityDescriptor(data); + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityEditor.cs b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityEditor.cs new file mode 100644 index 000000000..7ae349ce0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/AccessControl/SecurityEditor.cs @@ -0,0 +1,329 @@ +/* + * Process Hacker - + * ISecurityInformation implementation + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Security.AccessControl +{ + public class SecurityEditor : IDisposable, ISecurityInformation + { + private class GenericSecurableObject : ISecurable + { + private SeObjectType _objectType; + private Func _openMethod; + + public GenericSecurableObject(SeObjectType objectType, Func openMethod) + { + _objectType = objectType; + _openMethod = openMethod; + } + + public SecurityDescriptor GetSecurity(SecurityInformation securityInformation) + { + using (var dupHandle = _openMethod(StandardRights.ReadControl)) + return SecurityDescriptor.GetSecurity(dupHandle, _objectType, securityInformation); + } + + public void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor) + { + using (var dupHandle = _openMethod( + ((securityInformation & SecurityInformation.Dacl) != 0 ? StandardRights.WriteDac : 0) | + ((securityInformation & SecurityInformation.Owner) != 0 ? StandardRights.WriteOwner : 0) + )) + { + SecurityDescriptor.SetSecurity( + dupHandle, + _objectType, + securityInformation, + securityDescriptor + ); + } + } + } + + public static void EditSecurity(IWin32Window owner, ISecurable securable, string name, IEnumerable accessEntries) + { + using (var osi = new SecurityEditor(securable, name, accessEntries)) + Win32.EditSecurity(owner != null ? owner.Handle : IntPtr.Zero, osi); + } + + public static ISecurable GetSecurable(NativeTypeFactory.ObjectType objectType, IntPtr handle) + { + return GetSecurable(objectType, (access) => new NativeHandle(handle, access)); + } + + public static ISecurable GetSecurable(NativeTypeFactory.ObjectType objectType, Func openMethod) + { + return new GenericSecurableObject(NativeTypeFactory.GetSeObjectType(objectType), openMethod); + } + + private bool _disposed = false; + private ISecurable _securable; + private List _pool = new List(); + private string _name; + private MemoryAlloc _accessRights; + private int _accessRightCount; + + internal SecurityEditor(ISecurable securable, string name, IEnumerable accessEntries) + { + List accesses; + + _securable = securable; + _name = name; + + accesses = new List(); + + foreach (var entry in accessEntries) + { + if (entry.Mask != 0) + { + accesses.Add(new SiAccess() + { + Guid = IntPtr.Zero, + Mask = entry.Mask, + Flags = (entry.General ? SiAccessFlags.General : 0) | (entry.Specific ? SiAccessFlags.Specific : 0), + Name = this.AllocateStringFromPool(entry.Name) + }); + } + } + + _accessRights = this.AllocateStructArray(accesses.ToArray()); + _accessRightCount = accesses.Count; + } + + public void Dispose() + { + if (!_disposed) + { + _pool.ForEach((alloc) => alloc.Dispose()); + _pool.Clear(); + _disposed = true; + } + } + + private MemoryAlloc AllocateArray(IntPtr[] value) + { + MemoryAlloc alloc = new MemoryAlloc(IntPtr.Size * value.Length); + + for (int i = 0; i < value.Length; i++) + alloc.WriteIntPtr(i * IntPtr.Size, value[i]); + + return alloc; + } + + private MemoryAlloc AllocateArrayFromPool(IntPtr[] value) + { + MemoryAlloc m = this.AllocateArray(value); + _pool.Add(m); + return m; + } + + private MemoryAlloc AllocateString(string value) + { + MemoryAlloc alloc = new MemoryAlloc((value.Length + 1) * 2); + + alloc.WriteUnicodeString(0, value); + alloc.WriteInt16(value.Length * 2, 0); + + return alloc; + } + + private MemoryAlloc AllocateStringFromPool(string value) + { + MemoryAlloc m = this.AllocateString(value); + _pool.Add(m); + return m; + } + + private MemoryAlloc AllocateStruct(T value) + where T : struct + { + MemoryAlloc alloc = new MemoryAlloc(Marshal.SizeOf(typeof(T))); + + alloc.WriteStruct(0, value); + + return alloc; + } + + private MemoryAlloc AllocateStructFromPool(T value) + where T : struct + { + MemoryAlloc m = this.AllocateStruct(value); + _pool.Add(m); + return m; + } + + private MemoryAlloc AllocateStructArray(T[] value) + where T : struct + { + MemoryAlloc alloc = new MemoryAlloc(Marshal.SizeOf(typeof(T)) * value.Length); + + for (int i = 0; i < value.Length; i++) + alloc.WriteStruct(i, value[i]); + + return alloc; + } + + private MemoryAlloc AllocateStructArrayFromPool(T[] value) + where T : struct + { + MemoryAlloc m = this.AllocateStructArray(value); + _pool.Add(m); + return m; + } + + #region ISecurityInformation Members + + public HResult GetObjectInformation(out SiObjectInfo ObjectInfo) + { + SiObjectInfo soi = new SiObjectInfo(); + + soi.Flags = + SiObjectInfoFlags.EditAudits | + SiObjectInfoFlags.EditOwner | + SiObjectInfoFlags.EditPerms | + SiObjectInfoFlags.Advanced | + SiObjectInfoFlags.NoAclProtect | + SiObjectInfoFlags.NoTreeApply; + soi.Instance = IntPtr.Zero; + soi.ObjectName = this.AllocateStringFromPool(_name); + ObjectInfo = soi; + + return HResult.OK; + } + + public HResult GetSecurity(SecurityInformation RequestedInformation, out IntPtr SecurityDescriptor, bool Default) + { + try + { + using (var sd = _securable.GetSecurity(RequestedInformation)) + { + // Since the ACL editor will free the security descriptor using + // LocalFree, we need to use a local memory allocation and copy + // the security descriptor into it. + using (var localAlloc = new LocalMemoryAlloc(sd.Length)) + { + localAlloc.WriteMemory(0, sd.Memory, 0, sd.Length); + localAlloc.Reference(); // reference for ACL editor + SecurityDescriptor = localAlloc; + } + } + } + catch (WindowsException ex) + { + SecurityDescriptor = IntPtr.Zero; + + return ex.ErrorCode.GetHResult(); + } + + return HResult.OK; + } + + public HResult SetSecurity(SecurityInformation SecurityInformation, IntPtr SecurityDescriptor) + { + try + { + _securable.SetSecurity( + SecurityInformation, + new SecurityDescriptor(new MemoryRegion(SecurityDescriptor)) + ); + } + catch (WindowsException ex) + { + return ex.ErrorCode.GetHResult(); + } + + return HResult.OK; + } + + public HResult GetAccessRights(ref Guid ObjectType, SiObjectInfoFlags Flags, out IntPtr Access, out int Accesses, out int DefaultAccess) + { + Access = _accessRights; + Accesses = _accessRightCount; + DefaultAccess = 0; + + return HResult.OK; + } + + public HResult MapGeneric(ref Guid ObjectType, ref AceFlags AceFlags, ref int Mask) + { + return HResult.OK; + } + + public HResult GetInheritTypes(out IntPtr InheritTypes, out int InheritTypesCount) + { + InheritTypes = IntPtr.Zero; + InheritTypesCount = 0; + + return HResult.Fail; + } + + public HResult PropertySheetPageCallback(IntPtr hWnd, SiCallbackMessage Msg, SiPageType Page) + { + return HResult.OK; + } + + #endregion + } + + public struct AccessEntry + { + private bool _general; + private int _mask; + private string _name; + private bool _specific; + + public AccessEntry(string name, object mask, bool general, bool specific) + { + _name = name; + _mask = Convert.ToInt32(mask); + _general = general; + _specific = specific; + } + + public bool General + { + get { return _general; } + } + + public int Mask + { + get { return _mask; } + } + + public string Name + { + get { return _name; } + } + + public bool Specific + { + get { return _specific; } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/DebugObjectAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/DebugObjectAccess.cs new file mode 100644 index 000000000..4cef678bc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/DebugObjectAccess.cs @@ -0,0 +1,15 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum DebugObjectAccess : uint + { + ReadEvent = 0x1, + ProcessAssign = 0x2, + SetInformation = 0x4, + QueryInformation = 0x8, + All = StandardRights.Required | StandardRights.Synchronize | + ReadEvent | ProcessAssign | SetInformation | QueryInformation + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/DesktopAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/DesktopAccess.cs new file mode 100644 index 000000000..e6268edbc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/DesktopAccess.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum DesktopAccess : uint + { + ReadObjects = 0x0001, + CreateWindow = 0x0002, + CreateMenu = 0x0004, + HookControl = 0x0008, + JournalRecord = 0x0010, + JournalPlayback = 0x0020, + Enumerate = 0x0040, + WriteObjects = 0x0080, + SwitchDesktop = 0x0100, + All = CreateMenu | CreateWindow | Enumerate | HookControl | + JournalPlayback | JournalRecord | ReadObjects | SwitchDesktop | + WriteObjects | StandardRights.Required, + GenericRead = Enumerate | ReadObjects | StandardRights.Read, + GenericWrite = CreateMenu | CreateWindow | HookControl | JournalPlayback | + JournalRecord | WriteObjects | StandardRights.Write, + GenericExecute = SwitchDesktop | StandardRights.Execute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/DirectoryAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/DirectoryAccess.cs new file mode 100644 index 000000000..14b8f51fd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/DirectoryAccess.cs @@ -0,0 +1,15 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum DirectoryAccess : uint + { + Query = 0x1, + Traverse = 0x2, + CreateObject = 0x4, + CreateSubdirectory = 0x8, + All = StandardRights.Required | Query | Traverse | + CreateObject | CreateSubdirectory + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/EnlistmentAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/EnlistmentAccess.cs new file mode 100644 index 000000000..061fd1f79 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/EnlistmentAccess.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum EnlistmentAccess : uint + { + QueryInformation = 0x0001, + SetInformation = 0x0002, + Recover = 0x0004, + SubordinateRights = 0x0008, + SuperiorRights = 0x0010, + GenericRead = StandardRights.Read | QueryInformation, + GenericWrite = StandardRights.Write | SetInformation | Recover | + SubordinateRights | SuperiorRights, + GenericExecute = StandardRights.Execute | Recover | SubordinateRights | + SuperiorRights, + All = StandardRights.Required | GenericRead | GenericWrite | GenericExecute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/EventAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/EventAccess.cs new file mode 100644 index 000000000..39353d524 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/EventAccess.cs @@ -0,0 +1,13 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum EventAccess : uint + { + QueryState = 0x1, + ModifyState = 0x2, + All = StandardRights.Required | StandardRights.Synchronize | + QueryState | ModifyState + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/EventPairAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/EventPairAccess.cs new file mode 100644 index 000000000..aa2da32cd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/EventPairAccess.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum EventPairAccess : uint + { + All = StandardRights.Required | StandardRights.Synchronize + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/FileAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/FileAccess.cs new file mode 100644 index 000000000..a2af7cc03 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/FileAccess.cs @@ -0,0 +1,39 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum FileAccess : uint + { + ReadData = 0x0001, // File, Named Pipe + ListDirectory = 0x0001, // Directory + + WriteData = 0x0002, // File, Named Pipe + AddFile = 0x0002, // Directory + + AppendData = 0x0004, // File + AddSubdirectory = 0x0004, // Directory + CreatePipeInstance = 0x0004, // Named Pipe + + ReadEa = 0x0008, // File, Directory + + WriteEa = 0x0010, // File, Directory + + Execute = 0x0020, // File + Traverse = 0x0020, // Directory + + DeleteChild = 0x0040, // Directory + + ReadAttributes = 0x0080, // All + + WriteAttributes = 0x0100, // All + + All = StandardRights.Required | StandardRights.Synchronize | 0x1ff, + GenericRead = StandardRights.Read | ReadData | ReadAttributes | ReadEa | + StandardRights.Synchronize, + GenericWrite = StandardRights.Write | WriteData | WriteAttributes | WriteEa | + AppendData | StandardRights.Synchronize, + GenericExecute = StandardRights.Execute | ReadAttributes | Execute | + StandardRights.Synchronize + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/FltPortAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/FltPortAccess.cs new file mode 100644 index 000000000..84d5d0295 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/FltPortAccess.cs @@ -0,0 +1,11 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum FltPortAccess : uint + { + Connect = 0x1, + All = Connect | StandardRights.All + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ISecurable.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ISecurable.cs new file mode 100644 index 000000000..1c28e4ad1 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ISecurable.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Security +{ + public interface ISecurable + { + SecurityDescriptor GetSecurity(SecurityInformation securityInformation); + void SetSecurity(SecurityInformation securityInformation, SecurityDescriptor securityDescriptor); + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/IoCompletionAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/IoCompletionAccess.cs new file mode 100644 index 000000000..dc10c1ee0 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/IoCompletionAccess.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum IoCompletionAccess : uint + { + QueryState = 0x1, + ModifyState = 0x2, + All = StandardRights.Required | StandardRights.Synchronize | 0x3 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/JobObjectAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/JobObjectAccess.cs new file mode 100644 index 000000000..d40245ee1 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/JobObjectAccess.cs @@ -0,0 +1,15 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum JobObjectAccess : uint + { + AssignProcess = 0x0001, + SetAttributes = 0x0002, + Query = 0x0004, + Terminate = 0x0008, + SetSecurityAttributes = 0x0010, + All = StandardRights.Required | StandardRights.Synchronize | 0x1f + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/KeyAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/KeyAccess.cs new file mode 100644 index 000000000..b68d0782a --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/KeyAccess.cs @@ -0,0 +1,23 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum KeyAccess : uint + { + QueryValue = 0x0001, + SetValue = 0x0002, + CreateSubKey = 0x0004, + EnumerateSubKeys = 0x0008, + Notify = 0x0010, + CreateLink = 0x0020, + Wow64_32Key = 0x0200, + Wow64_64Key = 0x0100, + Wow64_Res = 0x0300, + All = (StandardRights.All | QueryValue | SetValue | CreateSubKey | + EnumerateSubKeys | Notify | CreateLink) & ~StandardRights.Synchronize, + GenericRead = (StandardRights.Read | QueryValue | EnumerateSubKeys | Notify) & ~StandardRights.Synchronize, + GenericWrite = (StandardRights.Write | SetValue | CreateSubKey) & ~StandardRights.Synchronize, + GenericExecute = GenericRead & ~StandardRights.Synchronize + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/KeyedEventAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/KeyedEventAccess.cs new file mode 100644 index 000000000..e910ec783 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/KeyedEventAccess.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum KeyedEventAccess : uint + { + Wait = 0x1, + Wake = 0x2, + All = StandardRights.Required | Wait | Wake + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/LsaAccountAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/LsaAccountAccess.cs new file mode 100644 index 000000000..34534a7f6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/LsaAccountAccess.cs @@ -0,0 +1,19 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum LsaAccountAccess : uint + { + View = 0x00000001, + AdjustPrivileges = 0x00000002, + AdjustQuotas = 0x00000004, + AdjustSystemAccess = 0x00000008, + All = StandardRights.Required | View | AdjustPrivileges | AdjustQuotas | + AdjustSystemAccess, + GenericRead = StandardRights.Read | View, + GenericWrite = StandardRights.Write | AdjustPrivileges | AdjustQuotas | + AdjustSystemAccess, + GenericExecute = StandardRights.Execute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/LsaPolicyAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/LsaPolicyAccess.cs new file mode 100644 index 000000000..adcf0d69c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/LsaPolicyAccess.cs @@ -0,0 +1,31 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum LsaPolicyAccess : uint + { + ViewLocalInformation = 0x00000001, + ViewAuditInformation = 0x00000002, + GetPrivateInformation = 0x00000004, + TrustAdmin = 0x00000008, + CreateAccount = 0x00000010, + CreateSecret = 0x00000020, + CreatePrivilege = 0x00000040, + SetDefaultQuotaLimits = 0x00000080, + SetAuditRequirements = 0x00000100, + AuditLogAdmin = 0x00000200, + ServerAdmin = 0x00000400, + LookupNames = 0x00000800, + Notification = 0x00001000, + All = StandardRights.Required | ViewLocalInformation | ViewAuditInformation | + GetPrivateInformation | TrustAdmin | CreateAccount | CreateSecret | + CreatePrivilege | SetDefaultQuotaLimits | SetAuditRequirements | + AuditLogAdmin | ServerAdmin | LookupNames, + GenericRead = StandardRights.Read | ViewAuditInformation | GetPrivateInformation, + GenericWrite = StandardRights.Write | TrustAdmin | CreateAccount | CreateSecret | + CreatePrivilege | SetDefaultQuotaLimits | SetAuditRequirements | + AuditLogAdmin | ServerAdmin, + GenericExecute = StandardRights.Execute | ViewLocalInformation | LookupNames + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/LsaSecretAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/LsaSecretAccess.cs new file mode 100644 index 000000000..d065b3cc5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/LsaSecretAccess.cs @@ -0,0 +1,15 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum LsaSecretAccess : uint + { + SetValue = 0x00000001, + QueryValue = 0x00000002, + All = StandardRights.Required | SetValue | QueryValue, + GenericRead = StandardRights.Read | QueryValue, + GenericWrite = StandardRights.Write | SetValue, + GenericExecute = StandardRights.Execute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/LsaTrustedAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/LsaTrustedAccess.cs new file mode 100644 index 000000000..6fc78a1ad --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/LsaTrustedAccess.cs @@ -0,0 +1,22 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum LsaTrustedAccess : uint + { + QueryDomainName = 0x00000001, + QueryControllers = 0x00000002, + SetControllers = 0x00000004, + QueryPosix = 0x00000008, + SetPosix = 0x00000010, + SetAuth = 0x00000020, + QueryAuth = 0x00000040, + All = StandardRights.Required | QueryDomainName | QueryControllers | + SetControllers | QueryPosix | SetPosix | SetAuth | QueryAuth, + GenericRead = StandardRights.Read | QueryDomainName, + GenericWrite = StandardRights.Write | SetControllers | SetPosix | + SetAuth, + GenericExecute = StandardRights.Execute | QueryControllers | QueryPosix + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/MutantAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/MutantAccess.cs new file mode 100644 index 000000000..07c1449e6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/MutantAccess.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum MutantAccess : uint + { + QueryState = 0x1, + All = StandardRights.Required | StandardRights.Synchronize | + QueryState + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ObjectTypeAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ObjectTypeAccess.cs new file mode 100644 index 000000000..34bb9bdf7 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ObjectTypeAccess.cs @@ -0,0 +1,11 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ObjectTypeAccess : uint + { + Create = 0x1, + All = StandardRights.Required | Create + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/PortAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/PortAccess.cs new file mode 100644 index 000000000..8c4b31e90 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/PortAccess.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum PortAccess : uint + { + Connect = 0x1, + All = StandardRights.Required | StandardRights.Synchronize | + Connect + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/Privilege.cs b/branches/ph-plugins/ProcessHacker.Native/Security/Privilege.cs new file mode 100644 index 000000000..3b15299b3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/Privilege.cs @@ -0,0 +1,274 @@ +/* + * Process Hacker - + * privilege + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Text; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Security +{ + /// + /// Represents a Windows security privilege. + /// + public sealed class Privilege : BaseObject + { + public static Privilege Enable(string name) + { + using (var thandle = ProcessHandle.Current.GetToken(TokenAccess.AdjustPrivileges)) + { + var privilege = new Privilege(thandle, name); + + privilege.Enable(); + + return privilege; + } + } + + private TokenHandle _tokenHandle; + private Luid _luid; + private SePrivilegeAttributes _attributes; + private string _name; + private string _displayName; + + public Privilege(string name) + : this(null, name) + { } + + public Privilege(TokenHandle tokenHandle, string name) + : this(tokenHandle, name, 0) + { } + + public Privilege(TokenHandle tokenHandle, string name, SePrivilegeAttributes attributes) + : this(tokenHandle, name, false, Luid.Empty, attributes) + { } + + public Privilege(Luid luid) + : this(null, luid) + { } + + public Privilege(LuidAndAttributes laa) + : this(null, laa.Luid, laa.Attributes) + { } + + public Privilege(TokenHandle tokenHandle, Luid luid) + : this(tokenHandle, luid, 0) + { } + + public Privilege(Luid luid, SePrivilegeAttributes attributes) + : this(null, luid, attributes) + { } + + public Privilege(TokenHandle tokenHandle, Luid luid, SePrivilegeAttributes attributes) + : this(tokenHandle, null, true, luid, attributes) + { } + + private Privilege(TokenHandle tokenHandle, string name, bool hasLuid, Luid luid, SePrivilegeAttributes attributes) + : base(tokenHandle != null) + { + _tokenHandle = tokenHandle; + + if (_tokenHandle != null) + _tokenHandle.Reference(); + + _name = name; + _attributes = attributes; + + if (!hasLuid) + { + if (_name == null) + throw new ArgumentException("You must specify either a LUID or a name."); + + _luid = LsaPolicyHandle.LookupPolicyHandle.LookupPrivilegeValue(_name); + } + else + { + _luid = luid; + } + } + + protected override void DisposeObject(bool disposing) + { + if (_tokenHandle != null) + _tokenHandle.Dereference(disposing); + } + + public SePrivilegeAttributes Attributes + { + get { return _attributes; } + } + + public bool Disabled + { + get + { + return (_attributes & SePrivilegeAttributes.Disabled) + != SePrivilegeAttributes.Disabled; + } + set + { + _attributes = SePrivilegeAttributes.Disabled; + } + } + + public string DisplayName + { + get + { + if (_displayName == null) + { + _displayName = LsaPolicyHandle.LookupPolicyHandle.LookupPrivilegeDisplayName(this.Name); + } + + return _displayName; + } + } + + public bool Enabled + { + get + { + return ((_attributes & SePrivilegeAttributes.Enabled) + == SePrivilegeAttributes.Enabled) || this.EnabledByDefault && !this.Disabled; + } + set + { + _attributes = SePrivilegeAttributes.Enabled; + } + } + + public bool EnabledByDefault + { + get + { + return ((_attributes & SePrivilegeAttributes.EnabledByDefault) == + SePrivilegeAttributes.EnabledByDefault) && !this.Disabled; + } + set + { + _attributes = SePrivilegeAttributes.EnabledByDefault; + } + } + + public Luid Luid + { + get { return _luid; } + } + + public string Name + { + get + { + if (_name == null) + { + _name = LsaPolicyHandle.LookupPolicyHandle.LookupPrivilegeName(_luid); + } + + return _name; + } + } + + public bool Removed + { + get + { + return (_attributes & SePrivilegeAttributes.Removed) == + SePrivilegeAttributes.Removed; + } + set + { + _attributes = SePrivilegeAttributes.Removed; + } + } + + public bool UsedForAccess + { + get + { + return (_attributes & SePrivilegeAttributes.UsedForAccess) + == SePrivilegeAttributes.UsedForAccess; + } + set + { + if (value) + _attributes |= SePrivilegeAttributes.UsedForAccess; + else + _attributes &= ~SePrivilegeAttributes.UsedForAccess; + } + } + + public void Disable() + { + if (_tokenHandle == null) + throw new InvalidOperationException( + "Cannot disable the privilege because there is no token associated with the instance."); + this.Disable(_tokenHandle); + } + + public void Disable(TokenHandle tokenHandle) + { + this.SetState(tokenHandle, SePrivilegeAttributes.Disabled); + } + + public void Enable() + { + if (_tokenHandle == null) + throw new InvalidOperationException( + "Cannot enable the privilege because there is no token associated with the instance."); + this.Enable(_tokenHandle); + } + + public void Enable(TokenHandle tokenHandle) + { + this.SetState(tokenHandle, SePrivilegeAttributes.Enabled); + } + + public void Remove() + { + if (_tokenHandle == null) + throw new InvalidOperationException( + "Cannot remove the privilege because there is no token associated with the instance."); + this.Remove(_tokenHandle); + } + + public void Remove(TokenHandle tokenHandle) + { + this.SetState(tokenHandle, SePrivilegeAttributes.Removed); + } + + private void SetState(TokenHandle tokenHandle, SePrivilegeAttributes attributes) + { + _attributes = attributes; + _tokenHandle.SetPrivilege(_luid, _attributes); + } + + public LuidAndAttributes ToLuidAndAttributes() + { + return new LuidAndAttributes() + { + Attributes = _attributes, + Luid = _luid + }; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/PrivilegeSet.cs b/branches/ph-plugins/ProcessHacker.Native/Security/PrivilegeSet.cs new file mode 100644 index 000000000..01c18e857 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/PrivilegeSet.cs @@ -0,0 +1,188 @@ +/* + * Process Hacker - + * privilege set + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Security +{ + public sealed class PrivilegeSet : IList + { + private static int _sizeOfLaa = Marshal.SizeOf(typeof(LuidAndAttributes)); + + private List _privileges; + private PrivilegeSetFlags _flags; + + public PrivilegeSet() + : this(null) + { } + + public PrivilegeSet(IEnumerable privileges) + : this(privileges, PrivilegeSetFlags.AllNecessary) + { } + + public PrivilegeSet(IEnumerable privileges, PrivilegeSetFlags flags) + { + if (privileges != null) + _privileges = new List(privileges); + else + _privileges = new List(); + + _flags = flags; + } + + public PrivilegeSet(IntPtr memory) + { + MemoryRegion memoryRegion = new MemoryRegion(memory); + PrivilegeSetStruct privilegeSet = memoryRegion.ReadStruct(); + + _flags = privilegeSet.Flags; + + _privileges = new List(privilegeSet.Count); + + for (int i = 0; i < privilegeSet.Count; i++) + { + _privileges.Add(new Privilege(memoryRegion.ReadStruct(PrivilegeSetStruct.PrivilegesOffset, i))); + } + } + + public PrivilegeSetFlags Flags + { + get { return _flags; } + set { _flags = value; } + } + + public MemoryAlloc ToMemory() + { + int requiredSize = 8 + _sizeOfLaa * _privileges.Count; + MemoryAlloc memory = new MemoryAlloc(requiredSize); + + memory.WriteInt32(0, _privileges.Count); + memory.WriteInt32(4, (int)_flags); + + for (int i = 0; i < _privileges.Count; i++) + memory.WriteStruct(8, i, _privileges[i].ToLuidAndAttributes()); + + return memory; + } + + public TokenPrivileges ToTokenPrivileges() + { + return new TokenPrivileges() + { + PrivilegeCount = _privileges.Count, + Privileges = _privileges.ConvertAll( + (privilege) => privilege.ToLuidAndAttributes()).ToArray() + }; + } + + #region IList Members + + public int IndexOf(Privilege item) + { + return _privileges.IndexOf(item); + } + + public void Insert(int index, Privilege item) + { + _privileges.Insert(index, item); + } + + public void RemoveAt(int index) + { + _privileges.RemoveAt(index); + } + + public Privilege this[int index] + { + get + { + return _privileges[index]; + } + set + { + _privileges[index] = value; + } + } + + #endregion + + #region ICollection Members + + public void Add(Privilege item) + { + _privileges.Add(item); + } + + public void Clear() + { + _privileges.Clear(); + } + + public bool Contains(Privilege item) + { + return _privileges.Contains(item); + } + + public void CopyTo(Privilege[] array, int arrayIndex) + { + _privileges.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return _privileges.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(Privilege item) + { + return _privileges.Remove(item); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return _privileges.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _privileges.GetEnumerator(); + } + + #endregion + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ProcessAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ProcessAccess.cs new file mode 100644 index 000000000..1cd99f35e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ProcessAccess.cs @@ -0,0 +1,25 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ProcessAccess : uint + { + Terminate = 0x0001, + CreateThread = 0x0002, + SetSessionId = 0x0004, + VmOperation = 0x0008, + VmRead = 0x0010, + VmWrite = 0x0020, + DupHandle = 0x0040, + CreateProcess = 0x0080, + SetQuota = 0x0100, + SetInformation = 0x0200, + QueryInformation = 0x0400, + SetPort = 0x0800, + SuspendResume = 0x0800, + QueryLimitedInformation = 0x1000, + // should be 0x1fff on Vista, but is 0xfff for backwards compatibility + All = StandardRights.Required | StandardRights.Synchronize | 0xfff + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ProfileAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ProfileAccess.cs new file mode 100644 index 000000000..58b06da8d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ProfileAccess.cs @@ -0,0 +1,11 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ProfileAccess : uint + { + Control = 0x1, + All = StandardRights.Required | Control + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ResourceManagerAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ResourceManagerAccess.cs new file mode 100644 index 000000000..9f4408446 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ResourceManagerAccess.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ResourceManagerAccess : uint + { + QueryInformation = 0x0001, + SetInformation = 0x0002, + Recover = 0x0004, + Enlist = 0x0008, + GetNotification = 0x0010, + RegisterProtocol = 0x0020, + CompletePropagation = 0x0040, + GenericRead = StandardRights.Read | QueryInformation | StandardRights.Synchronize, + GenericWrite = StandardRights.Write | SetInformation | Recover | Enlist | + GetNotification | RegisterProtocol | CompletePropagation | StandardRights.Synchronize, + GenericExecute = StandardRights.Execute | Recover | Enlist | GetNotification | + CompletePropagation | StandardRights.Synchronize, + All = StandardRights.Required | GenericRead | GenericWrite | GenericExecute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ScManagerAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ScManagerAccess.cs new file mode 100644 index 000000000..a847bf6ee --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ScManagerAccess.cs @@ -0,0 +1,17 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ScManagerAccess : uint + { + Connect = 0x0001, + CreateService = 0x0002, + EnumerateService = 0x0004, + Lock = 0x0008, + QueryLockStatus = 0x0010, + ModifyBootConfig = 0x0020, + All = StandardRights.Required | Connect | CreateService | EnumerateService | + Lock | QueryLockStatus | ModifyBootConfig + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/SectionAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/SectionAccess.cs new file mode 100644 index 000000000..8d303a7e6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/SectionAccess.cs @@ -0,0 +1,16 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum SectionAccess : uint + { + Query = 0x0001, + MapWrite = 0x0002, + MapRead = 0x0004, + MapExecute = 0x0008, + ExtendSize = 0x0010, + MapExecuteExplicit = 0x0020, + All = StandardRights.Required | Query | MapWrite | MapRead | MapExecute | ExtendSize + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/SemaphoreAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/SemaphoreAccess.cs new file mode 100644 index 000000000..9781a8b40 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/SemaphoreAccess.cs @@ -0,0 +1,13 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum SemaphoreAccess : uint + { + QueryState = 0x1, + ModifyState = 0x2, + All = StandardRights.Required | StandardRights.Synchronize | + QueryState | ModifyState + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ServiceAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ServiceAccess.cs new file mode 100644 index 000000000..1bcb4045d --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ServiceAccess.cs @@ -0,0 +1,20 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ServiceAccess : uint + { + QueryConfig = 0x0001, + ChangeConfig = 0x0002, + QueryStatus = 0x0004, + EnumerateDependents = 0x0008, + Start = 0x0010, + Stop = 0x0020, + PauseContinue = 0x0040, + Interrogate = 0x0080, + UserDefinedControl = 0x0100, + All = StandardRights.Required | QueryConfig | ChangeConfig | QueryStatus | + EnumerateDependents | Start | Stop | PauseContinue | Interrogate | UserDefinedControl + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/Sid.cs b/branches/ph-plugins/ProcessHacker.Native/Security/Sid.cs new file mode 100644 index 000000000..0567a1fd3 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/Sid.cs @@ -0,0 +1,435 @@ +/* + * Process Hacker - + * security identifier + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Text; +using ProcessHacker.Common; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Security +{ + /// + /// Represents a Windows security identifier (SID). + /// + public sealed class Sid : BaseObject, IEquatable + { + private static readonly byte[] _nullSidAuthority = { 0, 0, 0, 0, 0, 0 }; + private static readonly byte[] _worldSidAuthority = { 0, 0, 0, 0, 0, 1 }; + private static readonly byte[] _localSidAuthority = { 0, 0, 0, 0, 0, 2 }; + private static readonly byte[] _creatorSidAuthority = { 0, 0, 0, 0, 0, 3 }; + private static readonly byte[] _nonUniqueAuthority = { 0, 0, 0, 0, 0, 4 }; + private static readonly byte[] _ntAuthority = { 0, 0, 0, 0, 0, 5 }; + private static readonly byte[] _resourceManagerAuthority = { 0, 0, 0, 0, 0, 9 }; + + public static Sid FromName(string name) + { + return LsaPolicyHandle.LookupPolicyHandle.LookupSid(name); + } + + public static Sid FromPointer(IntPtr sid) + { + return new Sid(new MemoryRegion(sid), false); + } + + public static Sid GetWellKnownSid(WellKnownSidType sidType) + { + using (MemoryAlloc memory = new MemoryAlloc(Win32.SecurityMaxSidSize)) + { + int memorySize = memory.Size; + + if (!Win32.CreateWellKnownSid(sidType, IntPtr.Zero, memory, ref memorySize)) + Win32.ThrowLastError(); + + return new Sid(memory); + } + } + + public static byte[] GetWellKnownSidIdentifierAuthority(WellKnownSidIdentifierAuthority sidAuthority) + { + return GetWellKnownSidIdentifierAuthority(sidAuthority, true); + } + + private static byte[] GetWellKnownSidIdentifierAuthority(WellKnownSidIdentifierAuthority sidAuthority, bool copy) + { + byte[] array; + + switch (sidAuthority) + { + case WellKnownSidIdentifierAuthority.Null: + array = _nullSidAuthority; + break; + case WellKnownSidIdentifierAuthority.World: + array = _worldSidAuthority; + break; + case WellKnownSidIdentifierAuthority.Local: + array = _localSidAuthority; + break; + case WellKnownSidIdentifierAuthority.Creator: + array = _creatorSidAuthority; + break; + case WellKnownSidIdentifierAuthority.NonUnique: + array = _nonUniqueAuthority; + break; + case WellKnownSidIdentifierAuthority.NtAuthority: + array = _ntAuthority; + break; + case WellKnownSidIdentifierAuthority.ResourceManager: + array = _resourceManagerAuthority; + break; + default: + throw new ArgumentException("sidAuthority"); + } + + if (copy) + return array.Duplicate(); + else + return array; + } + + public static implicit operator IntPtr(Sid sid) + { + return sid.Memory; + } + + private MemoryRegion _memory; + private string _systemName; + private bool _hasAttributes; + private SidAttributes _attributes; + + private string _stringSid; + private string _domain; + private string _name; + private SidNameUse _nameUse = 0; + + private Sid(MemoryRegion sid, bool owned) + : base(owned) + { + _memory = sid; + } + + /// + /// Creates a SID from a string representation. + /// + /// The SID string. + public Sid(string stringSid) + : this(stringSid, null) + { } + + /// + /// Creates a SID from a string representation. + /// + /// The SID string. + /// The name of the system on which the SID is located. + public Sid(string stringSid, string systemName) + { + IntPtr sidMemory; + + if (!Win32.ConvertStringSidToSid(stringSid, out sidMemory)) + Win32.ThrowLastError(); + + _memory = new LocalMemoryAlloc(sidMemory, true); + _hasAttributes = false; + } + + /// + /// Copies the specified SID. + /// + /// A pointer to an existing SID. + public Sid(IntPtr sid) + : this(sid, null) + { } + + /// + /// Copies the specified SID. + /// + /// A pointer to an existing SID. + /// The name of the system on which the SID is located. + public Sid(IntPtr sid, string systemName) + : this(sid, false, 0, systemName) + { } + + /// + /// Copies the specified SID. + /// + /// A SID_AND_ATTRIBUTES structure. + public Sid(SidAndAttributes saa) + : this(saa.Sid, saa.Attributes) + { } + + /// + /// Copies the specified SID. + /// + /// A pointer to an existing SID. + /// The attributes associated with the SID. + public Sid(IntPtr sid, SidAttributes attributes) + : this(sid, attributes, null) + { } + + /// + /// Copies the specified SID. + /// + /// A pointer to an existing SID. + /// The attributes associated with the SID. + /// The name of the system on which the SID is located. + public Sid(IntPtr sid, SidAttributes attributes, string systemName) + : this(sid, true, attributes, systemName) + { } + + private Sid(IntPtr sid, bool hasAttributes, SidAttributes attributes, string systemName) + { + NtStatus status; + + _memory = new MemoryAlloc(Win32.RtlLengthSid(sid)); + + if ((status = Win32.RtlCopySid(_memory.Size, _memory, sid)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + _hasAttributes = hasAttributes; + _attributes = attributes; + _systemName = systemName; + } + + protected override void DisposeObject(bool disposing) + { + _memory.Dispose(disposing); + } + + public SidAttributes Attributes + { + get { return _attributes; } + } + + public string DomainName + { + get + { + if (_domain == null) + this.GetNameAndUse(out _domain, out _name, out _nameUse); + return _domain; + } + } + + public byte[] IdentifierAuthority + { + get + { + unsafe + { + return Utils.Create((*Win32.RtlIdentifierAuthoritySid(this)).Value, 6); + } + } + } + + public bool HasAttributes + { + get { return _hasAttributes; } + } + + public int Length + { + get { return Win32.RtlLengthSid(this); } + } + + public IntPtr Memory + { + get { return _memory; } + } + + public SidNameUse NameUse + { + get + { + if (_nameUse == 0) + this.GetNameAndUse(out _domain, out _name, out _nameUse); + return _nameUse; + } + } + + public string UserName + { + get + { + if (_name == null) + this.GetNameAndUse(out _domain, out _name, out _nameUse); + return _name; + } + } + + public int[] SubAuthorities + { + get + { + unsafe + { + byte count = *Win32.RtlSubAuthorityCountSid(this); + int[] subAuthorities = new int[count]; + + for (int i = 0; i < count; i++) + subAuthorities[i] = *Win32.RtlSubAuthoritySid(this, i); + + return subAuthorities; + } + } + } + + public string StringSid + { + get + { + if (_stringSid == null) + _stringSid = this.GetString(); + return _stringSid; + } + } + + public string SystemName + { + get { return _systemName; } + } + + public Sid Clone() + { + return new Sid(this); + } + + public bool DomainEquals(Sid obj) + { + bool equal; + + if (!Win32.EqualDomainSid(this, obj, out equal)) + Win32.ThrowLastError(); + + return equal; + } + + public bool Equals(Sid obj) + { + return Win32.RtlEqualSid(this, obj); + } + + public string GetFullName(bool includeDomain) + { + try + { + if (string.IsNullOrEmpty(this.UserName)) + return this.StringSid; + if (includeDomain) + return this.DomainName + "\\" + this.UserName; + else + return this.UserName; + } + catch + { + return this.StringSid; + } + } + + public override int GetHashCode() + { + int hashCode = 0x12345678; + byte[] identifierAuthority = this.IdentifierAuthority; + int[] subAuthorities = this.SubAuthorities; + + for (int i = 0; i < subAuthorities.Length; i++) + { + hashCode ^= identifierAuthority[(uint)hashCode % identifierAuthority.Length]; + // Reverse and XOR. + hashCode ^= (hashCode >> 24) | ((hashCode >> 16) << 8) | ((hashCode >> 24) << 16) | (hashCode << 24); + hashCode ^= subAuthorities[(uint)hashCode % subAuthorities.Length]; + } + + return hashCode; + } + + private void GetNameAndUse(out string domain, out string name, out SidNameUse nameUse) + { + name = LsaPolicyHandle.LookupPolicyHandle.LookupName(this, out nameUse, out domain); + } + + public WellKnownSidIdentifierAuthority GetWellKnownIdentifierAuthority() + { + byte[] identifierAuthority = this.IdentifierAuthority; + + foreach (WellKnownSidIdentifierAuthority value in + Enum.GetValues(typeof(WellKnownSidIdentifierAuthority))) + { + if (value == WellKnownSidIdentifierAuthority.None) + continue; + + if (Utils.Equals(identifierAuthority, GetWellKnownSidIdentifierAuthority(value, false))) + return value; + } + + return WellKnownSidIdentifierAuthority.None; + } + + private string GetString() + { + NtStatus status; + UnicodeString str = new UnicodeString(); + + if ((status = Win32.RtlConvertSidToUnicodeString(ref str, this, true)) >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (str) + return str.Read(); + } + + public bool IsValid() + { + return Win32.RtlValidSid(this); + } + + public bool PrefixEquals(Sid obj) + { + return Win32.RtlEqualPrefixSid(this, obj); + } + + public SidAndAttributes ToSidAndAttributes() + { + return new SidAndAttributes() + { + Attributes = _attributes, + Sid = this + }; + } + + public override string ToString() + { + return this.StringSid; + } + } + + public enum WellKnownSidIdentifierAuthority + { + None = 0, + Null, + World, + Local, + Creator, + NonUnique, + NtAuthority, + ResourceManager + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/StandardRights.cs b/branches/ph-plugins/ProcessHacker.Native/Security/StandardRights.cs new file mode 100644 index 000000000..4c878cd26 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/StandardRights.cs @@ -0,0 +1,27 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum StandardRights : uint + { + Delete = 0x00010000, + ReadControl = 0x00020000, + WriteDac = 0x00040000, + WriteOwner = 0x00080000, + Synchronize = 0x00100000, + Required = 0x000f0000, + Read = ReadControl, + Write = ReadControl, + Execute = ReadControl, + All = 0x001f0000, + + SpecificRightsAll = 0x0000ffff, + AccessSystemSecurity = 0x01000000, + MaximumAllowed = 0x02000000, + GenericRead = 0x80000000, + GenericWrite = 0x40000000, + GenericExecute = 0x20000000, + GenericAll = 0x10000000 + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/SymbolicLinkAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/SymbolicLinkAccess.cs new file mode 100644 index 000000000..e5ab7d6cf --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/SymbolicLinkAccess.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum SymbolicLinkAccess : uint + { + Query = 0x1, + All = StandardRights.Required | Query + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/ThreadAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/ThreadAccess.cs new file mode 100644 index 000000000..901dbc8d4 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/ThreadAccess.cs @@ -0,0 +1,23 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum ThreadAccess : uint + { + Terminate = 0x0001, + SuspendResume = 0x0002, + Alert = 0x0004, + GetContext = 0x0008, + SetContext = 0x0010, + SetInformation = 0x0020, + QueryInformation = 0x0040, + SetThreadToken = 0x0080, + Impersonate = 0x0100, + DirectImpersonation = 0x0200, + SetLimitedInformation = 0x0400, + QueryLimitedInformation = 0x0800, + // should be 0xfff on Vista, but is 0x3ff for backwards compatibility + All = StandardRights.Required | StandardRights.Synchronize | 0x3ff + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/TimerAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/TimerAccess.cs new file mode 100644 index 000000000..9e2a2d9cc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/TimerAccess.cs @@ -0,0 +1,13 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum TimerAccess : uint + { + QueryState = 0x1, + ModifyState = 0x2, + All = StandardRights.Required | StandardRights.Synchronize | + QueryState | ModifyState + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/TmAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/TmAccess.cs new file mode 100644 index 000000000..7794c5ecc --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/TmAccess.cs @@ -0,0 +1,21 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum TmAccess : uint + { + QueryInformation = 0x0001, + SetInformation = 0x0002, + Recover = 0x0004, + Rename = 0x0008, + CreateRm = 0x0010, + // About to be deprecated - for DTC use only. + BindTransaction = 0x0020, + GenericRead = StandardRights.Read | QueryInformation, + GenericWrite = StandardRights.Write | SetInformation | Recover | Rename | CreateRm, + GenericExecute = StandardRights.Execute, + All = StandardRights.Required | GenericRead | GenericWrite | + GenericExecute | BindTransaction, + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/TokenAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/TokenAccess.cs new file mode 100644 index 000000000..fd6876858 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/TokenAccess.cs @@ -0,0 +1,24 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum TokenAccess : uint + { + AssignPrimary = 0x0001, + Duplicate = 0x0002, + Impersonate = 0x0004, + Query = 0x0008, + QuerySource = 0x0010, + AdjustPrivileges = 0x0020, + AdjustGroups = 0x0040, + AdjustDefault = 0x0080, + AdjustSessionId = 0x0100, + All = StandardRights.Required | AssignPrimary | Duplicate | Impersonate | + Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | + AdjustSessionId, + GenericRead = StandardRights.Read | Query, + GenericWrite = StandardRights.Write | AdjustPrivileges | AdjustGroups | AdjustDefault, + GenericExecute = StandardRights.Execute + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/TransactionAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/TransactionAccess.cs new file mode 100644 index 000000000..870a38b8c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/TransactionAccess.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum TransactionAccess : uint + { + QueryInformation = 0x0001, + SetInformation = 0x0002, + Enlist = 0x0004, + Commit = 0x0008, + Rollback = 0x0010, + Propagate = 0x0020, + RightReserved1 = 0x0040, + GenericRead = StandardRights.Read | QueryInformation | StandardRights.Synchronize, + GenericWrite = StandardRights.Write | SetInformation | Commit | Enlist | Rollback | + Propagate | StandardRights.Synchronize, + GenericExecute = StandardRights.Execute | Commit | Rollback | StandardRights.Synchronize, + All = StandardRights.Required | GenericRead | GenericWrite | GenericExecute, + + ResourceManagerRights = GenericRead | StandardRights.Write | SetInformation | + Enlist | Rollback | Propagate | StandardRights.Synchronize + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Security/WindowStationAccess.cs b/branches/ph-plugins/ProcessHacker.Native/Security/WindowStationAccess.cs new file mode 100644 index 000000000..a94b8355f --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Security/WindowStationAccess.cs @@ -0,0 +1,27 @@ +using System; + +namespace ProcessHacker.Native.Security +{ + [Flags] + public enum WindowStationAccess : uint + { + EnumDesktops = 0x0001, + ReadAttributes = 0x0002, + AccessClipboard = 0x0004, + CreateDesktop = 0x0008, + WriteAttributes = 0x0010, + AccessGlobalAtoms = 0x0020, + ExitWindows = 0x0040, + Enumerate = 0x0100, + ReadScreen = 0x0200, + All = StandardRights.Required | AccessClipboard | + AccessGlobalAtoms | CreateDesktop | EnumDesktops | Enumerate | + ExitWindows | ReadAttributes | ReadScreen | WriteAttributes, + GenericRead = StandardRights.Read | EnumDesktops | Enumerate | + ReadAttributes | ReadScreen, + GenericWrite = StandardRights.Write | AccessClipboard | + CreateDesktop | WriteAttributes, + GenericExecute = StandardRights.Execute | AccessGlobalAtoms | + ExitWindows + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/FilterType.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/FilterType.cs new file mode 100644 index 000000000..111c53720 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/FilterType.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.SsLogging +{ + public enum FilterType + { + Include, + Exclude + } + + public static class FilterTypeExtensions + { + public static KphSsFilterType ToKphSs(this FilterType filterType) + { + if (filterType == FilterType.Include) + return KphSsFilterType.Include; + else + return KphSsFilterType.Exclude; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsClientId.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsClientId.cs new file mode 100644 index 000000000..7563d7766 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsClientId.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.SsLogging +{ + public class SsClientId : SsData + { + public SsClientId(MemoryRegion data) + { + this.Original = data.ReadStruct(); + } + + public ClientId Original + { + get; + internal set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsData.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsData.cs new file mode 100644 index 000000000..2e2b40531 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsData.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.SsLogging +{ + public class SsData + { + public int Index + { + get; + internal set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsEvent.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsEvent.cs new file mode 100644 index 000000000..ec697de49 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsEvent.cs @@ -0,0 +1,62 @@ +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.SsLogging +{ + public sealed class SsEvent + { + public int[] Arguments + { + get; + internal set; + } + + public bool ArgumentsCopyFailed + { + get; + internal set; + } + + public bool ArgumentsProbeFailed + { + get; + internal set; + } + + public int CallNumber + { + get; + internal set; + } + + public KProcessorMode Mode + { + get; + internal set; + } + + public int ProcessId + { + get; + internal set; + } + + public IntPtr[] StackTrace + { + get; + internal set; + } + + public int ThreadId + { + get; + internal set; + } + + public DateTime Time + { + get; + internal set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsHandle.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsHandle.cs new file mode 100644 index 000000000..d964e09e4 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsHandle.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.SsLogging +{ + public sealed class SsHandle : SsData + { + internal SsHandle(MemoryRegion data) + { + KphSsHandle handleInfo = data.ReadStruct(); + + if (handleInfo.TypeNameOffset != 0) + { + this.TypeName = SsLogger.ReadWString(new MemoryRegion(data, handleInfo.TypeNameOffset)); + } + + if (handleInfo.NameOffset != 0) + { + this.Name = SsLogger.ReadWString(new MemoryRegion(data, handleInfo.NameOffset)); + } + + this.ProcessId = handleInfo.ClientId.ProcessId; + this.ThreadId = handleInfo.ClientId.ThreadId; + } + + public string Name + { + get; + private set; + } + + public int ProcessId + { + get; + private set; + } + + public int ThreadId + { + get; + private set; + } + + public string TypeName + { + get; + private set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsLogger.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsLogger.cs new file mode 100644 index 000000000..75f67b471 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsLogger.cs @@ -0,0 +1,341 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; +using System.Threading; +using ProcessHacker.Native.Threading; +using System.Runtime.InteropServices; + +namespace ProcessHacker.Native.SsLogging +{ + public delegate void ArgumentBlockReceivedDelegate(SsData argBlock); + public delegate void EventBlockReceivedDelegate(SsEvent eventBlock); + + public sealed class SsLogger + { + private const int _highBlockSize = 0x200; + + internal static string ReadWString(MemoryRegion data) + { + KphSsWString wString = data.ReadStruct(); + + return data.ReadUnicodeString(KphSsWString.BufferOffset, wString.Length / 2); + } + + public event ArgumentBlockReceivedDelegate ArgumentBlockReceived; + public event EventBlockReceivedDelegate EventBlockReceived; + + private bool _started = false; + private object _startLock = new object(); + + private bool _terminating = false; + private Thread _bufferWorkerThread; + private ThreadHandle _bufferWorkerThreadHandle; + private Event _bufferWorkerThreadReadyEvent = new Event(true, false); + + private VirtualMemoryAlloc _buffer; + private SemaphoreHandle _readSemaphore; + private SemaphoreHandle _writeSemaphore; + private KphSsClientEntryHandle _clientEntryHandle; + private KphSsRuleSetEntryHandle _ruleSetEntryHandle; + + public SsLogger(int bufferedBlockCount, bool includeAll) + { + // Allocate a buffer. + _buffer = new VirtualMemoryAlloc(_highBlockSize * bufferedBlockCount); + + // Create the read and write semaphores. + + // Read semaphore: no blocks initially, so 0 initial count. + _readSemaphore = SemaphoreHandle.Create(SemaphoreAccess.All, 0, bufferedBlockCount); + // Write semaphore: all buffer blocks available so max. initial count. + _writeSemaphore = SemaphoreHandle.Create(SemaphoreAccess.All, bufferedBlockCount, bufferedBlockCount); + + // Create the client entry. + _clientEntryHandle = KProcessHacker.Instance.SsCreateClientEntry( + ProcessHandle.Current, + _readSemaphore, + _writeSemaphore, + _buffer, + _buffer.Size + ); + + // Create the ruleset entry. + _ruleSetEntryHandle = KProcessHacker.Instance.SsCreateRuleSetEntry( + _clientEntryHandle, + includeAll ? KphSsFilterType.Include : KphSsFilterType.Exclude, + KphSsRuleSetAction.Log + ); + } + + public IntPtr AddNumberRule(FilterType filterType, int number) + { + return KProcessHacker.Instance.SsAddNumberRule( + _ruleSetEntryHandle, + filterType.ToKphSs(), + number + ); + } + + public IntPtr AddPreviousModeRule(FilterType filterType, KProcessorMode previousMode) + { + return KProcessHacker.Instance.SsAddPreviousModeRule( + _ruleSetEntryHandle, + filterType.ToKphSs(), + previousMode + ); + } + + public IntPtr AddProcessIdRule(FilterType filterType, int pid) + { + return KProcessHacker.Instance.SsAddProcessIdRule( + _ruleSetEntryHandle, + filterType.ToKphSs(), + pid.ToIntPtr() + ); + } + + public IntPtr AddThreadIdRule(FilterType filterType, int tid) + { + return KProcessHacker.Instance.SsAddProcessIdRule( + _ruleSetEntryHandle, + filterType.ToKphSs(), + tid.ToIntPtr() + ); + } + + private void BufferWorkerThreadStart() + { + int cursor = 0; + + // Open a handle to the current thread so other functions + // can alert us. + _bufferWorkerThreadHandle = ThreadHandle.OpenCurrent(ThreadAccess.All); + + // We're ready. + _bufferWorkerThreadReadyEvent.Set(); + + while (!_terminating) + { + NtStatus status; + KphSsBlockHeader blockHeader; + + // Wait for a block to read (enable alerting so we can + // be interrupted if someone wants us to stop). + status = _readSemaphore.Wait(true); + + // Did we get alerted? + if (status == NtStatus.Alerted) + return; + + // Check if we have an implicit cursor reset. + if (_buffer.Size - cursor < Marshal.SizeOf(typeof(KphSsBlockHeader))) + cursor = 0; + + // Read the block header. + blockHeader = _buffer.ReadStruct(cursor, 0); + + // Check if we have an explicit cursor reset. + if (blockHeader.Type == KphSsBlockType.Reset) + { + cursor = 0; + blockHeader = _buffer.ReadStruct(cursor, 0); + } + + // Process the block. + if (blockHeader.Type == KphSsBlockType.Event) + { + var eventBlock = _buffer.ReadStruct(cursor, 0); + int[] arguments; + IntPtr[] stackTrace; + + // Reconstruct the argument and stack trace arrays. + + arguments = new int[eventBlock.NumberOfArguments]; + stackTrace = new IntPtr[eventBlock.TraceCount]; + + for (int i = 0; i < arguments.Length; i++) + arguments[i] = _buffer.ReadInt32(cursor + eventBlock.ArgumentsOffset, i); + for (int i = 0; i < stackTrace.Length; i++) + stackTrace[i] = _buffer.ReadIntPtr(cursor + eventBlock.TraceOffset, i); + + // Create an event object. + SsEvent ssEvent = new SsEvent(); + + // Basic information + ssEvent.Time = DateTime.FromFileTime(eventBlock.Time); + ssEvent.ThreadId = eventBlock.ClientId.ThreadId; + ssEvent.ProcessId = eventBlock.ClientId.ProcessId; + ssEvent.Arguments = arguments; + ssEvent.StackTrace = stackTrace; + + // Flags + ssEvent.ArgumentsCopyFailed = + (eventBlock.Flags & KphSsEventFlags.CopyArgumentsFailed) == KphSsEventFlags.CopyArgumentsFailed; + ssEvent.ArgumentsProbeFailed = + (eventBlock.Flags & KphSsEventFlags.ProbeArgumentsFailed) == KphSsEventFlags.ProbeArgumentsFailed; + ssEvent.CallNumber = eventBlock.Number; + + if ((eventBlock.Flags & KphSsEventFlags.UserMode) == KphSsEventFlags.UserMode) + ssEvent.Mode = KProcessorMode.UserMode; + else + ssEvent.Mode = KProcessorMode.KernelMode; + + // Raise the event. + if (this.EventBlockReceived != null) + this.EventBlockReceived(ssEvent); + } + else if (blockHeader.Type == KphSsBlockType.Argument) + { + var argBlock = _buffer.ReadStruct(cursor, 0); + MemoryRegion dataRegion; + SsData ssArg = null; + + dataRegion = new MemoryRegion(_buffer, cursor + KphSsArgumentBlock.DataOffset); + + // Process the argument block based on its type. + switch (argBlock.Type) + { + case KphSsArgumentType.Int8: + { + SsSimple simpleArg = new SsSimple(); + + simpleArg.Argument = argBlock.Data.Int8; + simpleArg.Type = typeof(Byte); + ssArg = simpleArg; + } + break; + case KphSsArgumentType.Int16: + { + SsSimple simpleArg = new SsSimple(); + + simpleArg.Argument = argBlock.Data.Int16; + simpleArg.Type = typeof(Int16); + ssArg = simpleArg; + } + break; + case KphSsArgumentType.Int32: + { + SsSimple simpleArg = new SsSimple(); + + simpleArg.Argument = argBlock.Data.Int32; + simpleArg.Type = typeof(Int32); + ssArg = simpleArg; + } + break; + case KphSsArgumentType.Int64: + { + SsSimple simpleArg = new SsSimple(); + + simpleArg.Argument = argBlock.Data.Int64; + simpleArg.Type = typeof(Int64); + ssArg = simpleArg; + } + break; + case KphSsArgumentType.Handle: + { + ssArg = new SsHandle(dataRegion); + } + break; + case KphSsArgumentType.UnicodeString: + { + ssArg = new SsUnicodeString(dataRegion); + } + break; + case KphSsArgumentType.ObjectAttributes: + { + ssArg = new SsObjectAttributes(dataRegion); + } + break; + case KphSsArgumentType.ClientId: + { + ssArg = new SsClientId(dataRegion); + } + break; + } + + ssArg.Index = argBlock.Index; + + // Raise the event. + if (ssArg != null) + { + if (this.ArgumentBlockReceived != null) + this.ArgumentBlockReceived(ssArg); + } + } + + // Advance the cursor. + cursor += blockHeader.Size; + // Signal that a buffer block is available for writing. + _writeSemaphore.Release(); + } + } + + public void GetStatistics(out int blocksWritten, out int blocksDropped) + { + KphSsClientInformation info; + int retLength; + + KProcessHacker.Instance.SsQueryClientEntry( + _clientEntryHandle, + out info, + Marshal.SizeOf(typeof(KphSsClientInformation)), + out retLength + ); + + blocksWritten = info.NumberOfBlocksWritten; + blocksDropped = info.NumberOfBlocksDropped; + } + + public void RemoveRule(IntPtr handle) + { + KProcessHacker.Instance.SsRemoveRule(_ruleSetEntryHandle, handle); + } + + public void Start() + { + lock (_startLock) + { + if (!_started) + { + KProcessHacker.Instance.SsRef(); + KProcessHacker.Instance.SsEnableClientEntry(_clientEntryHandle, true); + _started = true; + + _terminating = false; + + // Create the buffer worker thread. + _bufferWorkerThread = new Thread(this.BufferWorkerThreadStart); + _bufferWorkerThread.IsBackground = true; + _bufferWorkerThread.Start(); + // Wait for the thread to initialize. + _bufferWorkerThreadReadyEvent.Wait(); + } + } + } + + public void Stop() + { + lock (_startLock) + { + if (_started) + { + KProcessHacker.Instance.SsEnableClientEntry(_clientEntryHandle, false); + KProcessHacker.Instance.SsUnref(); + _started = false; + + // Tell the worker thread to stop. + _terminating = true; + // Alert it just in case it is waiting. + _bufferWorkerThreadHandle.Alert(); + // Wait for the worker thread to terminate. + _bufferWorkerThreadHandle.Wait(); + // Close the thread handle. + _bufferWorkerThreadHandle.Dispose(); + } + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsObjectAttributes.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsObjectAttributes.cs new file mode 100644 index 000000000..82e4cc591 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsObjectAttributes.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.SsLogging +{ + public class SsObjectAttributes : SsData + { + internal SsObjectAttributes(MemoryRegion data) + { + KphSsObjectAttributes oaInfo = data.ReadStruct(); + + if (oaInfo.ObjectNameOffset != 0) + this.ObjectName = new SsUnicodeString(new MemoryRegion(data, oaInfo.ObjectNameOffset)); + + this.Original = oaInfo.ObjectAttributes; + + if (oaInfo.RootDirectoryOffset != 0) + this.RootDirectory = new SsHandle(new MemoryRegion(data, oaInfo.RootDirectoryOffset)); + } + + public SsUnicodeString ObjectName + { + get; + private set; + } + + public ObjectAttributes Original + { + get; + private set; + } + + public SsHandle RootDirectory + { + get; + private set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsSimple.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsSimple.cs new file mode 100644 index 000000000..6c8d4eaf2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsSimple.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ProcessHacker.Native.SsLogging +{ + public sealed class SsSimple : SsData + { + public object Argument + { + get; + internal set; + } + + public Type Type + { + get; + internal set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsUnicodeString.cs b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsUnicodeString.cs new file mode 100644 index 000000000..a224af7d4 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/SsLogging/SsUnicodeString.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.SsLogging +{ + public sealed class SsUnicodeString : SsData + { + internal SsUnicodeString(MemoryRegion data) + { + KphSsUnicodeString unicodeStringInfo = data.ReadStruct(); + + this.Original = new UnicodeString() + { + Length = unicodeStringInfo.Length, + MaximumLength = unicodeStringInfo.MaximumLength, + Buffer = unicodeStringInfo.Pointer + }; + this.String = data.ReadUnicodeString( + KphSsUnicodeString.BufferOffset, + unicodeStringInfo.Length / 2 + ); + } + + public UnicodeString Original + { + get; + private set; + } + + public string String + { + get; + private set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolInformation.cs b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolInformation.cs new file mode 100644 index 000000000..f6c584178 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolInformation.cs @@ -0,0 +1,79 @@ +/* + * Process Hacker - + * symbol information + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native.Symbols +{ + public sealed class SymbolInformation + { + internal SymbolInformation(IntPtr symbolInfo, int symbolSize) + { + SymbolInfo si = (SymbolInfo)Marshal.PtrToStructure(symbolInfo, typeof(SymbolInfo)); + + this.Flags = si.Flags; + this.Index = si.Index; + this.ModuleBase = si.ModBase; + this.Name = Marshal.PtrToStringAnsi(symbolInfo.Increment(Win32.SymbolInfoNameOffset), si.NameLen); + this.Size = symbolSize; + this.Address = si.Address; + } + + public long Address + { + get; + private set; + } + + public SymbolFlags Flags + { + get; + private set; + } + + public int Index + { + get; + private set; + } + + public ulong ModuleBase + { + get; + private set; + } + + public string Name + { + get; + private set; + } + + public int Size + { + get; + private set; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolProvider.cs b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolProvider.cs new file mode 100644 index 000000000..8003911c5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolProvider.cs @@ -0,0 +1,512 @@ +/* + * Process Hacker - + * dbghelp.dll wrapper code + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using ProcessHacker.Common; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Symbols +{ + public delegate bool SymbolEnumDelegate(SymbolInformation symbolInfo); + + public sealed class SymbolProvider : IDisposable + { + private sealed class SymbolHandle : BaseObject + { + private ProcessHandle _processHandle; + private IntPtr _handle; + + public static implicit operator IntPtr(SymbolHandle symbolHandle) + { + return symbolHandle.Handle; + } + + public SymbolHandle() + { + _handle = new IntPtr(_idGen.Pop()); + + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymInitialize(_handle, null, false)) + Win32.ThrowLastError(); + } + } + + public SymbolHandle(ProcessHandle processHandle) + { + _processHandle = processHandle; + _handle = processHandle; + + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymInitialize(_handle, null, false)) + Win32.ThrowLastError(); + } + + _processHandle.Reference(); + } + + protected override void DisposeObject(bool disposing) + { + Win32.DbgHelpLock.Acquire(); + + try + { + Win32.SymCleanup(_handle); + + // If we didn't use a process handle, we got it from the ID generator. + if (_processHandle == null) + _idGen.Push(_handle.ToInt32()); + // Otherwise, dereference the process handle. + else + _processHandle.Dereference(disposing); + } + finally + { + Win32.DbgHelpLock.Release(); + } + } + + public IntPtr Handle + { + get { return _handle; } + } + } + + private const int _maxNameLen = 0x100; + private static IdGenerator _idGen = new IdGenerator(); + + public static SymbolOptions Options + { + get + { + using (Win32.DbgHelpLock.AcquireContext()) + return Win32.SymGetOptions(); + } + + set + { + using (Win32.DbgHelpLock.AcquireContext()) + Win32.SymSetOptions(value); + } + } + + private SymbolHandle _handle; + private List> _modules = new List>(); + + public SymbolProvider() + { + _handle = new SymbolHandle(); + } + + public SymbolProvider(ProcessHandle processHandle) + { + _handle = new SymbolHandle(processHandle); + } + + public void Dispose() + { + _handle.Dispose(); + } + + public bool Busy + { + get + { + if (!Win32.DbgHelpLock.TryAcquire()) + { + return true; + } + else + { + Win32.DbgHelpLock.Release(); + return false; + } + } + } + + public IntPtr Handle + { + get { return _handle; } + } + + public bool PreloadModules { get; set; } + + public string SearchPath + { + get + { + StringBuilder data = new StringBuilder(0x1000); + + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymGetSearchPath(_handle, data, data.Capacity)) + return ""; + } + + return data.ToString(); + } + + set + { + using (Win32.DbgHelpLock.AcquireContext()) + Win32.SymSetSearchPath(_handle, value); + } + } + + public void EnumSymbols(ulong moduleBase, SymbolEnumDelegate enumDelegate) + { + this.EnumSymbols(moduleBase, null, enumDelegate); + } + + public void EnumSymbols(string mask, SymbolEnumDelegate enumDelegate) + { + this.EnumSymbols(0, mask, enumDelegate); + } + + public void EnumSymbols(ulong moduleBase, string mask, SymbolEnumDelegate enumDelegate) + { + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymEnumSymbols( + _handle, + moduleBase, + mask, + (symbolInfo, symbolSize, userContext) => + enumDelegate(new SymbolInformation(symbolInfo, symbolSize)), + IntPtr.Zero + )) + Win32.ThrowLastError(); + } + } + + public string GetLineFromAddress(ulong address) + { + string fileName; + int lineNumber; + + this.GetLineFromAddress(address, out fileName, out lineNumber); + + if (fileName != null) + return fileName + ": line " + lineNumber.ToString(); + else + return null; + } + + public void GetLineFromAddress(ulong address, out string fileName, out int lineNumber) + { + int displacement; + + this.GetLineFromAddress(address, out fileName, out lineNumber, out displacement); + } + + public void GetLineFromAddress(ulong address, out string fileName, out int lineNumber, out int lineDisplacement) + { + ImagehlpLine64 line; + int displacement; + + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymGetLineFromAddr64(_handle, address, out displacement, out line)) + Win32.ThrowLastError(); + + fileName = line.FileName; + lineNumber = line.LineNumber; + lineDisplacement = displacement; + } + } + + public string GetModuleFromAddress(IntPtr address, out IntPtr baseAddress) + { + ulong baseAddressULong; + string fileName = this.GetModuleFromAddress(address.ToUInt64(), out baseAddressULong); + + baseAddress = baseAddressULong.ToIntPtr(); + + return fileName; + } + + public string GetModuleFromAddress(ulong address, out ulong baseAddress) + { + lock (_modules) + { + foreach (var kvp in _modules) + { + if (address >= kvp.Key) + { + baseAddress = kvp.Key; + return kvp.Value; + } + } + } + + baseAddress = 0; + + return null; + } + + public string GetSymbolFromAddress(ulong address) + { + SymbolFlags flags; + + return this.GetSymbolFromAddress(address, out flags); + } + + public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level) + { + SymbolFlags flags; + string fileName; + + return this.GetSymbolFromAddress(address, out level, out flags, out fileName); + } + + public string GetSymbolFromAddress(ulong address, out SymbolFlags flags) + { + SymbolResolveLevel level; + string fileName; + + return this.GetSymbolFromAddress(address, out level, out flags, out fileName); + } + + public string GetSymbolFromAddress(ulong address, out string fileName) + { + SymbolResolveLevel level; + SymbolFlags flags; + + return this.GetSymbolFromAddress(address, out level, out flags, out fileName); + } + + public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level, out SymbolFlags flags, out string fileName) + { + string symbolName; + ulong displacement; + + return this.GetSymbolFromAddress(address, out level, out flags, out fileName, out symbolName, out displacement); + } + + public string GetSymbolFromAddress(ulong address, out string fileName, out ulong displacement) + { + SymbolResolveLevel level; + SymbolFlags flags; + string symbolName; + + this.GetSymbolFromAddress(address, out level, out flags, out fileName, out symbolName, out displacement); + + return symbolName; + } + + public string GetSymbolFromAddress(ulong address, out SymbolResolveLevel level, out SymbolFlags flags, out string fileName, out string symbolName, out ulong displacement) + { + // Assume failure (and stop the compiler from complaining). + if (address == 0) + { + level = SymbolResolveLevel.Invalid; + flags = 0; + fileName = null; + } + + // Allocate some memory for the symbol information. + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(SymbolInfo)) + _maxNameLen)) + { + var info = new SymbolInfo(); + + info.SizeOfStruct = Marshal.SizeOf(info); + info.MaxNameLen = _maxNameLen - 1; + + Marshal.StructureToPtr(info, data, false); + + // Hack for drivers, since we don't get their module sizes. + // Preloading modules will fix this. + if (this.PreloadModules) + { + ulong b; + + this.GetModuleFromAddress(address, out b); + + using (Win32.DbgHelpLock.AcquireContext()) + Win32.SymFromAddr(_handle, b, out displacement, data); + + Marshal.StructureToPtr(info, data, false); + } + + // Get the symbol name. + using (Win32.DbgHelpLock.AcquireContext()) + { + if (Win32.SymFromAddr(_handle, address, out displacement, data)) + { + info = data.ReadStruct(); + } + } + + string modFileName; + ulong modBase; + + // Get the module name. + if (info.ModBase == 0) + { + modFileName = this.GetModuleFromAddress(address, out modBase); + } + else + { + modBase = info.ModBase; + + lock (_modules) + modFileName = _modules.Find(kvp => kvp.Key == info.ModBase).Value; + } + + // If we don't have a module name, return an address. + if (modFileName == null) + { + level = SymbolResolveLevel.Address; + flags = 0; + fileName = null; + symbolName = null; + + return Utils.FormatAddress(address); + } + + FileInfo fi = null; + + fileName = modFileName; + + try + { + fi = new FileInfo(modFileName); + fileName = fi.FullName; + } + catch + { } + + // If we have a module name but not a symbol name, + // return a module plus an offset: module+offset. + if (info.NameLen == 0) + { + level = SymbolResolveLevel.Module; + flags = 0; + symbolName = null; + + if (fi != null) + { + return fi.Name + "+0x" + (address - modBase).ToString("x"); + } + else + { + var s = modFileName.Split('\\'); + + return s[s.Length - 1] + "+0x" + (address - modBase).ToString("x"); + } + } + + // If we have everything, return the full symbol name: module!symbol+offset. + string name = Marshal.PtrToStringAnsi(data.Memory.Increment(Win32.SymbolInfoNameOffset), info.NameLen); + + level = SymbolResolveLevel.Function; + flags = info.Flags; + symbolName = name; + + if (displacement == 0) + return fi.Name + "!" + name; + else + return fi.Name + "!" + name + "+0x" + displacement.ToString("x"); + } + } + + public SymbolInformation GetSymbolFromName(string symbolName) + { + using (var data = new MemoryAlloc(Marshal.SizeOf(typeof(SymbolInfo)) + _maxNameLen)) + { + var info = new SymbolInfo(); + + info.SizeOfStruct = Marshal.SizeOf(info); + info.MaxNameLen = _maxNameLen - 1; + + Marshal.StructureToPtr(info, data, false); + + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymFromName(_handle, symbolName, data)) + Win32.ThrowLastError(); + } + + return new SymbolInformation(data, 0); + } + } + + public void LoadModule(string fileName, IntPtr baseAddress) + { + this.LoadModule(fileName, baseAddress.ToUInt64()); + } + + public void LoadModule(string fileName, ulong baseAddress) + { + this.LoadModule(fileName, baseAddress, 0); + } + + public void LoadModule(string fileName, IntPtr baseAddress, int size) + { + this.LoadModule(fileName, baseAddress.ToUInt64(), size); + } + + public void LoadModule(string fileName, ulong baseAddress, int size) + { + using (Win32.DbgHelpLock.AcquireContext()) + { + if (Win32.SymLoadModule64(_handle, IntPtr.Zero, fileName, null, baseAddress, size) == 0) + Win32.ThrowLastError(); + } + + lock (_modules) + { + _modules.Add(new KeyValuePair(baseAddress, fileName)); + _modules.Sort((kvp1, kvp2) => kvp2.Key.CompareTo(kvp1.Key)); + } + } + + public void UnloadModule(string fileName) + { + KeyValuePair pair; + + lock (_modules) + pair = _modules.Find(kvp => string.Compare(kvp.Value, fileName, true) == 0); + + this.UnloadModule(pair.Key); + } + + public void UnloadModule(ulong baseAddress) + { + using (Win32.DbgHelpLock.AcquireContext()) + { + if (!Win32.SymUnloadModule64(_handle, baseAddress)) + Win32.ThrowLastError(); + } + + lock (_modules) + _modules.RemoveAll(kvp => kvp.Key == baseAddress); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolResolveLevel.cs b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolResolveLevel.cs new file mode 100644 index 000000000..a5ac4bfa2 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Symbols/SymbolResolveLevel.cs @@ -0,0 +1,53 @@ +/* + * Process Hacker - + * symbol resolve-level + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +namespace ProcessHacker.Native.Symbols +{ + /// + /// Specifies the detail with which the address's name was resolved. + /// + public enum SymbolResolveLevel + { + /// + /// Indicates that the address was resolved to a module, a function and possibly an offset. + /// For example: mymodule.dll!MyExportedFunction+0x123 + /// + Function, + + /// + /// Indicates that the address was resolved to a module and an offset. + /// For example: mymodule.dll+0x4321 + /// + Module, + + /// + /// Indicates that the address was not resolved. + /// For example: 0x12345678 + /// + Address, + + /// + /// Indicates that the address was invalid (for example, 0x0). + /// + Invalid + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/CurrentThread.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/CurrentThread.cs new file mode 100644 index 000000000..a269c9e0f --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/CurrentThread.cs @@ -0,0 +1,70 @@ +/* + * Process Hacker - + * thread functions + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Provides methods for manipulating the current thread. + /// + public static class CurrentThread + { + /// + /// Switches to another thread. + /// + public static void Sleep() + { + Yield(); + } + + /// + /// Suspends execution of the current thread. + /// + /// The interval to sleep, in milliseconds. + public static void Sleep(int interval) + { + ThreadHandle.Sleep(interval * Win32.TimeMsTo100Ns, true); + } + + /// + /// Suspends execution of the current thread. + /// + /// The time at which wake up. + public static void Sleep(DateTime time) + { + ThreadHandle.Sleep(time.ToFileTime(), false); + } + + /// + /// Switches to another thread. + /// + public static void Yield() + { + ThreadHandle.Yield(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/Event.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/Event.cs new file mode 100644 index 000000000..19b716f82 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/Event.cs @@ -0,0 +1,139 @@ +/* + * Process Hacker - + * event + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents a thread synchronization event. + /// + public sealed class Event : NativeObject + { + /// + /// Creates an event. + /// + public Event() + : this(null) + { } + + /// + /// Creates an event. + /// + /// + /// Whether the event should automatically reset to a non-signaled state + /// after all waiters are released. + /// + /// + /// Whether the event should be set to a signaled state initially. + /// + public Event(bool autoReset, bool initialState) + : this(null, autoReset, initialState) + { } + + /// + /// Creates or opens an event. + /// + /// + /// The name of the new event, or the name of an existing event to open. + /// + public Event(string name) + : this(name, false, false) + { } + + /// + /// Creates an event. + /// + /// + /// The name of the new event. + /// + /// + /// Whether the event should automatically reset to a non-signaled state + /// after all waiters are released. + /// + /// + /// Whether the event should be set to a signaled state initially. + /// + public Event(string name, bool autoReset, bool initialState) + { + this.Handle = EventHandle.Create( + EventAccess.All, + name, + ObjectFlags.OpenIf, + null, + autoReset ? EventType.SynchronizationEvent : EventType.NotificationEvent, + initialState + ); + } + + /// + /// Gets whether the event will automatically reset + /// after waiters are released. + /// + public bool AutoReset + { + get + { + return this.Handle.GetBasicInformation().EventType == + EventType.SynchronizationEvent; + } + } + + /// + /// Gets whether the event is in the signaled state. + /// + public bool Signaled + { + get { return this.Handle.GetBasicInformation().EventState != 0; } + } + + /// + /// Attempts to satisfy as many waits as possible and sets + /// the event's state to non-signaled. + /// + public void Pulse() + { + this.Handle.Pulse(); + } + + /// + /// Sets the event's state to non-signaled. + /// + public void Reset() + { + this.Handle.Reset(); + } + + /// + /// Sets the event's state to signaled. + /// + public void Set() + { + this.Handle.Set(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/EventPair.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/EventPair.cs new file mode 100644 index 000000000..4b78b733e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/EventPair.cs @@ -0,0 +1,109 @@ +/* + * Process Hacker - + * event pair + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents an event pair which contains two events, high and low. + /// + public sealed class EventPair : NativeObject + { + /// + /// Creates an event pair. + /// + public EventPair() + : this(null) + { } + + /// + /// Creates or opens an event pair. + /// + /// + /// The name of the new event pair, or the name of an + /// existing event pair. + /// + public EventPair(string name) + { + this.Handle = EventPairHandle.Create( + EventPairAccess.All, + name, + ObjectFlags.OpenIf, + null + ); + } + + /// + /// Sets the high event. + /// + public void SetHigh() + { + this.Handle.SetHigh(); + } + + /// + /// Sets the high event and waits for the low event. + /// + public WaitStatus SetHighWaitLow() + { + return (WaitStatus)this.Handle.SetHighWaitLow(); + } + + /// + /// Sets the low event. + /// + public void SetLow() + { + this.Handle.SetLow(); + } + + /// + /// Sets the low event and waits for the high event. + /// + public WaitStatus SetLowWaitHigh() + { + return (WaitStatus)this.Handle.SetLowWaitHigh(); + } + + /// + /// Waits for the high event. + /// + public WaitStatus WaitHigh() + { + return (WaitStatus)this.Handle.WaitHigh(); + } + + /// + /// Waits for the low event. + /// + public WaitStatus WaitLow() + { + return (WaitStatus)this.Handle.WaitLow(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/KeyedEvent.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/KeyedEvent.cs new file mode 100644 index 000000000..8c38b1f4c --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/KeyedEvent.cs @@ -0,0 +1,122 @@ +/* + * Process Hacker - + * keyed event + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents a keyed event. + /// + public sealed class KeyedEvent : NativeObject + { + /// + /// Creates a new keyed event. + /// + public KeyedEvent() + : this(null) + { } + + /// + /// Creates or opens a keyed event. + /// + /// + /// The name of the new keyed event, or the name of an + /// existing keyed event. + /// + public KeyedEvent(string name) + { + this.Handle = KeyedEventHandle.Create( + KeyedEventAccess.All, + name, + ObjectFlags.OpenIf, + null + ); + } + + /// + /// Releases the specified key. If no other thread is waiting + /// on the key, the function blocks until a thread does. + /// + /// The key, which must be divisible by 2. + public void ReleaseKey(int key) + { + this.Handle.ReleaseKey(new IntPtr(key), false, long.MinValue, false); + } + + /// + /// Releases the specified key. If no other thread is waiting + /// on the key, the function blocks until a thread does. + /// + /// The key, which must be divisible by 2. + /// A timeout value, in milliseconds. + public void ReleaseKey(int key, int timeout) + { + this.Handle.ReleaseKey(new IntPtr(key), false, timeout * Win32.TimeMsTo100Ns, true); + } + + /// + /// Releases the specified key. If no other thread is waiting + /// on the key, the function blocks until a thread does. + /// + /// The key, which must be divisible by 2. + /// A time to wait until. + public void ReleaseKey(int key, DateTime timeout) + { + this.Handle.ReleaseKey(new IntPtr(key), false, timeout.ToFileTime(), false); + } + + /// + /// Waits for the specified key to be released. + /// + /// The key, which must be divisible by 2. + public void WaitKey(int key) + { + this.Handle.WaitKey(new IntPtr(key), false, long.MinValue, false); + } + + /// + /// Waits for the specified key to be released. + /// + /// The key, which must be divisible by 2. + /// A time to wait until. + public void WaitKey(int key, int timeout) + { + this.Handle.WaitKey(new IntPtr(key), false, timeout * Win32.TimeMsTo100Ns, true); + } + + /// + /// Waits for the specified key to be released. + /// + /// The key, which must be divisible by 2. + /// A time to wait until. + public void WaitKey(int key, DateTime timeout) + { + this.Handle.WaitKey(new IntPtr(key), false, timeout.ToFileTime(), false); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/Mutant.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/Mutant.cs new file mode 100644 index 000000000..f3841fcbf --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/Mutant.cs @@ -0,0 +1,102 @@ +/* + * Process Hacker - + * mutant + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents a mutant which can be used to synchronize access to a shared resource. + /// + public sealed class Mutant : NativeObject + { + /// + /// Creates a mutant. + /// + public Mutant() + : this(null) + { } + + /// + /// Creates a mutant. + /// + /// + /// Whether the mutant should become owned by the current + /// thread when it is created. + /// + public Mutant(bool owned) + : this(null, owned) + { } + + /// + /// Creates or opens a mutant. + /// + /// + /// The name of the new mutant, or the name of an existing mutant to open. + /// + public Mutant(string name) + : this(name, false) + { } + + /// + /// Creates a mutant. + /// + /// + /// The name of the new mutant. + /// + /// + /// Whether the mutant should become owned by the current + /// thread when it is created. + /// + public Mutant(string name, bool owned) + { + this.Handle = MutantHandle.Create( + MutantAccess.All, + name, + ObjectFlags.OpenIf, + null, + owned + ); + } + + /// + /// Gets whether the mutant is currently owned. + /// + public bool Owned + { + get { return this.Handle.GetBasicInformation().CurrentCount <= 0; } + } + + /// + /// Releases the mutant, allowing other waiting threads to own it. + /// + public void Release() + { + this.Handle.Release(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/NativeThreadPool.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/NativeThreadPool.cs new file mode 100644 index 000000000..3074a7801 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/NativeThreadPool.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; + +namespace ProcessHacker.Native.Threading +{ + public delegate void RegisterWaitCallback(object argument, bool timeout); + + public static class NativeThreadPool + { + public static void QueueWorkItem(Action work, object argument) + { + Win32.RtlQueueWorkItem((context) => work(argument), IntPtr.Zero, WtFlags.ExecuteDefault).ThrowIf(); + } + + public static IntPtr RegisterWait(IntPtr handle, RegisterWaitCallback callback, object argument, int timeoutMilliseconds) + { + IntPtr waitHandle; + + Win32.RtlRegisterWait( + out waitHandle, + handle, + (context, timeout) => callback(argument, timeout), + IntPtr.Zero, + timeoutMilliseconds, + WtFlags.ExecuteDefault + ).ThrowIf(); + + return waitHandle; + } + + public static void UnregisterWait(IntPtr waitHandle) + { + Win32.RtlDeregisterWait(waitHandle).ThrowIf(); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/Semaphore.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/Semaphore.cs new file mode 100644 index 000000000..cfbf60aea --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/Semaphore.cs @@ -0,0 +1,115 @@ +/* + * Process Hacker - + * semaphore + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents a semaphore which can be used to control access to a shared resource. + /// + public sealed class Semaphore : NativeObject + { + /// + /// Creates a binary semaphore. + /// + public Semaphore() + : this(null) + { } + + /// + /// Creates a binary semaphore. + /// + /// The initial count of the semaphore. + /// The maximum count of the semaphore. + public Semaphore(int initialCount, int maximumCount) + : this(null, initialCount, maximumCount) + { } + + /// + /// Creates or opens a semaphore. + /// + /// + /// The name of the new semaphore, or the name of an existing semaphore. + /// + public Semaphore(string name) + : this(name, 1, 1) + { } + + /// + /// Creates a semaphore. + /// + /// The name of the new semaphore. + /// The initial count of the semaphore. + /// The maximum count of the semaphore. + public Semaphore(string name, int initialCount, int maximumCount) + { + this.Handle = SemaphoreHandle.Create( + SemaphoreAccess.All, + name, + ObjectFlags.OpenIf, + null, + initialCount, + maximumCount + ); + } + + /// + /// Gets the current count of the semaphore. + /// + public int Count + { + get { return this.Handle.GetBasicInformation().CurrentCount; } + } + + /// + /// Gets the maximum count of the semaphore. + /// + public int MaximumCount + { + get { return this.Handle.GetBasicInformation().MaximumCount; } + } + + /// + /// Releases the semaphore, incrementing the count. + /// + public void Release() + { + this.Handle.Release(); + } + + /// + /// Releases the semaphore, incrementing the count by the + /// specified amount. + /// + /// The amount to increment the count by. + public void Release(int count) + { + this.Handle.Release(count); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/Timer.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/Timer.cs new file mode 100644 index 000000000..4e9b104a5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/Timer.cs @@ -0,0 +1,216 @@ +/* + * Process Hacker - + * timer + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + /// + /// Represents a callback to be called when a timer is signaled. + /// + /// The context passed when the timer was set. + public delegate void TimerCallback(IntPtr context); + + /// + /// Represents a timer. + /// + public sealed class Timer : NativeObject + { + private TimerCallback _callback; + + /// + /// Creates a timer. + /// + public Timer() + : this(null) + { } + + /// + /// Creates a timer. + /// + /// + /// Whether the timer should automatically reset to a + /// non-signaled state after waiters have been released. + /// + public Timer(bool autoReset) + : this(null, autoReset) + { } + + /// + /// Creates or opens a timer. + /// + /// + /// The name of the new timer, or the name of an existing timer. + /// + public Timer(string name) + : this(name, false) + { } + + /// + /// Creates a timer. + /// + /// The name of the new timer. + /// + /// Whether the timer should automatically reset to a + /// non-signaled state after waiters have been released. + /// + public Timer(string name, bool autoReset) + { + this.Handle = TimerHandle.Create( + TimerAccess.All, + name, + ObjectFlags.OpenIf, + null, + autoReset ? TimerType.SynchronizationTimer : TimerType.NotificationTimer + ); + } + + /// + /// Gets the remaining time before the timer is signaled. + /// + public TimeSpan RemainingTime + { + get { return new TimeSpan(this.Handle.GetBasicInformation().RemainingTime); } + } + + /// + /// Gets whether the timer is signaled. + /// + public bool Signaled + { + get { return this.Handle.GetBasicInformation().TimerState; } + } + + /// + /// Cancels the timer, preventing it from being signaled. + /// + public void Cancel() + { + this.Handle.Cancel(); + } + + /// + /// Starts the timer. + /// + /// The due time, in milliseconds. + public void Set(int dueTime) + { + this.Set(dueTime, 0); + } + + /// + /// Starts the timer. + /// + /// The due time, in milliseconds. + /// The interval to use for periodic signaling, in milliseconds. + public void Set(int dueTime, int period) + { + this.Set(null, dueTime, period); + } + + /// + /// Starts the timer. + /// + /// A function to be called when the timer is signaled. + /// The due time, in milliseconds. + /// The interval to use for periodic signaling, in milliseconds. + public void Set(TimerCallback callback, int dueTime, int period) + { + this.Set(callback, dueTime, period, IntPtr.Zero); + } + + /// + /// Starts the timer. + /// + /// A function to be called when the timer is signaled. + /// The due time, in milliseconds. + /// The interval to use for periodic signaling, in milliseconds. + /// A value to pass to the callback function. + public void Set(TimerCallback callback, int dueTime, int period, IntPtr context) + { + TimerApcRoutine apcRoutine = (context_, lowPart, highPart) => callback(context_); + + _callback = callback; + this.Handle.Set( + dueTime * Win32.TimeMsTo100Ns, + true, + callback != null ? apcRoutine : null, + context, + period + ); + } + + /// + /// Starts the timer. + /// + /// The time at which the timer will be signaled. + public void Set(DateTime dueTime) + { + this.Set(dueTime, 0); + } + + /// + /// Starts the timer. + /// + /// The time at which the timer will be signaled. + /// The interval to use for periodic signaling, in milliseconds. + public void Set(DateTime dueTime, int period) + { + this.Set(null, dueTime, period); + } + + /// + /// Starts the timer. + /// + /// A function to be called when the timer is signaled. + /// The time at which the timer will be signaled. + /// The interval to use for periodic signaling, in milliseconds. + public void Set(TimerCallback callback, DateTime dueTime, int period) + { + this.Set(callback, dueTime, period, IntPtr.Zero); + } + + /// + /// Starts the timer. + /// + /// A function to be called when the timer is signaled. + /// The time at which the timer will be signaled. + /// The interval to use for periodic signaling, in milliseconds. + /// A value to pass to the callback function. + public void Set(TimerCallback callback, DateTime dueTime, int period, IntPtr context) + { + TimerApcRoutine apcRoutine = (context_, lowPart, highPart) => callback(context_); + + _callback = callback; + this.Handle.Set( + dueTime.ToFileTime(), + false, + callback != null ? apcRoutine : null, + context, + period + ); + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Threading/Waiter.cs b/branches/ph-plugins/ProcessHacker.Native/Threading/Waiter.cs new file mode 100644 index 000000000..2b4dc066e --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Threading/Waiter.cs @@ -0,0 +1,367 @@ +/* + * Process Hacker - + * wait manager + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System.Collections.Generic; +using System.Threading; +using ProcessHacker.Common.Objects; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Threading +{ + public delegate void ObjectSignaledDelegate(ISynchronizable obj); + + /// + /// Provides methods for waiting on dispatcher objects. + /// + public sealed class Waiter : BaseObject + { + private class WaiterThread : BaseObject + { + public event ObjectSignaledDelegate ObjectSignaled; + + private Waiter _owner; + private bool _terminating = false; + private Thread _thread; + private bool _threadInitialized = false; + private ThreadHandle _threadHandle; + private List _waitObjects = new List(); + + public WaiterThread(Waiter owner) + { + _owner = owner; + + // Create the waiter thread. + _thread = new Thread(this.WaiterThreadStart); + _thread.IsBackground = true; + _thread.SetApartmentState(ApartmentState.STA); + _thread.Start(); + + // Wait for the thread to initialize. + lock (_thread) + { + if (!_threadInitialized) + Monitor.Wait(_thread); + } + } + + protected override void DisposeObject(bool disposing) + { + lock (_thread) + { + if (_threadInitialized) + { + // Terminate the waiter thread. + this.Terminate(); + } + } + + if (_threadHandle != null) + { + // Close the thread handle. + _threadHandle.Dispose(); + } + + // Avoid hanging on to objects. + lock (_waitObjects) + _waitObjects.Clear(); + } + + public int Count + { + get + { + lock (_waitObjects) + return _waitObjects.Count; + } + } + + public ISynchronizable[] Objects + { + get + { + lock (_waitObjects) + return _waitObjects.ToArray(); + } + } + + public bool Add(ISynchronizable obj) + { + lock (_waitObjects) + { + // Check if we already have the maximum number of wait objects. + if (_waitObjects.Count >= Win32.MaximumWaitObjects) + return false; + + _waitObjects.Add(obj); + this.NotifyChange(); + return true; + } + } + + public void NotifyChange() + { + _threadHandle.Alert(); + } + + private void OnObjectSignaled(ISynchronizable obj) + { + if (this.ObjectSignaled != null) + this.ObjectSignaled(obj); + } + + public bool Remove(ISynchronizable obj) + { + lock (_waitObjects) + { + if (!_waitObjects.Contains(obj)) + return false; + + _waitObjects.Remove(obj); + this.NotifyChange(); + return true; + } + } + + public void Terminate() + { + _terminating = true; + this.NotifyChange(); + } + + private void WaiterThreadStart() + { + ISynchronizable[] waitObjects = null; + + // Open a handle to the current thread. + _threadHandle = ThreadHandle.OpenCurrent(ThreadAccess.Alert); + + // Signal that the thread has been initialized. + lock (_thread) + { + _threadInitialized = true; + Monitor.PulseAll(_thread); + } + + while (!_terminating) + { + bool doWait; + + lock (_waitObjects) + { + // Check if we have any objects to wait for. If we do, use WaitAny. + // Otherwise, wait forever (alertably). + if (_waitObjects.Count > 0) + { + waitObjects = _waitObjects.ToArray(); + doWait = true; + } + else + { + doWait = false; + } + } + + NtStatus waitStatus; + + if (doWait) + { + // Wait for the objects, (almost) forever. + waitStatus = NativeHandle.WaitAny(waitObjects, true, long.MinValue, false); + } + else + { + // Wait forever. + waitStatus = ThreadHandle.Sleep(true, long.MinValue, false); + } + + if (waitStatus == NtStatus.Alerted) + { + // The wait was changed. Go back to refresh the wait objects array. + // The thread is also alerted to notify that the thread should terminate. + continue; + } + else if (waitStatus >= NtStatus.Wait0 && waitStatus <= NtStatus.Wait63) + { + // One of the objects was signaled. + ISynchronizable signaledObject = waitObjects[(int)(waitStatus - NtStatus.Wait0)]; + + // Remove the object now that it is signaled. + lock (_waitObjects) + { + // Just in case someone already removed the object. + if (_waitObjects.Contains(signaledObject)) + _waitObjects.Remove(signaledObject); + } + + // Call the object-signaled event. + OnObjectSignaled(signaledObject); + + // Balance the threads (which may involve terminating the current one). + _owner.BalanceWaiterThreads(); + } + } + } + } + + /// + /// Raised when an object is signaled. + /// + public event ObjectSignaledDelegate ObjectSignaled; + + private List _waiterThreads = new List(); + private List _waitObjects = new List(); + + /// + /// Creates a waiter. + /// + public Waiter() + { + + } + + protected override void DisposeObject(bool disposing) + { + // Tell the waiter threads to terminate. + foreach (var waiterThread in _waiterThreads) + waiterThread.Terminate(); + _waiterThreads.Clear(); + } + + public int Count + { + get + { + lock (_waitObjects) + return _waitObjects.Count; + } + } + + public ISynchronizable[] Objects + { + get + { + lock (_waitObjects) + return _waitObjects.ToArray(); + } + } + + /// + /// Adds an object for the waiter to wait on. + /// + /// The object to wait for. + public void Add(ISynchronizable obj) + { + lock (_waitObjects) + _waitObjects.Add(obj); + + foreach (var waiterThread in this.GetWaiterThreads()) + { + if (waiterThread.Add(obj)) + return; + } + + // We couldn't add the object to any existing waiter thread. + // Create a new waiter thread and add the object to that. + this.CreateWaiterThread(obj); + } + + internal void BalanceWaiterThreads() + { + lock (_waitObjects) + { + // Eliminate waiter threads with no objects. + foreach (var waiterThread in this.GetWaiterThreads()) + { + if (waiterThread.Count == 0) + this.DeleteWaiterThread(waiterThread); + } + } + } + + private WaiterThread CreateWaiterThread() + { + return this.CreateWaiterThread(null); + } + + private WaiterThread CreateWaiterThread(ISynchronizable obj) + { + WaiterThread waiterThread = new WaiterThread(this); + + waiterThread.ObjectSignaled += this.OnObjectSignaled; + + if (obj != null) + waiterThread.Add(obj); + + lock (_waiterThreads) + _waiterThreads.Add(waiterThread); + + return waiterThread; + } + + private void DeleteWaiterThread(WaiterThread waiterThread) + { + lock (_waiterThreads) + { + _waiterThreads.Remove(waiterThread); + waiterThread.ObjectSignaled -= this.OnObjectSignaled; + waiterThread.Dispose(); + } + } + + private WaiterThread[] GetWaiterThreads() + { + lock (_waiterThreads) + return _waiterThreads.ToArray(); + } + + private void OnObjectSignaled(ISynchronizable obj) + { + if (ObjectSignaled != null) + ObjectSignaled(obj); + } + + /// + /// Removes an object the waiter is waiting on. + /// + /// An object which is currently being waited on. + /// Whether the object was successfully removed. + public bool Remove(ISynchronizable obj) + { + foreach (var waiterThread in this.GetWaiterThreads()) + { + if (waiterThread.Remove(obj)) + { + lock (_waitObjects) + _waitObjects.Remove(obj); + + this.BalanceWaiterThreads(); + return true; + } + } + + // We couldn't remove the object. + return false; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.Designer.cs b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.Designer.cs new file mode 100644 index 000000000..f47da2a50 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.Designer.cs @@ -0,0 +1,156 @@ +namespace ProcessHacker.Native.Ui +{ + partial class ChooseProcessDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChooseProcessDialog)); + this.listProcesses = new System.Windows.Forms.ListView(); + this.columnName = new System.Windows.Forms.ColumnHeader(); + this.columnPID = new System.Windows.Forms.ColumnHeader(); + this.columnUsername = new System.Windows.Forms.ColumnHeader(); + this.imageList = new System.Windows.Forms.ImageList(this.components); + this.buttonCancel = new System.Windows.Forms.Button(); + this.buttonOK = new System.Windows.Forms.Button(); + this.buttonRefresh = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // listProcesses + // + this.listProcesses.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listProcesses.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnName, + this.columnPID, + this.columnUsername}); + this.listProcesses.FullRowSelect = true; + this.listProcesses.HideSelection = false; + this.listProcesses.Location = new System.Drawing.Point(12, 12); + this.listProcesses.MultiSelect = false; + this.listProcesses.Name = "listProcesses"; + this.listProcesses.Size = new System.Drawing.Size(451, 350); + this.listProcesses.SmallImageList = this.imageList; + this.listProcesses.TabIndex = 0; + this.listProcesses.UseCompatibleStateImageBehavior = false; + this.listProcesses.View = System.Windows.Forms.View.Details; + this.listProcesses.SelectedIndexChanged += new System.EventHandler(this.listProcesses_SelectedIndexChanged); + this.listProcesses.DoubleClick += new System.EventHandler(this.listProcesses_DoubleClick); + // + // columnName + // + this.columnName.Text = "Name"; + this.columnName.Width = 200; + // + // columnPID + // + this.columnPID.Text = "PID"; + // + // columnUsername + // + this.columnUsername.Text = "Username"; + this.columnUsername.Width = 150; + // + // imageList + // + this.imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream"))); + this.imageList.TransparentColor = System.Drawing.Color.Transparent; + this.imageList.Images.SetKeyName(0, "generic_process"); + // + // buttonCancel + // + this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonCancel.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonCancel.Location = new System.Drawing.Point(388, 368); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 1; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); + // + // buttonOK + // + this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonOK.Enabled = false; + this.buttonOK.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonOK.Location = new System.Drawing.Point(307, 368); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(75, 23); + this.buttonOK.TabIndex = 2; + this.buttonOK.Text = "OK"; + this.buttonOK.UseVisualStyleBackColor = true; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // buttonRefresh + // + this.buttonRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonRefresh.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonRefresh.Location = new System.Drawing.Point(12, 368); + this.buttonRefresh.Name = "buttonRefresh"; + this.buttonRefresh.Size = new System.Drawing.Size(75, 23); + this.buttonRefresh.TabIndex = 3; + this.buttonRefresh.Text = "Refresh"; + this.buttonRefresh.UseVisualStyleBackColor = true; + this.buttonRefresh.Click += new System.EventHandler(this.buttonRefresh_Click); + // + // ChooseProcessDialog + // + this.AcceptButton = this.buttonOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(475, 403); + this.Controls.Add(this.buttonRefresh); + this.Controls.Add(this.buttonOK); + this.Controls.Add(this.buttonCancel); + this.Controls.Add(this.listProcesses); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ChooseProcessDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Choose Process"; + this.Load += new System.EventHandler(this.ChooseProcessDialog_Load); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listProcesses; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.Button buttonOK; + private System.Windows.Forms.Button buttonRefresh; + private System.Windows.Forms.ColumnHeader columnName; + private System.Windows.Forms.ColumnHeader columnPID; + private System.Windows.Forms.ColumnHeader columnUsername; + private System.Windows.Forms.ImageList imageList; + } +} \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.cs b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.cs new file mode 100644 index 000000000..12d82eb22 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.cs @@ -0,0 +1,122 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native.Ui +{ + public partial class ChooseProcessDialog : Form + { + private int _selectedPid; + + public ChooseProcessDialog() + { + InitializeComponent(); + } + + private void ChooseProcessDialog_Load(object sender, EventArgs e) + { + this.RefreshProcesses(); + } + + public int SelectedPid + { + get { return _selectedPid; } + } + + private void RefreshProcesses() + { + var processes = Windows.GetProcesses(); + + listProcesses.BeginUpdate(); + listProcesses.Items.Clear(); + + var generic_process = imageList.Images["generic_process"]; + imageList.Images.Clear(); + imageList.Images.Add("generic_process", generic_process); + + foreach (var process in processes.Values) + { + string userName = ""; + string fileName = null; + + try + { + using (var phandle = new ProcessHandle(process.Process.ProcessId, OSVersion.MinProcessQueryInfoAccess)) + { + using (var thandle = phandle.GetToken(TokenAccess.Query)) + using (var sid = thandle.GetUser()) + userName = sid.GetFullName(true); + + fileName = FileUtils.GetFileName(phandle.GetImageFileName()); + } + } + catch + { } + + ListViewItem item = new ListViewItem( + new string[] + { + process.Process.ProcessId == 0 ? "System Idle Process" : process.Name, + process.Process.ProcessId.ToString(), + userName + }); + + if (!string.IsNullOrEmpty(fileName)) + { + Icon fileIcon = FileUtils.GetFileIcon(fileName); + + if (fileIcon != null) + { + imageList.Images.Add(process.Process.ProcessId.ToString(), fileIcon); + item.ImageKey = process.Process.ProcessId.ToString(); + } + } + + if (string.IsNullOrEmpty(item.ImageKey)) + item.ImageKey = "generic_process"; + + listProcesses.Items.Add(item); + } + + listProcesses.EndUpdate(); + } + + private void ChooseProcess() + { + if (listProcesses.SelectedItems.Count != 1) + return; + + _selectedPid = int.Parse(listProcesses.SelectedItems[0].SubItems[1].Text); + this.DialogResult = DialogResult.OK; + this.Close(); + } + + private void buttonRefresh_Click(object sender, EventArgs e) + { + this.RefreshProcesses(); + } + + private void buttonOK_Click(object sender, EventArgs e) + { + this.ChooseProcess(); + } + + private void buttonCancel_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + private void listProcesses_DoubleClick(object sender, EventArgs e) + { + this.ChooseProcess(); + } + + private void listProcesses_SelectedIndexChanged(object sender, EventArgs e) + { + buttonOK.Enabled = listProcesses.SelectedItems.Count == 1; + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.resx b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.resx new file mode 100644 index 000000000..242013521 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/ChooseProcessDialog.resx @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACc + BQAAAk1TRnQBSQFMAwEBAAEEAQABBAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA + ARADAAEBAQABIAYAARD/AP8AFAABlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/ + AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGS + AY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/8AAAZcBkgGPCf8D/gH/A/wB/wP6Af8D+AH/A/UB/wPz + Af8D8QH/A+4B/wPsAf8D6QH/A+gB/wPmAf8BlwGSAY8B/8AAAZcBkgGPCf8D/gH/A/wB/wP6Af8D+AH/ + A/UB/wP1Af8D8wH/A/AB/wPuAf8D6wH/A+kB/wPnAf8BlwGSAY8B/8AAAZcBkgGPBf8BhwGdAVIB/wGC + AaIBVgH/AXoBqAFaAf8BdAGtAV4B/wFwAbEBYgH/A/cB/wHNAcwBygH/Ac0BzAHKAf8BzQHMAcoB/wHN + AcwBygH/A+0B/wPrAf8D6AH/AZcBkgGPAf/AAAGXAZIBjwX/AY0BlAFMAf8BiQGaAVAB/wGEAaABVAH/ + AXwBpQFYAf8BdwGqAVwB/wP5Af8D9gH/A/QB/wP0Af8D8QH/A+8B/wPsAf8D6gH/AZcBkgGPAf/AAAGX + AZIBjwX/AZMBiQFHAf8BjwGQAUoB/wGLAZcBTgH/AYcBnQFSAf8BggGiAVYB/wP7Af8BzQHMAcoB/wHN + AcwBygH/Ac0BzAHKAf8BzQHMAcoB/wPxAf8BeAGpAVsB/wPsAf8BlwGSAY8B/8AAAZcBkgGPBf8BmAF5 + AUEB/wGVAYQBRQH/AZEBjAFIAf8BjQGUAUwB/wGJAZoBUAH/A/wB/wP6Af8D+AH/A/gB/wP1Af8D8wH/ + AYsBlwFOAf8D7gH/AZcBkgGPAf/AAAGXAZIBjwX/AZ0BbwE8Af8BmgF2AT8B/wGWAYABQwH/AZMBiQFH + Af8BjwGQAUoB/wP9Af8BzQHMAcoB/wHNAcwBygH/Ac0BzAHKAf8BzQHMAcoB/wP1Af8BlwF7AUIB/wPv + Af8BlwGSAY8B/8AAAZcBkgGPBf8BoAFnATkB/wGeAWwBOwH/AZsBcgE+Af8BmAF5AUEB/wGVAYQBRQH/ + A/4B/wP+Af8D/QH/A/sB/wP5Af8D9gH/AaABZwE5Af8D8QH/AZcBkgGPAf/AAAGXAZIBjxn/A/4B/wP+ + Af8D/QH/A/sB/wP5Af8D9gH/A/QB/wPxAf8BlwGSAY8B/8AAAZcBkgGPAf8BzQHMAcoB/wHNAcwBygH/ + Ac0BzAHKAf8BzQHMAcoB/wHNAcwBygH/Ac0BzAHKAf8BzQHMAcoB/wHNAcwBygH/Ac0BzAHKAf8BzQHM + AcoB/wHNAcwBygH/Ac0BzAHKAf8BzQHMAcoB/wHNAcwBygH/AZcBkgGPAf/AAAGXAZIBjwH/AeAB2QHT + Af8B4AHZAdMB/wHgAdkB0wH/AeAB2QHTAf8B4AHZAdMB/wHgAdkB0wH/AeAB2QHTAf8B4AHZAdMB/wHg + AdkB0wH/AZEBdgFlAf8B4AHZAdMB/wGRAXYBZQH/AeAB2QHTAf8BkQF2AWUB/wGXAZIBjwH/wAABlwGS + AY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/ + AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGSAY8B/wGXAZIBjwH/AZcBkgGPAf8BlwGS + AY8B//8AwQABQgFNAT4HAAE+AwABKAMAAUADAAEQAwABAQEAAQEFAAGAFwAD/wEAAv8GAAL/bgAC/wYA + Cw== + + + \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.Designer.cs b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.Designer.cs new file mode 100644 index 000000000..3b5f6a828 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.Designer.cs @@ -0,0 +1,327 @@ +namespace ProcessHacker.Native.Ui +{ + partial class HandlePropertiesWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tabControl = new System.Windows.Forms.TabControl(); + this.tabDetails = new System.Windows.Forms.TabPage(); + this.groupObjectInfo = new System.Windows.Forms.GroupBox(); + this.groupQuotaCharges = new System.Windows.Forms.GroupBox(); + this.labelNonPaged = new System.Windows.Forms.Label(); + this.labelPaged = new System.Windows.Forms.Label(); + this.groupReferences = new System.Windows.Forms.GroupBox(); + this.labelHandles = new System.Windows.Forms.Label(); + this.labelReferences = new System.Windows.Forms.Label(); + this.groupBasicInfo = new System.Windows.Forms.GroupBox(); + this.textGrantedAccess = new System.Windows.Forms.TextBox(); + this.textAddress = new System.Windows.Forms.TextBox(); + this.textType = new System.Windows.Forms.TextBox(); + this.textName = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.buttonClose = new System.Windows.Forms.Button(); + this.buttonPermissions = new System.Windows.Forms.Button(); + this.tabControl.SuspendLayout(); + this.tabDetails.SuspendLayout(); + this.groupQuotaCharges.SuspendLayout(); + this.groupReferences.SuspendLayout(); + this.groupBasicInfo.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl + // + this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl.Controls.Add(this.tabDetails); + this.tabControl.Location = new System.Drawing.Point(12, 12); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(370, 382); + this.tabControl.TabIndex = 0; + // + // tabDetails + // + this.tabDetails.Controls.Add(this.groupObjectInfo); + this.tabDetails.Controls.Add(this.groupQuotaCharges); + this.tabDetails.Controls.Add(this.groupReferences); + this.tabDetails.Controls.Add(this.groupBasicInfo); + this.tabDetails.Location = new System.Drawing.Point(4, 22); + this.tabDetails.Name = "tabDetails"; + this.tabDetails.Padding = new System.Windows.Forms.Padding(3); + this.tabDetails.Size = new System.Drawing.Size(362, 356); + this.tabDetails.TabIndex = 0; + this.tabDetails.Text = "Details"; + this.tabDetails.UseVisualStyleBackColor = true; + // + // groupObjectInfo + // + this.groupObjectInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupObjectInfo.Location = new System.Drawing.Point(6, 215); + this.groupObjectInfo.Name = "groupObjectInfo"; + this.groupObjectInfo.Size = new System.Drawing.Size(350, 135); + this.groupObjectInfo.TabIndex = 3; + this.groupObjectInfo.TabStop = false; + this.groupObjectInfo.Text = "Object Information"; + // + // groupQuotaCharges + // + this.groupQuotaCharges.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupQuotaCharges.Controls.Add(this.labelNonPaged); + this.groupQuotaCharges.Controls.Add(this.labelPaged); + this.groupQuotaCharges.Location = new System.Drawing.Point(179, 138); + this.groupQuotaCharges.Name = "groupQuotaCharges"; + this.groupQuotaCharges.Size = new System.Drawing.Size(177, 71); + this.groupQuotaCharges.TabIndex = 2; + this.groupQuotaCharges.TabStop = false; + this.groupQuotaCharges.Text = "Quota Charges"; + // + // labelNonPaged + // + this.labelNonPaged.AutoSize = true; + this.labelNonPaged.Location = new System.Drawing.Point(6, 42); + this.labelNonPaged.Name = "labelNonPaged"; + this.labelNonPaged.Size = new System.Drawing.Size(64, 13); + this.labelNonPaged.TabIndex = 2; + this.labelNonPaged.Text = "Non-Paged:"; + // + // labelPaged + // + this.labelPaged.AutoSize = true; + this.labelPaged.Location = new System.Drawing.Point(6, 21); + this.labelPaged.Name = "labelPaged"; + this.labelPaged.Size = new System.Drawing.Size(41, 13); + this.labelPaged.TabIndex = 2; + this.labelPaged.Text = "Paged:"; + // + // groupReferences + // + this.groupReferences.Controls.Add(this.labelHandles); + this.groupReferences.Controls.Add(this.labelReferences); + this.groupReferences.Location = new System.Drawing.Point(6, 138); + this.groupReferences.Name = "groupReferences"; + this.groupReferences.Size = new System.Drawing.Size(167, 71); + this.groupReferences.TabIndex = 1; + this.groupReferences.TabStop = false; + this.groupReferences.Text = "References"; + // + // labelHandles + // + this.labelHandles.AutoSize = true; + this.labelHandles.Location = new System.Drawing.Point(6, 42); + this.labelHandles.Name = "labelHandles"; + this.labelHandles.Size = new System.Drawing.Size(49, 13); + this.labelHandles.TabIndex = 2; + this.labelHandles.Text = "Handles:"; + // + // labelReferences + // + this.labelReferences.AutoSize = true; + this.labelReferences.Location = new System.Drawing.Point(6, 21); + this.labelReferences.Name = "labelReferences"; + this.labelReferences.Size = new System.Drawing.Size(65, 13); + this.labelReferences.TabIndex = 2; + this.labelReferences.Text = "References:"; + // + // groupBasicInfo + // + this.groupBasicInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBasicInfo.Controls.Add(this.buttonPermissions); + this.groupBasicInfo.Controls.Add(this.textGrantedAccess); + this.groupBasicInfo.Controls.Add(this.textAddress); + this.groupBasicInfo.Controls.Add(this.textType); + this.groupBasicInfo.Controls.Add(this.textName); + this.groupBasicInfo.Controls.Add(this.label4); + this.groupBasicInfo.Controls.Add(this.label3); + this.groupBasicInfo.Controls.Add(this.label2); + this.groupBasicInfo.Controls.Add(this.label1); + this.groupBasicInfo.Location = new System.Drawing.Point(6, 6); + this.groupBasicInfo.Name = "groupBasicInfo"; + this.groupBasicInfo.Size = new System.Drawing.Size(350, 126); + this.groupBasicInfo.TabIndex = 0; + this.groupBasicInfo.TabStop = false; + this.groupBasicInfo.Text = "Basic Information"; + // + // textGrantedAccess + // + this.textGrantedAccess.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textGrantedAccess.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textGrantedAccess.Location = new System.Drawing.Point(98, 76); + this.textGrantedAccess.Name = "textGrantedAccess"; + this.textGrantedAccess.Size = new System.Drawing.Size(246, 13); + this.textGrantedAccess.TabIndex = 1; + // + // textAddress + // + this.textAddress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textAddress.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textAddress.Location = new System.Drawing.Point(98, 57); + this.textAddress.Name = "textAddress"; + this.textAddress.Size = new System.Drawing.Size(246, 13); + this.textAddress.TabIndex = 1; + // + // textType + // + this.textType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textType.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textType.Location = new System.Drawing.Point(60, 38); + this.textType.Name = "textType"; + this.textType.Size = new System.Drawing.Size(284, 13); + this.textType.TabIndex = 1; + // + // textName + // + this.textName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textName.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textName.Location = new System.Drawing.Point(60, 19); + this.textName.Name = "textName"; + this.textName.Size = new System.Drawing.Size(284, 13); + this.textName.TabIndex = 1; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(6, 76); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(86, 13); + this.label4.TabIndex = 0; + this.label4.Text = "Granted Access:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(6, 57); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(82, 13); + this.label3.TabIndex = 0; + this.label3.Text = "Object Address:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 38); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(34, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Type:"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 19); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(38, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Name:"; + // + // buttonClose + // + this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonClose.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonClose.Location = new System.Drawing.Point(307, 400); + this.buttonClose.Name = "buttonClose"; + this.buttonClose.Size = new System.Drawing.Size(75, 23); + this.buttonClose.TabIndex = 1; + this.buttonClose.Text = "Close"; + this.buttonClose.UseVisualStyleBackColor = true; + this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); + // + // buttonPermissions + // + this.buttonPermissions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonPermissions.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.buttonPermissions.Location = new System.Drawing.Point(269, 97); + this.buttonPermissions.Name = "buttonPermissions"; + this.buttonPermissions.Size = new System.Drawing.Size(75, 23); + this.buttonPermissions.TabIndex = 2; + this.buttonPermissions.Text = "Permissions"; + this.buttonPermissions.UseVisualStyleBackColor = true; + this.buttonPermissions.Click += new System.EventHandler(this.buttonPermissions_Click); + // + // HandlePropertiesWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(394, 435); + this.Controls.Add(this.buttonClose); + this.Controls.Add(this.tabControl); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "HandlePropertiesWindow"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Handle Properties"; + this.Load += new System.EventHandler(this.HandlePropertiesWindow_Load); + this.tabControl.ResumeLayout(false); + this.tabDetails.ResumeLayout(false); + this.groupQuotaCharges.ResumeLayout(false); + this.groupQuotaCharges.PerformLayout(); + this.groupReferences.ResumeLayout(false); + this.groupReferences.PerformLayout(); + this.groupBasicInfo.ResumeLayout(false); + this.groupBasicInfo.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl; + private System.Windows.Forms.TabPage tabDetails; + private System.Windows.Forms.Button buttonClose; + private System.Windows.Forms.GroupBox groupBasicInfo; + private System.Windows.Forms.TextBox textName; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textGrantedAccess; + private System.Windows.Forms.TextBox textAddress; + private System.Windows.Forms.TextBox textType; + private System.Windows.Forms.GroupBox groupReferences; + private System.Windows.Forms.Label labelHandles; + private System.Windows.Forms.Label labelReferences; + private System.Windows.Forms.GroupBox groupQuotaCharges; + private System.Windows.Forms.Label labelNonPaged; + private System.Windows.Forms.Label labelPaged; + private System.Windows.Forms.GroupBox groupObjectInfo; + private System.Windows.Forms.Button buttonPermissions; + } +} \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.cs b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.cs new file mode 100644 index 000000000..c38562225 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.cs @@ -0,0 +1,151 @@ +/* + * Process Hacker - + * handle properties window + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Windows.Forms; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security.AccessControl; + +namespace ProcessHacker.Native.Ui +{ + public partial class HandlePropertiesWindow : Form + { + public delegate void HandlePropertiesDelegate(Control objectGroup, string name, string typeName); + + public event HandlePropertiesDelegate HandlePropertiesCallback; + + private string _name, _typeName; + private NativeHandle _objectHandle; + + public HandlePropertiesWindow(SystemHandleEntry handle) + { + InitializeComponent(); + this.KeyPreview = true; + this.KeyDown += (sender, e) => + { + if (e.KeyCode == Keys.Escape) + { + this.Close(); + e.Handled = true; + } + }; + + var handleInfo = handle.GetHandleInfo(); + + textName.Text = _name = handleInfo.BestName; + if (textName.Text == "") + textName.Text = "(unnamed object)"; + textType.Text = _typeName = handleInfo.TypeName; + textAddress.Text = "0x" + handle.Object.ToString("x"); + textGrantedAccess.Text = "0x" + handle.GrantedAccess.ToString("x"); + + if (handle.GrantedAccess != 0) + { + try + { + Type accessEnumType = NativeTypeFactory.GetAccessType(handleInfo.TypeName); + + textGrantedAccess.Text += " (" + + NativeTypeFactory.GetAccessString(accessEnumType, handle.GrantedAccess) + + ")"; + } + catch (NotSupportedException) + { } + } + + var basicInfo = handle.GetBasicInfo(); + + labelReferences.Text = "References: " + (basicInfo.PointerCount - 1).ToString(); + labelHandles.Text = "Handles: " + basicInfo.HandleCount.ToString(); + labelPaged.Text = "Paged: " + basicInfo.PagedPoolUsage.ToString(); + labelNonPaged.Text = "Non-Paged: " + basicInfo.NonPagedPoolUsage.ToString(); + } + + private void HandlePropertiesWindow_Load(object sender, EventArgs e) + { + if (HandlePropertiesCallback != null) + { + try + { + HandlePropertiesCallback(groupObjectInfo, _name, _typeName); + } + catch + { } + + if (groupObjectInfo.Controls.Count == 0) + { + groupObjectInfo.Visible = false; + } + else if (groupObjectInfo.Controls.Count == 1) + { + Control control = groupObjectInfo.Controls[0]; + + // If it's a user control, dock it. + if (control is UserControl) + { + control.Dock = DockStyle.Fill; + control.Margin = new Padding(3); + } + else + { + control.Location = new System.Drawing.Point(10, 20); + } + } + } + + if (this.ObjectHandle == null) + buttonPermissions.Visible = false; + } + + public NativeHandle ObjectHandle + { + get { return _objectHandle; } + set { _objectHandle = value; } + } + + private void buttonClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void buttonPermissions_Click(object sender, EventArgs e) + { + if (_objectHandle != null) + { + try + { + SecurityEditor.EditSecurity( + this, + SecurityEditor.GetSecurable(NativeTypeFactory.GetObjectType(_typeName), _objectHandle), + _name, + NativeTypeFactory.GetAccessEntries(NativeTypeFactory.GetObjectType(_typeName)) + ); + } + catch (Exception ex) + { + MessageBox.Show("Unable to edit security: " + ex.Message, "Security Editor", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.resx b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.resx new file mode 100644 index 000000000..ff31a6db5 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Ui/HandlePropertiesWindow.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/branches/ph-plugins/ProcessHacker.Native/Windows.cs b/branches/ph-plugins/ProcessHacker.Native/Windows.cs new file mode 100644 index 000000000..167c3b7fb --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/Windows.cs @@ -0,0 +1,1030 @@ +/* + * Process Hacker - + * system-related functions + * + * Copyright (C) 2009 Flavio Erlich + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// 'member' is obsolete: 'text' +#pragma warning disable 0618 + +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.InteropServices; +using ProcessHacker.Common; +using ProcessHacker.Native.Api; +using ProcessHacker.Native.Objects; +using ProcessHacker.Native.Security; + +namespace ProcessHacker.Native +{ + /// + /// Provides methods for manipulating the operating system. + /// + public static class Windows + { + public delegate bool EnumKernelModulesDelegate(KernelModule kernelModule); + + public delegate string GetProcessNameCallback(int pid); + + public static GetProcessNameCallback GetProcessName; + + /// + /// A cache for type names; QuerySystemInformation with ALL_TYPES_INFORMATION fails for some + /// reason. The dictionary relates object type numbers to their names. + /// + internal static Dictionary ObjectTypes = new Dictionary(); + + [ThreadStatic] + private static MemoryAlloc _handlesBuffer; + [ThreadStatic] + private static MemoryAlloc _kernelModulesBuffer; + [ThreadStatic] + private static MemoryAlloc _processesBuffer; + [ThreadStatic] + private static MemoryAlloc _servicesBuffer; + + private static int _numberOfProcessors = 0; + private static int _pageSize = 0; + private static IntPtr _kernelBase = IntPtr.Zero; + private static string _kernelFileName = null; + + /// + /// Gets the number of active processors. + /// + public static int NumberOfProcessors + { + get + { + if (_numberOfProcessors == 0) + _numberOfProcessors = GetBasicInformation().NumberOfProcessors; + + return _numberOfProcessors; + } + } + + /// + /// Gets the page size. + /// + public static int PageSize + { + get + { + if (_pageSize == 0) + _pageSize = GetBasicInformation().PageSize; + + return _pageSize; + } + } + + /// + /// Gets the base address of the kernel. + /// + public static IntPtr KernelBase + { + get + { + if (_kernelBase == IntPtr.Zero) + _kernelBase = GetKernelBase(); + + return _kernelBase; + } + } + + /// + /// Gets the file name of the kernel. + /// + public static string KernelFileName + { + get + { + if (_kernelFileName == null) + _kernelFileName = GetKernelFileName(); + + return _kernelFileName; + } + } + + /// + /// Gets the number of pages needed to store the + /// specified number of bytes. + /// + /// The number of bytes. + /// The number of pages needed. + public static int BytesToPages(int bytes) + { + return Utils.DivideUp(bytes, PageSize); + } + + /// + /// Enumerates the modules loaded by the kernel. + /// + /// A callback for the enumeration. + public static void EnumKernelModules(EnumKernelModulesDelegate enumCallback) + { + NtStatus status; + int retLength; + + if (_kernelModulesBuffer == null) + _kernelModulesBuffer = new MemoryAlloc(0x1000); + + status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemModuleInformation, + _kernelModulesBuffer, + _kernelModulesBuffer.Size, + out retLength + ); + + if (status == NtStatus.InfoLengthMismatch) + { + _kernelModulesBuffer.Resize(retLength); + + status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemModuleInformation, + _kernelModulesBuffer, + _kernelModulesBuffer.Size, + out retLength + ); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + RtlProcessModules modules = _kernelModulesBuffer.ReadStruct(); + + for (int i = 0; i < modules.NumberOfModules; i++) + { + var module = _kernelModulesBuffer.ReadStruct(RtlProcessModules.ModulesOffset, i); + var moduleInfo = new Debugging.ModuleInformation(module); + + if (!enumCallback(new KernelModule( + moduleInfo.BaseAddress, + moduleInfo.Size, + moduleInfo.Flags, + moduleInfo.BaseName, + FileUtils.GetFileName(moduleInfo.FileName) + ))) + break; + } + } + + /// + /// Gets basic information about the system. + /// + /// A structure containing basic information. + public static SystemBasicInformation GetBasicInformation() + { + NtStatus status; + SystemBasicInformation sbi; + int retLength; + + if ((status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemBasicInformation, + out sbi, + Marshal.SizeOf(typeof(SystemBasicInformation)), + out retLength + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + return sbi; + } + + /// + /// Enumerates the handles opened by every running process. + /// + /// An array containing information about the handles. + public static SystemHandleEntry[] GetHandles() + { + int retLength = 0; + int handleCount = 0; + SystemHandleEntry[] returnHandles; + + if (_handlesBuffer == null) + _handlesBuffer = new MemoryAlloc(0x1000); + + MemoryAlloc data = _handlesBuffer; + + NtStatus status; + + // This is needed because NtQuerySystemInformation with SystemHandleInformation doesn't + // actually give a real return length when called with an insufficient buffer. This code + // tries repeatedly to call the function, doubling the buffer size each time it fails. + while ((status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemHandleInformation, + data, + data.Size, + out retLength) + ) == NtStatus.InfoLengthMismatch) + { + data.Resize(data.Size * 2); + + // Fail if we've resized it to over 16MB - protect from infinite resizing + if (data.Size > 16 * 1024 * 1024) + throw new OutOfMemoryException(); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + // The structure of the buffer is the handle count plus an array of SYSTEM_HANDLE_INFORMATION + // structures. + handleCount = data.ReadStruct().NumberOfHandles; + returnHandles = new SystemHandleEntry[handleCount]; + + for (int i = 0; i < handleCount; i++) + { + returnHandles[i] = data.ReadStruct(SystemHandleInformation.HandlesOffset, i); + } + + return returnHandles; + } + + /// + /// Gets the base address of the currently running kernel. + /// + /// The kernel's base address. + private static IntPtr GetKernelBase() + { + IntPtr kernelBase = IntPtr.Zero; + + Windows.EnumKernelModules((module) => + { + kernelBase = module.BaseAddress; + return false; + }); + + return kernelBase; + } + + /// + /// Gets the file name of the currently running kernel. + /// + /// The kernel file name. + private static string GetKernelFileName() + { + string kernelFileName = null; + + EnumKernelModules((module) => + { + kernelFileName = module.FileName; + return false; + }); + + return kernelFileName; + } + + /// + /// Gets the modules loaded by the kernel. + /// + /// A collection of module information structures. + public static KernelModule[] GetKernelModules() + { + List kernelModules = new List(); + + EnumKernelModules((kernelModule) => + { + kernelModules.Add(kernelModule); + return true; + }); + + return kernelModules.ToArray(); + } + + public static SystemLogonSession GetLogonSession(Luid logonId) + { + NtStatus status; + IntPtr logonSessionData; + + if ((status = Win32.LsaGetLogonSessionData( + ref logonId, + out logonSessionData + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + using (var logonSessionDataAlloc = new LsaMemoryAlloc(logonSessionData, true)) + { + var info = logonSessionDataAlloc.ReadStruct(); + + return new SystemLogonSession( + info.AuthenticationPackage.Read(), + info.DnsDomainName.Read(), + info.LogonDomain.Read(), + info.LogonId, + info.LogonServer.Read(), + DateTime.FromFileTime(info.LogonTime), + info.LogonType, + info.Session, + new Sid(info.Sid), + info.Upn.Read(), + info.UserName.Read() + ); + } + } + + public static Luid[] GetLogonSessions() + { + NtStatus status; + int logonSessionCount; + IntPtr logonSessionList; + + if ((status = Win32.LsaEnumerateLogonSessions( + out logonSessionCount, + out logonSessionList + )) >= NtStatus.Error) + Win32.ThrowLastError(status); + + Luid[] logonSessions = new Luid[logonSessionCount]; + + using (var logonSessionListAlloc = new LsaMemoryAlloc(logonSessionList, true)) + { + for (int i = 0; i < logonSessionCount; i++) + logonSessions[i] = logonSessionListAlloc.ReadStruct(i); + + return logonSessions; + } + } + + /// + /// Gets the network connections currently active. + /// + /// A dictionary of network connections. + public static Dictionary> GetNetworkConnections() + { + var retDict = new Dictionary>(); + int length; + + // TCP IPv4 + + length = 0; + Win32.GetExtendedTcpTable(IntPtr.Zero, ref length, false, AiFamily.INet, TcpTableClass.OwnerPidAll, 0); + + using (var mem = new MemoryAlloc(length)) + { + if (Win32.GetExtendedTcpTable(mem, ref length, false, AiFamily.INet, TcpTableClass.OwnerPidAll, 0) != 0) + Win32.ThrowLastError(); + + int count = mem.ReadInt32(0); + + for (int i = 0; i < count; i++) + { + var struc = mem.ReadStruct(sizeof(int), i); + + if (!retDict.ContainsKey(struc.OwningProcessId)) + retDict.Add(struc.OwningProcessId, new List()); + + retDict[struc.OwningProcessId].Add(new NetworkConnection() + { + Protocol = NetworkProtocol.Tcp, + Local = new IPEndPoint(struc.LocalAddress, ((ushort)struc.LocalPort).Reverse()), + Remote = new IPEndPoint(struc.RemoteAddress, ((ushort)struc.RemotePort).Reverse()), + State = struc.State, + Pid = struc.OwningProcessId + }); + } + } + + // UDP IPv4 + + length = 0; + Win32.GetExtendedUdpTable(IntPtr.Zero, ref length, false, AiFamily.INet, UdpTableClass.OwnerPid, 0); + + using (var mem = new MemoryAlloc(length)) + { + if (Win32.GetExtendedUdpTable(mem, ref length, false, AiFamily.INet, UdpTableClass.OwnerPid, 0) != 0) + Win32.ThrowLastError(); + + int count = mem.ReadInt32(0); + + for (int i = 0; i < count; i++) + { + var struc = mem.ReadStruct(sizeof(int), i); + + if (!retDict.ContainsKey(struc.OwningProcessId)) + retDict.Add(struc.OwningProcessId, new List()); + + retDict[struc.OwningProcessId].Add( + new NetworkConnection() + { + Protocol = NetworkProtocol.Udp, + Local = new IPEndPoint(struc.LocalAddress, ((ushort)struc.LocalPort).Reverse()), + Pid = struc.OwningProcessId + }); + } + } + + // TCP IPv6 + + length = 0; + Win32.GetExtendedTcpTable(IntPtr.Zero, ref length, false, AiFamily.INet6, TcpTableClass.OwnerPidAll, 0); + + using (var mem = new MemoryAlloc(length)) + { + if (Win32.GetExtendedTcpTable(mem, ref length, false, AiFamily.INet6, TcpTableClass.OwnerPidAll, 0) == 0) + { + int count = mem.ReadInt32(0); + + for (int i = 0; i < count; i++) + { + var struc = mem.ReadStruct(sizeof(int), i); + + if (!retDict.ContainsKey(struc.OwningProcessId)) + retDict.Add(struc.OwningProcessId, new List()); + + retDict[struc.OwningProcessId].Add(new NetworkConnection() + { + Protocol = NetworkProtocol.Tcp6, + Local = new IPEndPoint(new IPAddress(struc.LocalAddress, struc.LocalScopeId), ((ushort)struc.LocalPort).Reverse()), + Remote = new IPEndPoint(new IPAddress(struc.RemoteAddress, struc.RemoteScopeId), ((ushort)struc.RemotePort).Reverse()), + State = struc.State, + Pid = struc.OwningProcessId + }); + } + } + } + + // UDP IPv6 + + length = 0; + Win32.GetExtendedUdpTable(IntPtr.Zero, ref length, false, AiFamily.INet6, UdpTableClass.OwnerPid, 0); + + using (var mem = new MemoryAlloc(length)) + { + if (Win32.GetExtendedUdpTable(mem, ref length, false, AiFamily.INet6, UdpTableClass.OwnerPid, 0) == 0) + { + int count = mem.ReadInt32(0); + + for (int i = 0; i < count; i++) + { + var struc = mem.ReadStruct(sizeof(int), i); + + if (!retDict.ContainsKey(struc.OwningProcessId)) + retDict.Add(struc.OwningProcessId, new List()); + + retDict[struc.OwningProcessId].Add( + new NetworkConnection() + { + Protocol = NetworkProtocol.Udp6, + Local = new IPEndPoint(new IPAddress(struc.LocalAddress, struc.LocalScopeId), ((ushort)struc.LocalPort).Reverse()), + Pid = struc.OwningProcessId + }); + } + } + } + + return retDict; + } + + /// + /// Gets the page files currently active. + /// + /// A collection of page file information structures. + public static SystemPagefile[] GetPagefiles() + { + int retLength; + List pagefiles = new List(); + + using (MemoryAlloc data = new MemoryAlloc(0x200)) + { + NtStatus status; + + while ((status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemPageFileInformation, + data, + data.Size, + out retLength) + ) == NtStatus.InfoLengthMismatch) + { + data.Resize(data.Size * 2); + + // Fail if we've resized it to over 16MB - protect from infinite resizing + if (data.Size > 16 * 1024 * 1024) + throw new OutOfMemoryException(); + } + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + pagefiles = new List(2); + + int i = 0; + SystemPagefileInformation currentPagefile; + + do + { + currentPagefile = data.ReadStruct(i, 0); + + pagefiles.Add(new SystemPagefile( + currentPagefile.TotalSize, + currentPagefile.TotalInUse, + currentPagefile.PeakUsage, + FileUtils.GetFileName(currentPagefile.PageFileName.Read()) + )); + + i += currentPagefile.NextEntryOffset; + } while (currentPagefile.NextEntryOffset != 0); + + return pagefiles.ToArray(); + } + } + + /// + /// Gets a dictionary containing the currently running processes. + /// + /// A dictionary, indexed by process ID. + public static Dictionary GetProcesses() + { + return GetProcesses(false); + } + + /// + /// Gets a dictionary containing the currently running processes. + /// + /// Whether to get thread information. + /// A dictionary, indexed by process ID. + public static Dictionary GetProcesses(bool getThreads) + { + int retLength; + Dictionary returnProcesses; + + if (_processesBuffer == null) + _processesBuffer = new MemoryAlloc(0x10000); + + MemoryAlloc data = _processesBuffer; + + NtStatus status; + int attempts = 0; + + while (true) + { + attempts++; + + if ((status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemProcessInformation, + data, + data.Size, + out retLength + )) >= NtStatus.Error) + { + if (attempts > 3) + Win32.ThrowLastError(status); + + data.Resize(retLength); + } + else + { + break; + } + } + + returnProcesses = new Dictionary(32); // 32 processes on a computer? + + int i = 0; + SystemProcess currentProcess = new SystemProcess(); + + do + { + currentProcess.Process = data.ReadStruct(i, 0); + currentProcess.Name = currentProcess.Process.ImageName.Read(); + + if (getThreads && + currentProcess.Process.ProcessId != 0) + { + currentProcess.Threads = new Dictionary(); + + for (int j = 0; j < currentProcess.Process.NumberOfThreads; j++) + { + var thread = data.ReadStruct(i + + Marshal.SizeOf(typeof(SystemProcessInformation)), j); + + currentProcess.Threads.Add(thread.ClientId.ThreadId, thread); + } + } + + returnProcesses.Add(currentProcess.Process.ProcessId, currentProcess); + + i += currentProcess.Process.NextEntryOffset; + } while (currentProcess.Process.NextEntryOffset != 0); + + return returnProcesses; + } + + /// + /// Gets a dictionary containing the threads owned by the specified process. + /// + /// A process ID. + /// A dictionary, indexed by thread ID. + public static Dictionary GetProcessThreads(int pid) + { + int retLength; + + if (_processesBuffer == null) + _processesBuffer = new MemoryAlloc(0x10000); + + MemoryAlloc data = _processesBuffer; + + NtStatus status; + int attempts = 0; + + while (true) + { + attempts++; + + if ((status = Win32.NtQuerySystemInformation(SystemInformationClass.SystemProcessInformation, data.Memory, + data.Size, out retLength)) >= NtStatus.Error) + { + if (attempts > 3) + Win32.ThrowLastError(status); + + data.Resize(retLength); + } + else + { + break; + } + } + + int i = 0; + SystemProcessInformation process; + + do + { + process = data.ReadStruct(i, 0); + + if (process.ProcessId == pid) + { + var threads = new Dictionary(); + + for (int j = 0; j < process.NumberOfThreads; j++) + { + var thread = data.ReadStruct(i + + Marshal.SizeOf(typeof(SystemProcessInformation)), j); + + threads.Add(thread.ClientId.ThreadId, thread); + } + + return threads; + } + + i += process.NextEntryOffset; + + } while (process.NextEntryOffset != 0); + + return null; + } + + /// + /// Gets a dictionary containing the services on the system. + /// + /// A dictionary, indexed by service name. + public static Dictionary GetServices() + { + using (ServiceManagerHandle manager = + new ServiceManagerHandle(ScManagerAccess.EnumerateService)) + { + int requiredSize; + int servicesReturned; + int resume = 0; + + if (_servicesBuffer == null) + _servicesBuffer = new MemoryAlloc(0x10000); + + MemoryAlloc data = _servicesBuffer; + + if (!Win32.EnumServicesStatusEx(manager, IntPtr.Zero, ServiceQueryType.Win32 | ServiceQueryType.Driver, + ServiceQueryState.All, data, + data.Size, out requiredSize, out servicesReturned, + ref resume, null)) + { + // resize buffer + data.Resize(requiredSize); + + if (!Win32.EnumServicesStatusEx(manager, IntPtr.Zero, ServiceQueryType.Win32 | ServiceQueryType.Driver, + ServiceQueryState.All, data, + data.Size, out requiredSize, out servicesReturned, + ref resume, null)) + Win32.ThrowLastError(); + } + + var dictionary = new Dictionary(servicesReturned); + + for (int i = 0; i < servicesReturned; i++) + { + var service = data.ReadStruct(i); + + dictionary.Add(service.ServiceName, service); + } + + return dictionary; + } + } + + /// + /// Gets the 64-bit tick count. + /// + /// A 64-bit tick count. + public static long GetTickCount() + { + // Read the tick count multiplier. + int tickCountMultiplier = Marshal.ReadInt32(Win32.UserSharedData.Increment( + KUserSharedData.TickCountMultiplierOffset)); + + // Read the tick count. + var tickCount = QueryKSystemTime(Win32.UserSharedData.Increment( + KUserSharedData.TickCountOffset)); + + return (((long)tickCount.LowPart * tickCountMultiplier) >> (int)24) + + (((long)tickCount.HighPart * tickCountMultiplier) << (int)8); + } + + /// + /// Gets information about the system time. + /// + /// A time of day structure. + public static SystemTimeOfDayInformation GetTimeOfDay() + { + NtStatus status; + SystemTimeOfDayInformation timeOfDay; + int retLength; + + status = Win32.NtQuerySystemInformation( + SystemInformationClass.SystemTimeOfDayInformation, + out timeOfDay, + Marshal.SizeOf(typeof(SystemTimeOfDayInformation)), + out retLength + ); + + if (status >= NtStatus.Error) + Win32.ThrowLastError(status); + + return timeOfDay; + } + + /// + /// Gets the uptime of the system. + /// + /// A time span describing the time elapsed since the system was booted. + public static TimeSpan GetUptime() + { + var timeOfDay = GetTimeOfDay(); + + return new TimeSpan(timeOfDay.CurrentTime - timeOfDay.BootTime); + } + + /// + /// Loads a driver. + /// + /// The service name of the driver. + public static void LoadDriver(string serviceName) + { + var str = new UnicodeString( + "\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" + serviceName); + + try + { + NtStatus status; + + if ((status = Win32.NtLoadDriver(ref str)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + str.Dispose(); + } + } + + /// + /// Reads a KSYSTEM_TIME value atomically. + /// + /// A pointer to a KSYSTEM_TIME value. + /// A 64-bit time value. + private static LargeInteger QueryKSystemTime(IntPtr time) + { + unsafe + { + return QueryKSystemTime((KSystemTime*)time); + } + } + + /// + /// Reads a KSYSTEM_TIME value atomically. + /// + /// A pointer to a KSYSTEM_TIME value. + /// A 64-bit time value. + private unsafe static LargeInteger QueryKSystemTime(KSystemTime* time) + { + LargeInteger localTime = new LargeInteger(); + + // If we're on 32-bit, we need to use a special + // method to read the time atomically. On 64-bit, + // we can simply read the time. + + if (IntPtr.Size == 4) + { + localTime.QuadPart = 0; + + while (true) + { + localTime.HighPart = time->High1Time; + localTime.LowPart = time->LowPart; + + // Check if someone started changing the time + // while we were reading the two values. + if (localTime.HighPart == time->High2Time) + break; + + System.Threading.Thread.SpinWait(1); + } + } + else + { + localTime.QuadPart = time->QuadPart; + } + + return localTime; + } + + /// + /// Unloads a driver. + /// + /// The service name of the driver. + public static void UnloadDriver(string serviceName) + { + var str = new UnicodeString( + "\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\" + serviceName); + + try + { + NtStatus status; + + if ((status = Win32.NtUnloadDriver(ref str)) >= NtStatus.Error) + Win32.ThrowLastError(status); + } + finally + { + str.Dispose(); + } + } + } + + public enum NetworkProtocol + { + Tcp, + Udp, + Tcp6, + Udp6 + } + + public struct ObjectInformation + { + public string OrigName; + public string BestName; + public string TypeName; + } + + public struct NetworkConnection + { + public int Pid; + public NetworkProtocol Protocol; + public IPEndPoint Local; + public IPEndPoint Remote; + public MibTcpState State; + public object Tag; + + public void CloseTcpConnection() + { + MibTcpRow row = new MibTcpRow() + { + State = MibTcpState.DeleteTcb, + LocalAddress = (uint)this.Local.Address.Address, + LocalPort = ((ushort)this.Local.Port).Reverse(), + RemoteAddress = this.Remote != null ? (uint)this.Remote.Address.Address : 0, + RemotePort = this.Remote != null ? ((ushort)this.Remote.Port).Reverse() : 0 + }; + int result = Win32.SetTcpEntry(ref row); + + if (result != 0) + Win32.ThrowLastError(result); + } + } + + public struct SystemProcess + { + public string Name; + public SystemProcessInformation Process; + public Dictionary Threads; + } + + public class KernelModule : ILoadedModule + { + public KernelModule( + IntPtr baseAddress, + int size, + LdrpDataTableEntryFlags flags, + string baseName, + string fileName + ) + { + this.BaseAddress = baseAddress; + this.Size = size; + this.Flags = flags; + this.BaseName = baseName; + this.FileName = fileName; + } + + /// + /// The base address of the module. + /// + public IntPtr BaseAddress { get; private set; } + /// + /// The size of the module. + /// + public int Size { get; private set; } + /// + /// The flags set by the loader for this module. + /// + public LdrpDataTableEntryFlags Flags { get; private set; } + /// + /// The base name of the module (e.g. module.dll). + /// + public string BaseName { get; private set; } + /// + /// The file name of the module (e.g. C:\Windows\system32\module.dll). + /// + public string FileName { get; private set; } + } + + public class SystemLogonSession + { + public SystemLogonSession( + string authenticationPackage, + string dnsDomainName, + string logonDomain, + Luid logonId, + string logonServer, + DateTime logonTime, + LogonType logonType, + int session, + Sid sid, + string upn, + string userName + ) + { + this.AuthenticationPackage = authenticationPackage; + this.DnsDomainName = dnsDomainName; + this.LogonDomain = logonDomain; + this.LogonId = logonId; + this.LogonServer = logonServer; + this.LogonTime = logonTime; + this.LogonType = logonType; + this.Session = session; + this.Sid = sid; + this.Upn = upn; + this.UserName = userName; + } + + public string AuthenticationPackage { get; private set; } + public string DnsDomainName { get; private set; } + public string LogonDomain { get; private set; } + public Luid LogonId { get; private set; } + public string LogonServer { get; private set; } + public DateTime LogonTime { get; private set; } + public LogonType LogonType { get; private set; } + public int Session { get; private set; } + public Sid Sid { get; private set; } + public string Upn { get; private set; } + public string UserName { get; private set; } + } + + public class SystemPagefile + { + public SystemPagefile(int totalSize, int totalInUse, int peakUsage, string fileName) + { + this.TotalSize = totalSize; + this.TotalInUse = TotalInUse; + this.PeakUsage = peakUsage; + this.FileName = fileName; + } + + public int TotalSize { get; private set; } + public int TotalInUse { get; private set; } + public int PeakUsage { get; private set; } + public string FileName { get; private set; } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/WindowsException.cs b/branches/ph-plugins/ProcessHacker.Native/WindowsException.cs new file mode 100644 index 000000000..74dd7cfcd --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/WindowsException.cs @@ -0,0 +1,131 @@ +/* + * Process Hacker - + * windows exception + * + * Copyright (C) 2008-2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using ProcessHacker.Native.Api; + +namespace ProcessHacker.Native +{ + /// + /// Represents a Win32 or Native exception. + /// + /// + /// Unlike the System.ComponentModel.Win32Exception class, + /// this class does not get the error's associated + /// message unless it is requested. + /// + public class WindowsException : Exception + { + private bool _isNtStatus = false; + private Win32Error _errorCode = 0; + private NtStatus _status; + private string _message = null; + + /// + /// Creates an exception with no error. + /// + public WindowsException() + { } + + /// + /// Creates an exception from a Win32 error code. + /// + /// The Win32 error code. + public WindowsException(Win32Error errorCode) + { + _errorCode = errorCode; + } + + /// + /// Creates an exception from a NT status value. + /// + /// The NT status value. + public WindowsException(NtStatus status) + { + _status = status; + _errorCode = status.ToDosError(); + _isNtStatus = true; + } + + /// + /// Gets whether the NT status value is valid. + /// + public bool IsNtStatus + { + get { return _isNtStatus; } + } + + /// + /// Gets a Win32 error code which represents the exception. + /// + public Win32Error ErrorCode + { + get { return _errorCode; } + } + + /// + /// Gets a NT status value which represents the exception. + /// + public NtStatus Status + { + get { return _status; } + } + + /// + /// Gets a message describing the exception. + /// + public override string Message + { + get + { + // No locking, for performance reasons. Getting the + // message doesn't have any side-effects anyway. + if (_message == null) + { + // We prefer native status messages because they are usually + // more detailed. However, for some status values we do + // prefer the shorter Win32 error message. + + if ( + _isNtStatus && + _status != NtStatus.AccessDenied && + _status != NtStatus.AccessViolation + ) + { + string message = _status.GetMessage(); + + if (message == null) + message = "Could not retrieve the error message (0x" + ((int)_status).ToString("x") + ")."; + + _message = message; + } + else + { + _message = _errorCode.GetMessage(); + } + } + + return _message; + } + } + } +} diff --git a/branches/ph-plugins/ProcessHacker.Native/app.config b/branches/ph-plugins/ProcessHacker.Native/app.config new file mode 100644 index 000000000..b7db28170 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.Native/app.config @@ -0,0 +1,3 @@ + + + diff --git a/branches/ph-plugins/ProcessHacker.sln b/branches/ph-plugins/ProcessHacker.sln new file mode 100644 index 000000000..c93bbacd6 --- /dev/null +++ b/branches/ph-plugins/ProcessHacker.sln @@ -0,0 +1,47 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker", "ProcessHacker\ProcessHacker.csproj", "{EEEA1778-1702-4964-8793-A98FE37E4D2B}" + ProjectSection(ProjectDependencies) = postProject + {0710ADEF-F89E-4CBC-8150-B340460BC9D6} = {0710ADEF-F89E-4CBC-8150-B340460BC9D6} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assistant", "Assistant\Assistant.csproj", "{0710ADEF-F89E-4CBC-8150-B340460BC9D6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Common", "ProcessHacker.Common\ProcessHacker.Common.csproj", "{8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Native", "ProcessHacker.Native\ProcessHacker.Native.csproj", "{8A448157-E1A7-4DDF-954E-287F1117832B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aga.Controls", "TreeViewAdv\Aga.Controls.csproj", "{E73BB233-D88B-44A7-A98F-D71EE158381D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEEA1778-1702-4964-8793-A98FE37E4D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEEA1778-1702-4964-8793-A98FE37E4D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEEA1778-1702-4964-8793-A98FE37E4D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEEA1778-1702-4964-8793-A98FE37E4D2B}.Release|Any CPU.Build.0 = Release|Any CPU + {0710ADEF-F89E-4CBC-8150-B340460BC9D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0710ADEF-F89E-4CBC-8150-B340460BC9D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0710ADEF-F89E-4CBC-8150-B340460BC9D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0710ADEF-F89E-4CBC-8150-B340460BC9D6}.Release|Any CPU.Build.0 = Release|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.Build.0 = Release|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.Build.0 = Release|Any CPU + {E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/branches/ph-plugins/ProcessHacker/Build/7za/7za.exe b/branches/ph-plugins/ProcessHacker/Build/7za/7za.exe new file mode 100644 index 0000000000000000000000000000000000000000..12b9499a28960378cd26a7b7f0733681c49a5d71 GIT binary patch literal 536064 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�cMLak1v=h`Ytqds~iE6Xn0Xu7% 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